UNPKG

shelving

Version:

Toolkit for using data in JavaScript.

93 lines (92 loc) 3.69 kB
/** Debug a random value as a string. */ export function debug(value, depth = 1) { if (value === null) return "null"; if (value === undefined) return "undefined"; if (typeof value === "boolean") return value ? "true" : "false"; if (typeof value === "string") return debugString(value); if (typeof value === "number") return value.toString(); if (typeof value === "symbol") return value.toString(); if (typeof value === "function") return `function ${value.name || ""}()`; if (typeof value === "object") { if (value instanceof Date) return value.toISOString(); if (value instanceof Error) return value.toString(); // biome-ignore lint/suspicious/useIsArray: Intential in this context. if (value instanceof Array) return debugArray(value, depth); if (value instanceof Map) return debugMap(value, depth); if (value instanceof Set) return debugSet(value, depth); return debugObject(value, depth); } return typeof value; } /** Debug a string. */ export const debugString = (value) => `"${value.replace(ESCAPE_REGEXP, _escapeChar)}"`; // biome-ignore lint/suspicious/noControlCharactersInRegex: Intentional. const ESCAPE_REGEXP = /[\x00-\x08\x0B-\x1F\x7F-\x9F"\\]/g; // Match control characters, `"` double quote, `\` backslash. const ESCAPE_LIST = { '"': '\\"', "\\": "\\\\", "\r": "\\r", "\n": "\\n", "\t": "\\t", "\b": "\\b", "\f": "\\f", "\v": "\\v", }; const _escapeChar = (char) => ESCAPE_LIST[char] || `\\x${char.charCodeAt(0).toString(16).padStart(2, "00")}`; /** Debug an array. */ export function debugArray(value, depth = 1) { const prototype = Object.getPrototypeOf(value); const name = prototype === Array.prototype ? "" : prototype.constructor.name || ""; const items = depth > 0 && value.length ? value.map(v => debug(v, depth - 1)).join(",\n\t") : ""; return `${name ? `${name} ` : ""}${value.length ? `[\n\t${items}\n]` : "[]"}`; } /** Debug a set. */ export function debugSet(value, depth = 1) { const prototype = Object.getPrototypeOf(value); const name = prototype === Set.prototype ? "" : prototype.constructor.name || "Set"; const items = depth > 0 && value.size ? Array.from(value) .map(v => debug(v, depth - 1)) .join(",\n\t") : ""; return `${name}(value.size) ${items ? `{\n\t${items}\n}` : "{}"}`; } /** Debug a map. */ export function debugMap(value, depth = 1) { const prototype = Object.getPrototypeOf(value); const name = prototype === Map.prototype ? "" : prototype.constructor.name || "Map"; const entries = depth > 0 && value.size ? Array.from(value) .map(([k, v]) => `${debug(k)}: ${debug(v, depth - 1)}`) .join(",\n\t") : ""; return `${name}(value.size) ${entries ? `{\n\t${entries}\n}` : "{}"}`; } /** Debug an object. */ export function debugObject(value, depth = 1) { const prototype = Object.getPrototypeOf(value); const name = prototype === Object.prototype ? "" : prototype.constructor.name || ""; const entries = depth > 0 ? Object.entries(value) .map(([k, v]) => `${debug(k)}: ${debug(v, depth - 1)}`) .join(",\n\t") : ""; return `${name ? `${name} ` : ""}${entries ? `{\n\t${entries}\n}` : "{}"}`; } /** If a string is multiline, push it onto the next line and prepend a tab to each line.. */ export function indent(str) { const lines = str.split("\n"); return lines.length > 1 ? `\n${lines.join("\n\t")}` : ` ${str}`; }