src/util/util.js
const has = (o, k) => Object.prototype.hasOwnProperty.call(o, k);
const isObject = d => typeof d === 'object' && d !== 'null';
const CustomError = require('./error');
const { DataTypes } = require('./constants');
/**
* Contains various utility methods
* @private
*/
class Util {
constructor() {
throw new CustomError(`The class ${this.constructor.name} may not be initialized`, 'STATIC_CLASS');
}
/**
* Sets default properties on an object that aren't already specified
* @param {Object} def Default properties
* @param {Object} given Object to assign defaults to
* @returns {Object}
* @private
*/
static mergeDefault(def, given) {
if (!given) return def;
for (const key in def) {
if (!has(given, key) || given[key] === undefined) {
given[key] = def[key];
} else if (given[key] === Object(given[key])) {
given[key] = Util.mergeDefault(def[key], given[key]);
}
}
return given;
}
/**
* Checks a table name against basic SQL naming rules
* @param {string} name The name of the table
* @returns {boolean}
* @private
*/
static checkTableName(name) {
if (name.length > 18) throw new CustomError(`The name ${name} is invalid: Above 18 characters`, 'INVALID_TABLE_NAME');
if (/^[0-9]/.test(name)) throw new CustomError(`The name ${name} is invalid: Starts with a number`, 'INVALID_TABLE_NAME');
if (!/^[a-zA-Z0-9_]+$/.test(name)) throw new CustomError(`The name ${name} is invalid: Contains invalid character ${name.replace(/[a-zA-Z0-9_]+/, '').slice(0, 1)}`, 'INVALID_TABLE_NAME');
}
/**
* Flatten an object. Any properties that are Maps will get converted to an array of keys
* @param {Object} obj The object to flatten
* @param {...Object<string, boolean|string>} [props] Specific properties to include/exclude
* @returns {Object}
* @private
*/
static flatten(obj, ...props) {
if (!isObject(obj)) return obj;
props = Object.assign(...Object.keys(obj).filter(k => !k.startsWith('_')).map(k => ({ [k]: true })), ...props);
const out = {};
// eslint-disable-next-line prefer-const
for (let [prop, newProp] of Object.entries(props)) {
if (!newProp) continue;
newProp = newProp === true ? prop : newProp;
const element = obj[prop];
const elemIsObj = isObject(element);
const valueOf = elemIsObj && typeof element.valueOf === 'function' ? element.valueOf() : null;
if (element instanceof Map) out[newProp] = this.mapMap(element, e => Util.flatten(e));
else if (Array.isArray(element)) out[newProp] = element.map(e => Util.flatten(e));
else if (typeof valueOf !== 'object') out[newProp] = valueOf;
else if (!elemIsObj) out[newProp] = element;
}
return out;
}
/**
* Does the same as [Array.map()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map)
* @param {Map} map Map to map
* @param {Function} fn Function that produces an element of the new array, taking three arguments
* @param {*} [thisArg] Value to use as `this` when executing function
* @returns {Array}
* @private
*/
static mapMap(map, fn, thisArg) {
if (typeof thisArg !== 'undefined') fn = fn.bind(thisArg);
const arr = new Array(map.size);
let i = 0;
for (const [key, val] of map) arr[i++] = fn(val, key, map);
return arr;
}
/**
* Parses an SQL string into an array of {@link KeyOptions}
* @param {string} string String to parse
* @param {string} tableName The name of the table (used internally)
* @returns {KeyOptions[]}
* @private
*/
static _parseSql(string, tableName) {
string = string.replace(new RegExp(`${tableName}`, 'g'), '');
console.log(string);
const strArr = string.split(' ');
strArr.splice(0, 2);
console.log(strArr);
}
}
module.exports = Util;