UNPKG

@lorefnon/tslog

Version:

Extensible TypeScript Logger for Node.js and Browser.

451 lines (450 loc) 14.6 kB
import { prettyLogStyles } from "../../prettyLogStyles.js"; import { jsonStringifyRecursive } from "./helper.jsonStringifyRecursive.js"; export function inspect(obj, opts) { const ctx = { seen: [], stylize: stylizeNoColor, }; if (opts != null) { _extend(ctx, opts); } if (isUndefined(ctx.showHidden)) ctx.showHidden = false; if (isUndefined(ctx.depth)) ctx.depth = 2; if (isUndefined(ctx.colors)) ctx.colors = true; if (isUndefined(ctx.customInspect)) ctx.customInspect = true; if (ctx.colors) ctx.stylize = stylizeWithColor; return formatValue(ctx, obj, ctx.depth); } inspect.colors = prettyLogStyles; inspect.styles = { special: "cyan", number: "yellow", boolean: "yellow", undefined: "grey", null: "bold", string: "green", date: "magenta", regexp: "red", }; function isBoolean(arg) { return typeof arg === "boolean"; } function isUndefined(arg) { return arg == null; } function stylizeNoColor(str) { return str; } function stylizeWithColor(str, styleType) { const style = inspect.styles[styleType]; if (style != null && inspect?.colors?.[style]?.[0] != null && inspect?.colors?.[style]?.[1] != null) { return "\u001b[" + inspect.colors[style][0] + "m" + str + "\u001b[" + inspect.colors[style][1] + "m"; } else { return str; } } function isFunction(arg) { return typeof arg === "function"; } function isString(arg) { return typeof arg === "string"; } function isNumber(arg) { return typeof arg === "number"; } function isNull(arg) { return arg === null; } function hasOwn(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); } function isRegExp(re) { return isObject(re) && objectToString(re) === "[object RegExp]"; } function isObject(arg) { return typeof arg === "object" && arg !== null; } function isError(e) { return isObject(e) && (objectToString(e) === "[object Error]" || e instanceof Error); } function isDate(d) { return isObject(d) && objectToString(d) === "[object Date]"; } function objectToString(o) { return Object.prototype.toString.call(o); } function arrayToHash(array) { const hash = {}; array.forEach((val) => { hash[val] = true; }); return hash; } function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { const output = []; for (let i = 0, l = value.length; i < l; ++i) { if (hasOwn(value, String(i))) { output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, String(i), true)); } else { output.push(""); } } keys.forEach((key) => { if (!key.match(/^\d+$/)) { output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, key, true)); } }); return output; } function formatError(value) { return "[" + Error.prototype.toString.call(value) + "]"; } export function formatValue(ctx, value, recurseTimes = 0) { if (ctx.customInspect && value != null && isFunction(value) && value?.inspect !== inspect && !(value?.constructor && value?.constructor.prototype === value)) { if (typeof value.inspect !== "function" && value.toString != null) { return value.toString(); } let ret = value?.inspect(recurseTimes, ctx); if (!isString(ret)) { ret = formatValue(ctx, ret, recurseTimes); } return ret; } const primitive = formatPrimitive(ctx, value); if (primitive) { return primitive; } let keys = Object.keys(value); const visibleKeys = arrayToHash(keys); try { if (ctx.showHidden && Object.getOwnPropertyNames) { keys = Object.getOwnPropertyNames(value); } } catch (e) { } if (isError(value) && (keys.indexOf("message") >= 0 || keys.indexOf("description") >= 0)) { return formatError(value); } if (keys.length === 0) { if (isFunction(ctx.stylize)) { if (isFunction(value)) { const name = value.name ? ": " + value.name : ""; return ctx.stylize("[Function" + name + "]", "special"); } if (isRegExp(value)) { return ctx.stylize(RegExp.prototype.toString.call(value), "regexp"); } if (isDate(value)) { return ctx.stylize(Date.prototype.toISOString.call(value), "date"); } if (isError(value)) { return formatError(value); } } else { return value; } } let base = ""; let array = false; let braces = ["{\n", "\n}"]; if (Array.isArray(value)) { array = true; braces = ["[\n", "\n]"]; } if (isFunction(value)) { const n = value.name ? ": " + value.name : ""; base = " [Function" + n + "]"; } if (isRegExp(value)) { base = " " + RegExp.prototype.toString.call(value); } if (isDate(value)) { base = " " + Date.prototype.toUTCString.call(value); } if (isError(value)) { base = " " + formatError(value); } if (keys.length === 0 && (!array || value.length == 0)) { return braces[0] + base + braces[1]; } if (recurseTimes < 0) { if (isRegExp(value)) { return ctx.stylize(RegExp.prototype.toString.call(value), "regexp"); } else { return ctx.stylize("[Object]", "special"); } } ctx.seen.push(value); let output; if (array) { output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); } else { output = keys.map((key) => { return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); }); } ctx.seen.pop(); return reduceToSingleString(output, base, braces); } function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { let name, str; let desc = { value: void 0 }; try { desc.value = value[key]; } catch (e) { } try { if (Object.getOwnPropertyDescriptor) { desc = Object.getOwnPropertyDescriptor(value, key) || desc; } } catch (e) { } if (desc.get) { if (desc.set) { str = ctx.stylize("[Getter/Setter]", "special"); } else { str = ctx.stylize("[Getter]", "special"); } } else { if (desc.set) { str = ctx.stylize("[Setter]", "special"); } } if (!hasOwn(visibleKeys, key)) { name = "[" + key + "]"; } if (!str) { if (ctx.seen.indexOf(desc.value) < 0) { if (isNull(recurseTimes)) { str = formatValue(ctx, desc.value, undefined); } else { str = formatValue(ctx, desc.value, recurseTimes - 1); } if (str.indexOf("\n") > -1) { if (array) { str = str .split("\n") .map((line) => { return " " + line; }) .join("\n") .substr(2); } else { str = "\n" + str .split("\n") .map((line) => { return " " + line; }) .join("\n"); } } } else { str = ctx.stylize("[Circular]", "special"); } } if (isUndefined(name)) { if (array && key.match(/^\d+$/)) { return str; } name = JSON.stringify("" + key); if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { name = name.substr(1, name.length - 2); name = ctx.stylize(name, "name"); } else { name = name .replace(/'/g, "\\'") .replace(/\\"/g, "\\'") .replace(/(^"|"$)/g, "'"); name = ctx.stylize(name, "string"); } } return name + ": " + str; } function formatPrimitive(ctx, value) { if (isUndefined(value)) return ctx.stylize("undefined", "undefined"); if (isString(value)) { const simple = "'" + JSON.stringify(value).replace(/^"|"$/g, "").replace(/'/g, "\\'").replace(/\\"/g, "\\'") + "'"; return ctx.stylize(simple, "string"); } if (isNumber(value)) return ctx.stylize("" + value, "number"); if (isBoolean(value)) return ctx.stylize("" + value, "boolean"); if (isNull(value)) return ctx.stylize("null", "null"); } function reduceToSingleString(output, base, braces) { return braces[0] + (base === "" ? "" : base + "\n") + " " + output.join(",\n ") + " " + braces[1]; } function _extend(origin, add) { if (!add || !isObject(add)) return origin; const clonedOrigin = { ...origin }; const clonedAdd = { ...add }; const keys = Object.keys(add); let i = keys.length; while (i--) { clonedOrigin[keys[i]] = clonedAdd[keys[i]]; } return origin; } export function formatWithOptions(inspectOptions, ...args) { const ctx = { seen: [], stylize: stylizeNoColor, }; if (inspectOptions != null) { _extend(ctx, inspectOptions); } const first = args[0]; let a = 0; let str = ""; let join = ""; if (typeof first === "string") { if (args.length === 1) { return first; } let tempStr; let lastPos = 0; for (let i = 0; i < first.length - 1; i++) { if (first.charCodeAt(i) === 37) { const nextChar = first.charCodeAt(++i); if (a + 1 !== args.length) { switch (nextChar) { case 115: { const tempArg = args[++a]; if (typeof tempArg === "number") { tempStr = formatPrimitive(ctx, tempArg); } else if (typeof tempArg === "bigint") { tempStr = formatPrimitive(ctx, tempArg); } else if (typeof tempArg !== "object" || tempArg === null) { tempStr = String(tempArg); } else { tempStr = inspect(tempArg, { ...inspectOptions, compact: 3, colors: false, depth: 0, }); } break; } case 106: tempStr = jsonStringifyRecursive(args[++a]); break; case 100: { const tempNum = args[++a]; if (typeof tempNum === "bigint") { tempStr = formatPrimitive(ctx, tempNum); } else if (typeof tempNum === "symbol") { tempStr = "NaN"; } else { tempStr = formatPrimitive(ctx, tempNum); } break; } case 79: tempStr = inspect(args[++a], inspectOptions); break; case 111: tempStr = inspect(args[++a], { ...inspectOptions, showHidden: true, showProxy: true, depth: 4, }); break; case 105: { const tempInteger = args[++a]; if (typeof tempInteger === "bigint") { tempStr = formatPrimitive(ctx, tempInteger); } else if (typeof tempInteger === "symbol") { tempStr = "NaN"; } else { tempStr = formatPrimitive(ctx, parseInt(tempStr)); } break; } case 102: { const tempFloat = args[++a]; if (typeof tempFloat === "symbol") { tempStr = "NaN"; } else { tempStr = formatPrimitive(ctx, parseInt(tempFloat)); } break; } case 99: a += 1; tempStr = ""; break; case 37: str += first.slice(lastPos, i); lastPos = i + 1; continue; default: continue; } if (lastPos !== i - 1) { str += first.slice(lastPos, i - 1); } str += tempStr; lastPos = i + 1; } else if (nextChar === 37) { str += first.slice(lastPos, i); lastPos = i + 1; } } } if (lastPos !== 0) { a++; join = " "; if (lastPos < first.length) { str += first.slice(lastPos); } } } while (a < args.length) { const value = args[a]; str += join; str += typeof value !== "string" ? inspect(value, inspectOptions) : value; join = " "; a++; } return str; }