diff options
Diffstat (limited to 'cli/js/web/console.ts')
-rw-r--r-- | cli/js/web/console.ts | 1072 |
1 files changed, 0 insertions, 1072 deletions
diff --git a/cli/js/web/console.ts b/cli/js/web/console.ts deleted file mode 100644 index 181cdb664..000000000 --- a/cli/js/web/console.ts +++ /dev/null @@ -1,1072 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. - -import { isInvalidDate, isTypedArray, TypedArray } from "./util.ts"; -import { cliTable } from "./console_table.ts"; -import { exposeForTest } from "../internals.ts"; -import { PromiseState } from "./promise.ts"; -import { - stripColor, - yellow, - dim, - cyan, - red, - green, - magenta, - bold, -} from "../colors.ts"; - -type ConsoleContext = Set<unknown>; - -export interface InspectOptions { - depth?: number; - indentLevel?: number; - sorted?: boolean; - trailingComma?: boolean; - compact?: boolean; - iterableLimit?: number; -} - -const DEFAULT_INSPECT_OPTIONS: Required<InspectOptions> = { - depth: 4, - indentLevel: 0, - sorted: false, - trailingComma: false, - compact: true, - iterableLimit: 100, -}; - -const DEFAULT_INDENT = " "; // Default indent string - -const LINE_BREAKING_LENGTH = 80; -const MIN_GROUP_LENGTH = 6; -const STR_ABBREVIATE_SIZE = 100; -// Char codes -const CHAR_PERCENT = 37; /* % */ -const CHAR_LOWERCASE_S = 115; /* s */ -const CHAR_LOWERCASE_D = 100; /* d */ -const CHAR_LOWERCASE_I = 105; /* i */ -const CHAR_LOWERCASE_F = 102; /* f */ -const CHAR_LOWERCASE_O = 111; /* o */ -const CHAR_UPPERCASE_O = 79; /* O */ -const CHAR_LOWERCASE_C = 99; /* c */ - -const PROMISE_STRING_BASE_LENGTH = 12; - -export class CSI { - static kClear = "\x1b[1;1H"; - static kClearScreenDown = "\x1b[0J"; -} - -/* eslint-disable @typescript-eslint/no-use-before-define */ - -function getClassInstanceName(instance: unknown): string { - if (typeof instance !== "object") { - return ""; - } - if (!instance) { - return ""; - } - - const proto = Object.getPrototypeOf(instance); - if (proto && proto.constructor) { - return proto.constructor.name; // could be "Object" or "Array" - } - - return ""; -} - -function inspectFunction(value: Function, _ctx: ConsoleContext): string { - // Might be Function/AsyncFunction/GeneratorFunction - const cstrName = Object.getPrototypeOf(value).constructor.name; - if (value.name && value.name !== "anonymous") { - // from MDN spec - return `[${cstrName}: ${value.name}]`; - } - return `[${cstrName}]`; -} - -interface InspectIterableOptions<T> { - typeName: string; - displayName: string; - delims: [string, string]; - entryHandler: ( - entry: [unknown, T], - ctx: ConsoleContext, - level: number, - inspectOptions: Required<InspectOptions>, - next: () => IteratorResult<[unknown, T], unknown>, - ) => string; - group: boolean; - sort: boolean; -} -type IterableEntries<T> = Iterable<T> & { - entries(): IterableIterator<[unknown, T]>; -}; -function inspectIterable<T>( - value: IterableEntries<T>, - ctx: ConsoleContext, - level: number, - options: InspectIterableOptions<T>, - inspectOptions: Required<InspectOptions>, -): string { - if (level >= inspectOptions.depth) { - return cyan(`[${options.typeName}]`); - } - ctx.add(value); - - const entries: string[] = []; - - const iter = value.entries(); - let entriesLength = 0; - const next = (): IteratorResult<[unknown, T], unknown> => { - return iter.next(); - }; - for (const el of iter) { - if (entriesLength < inspectOptions.iterableLimit) { - entries.push( - options.entryHandler( - el, - ctx, - level + 1, - inspectOptions, - next.bind(iter), - ), - ); - } - entriesLength++; - } - ctx.delete(value); - - if (options.sort) { - entries.sort(); - } - - if (entriesLength > inspectOptions.iterableLimit) { - const nmore = entriesLength - inspectOptions.iterableLimit; - entries.push(`... ${nmore} more items`); - } - - const iPrefix = `${options.displayName ? options.displayName + " " : ""}`; - - const initIndentation = `\n${DEFAULT_INDENT.repeat(level + 1)}`; - const entryIndentation = `,\n${DEFAULT_INDENT.repeat(level + 1)}`; - const closingIndentation = `${inspectOptions.trailingComma ? "," : ""}\n${ - DEFAULT_INDENT.repeat(level) - }`; - - let iContent: string; - if (options.group && entries.length > MIN_GROUP_LENGTH) { - const groups = groupEntries(entries, level, value); - iContent = `${initIndentation}${ - groups.join(entryIndentation) - }${closingIndentation}`; - } else { - iContent = entries.length === 0 ? "" : ` ${entries.join(", ")} `; - if ( - stripColor(iContent).length > LINE_BREAKING_LENGTH || - !inspectOptions.compact - ) { - iContent = `${initIndentation}${ - entries.join(entryIndentation) - }${closingIndentation}`; - } - } - - return `${iPrefix}${options.delims[0]}${iContent}${options.delims[1]}`; -} - -// Ported from Node.js -// Copyright Node.js contributors. All rights reserved. -function groupEntries<T>( - entries: string[], - level: number, - value: Iterable<T>, - iterableLimit = 100, -): string[] { - let totalLength = 0; - let maxLength = 0; - let entriesLength = entries.length; - if (iterableLimit < entriesLength) { - // This makes sure the "... n more items" part is not taken into account. - entriesLength--; - } - const separatorSpace = 2; // Add 1 for the space and 1 for the separator. - const dataLen = new Array(entriesLength); - // Calculate the total length of all output entries and the individual max - // entries length of all output entries. - // IN PROGRESS: Colors are being taken into account. - for (let i = 0; i < entriesLength; i++) { - // Taking colors into account: removing the ANSI color - // codes from the string before measuring its length - const len = stripColor(entries[i]).length; - dataLen[i] = len; - totalLength += len + separatorSpace; - if (maxLength < len) maxLength = len; - } - // Add two to `maxLength` as we add a single whitespace character plus a comma - // in-between two entries. - const actualMax = maxLength + separatorSpace; - // Check if at least three entries fit next to each other and prevent grouping - // of arrays that contains entries of very different length (i.e., if a single - // entry is longer than 1/5 of all other entries combined). Otherwise the - // space in-between small entries would be enormous. - if ( - actualMax * 3 + (level + 1) < LINE_BREAKING_LENGTH && - (totalLength / actualMax > 5 || maxLength <= 6) - ) { - const approxCharHeights = 2.5; - const averageBias = Math.sqrt(actualMax - totalLength / entries.length); - const biasedMax = Math.max(actualMax - 3 - averageBias, 1); - // Dynamically check how many columns seem possible. - const columns = Math.min( - // Ideally a square should be drawn. We expect a character to be about 2.5 - // times as high as wide. This is the area formula to calculate a square - // which contains n rectangles of size `actualMax * approxCharHeights`. - // Divide that by `actualMax` to receive the correct number of columns. - // The added bias increases the columns for short entries. - Math.round( - Math.sqrt(approxCharHeights * biasedMax * entriesLength) / biasedMax, - ), - // Do not exceed the breakLength. - Math.floor((LINE_BREAKING_LENGTH - (level + 1)) / actualMax), - // Limit the columns to a maximum of fifteen. - 15, - ); - // Return with the original output if no grouping should happen. - if (columns <= 1) { - return entries; - } - const tmp = []; - const maxLineLength = []; - for (let i = 0; i < columns; i++) { - let lineMaxLength = 0; - for (let j = i; j < entries.length; j += columns) { - if (dataLen[j] > lineMaxLength) lineMaxLength = dataLen[j]; - } - lineMaxLength += separatorSpace; - maxLineLength[i] = lineMaxLength; - } - let order: "padStart" | "padEnd" = "padStart"; - if (value !== undefined) { - for (let i = 0; i < entries.length; i++) { - /* eslint-disable @typescript-eslint/no-explicit-any */ - if ( - typeof (value as any)[i] !== "number" && - typeof (value as any)[i] !== "bigint" - ) { - order = "padEnd"; - break; - } - /* eslint-enable */ - } - } - // Each iteration creates a single line of grouped entries. - for (let i = 0; i < entriesLength; i += columns) { - // The last lines may contain less entries than columns. - const max = Math.min(i + columns, entriesLength); - let str = ""; - let j = i; - for (; j < max - 1; j++) { - // In future, colors should be taken here into the account - const padding = maxLineLength[j - i]; - str += `${entries[j]}, `[order](padding, " "); - } - if (order === "padStart") { - const padding = maxLineLength[j - i] + - entries[j].length - - dataLen[j] - - separatorSpace; - str += entries[j].padStart(padding, " "); - } else { - str += entries[j]; - } - tmp.push(str); - } - if (iterableLimit < entries.length) { - tmp.push(entries[entriesLength]); - } - entries = tmp; - } - return entries; -} - -function inspectValue( - value: unknown, - ctx: ConsoleContext, - level: number, - inspectOptions: Required<InspectOptions>, -): string { - switch (typeof value) { - case "string": - return value; - case "number": // Numbers are yellow - // Special handling of -0 - return yellow(Object.is(value, -0) ? "-0" : `${value}`); - case "boolean": // booleans are yellow - return yellow(String(value)); - case "undefined": // undefined is dim - return dim(String(value)); - case "symbol": // Symbols are green - return green(String(value)); - case "bigint": // Bigints are yellow - return yellow(`${value}n`); - case "function": // Function string is cyan - return cyan(inspectFunction(value as Function, ctx)); - case "object": // null is bold - if (value === null) { - return bold("null"); - } - - if (ctx.has(value)) { - // Circular string is cyan - return cyan("[Circular]"); - } - - return inspectObject(value, ctx, level, inspectOptions); - default: - // Not implemented is red - return red("[Not Implemented]"); - } -} - -// We can match Node's quoting behavior exactly by swapping the double quote and -// single quote in this array. That would give preference to single quotes. -// However, we prefer double quotes as the default. -const QUOTES = ['"', "'", "`"]; - -/** Surround the string in quotes. - * - * The quote symbol is chosen by taking the first of the `QUOTES` array which - * does not occur in the string. If they all occur, settle with `QUOTES[0]`. - * - * Insert a backslash before any occurrence of the chosen quote symbol and - * before any backslash. */ -function quoteString(string: string): string { - const quote = QUOTES.find((c) => !string.includes(c)) ?? QUOTES[0]; - const escapePattern = new RegExp(`(?=[${quote}\\\\])`, "g"); - return `${quote}${string.replace(escapePattern, "\\")}${quote}`; -} - -// Print strings when they are inside of arrays or objects with quotes -function inspectValueWithQuotes( - value: unknown, - ctx: ConsoleContext, - level: number, - inspectOptions: Required<InspectOptions>, -): string { - switch (typeof value) { - case "string": - const trunc = value.length > STR_ABBREVIATE_SIZE - ? value.slice(0, STR_ABBREVIATE_SIZE) + "..." - : value; - return green(quoteString(trunc)); // Quoted strings are green - default: - return inspectValue(value, ctx, level, inspectOptions); - } -} - -function inspectArray( - value: unknown[], - ctx: ConsoleContext, - level: number, - inspectOptions: Required<InspectOptions>, -): string { - const options: InspectIterableOptions<unknown> = { - typeName: "Array", - displayName: "", - delims: ["[", "]"], - entryHandler: (entry, ctx, level, inspectOptions, next): string => { - const [index, val] = entry as [number, unknown]; - let i = index; - if (!value.hasOwnProperty(i)) { - i++; - while (!value.hasOwnProperty(i) && i < value.length) { - next(); - i++; - } - const emptyItems = i - index; - const ending = emptyItems > 1 ? "s" : ""; - return dim(`<${emptyItems} empty item${ending}>`); - } else { - return inspectValueWithQuotes(val, ctx, level, inspectOptions); - } - }, - group: inspectOptions.compact, - sort: false, - }; - return inspectIterable(value, ctx, level, options, inspectOptions); -} - -function inspectTypedArray( - typedArrayName: string, - value: TypedArray, - ctx: ConsoleContext, - level: number, - inspectOptions: Required<InspectOptions>, -): string { - const valueLength = value.length; - const options: InspectIterableOptions<unknown> = { - typeName: typedArrayName, - displayName: `${typedArrayName}(${valueLength})`, - delims: ["[", "]"], - entryHandler: (entry, ctx, level, inspectOptions): string => { - const val = entry[1]; - return inspectValueWithQuotes(val, ctx, level + 1, inspectOptions); - }, - group: inspectOptions.compact, - sort: false, - }; - return inspectIterable(value, ctx, level, options, inspectOptions); -} - -function inspectSet( - value: Set<unknown>, - ctx: ConsoleContext, - level: number, - inspectOptions: Required<InspectOptions>, -): string { - const options: InspectIterableOptions<unknown> = { - typeName: "Set", - displayName: "Set", - delims: ["{", "}"], - entryHandler: (entry, ctx, level, inspectOptions): string => { - const val = entry[1]; - return inspectValueWithQuotes(val, ctx, level + 1, inspectOptions); - }, - group: false, - sort: inspectOptions.sorted, - }; - return inspectIterable(value, ctx, level, options, inspectOptions); -} - -function inspectMap( - value: Map<unknown, unknown>, - ctx: ConsoleContext, - level: number, - inspectOptions: Required<InspectOptions>, -): string { - const options: InspectIterableOptions<[unknown]> = { - typeName: "Map", - displayName: "Map", - delims: ["{", "}"], - entryHandler: (entry, ctx, level, inspectOptions): string => { - const [key, val] = entry; - return `${ - inspectValueWithQuotes( - key, - ctx, - level + 1, - inspectOptions, - ) - } => ${inspectValueWithQuotes(val, ctx, level + 1, inspectOptions)}`; - }, - group: false, - sort: inspectOptions.sorted, - }; - return inspectIterable( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - value as any, - ctx, - level, - options, - inspectOptions, - ); -} - -function inspectWeakSet(): string { - return `WeakSet { ${cyan("[items unknown]")} }`; // as seen in Node, with cyan color -} - -function inspectWeakMap(): string { - return `WeakMap { ${cyan("[items unknown]")} }`; // as seen in Node, with cyan color -} - -function inspectDate(value: Date): string { - // without quotes, ISO format, in magenta like before - return magenta(isInvalidDate(value) ? "Invalid Date" : value.toISOString()); -} - -function inspectRegExp(value: RegExp): string { - return red(value.toString()); // RegExps are red -} - -/* eslint-disable @typescript-eslint/ban-types */ - -function inspectStringObject(value: String): string { - return cyan(`[String: "${value.toString()}"]`); // wrappers are in cyan -} - -function inspectBooleanObject(value: Boolean): string { - return cyan(`[Boolean: ${value.toString()}]`); // wrappers are in cyan -} - -function inspectNumberObject(value: Number): string { - return cyan(`[Number: ${value.toString()}]`); // wrappers are in cyan -} - -/* eslint-enable @typescript-eslint/ban-types */ - -function inspectPromise( - value: Promise<unknown>, - ctx: ConsoleContext, - level: number, - inspectOptions: Required<InspectOptions>, -): string { - const [state, result] = Deno.core.getPromiseDetails(value); - - if (state === PromiseState.Pending) { - return `Promise { ${cyan("<pending>")} }`; - } - - const prefix = state === PromiseState.Fulfilled - ? "" - : `${red("<rejected>")} `; - - const str = `${prefix}${ - inspectValueWithQuotes( - result, - ctx, - level + 1, - inspectOptions, - ) - }`; - - if (str.length + PROMISE_STRING_BASE_LENGTH > LINE_BREAKING_LENGTH) { - return `Promise {\n${DEFAULT_INDENT.repeat(level + 1)}${str}\n}`; - } - - return `Promise { ${str} }`; -} - -// TODO: Proxy - -function inspectRawObject( - value: Record<string, unknown>, - ctx: ConsoleContext, - level: number, - inspectOptions: Required<InspectOptions>, -): string { - if (level >= inspectOptions.depth) { - return cyan("[Object]"); // wrappers are in cyan - } - ctx.add(value); - - let baseString: string; - - let shouldShowDisplayName = false; - let displayName = (value as { [Symbol.toStringTag]: string })[ - Symbol.toStringTag - ]; - if (!displayName) { - displayName = getClassInstanceName(value); - } - if (displayName && displayName !== "Object" && displayName !== "anonymous") { - shouldShowDisplayName = true; - } - - const entries: string[] = []; - const stringKeys = Object.keys(value); - const symbolKeys = Object.getOwnPropertySymbols(value); - if (inspectOptions.sorted) { - stringKeys.sort(); - symbolKeys.sort((s1, s2) => - (s1.description ?? "").localeCompare(s2.description ?? "") - ); - } - - for (const key of stringKeys) { - entries.push( - `${key}: ${ - inspectValueWithQuotes( - value[key], - ctx, - level + 1, - inspectOptions, - ) - }`, - ); - } - for (const key of symbolKeys) { - entries.push( - `${key.toString()}: ${ - inspectValueWithQuotes( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - value[key as any], - ctx, - level + 1, - inspectOptions, - ) - }`, - ); - } - // Making sure color codes are ignored when calculating the total length - const totalLength = entries.length + level + - stripColor(entries.join("")).length; - - ctx.delete(value); - - if (entries.length === 0) { - baseString = "{}"; - } else if (totalLength > LINE_BREAKING_LENGTH || !inspectOptions.compact) { - const entryIndent = DEFAULT_INDENT.repeat(level + 1); - const closingIndent = DEFAULT_INDENT.repeat(level); - baseString = `{\n${entryIndent}${entries.join(`,\n${entryIndent}`)}${ - inspectOptions.trailingComma ? "," : "" - }\n${closingIndent}}`; - } else { - baseString = `{ ${entries.join(", ")} }`; - } - - if (shouldShowDisplayName) { - baseString = `${displayName} ${baseString}`; - } - - return baseString; -} - -function inspectObject( - value: {}, - consoleContext: ConsoleContext, - level: number, - inspectOptions: Required<InspectOptions>, -): string { - if (customInspect in value && typeof value[customInspect] === "function") { - try { - return String(value[customInspect]!()); - } catch {} - } - if (value instanceof Error) { - return String(value.stack); - } else if (Array.isArray(value)) { - return inspectArray(value, consoleContext, level, inspectOptions); - } else if (value instanceof Number) { - return inspectNumberObject(value); - } else if (value instanceof Boolean) { - return inspectBooleanObject(value); - } else if (value instanceof String) { - return inspectStringObject(value); - } else if (value instanceof Promise) { - return inspectPromise(value, consoleContext, level, inspectOptions); - } else if (value instanceof RegExp) { - return inspectRegExp(value); - } else if (value instanceof Date) { - return inspectDate(value); - } else if (value instanceof Set) { - return inspectSet(value, consoleContext, level, inspectOptions); - } else if (value instanceof Map) { - return inspectMap(value, consoleContext, level, inspectOptions); - } else if (value instanceof WeakSet) { - return inspectWeakSet(); - } else if (value instanceof WeakMap) { - return inspectWeakMap(); - } else if (isTypedArray(value)) { - return inspectTypedArray( - Object.getPrototypeOf(value).constructor.name, - value, - consoleContext, - level, - inspectOptions, - ); - } else { - // Otherwise, default object formatting - return inspectRawObject(value, consoleContext, level, inspectOptions); - } -} - -export function inspectArgs( - args: unknown[], - inspectOptions: InspectOptions = {}, -): string { - const rInspectOptions = { ...DEFAULT_INSPECT_OPTIONS, ...inspectOptions }; - const first = args[0]; - let a = 0; - let str = ""; - let join = ""; - - if (typeof first === "string") { - let tempStr: string; - let lastPos = 0; - - for (let i = 0; i < first.length - 1; i++) { - if (first.charCodeAt(i) === CHAR_PERCENT) { - const nextChar = first.charCodeAt(++i); - if (a + 1 !== args.length) { - switch (nextChar) { - case CHAR_LOWERCASE_S: - // format as a string - tempStr = String(args[++a]); - break; - case CHAR_LOWERCASE_D: - case CHAR_LOWERCASE_I: - // format as an integer - const tempInteger = args[++a]; - if (typeof tempInteger === "bigint") { - tempStr = `${tempInteger}n`; - } else if (typeof tempInteger === "symbol") { - tempStr = "NaN"; - } else { - tempStr = `${parseInt(String(tempInteger), 10)}`; - } - break; - case CHAR_LOWERCASE_F: - // format as a floating point value - const tempFloat = args[++a]; - if (typeof tempFloat === "symbol") { - tempStr = "NaN"; - } else { - tempStr = `${parseFloat(String(tempFloat))}`; - } - break; - case CHAR_LOWERCASE_O: - case CHAR_UPPERCASE_O: - // format as an object - tempStr = inspectValue( - args[++a], - new Set<unknown>(), - 0, - rInspectOptions, - ); - break; - case CHAR_PERCENT: - str += first.slice(lastPos, i); - lastPos = i + 1; - continue; - case CHAR_LOWERCASE_C: - // TODO: applies CSS style rules to the output string as specified - continue; - default: - // any other character is not a correct placeholder - continue; - } - - if (lastPos !== i - 1) { - str += first.slice(lastPos, i - 1); - } - - str += tempStr; - lastPos = i + 1; - } else if (nextChar === CHAR_PERCENT) { - 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; - if (typeof value === "string") { - str += value; - } else { - // use default maximum depth for null or undefined argument - str += inspectValue(value, new Set<unknown>(), 0, rInspectOptions); - } - join = " "; - a++; - } - - if (rInspectOptions.indentLevel > 0) { - const groupIndent = DEFAULT_INDENT.repeat(rInspectOptions.indentLevel); - if (str.indexOf("\n") !== -1) { - str = str.replace(/\n/g, `\n${groupIndent}`); - } - str = groupIndent + str; - } - - return str; -} - -type PrintFunc = (x: string, isErr?: boolean) => void; - -const countMap = new Map<string, number>(); -const timerMap = new Map<string, number>(); -const isConsoleInstance = Symbol("isConsoleInstance"); - -export class Console { - readonly #printFunc: PrintFunc; - indentLevel: number; - [isConsoleInstance] = false; - - constructor(printFunc: PrintFunc) { - this.#printFunc = printFunc; - this.indentLevel = 0; - this[isConsoleInstance] = true; - - // ref https://console.spec.whatwg.org/#console-namespace - // For historical web-compatibility reasons, the namespace object for - // console must have as its [[Prototype]] an empty object, created as if - // by ObjectCreate(%ObjectPrototype%), instead of %ObjectPrototype%. - const console = Object.create({}) as Console; - Object.assign(console, this); - return console; - } - - log = (...args: unknown[]): void => { - this.#printFunc( - inspectArgs(args, { - indentLevel: this.indentLevel, - }) + "\n", - false, - ); - }; - - debug = this.log; - info = this.log; - - dir = (obj: unknown, options: InspectOptions = {}): void => { - this.#printFunc(inspectArgs([obj], options) + "\n", false); - }; - - dirxml = this.dir; - - warn = (...args: unknown[]): void => { - this.#printFunc( - inspectArgs(args, { - indentLevel: this.indentLevel, - }) + "\n", - true, - ); - }; - - error = this.warn; - - assert = (condition = false, ...args: unknown[]): void => { - if (condition) { - return; - } - - if (args.length === 0) { - this.error("Assertion failed"); - return; - } - - const [first, ...rest] = args; - - if (typeof first === "string") { - this.error(`Assertion failed: ${first}`, ...rest); - return; - } - - this.error(`Assertion failed:`, ...args); - }; - - count = (label = "default"): void => { - label = String(label); - - if (countMap.has(label)) { - const current = countMap.get(label) || 0; - countMap.set(label, current + 1); - } else { - countMap.set(label, 1); - } - - this.info(`${label}: ${countMap.get(label)}`); - }; - - countReset = (label = "default"): void => { - label = String(label); - - if (countMap.has(label)) { - countMap.set(label, 0); - } else { - this.warn(`Count for '${label}' does not exist`); - } - }; - - table = (data: unknown, properties?: string[]): void => { - if (properties !== undefined && !Array.isArray(properties)) { - throw new Error( - "The 'properties' argument must be of type Array. " + - "Received type string", - ); - } - - if (data === null || typeof data !== "object") { - return this.log(data); - } - - const objectValues: { [key: string]: string[] } = {}; - const indexKeys: string[] = []; - const values: string[] = []; - - const stringifyValue = (value: unknown): string => - inspectValueWithQuotes(value, new Set<unknown>(), 0, { - ...DEFAULT_INSPECT_OPTIONS, - depth: 1, - }); - const toTable = (header: string[], body: string[][]): void => - this.log(cliTable(header, body)); - const createColumn = (value: unknown, shift?: number): string[] => [ - ...(shift ? [...new Array(shift)].map((): string => "") : []), - stringifyValue(value), - ]; - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let resultData: any; - const isSet = data instanceof Set; - const isMap = data instanceof Map; - const valuesKey = "Values"; - const indexKey = isSet || isMap ? "(iter idx)" : "(idx)"; - - if (data instanceof Set) { - resultData = [...data]; - } else if (data instanceof Map) { - let idx = 0; - resultData = {}; - - data.forEach((v: unknown, k: unknown): void => { - resultData[idx] = { Key: k, Values: v }; - idx++; - }); - } else { - resultData = data!; - } - - let hasPrimitives = false; - Object.keys(resultData).forEach((k, idx): void => { - const value: unknown = resultData[k]!; - const primitive = value === null || - (typeof value !== "function" && typeof value !== "object"); - if (properties === undefined && primitive) { - hasPrimitives = true; - values.push(stringifyValue(value)); - } else { - const valueObj = (value as { [key: string]: unknown }) || {}; - const keys = properties || Object.keys(valueObj); - for (const k of keys) { - if (primitive || !valueObj.hasOwnProperty(k)) { - if (objectValues[k]) { - // fill with blanks for idx to avoid misplacing from later values - objectValues[k].push(""); - } - } else { - if (objectValues[k]) { - objectValues[k].push(stringifyValue(valueObj[k])); - } else { - objectValues[k] = createColumn(valueObj[k], idx); - } - } - } - values.push(""); - } - - indexKeys.push(k); - }); - - const headerKeys = Object.keys(objectValues); - const bodyValues = Object.values(objectValues); - const header = [ - indexKey, - ...(properties || [...headerKeys, !isMap && hasPrimitives && valuesKey]), - ].filter(Boolean) as string[]; - const body = [indexKeys, ...bodyValues, values]; - - toTable(header, body); - }; - - time = (label = "default"): void => { - label = String(label); - - if (timerMap.has(label)) { - this.warn(`Timer '${label}' already exists`); - return; - } - - timerMap.set(label, Date.now()); - }; - - timeLog = (label = "default", ...args: unknown[]): void => { - label = String(label); - - if (!timerMap.has(label)) { - this.warn(`Timer '${label}' does not exists`); - return; - } - - const startTime = timerMap.get(label) as number; - const duration = Date.now() - startTime; - - this.info(`${label}: ${duration}ms`, ...args); - }; - - timeEnd = (label = "default"): void => { - label = String(label); - - if (!timerMap.has(label)) { - this.warn(`Timer '${label}' does not exists`); - return; - } - - const startTime = timerMap.get(label) as number; - timerMap.delete(label); - const duration = Date.now() - startTime; - - this.info(`${label}: ${duration}ms`); - }; - - group = (...label: unknown[]): void => { - if (label.length > 0) { - this.log(...label); - } - this.indentLevel += 2; - }; - - groupCollapsed = this.group; - - groupEnd = (): void => { - if (this.indentLevel > 0) { - this.indentLevel -= 2; - } - }; - - clear = (): void => { - this.indentLevel = 0; - this.#printFunc(CSI.kClear, false); - this.#printFunc(CSI.kClearScreenDown, false); - }; - - trace = (...args: unknown[]): void => { - const message = inspectArgs(args, { indentLevel: 0 }); - const err = { - name: "Trace", - message, - }; - Error.captureStackTrace(err, this.trace); - this.error((err as Error).stack); - }; - - static [Symbol.hasInstance](instance: Console): boolean { - return instance[isConsoleInstance]; - } -} - -export const customInspect = Symbol("Deno.customInspect"); - -export function inspect( - value: unknown, - inspectOptions: InspectOptions = {}, -): string { - if (typeof value === "string") { - return value; - } else { - return inspectValue(value, new Set<unknown>(), 0, { - ...DEFAULT_INSPECT_OPTIONS, - ...inspectOptions, - // TODO(nayeemrmn): Indent level is not supported. - indentLevel: 0, - }); - } -} - -// Expose these fields to internalObject for tests. -exposeForTest("Console", Console); -exposeForTest("inspectArgs", inspectArgs); |