SockDrawer/SockBot

View on GitHub
lib/utils.js

Summary

Maintainability
A
1 hr
Test Coverage
/**
* Core Utilities for Sockbot
* @module utils
* @license MIT
*/
'use strict';
 
const sanitizeHtml = require('sanitize-html');
const storage = new WeakMap();
 
const debug = require('debug')('sockbot:utils');
 
/**
* Write an extended log entry
*
* @param {number} level Log Level
* @param {string} message Log Message
* @param {*} [data] Optional extended log data
*/
exports.logExtended = function logExtended(level, message, data) {
const stamp = new Date().toISOString();
const extra = data !== undefined ? ` : ${JSON.stringify(data)}` : '';
debug(`Log ${level} : ${stamp} : ${message} ${extra}`);
};
 
/**
* Clone object
*
* @param {*} original Data to clone
* @returns {*} Cloned `original` data
*/
exports.cloneData = function cloneData(original) {
if (original === undefined) {
return undefined;
}
return JSON.parse(JSON.stringify(original));
};
 
/**
* Recursively merge objects
*
* @param {object} base Base object to merge `mixin` into
* @param {object} mixin Mixin object to merge into `base`
* @param {boolean} [mergeArrays] Merge arrays instead of concatenating them
*/
function mergeInner(base, mixin, mergeArrays) {
if (base === null || typeof base !== 'object' || Array.isArray(base)) {
throw new Error('base must be object');
}
if (mixin === null || typeof mixin !== 'object' || Array.isArray(mixin)) {
throw new Error('mixin must be object');
}
Object.keys(mixin).forEach((name) => {
mergeHelper(base, mixin, name, mergeArrays);
});
}
 
/**
* Merge helper - FOR INTERNAL USE ONLY
*
* @param {object} base Base object to merge `mixin` into
* @param {object} mixin Mixin object to merge into `base`
* @param {string} name Name of property to merge
* @param {boolean} [mergeArrays] Merge arrays instead of concatenating them
*/
Function `mergeHelper` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.
function mergeHelper(base, mixin, name, mergeArrays) {
if (Array.isArray(mixin[name])) {
if (!mergeArrays && base[name] && Array.isArray(base[name])) {
base[name] = base[name].concat(mixin[name]);
} else {
base[name] = mixin[name];
}
} else if (typeof mixin[name] === 'object' && mixin[name] !== null) {
let newBase = base[name] || {};
if (Array.isArray(newBase)) {
newBase = {};
}
mergeInner(newBase, mixin[name], mergeArrays);
base[name] = newBase;
} else {
base[name] = mixin[name];
}
}
 
/**
* Merge multiple objects into one object
*
* Later objects override earlier objects
*
* @param {boolean} [mergeArrays] Merge arrays instead of concatenating them
* @param {...object} mixin Objects to merge
* @returns {object} object constructed by merging `mixin`s from left to right
*/
exports.mergeObjects = function mergeObjects(mergeArrays, mixin) { //eslint-disable-line no-unused-vars
const args = Array.prototype.slice.apply(arguments),
res = {};
let mergeContents = false;
if (typeof args[0] === 'boolean') {
mergeContents = args.shift();
}
let obj = args.shift();
while (obj) {
mergeInner(res, exports.cloneData(obj), mergeContents);
obj = args.shift();
}
return res;
};
 
/**
* Get value from WeakMap store
*
* @param {*} obj Object key for weakmap store
* @param {string} [key] Object key to retrieve from stored value
* @returns {*} Stored value
*/
exports.mapGet = function mapGet(obj, key) {
const val = storage.get(obj) || {};
return key ? val[key] : val;
};
 
/**
* Store values in weakmap store
*
* If `value` is omitted `key` is stored instead
*
* @param {*} obj Object key for weakmap store
* @param {string|*} key Key to store value under or value object to store
* @param {*} [value] Value to store for `key`
*/
exports.mapSet = function mapSet(obj, key, value) {
let values = key;
if (value) {
values = storage.get(obj) || {};
values[key] = value;
}
storage.set(obj, values);
};
 
/**
* Parse JSON data to object
*
* @param {string|object} json stringified object to parse
* @returns {object} Parsed object
*/
Function `parseJSON` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.
exports.parseJSON = function parseJSON(json) {
if (!json) {
throw new Error('[[invalid-argument:required]]');
}
if (typeof json === 'string') {
try {
json = JSON.parse(json);
} catch (err) {
throw new Error(`[[invalid-json:${err.message}]]`);
}
}
const type = typeof json;
if (json === null) {
throw new Error('[[invalid-argument:expected object but received null]]');
}
if (type !== 'object') {
throw new Error(`[[invalid-argument:expected object but received ${type}]]`);
}
return json;
};
 
exports.iterate = function iterate(arr, each) {
return new Promise((resolve, reject) => {
const next = () => {
if (!arr || !arr.length) {
return resolve();
}
const item = arr.shift();
return each(item)
.then(() => next())
.catch(reject);
};
next();
});
};
 
exports.htmlToRaw = function htmlToRaw(markup) {
markup = sanitizeHtml(markup, {
allowedTags: ['code', 'blockquote'],
exclusiveFilter: (frame) => frame.tag === 'code' || frame.tag === 'blockquote'
});
return markup;
};
 
/* istanbul ignore else */
if (typeof global.describe === 'function') {
//test is running
exports.mergeInner = mergeInner;
exports.storage = storage;
}