@neodx/log
Version:
A lightweight universal logging framework
178 lines (170 loc) • 6.44 kB
JavaScript
;
/* eslint-disable @typescript-eslint/no-explicit-any */ const toArray = value =>
Array.isArray(value) ? value : [value];
const toInt = value => Number.parseInt(value, 10);
const is = target => value => target === value;
const identity = value => value;
const isTruthy = Boolean;
const True = () => true;
const setMapValue = (map, key, value) => {
map.set(key, value);
return value;
};
const getOrCreateMapValue = (map, key, create) =>
map.has(key) ? map.get(key) : setMapValue(map, key, create());
//#region Object
const values = Object.values;
const fromEntries = Object.fromEntries;
const entries = Object.entries;
const hasOwn = Object.hasOwn;
const keys = Object.keys;
//#endregion
const toString = Object.prototype.toString;
const getPrototypeOf = Object.getPrototypeOf;
const objectString = '[object Object]';
const isEmpty = target => target.length === 0;
const isError = target => target instanceof Error;
const isEmptyObject = target => isEmpty(keys(target));
const createTypeof = type => value => typeof value === type;
const isTypeOfString = createTypeof('string');
const isTypeOfFunction = createTypeof('function');
const isPrimitive = value =>
value === null || (typeof value !== 'function' && typeof value !== 'object');
const isObject = target => {
if (isNil(target) || !isObjectLike(target) || toString.call(target) !== objectString) {
return false;
}
const proto = getPrototypeOf(target);
return proto === null || proto === getLastPrototypeOf(target);
};
const isNil = target => target == null;
const isObjectLike = target => typeof target === 'object' && target !== null;
const getLastPrototypeOf = target => {
const proto = getPrototypeOf(target);
return proto === null ? target : getLastPrototypeOf(proto);
};
function mapValues(target, fn) {
return mapEntries(target, ([key, value]) => [key, fn(value, key)]);
}
function mapEntries(target, fn) {
return fromEntries(entries(target).map(fn));
}
const createCase =
pattern =>
(input, delimiters = DEFAULT_DELIMITERS) =>
toCase(input, pattern, delimiters);
/**
* Converts a string to a specified case pattern.
* @param input The string to convert.
* @param pattern The pattern to convert to.
* @param delimiters The delimiters to use when splitting the string.
* @example toCase('foo-bar baz', 'CaSe') // PascalCase 'FooBarBaz'
* @example toCase('foo_bar baz', 'caSe') // camelCase 'fooBarBaz'
* @example toCase('foo_bar baz', 'CASE') // upper case 'FOOBARBAZ'
* @example toCase('foo_bar baz', 'case') // lower case 'foobarbaz'
* @example toCase('foo_bar baz', 'CA_SE') // screaming snake case 'FOO_BAR_BAZ'
* @example toCase('foo_bar baz', 'ca-se') // kebab case 'foo-bar-baz'
* @example toCase('foo_bar baz', 'ca_se') // snake case 'foo_bar_baz'
* @example toCase('foo_bar baz', 'Case') // capitalised 'Foobarbaz'
*/ function toCase(input, pattern, delimiters = DEFAULT_DELIMITERS) {
const end = pattern.slice(Math.max(0, pattern.length - 2));
const sep = pattern.slice(2, -2);
const start = pattern.slice(0, 2);
return matches(input, delimiters)
.map((match, index) => applyPattern(match, index === 0 ? start : end))
.join(sep);
}
/**
* Presets for common cases.
*/ mapValues(
{
camel: 'caSe',
kebab: 'ca-se',
snake: 'ca_se',
upper: 'CASE',
lower: 'case',
pascal: 'CaSe',
capital: 'Case',
screamingSnake: 'CA_SE'
},
createCase
);
const applyPattern = (str, pattern) => apply(pattern[0], str[0]) + apply(pattern[1], str.slice(1));
function matches(str, delimiters) {
const regex = new RegExp('([A-Z]?)([^' + delimiters + ']*)', 'g');
return str.match(regex)?.filter(Boolean) ?? [];
}
function apply(letter, str) {
if (letter === '-') return '';
if (letter === '*') return str;
const isUpperCase = letter === letter.toUpperCase();
return isUpperCase ? str.toUpperCase() : str.toLowerCase();
}
const DEFAULT_DELIMITERS = 'A-Z\\s_-';
const createLoggerAutoFactory = factory => (log, defaultParams) => {
const params = isTypeOfString(log)
? {
level: log
}
: log;
return 'level' in params
? factory({
...defaultParams,
...params
})
: params;
};
/**
* Reads arguments array and extract fields, error and message arguments.
* @return [messageFragments, fields, error]
*
* Strings
* @example readArguments('hello') -> [ ['hello'], {} ]
* @example readArguments('hello %s', 'world') -> [ ['hello %s', 'world'], {} ]
* @example readArguments('hello %s %d %j', 'world', 1, { id: 2 }) -> [ ['hello %s %d %j', 'world', 1, { id: 2 }], {} ]
*
* Additional fields
* @example readArguments({ id: 2 }) -> [ [], { id: 2 } ]
* @example readArguments({ id: 2 }, 'hello') -> [ ['hello'], { id: 2 } ]
* @example readArguments({ id: 2 }, 'hello %s', 'world') -> [ ['hello %s', 'world'], { id: 2 } ]
*
* Errors
* @example readArguments(myError) -> [ ['my error'], {}, myError ]
* @example readArguments({ err: myError }) -> [ ['my error'], {}, myError ]
* @example readArguments({ err: myError, id: 2 }) -> [ ['my error'], { id: 2 }, myError ]
* @example readArguments({ err: myError, id: 2 }, 'hello') -> [ ['hello'], { id: 2 }, myError ]
* @example readArguments({ err: myError, id: 2 }, 'hello %s', 'world') -> [ ['hello %s', 'world'], { id: 2 }, myError ]
*/ function readArguments(args) {
const [firstArg, ...otherArgs] = args;
if (isError(firstArg)) {
return [isEmpty(otherArgs) ? [firstArg.message] : otherArgs, {}, firstArg];
}
if (isObjectLike(firstArg)) {
if ('err' in firstArg && isError(firstArg.err)) {
const { err, ...fields } = firstArg;
return [isEmpty(otherArgs) ? [err.message] : otherArgs, fields, err];
}
return [otherArgs, firstArg];
}
return [args, {}];
}
exports.True = True;
exports.createLoggerAutoFactory = createLoggerAutoFactory;
exports.entries = entries;
exports.getOrCreateMapValue = getOrCreateMapValue;
exports.hasOwn = hasOwn;
exports.identity = identity;
exports.is = is;
exports.isEmpty = isEmpty;
exports.isEmptyObject = isEmptyObject;
exports.isObject = isObject;
exports.isPrimitive = isPrimitive;
exports.isTruthy = isTruthy;
exports.isTypeOfFunction = isTypeOfFunction;
exports.isTypeOfString = isTypeOfString;
exports.keys = keys;
exports.readArguments = readArguments;
exports.toArray = toArray;
exports.toInt = toInt;
exports.values = values;
//# sourceMappingURL=read-arguments-BBlq0hOP.cjs.map