mirror of
https://github.com/denoland/deno.git
synced 2024-11-29 16:30:56 -05:00
Add more console types formatting support (#1299)
This commit is contained in:
parent
c427c2df42
commit
1548792fb3
2 changed files with 225 additions and 36 deletions
226
js/console.ts
226
js/console.ts
|
@ -1,4 +1,5 @@
|
||||||
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
|
||||||
|
import { isTypedArray } from "./util";
|
||||||
|
|
||||||
// tslint:disable-next-line:no-any
|
// tslint:disable-next-line:no-any
|
||||||
type ConsoleContext = Set<any>;
|
type ConsoleContext = Set<any>;
|
||||||
|
@ -9,7 +10,7 @@ type ConsoleOptions = Partial<{
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
// Default depth of logging nested objects
|
// Default depth of logging nested objects
|
||||||
const DEFAULT_MAX_DEPTH = 2;
|
const DEFAULT_MAX_DEPTH = 4;
|
||||||
|
|
||||||
// tslint:disable-next-line:no-any
|
// tslint:disable-next-line:no-any
|
||||||
function getClassInstanceName(instance: any): string {
|
function getClassInstanceName(instance: any): string {
|
||||||
|
@ -35,6 +36,42 @@ function createFunctionString(value: Function, ctx: ConsoleContext): string {
|
||||||
return `[${cstrName}]`;
|
return `[${cstrName}]`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface IterablePrintConfig {
|
||||||
|
typeName: string;
|
||||||
|
displayName: string;
|
||||||
|
delims: [string, string];
|
||||||
|
entryHandler: (
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
entry: any,
|
||||||
|
ctx: ConsoleContext,
|
||||||
|
level: number,
|
||||||
|
maxLevel: number
|
||||||
|
) => string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createIterableString(
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
value: any,
|
||||||
|
ctx: ConsoleContext,
|
||||||
|
level: number,
|
||||||
|
maxLevel: number,
|
||||||
|
config: IterablePrintConfig
|
||||||
|
): string {
|
||||||
|
if (level >= maxLevel) {
|
||||||
|
return `[${config.typeName}]`;
|
||||||
|
}
|
||||||
|
ctx.add(value);
|
||||||
|
|
||||||
|
const entries: string[] = [];
|
||||||
|
for (const el of value) {
|
||||||
|
entries.push(config.entryHandler(el, ctx, level + 1, maxLevel));
|
||||||
|
}
|
||||||
|
ctx.delete(value);
|
||||||
|
const iPrefix = `${config.displayName ? config.displayName + " " : ""}`;
|
||||||
|
const iContent = entries.length === 0 ? "" : ` ${entries.join(", ")} `;
|
||||||
|
return `${iPrefix}${config.delims[0]}${iContent}${config.delims[1]}`;
|
||||||
|
}
|
||||||
|
|
||||||
function createArrayString(
|
function createArrayString(
|
||||||
// tslint:disable-next-line:no-any
|
// tslint:disable-next-line:no-any
|
||||||
value: any[],
|
value: any[],
|
||||||
|
@ -42,24 +79,122 @@ function createArrayString(
|
||||||
level: number,
|
level: number,
|
||||||
maxLevel: number
|
maxLevel: number
|
||||||
): string {
|
): string {
|
||||||
const entries: string[] = [];
|
const printConfig: IterablePrintConfig = {
|
||||||
for (const el of value) {
|
typeName: "Array",
|
||||||
entries.push(stringifyWithQuotes(ctx, el, level + 1, maxLevel));
|
displayName: "",
|
||||||
}
|
delims: ["[", "]"],
|
||||||
ctx.delete(value);
|
entryHandler: (el, ctx, level, maxLevel) =>
|
||||||
if (entries.length === 0) {
|
stringifyWithQuotes(el, ctx, level + 1, maxLevel)
|
||||||
return "[]";
|
};
|
||||||
}
|
return createIterableString(value, ctx, level, maxLevel, printConfig);
|
||||||
return `[ ${entries.join(", ")} ]`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function createObjectString(
|
function createTypedArrayString(
|
||||||
|
typedArrayName: string,
|
||||||
// tslint:disable-next-line:no-any
|
// tslint:disable-next-line:no-any
|
||||||
value: any,
|
value: any,
|
||||||
ctx: ConsoleContext,
|
ctx: ConsoleContext,
|
||||||
level: number,
|
level: number,
|
||||||
maxLevel: number
|
maxLevel: number
|
||||||
): string {
|
): string {
|
||||||
|
const printConfig: IterablePrintConfig = {
|
||||||
|
typeName: typedArrayName,
|
||||||
|
displayName: typedArrayName,
|
||||||
|
delims: ["[", "]"],
|
||||||
|
entryHandler: (el, ctx, level, maxLevel) =>
|
||||||
|
stringifyWithQuotes(el, ctx, level + 1, maxLevel)
|
||||||
|
};
|
||||||
|
return createIterableString(value, ctx, level, maxLevel, printConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createSetString(
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
value: Set<any>,
|
||||||
|
ctx: ConsoleContext,
|
||||||
|
level: number,
|
||||||
|
maxLevel: number
|
||||||
|
): string {
|
||||||
|
const printConfig: IterablePrintConfig = {
|
||||||
|
typeName: "Set",
|
||||||
|
displayName: "Set",
|
||||||
|
delims: ["{", "}"],
|
||||||
|
entryHandler: (el, ctx, level, maxLevel) =>
|
||||||
|
stringifyWithQuotes(el, ctx, level + 1, maxLevel)
|
||||||
|
};
|
||||||
|
return createIterableString(value, ctx, level, maxLevel, printConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createMapString(
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
value: Map<any, any>,
|
||||||
|
ctx: ConsoleContext,
|
||||||
|
level: number,
|
||||||
|
maxLevel: number
|
||||||
|
): string {
|
||||||
|
const printConfig: IterablePrintConfig = {
|
||||||
|
typeName: "Map",
|
||||||
|
displayName: "Map",
|
||||||
|
delims: ["{", "}"],
|
||||||
|
entryHandler: (el, ctx, level, maxLevel) => {
|
||||||
|
const [key, val] = el;
|
||||||
|
return `${stringifyWithQuotes(
|
||||||
|
key,
|
||||||
|
ctx,
|
||||||
|
level + 1,
|
||||||
|
maxLevel
|
||||||
|
)} => ${stringifyWithQuotes(val, ctx, level + 1, maxLevel)}`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return createIterableString(value, ctx, level, maxLevel, printConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createWeakSetString(): string {
|
||||||
|
return "WeakSet { [items unknown] }"; // as seen in Node
|
||||||
|
}
|
||||||
|
|
||||||
|
function createWeakMapString(): string {
|
||||||
|
return "WeakMap { [items unknown] }"; // as seen in Node
|
||||||
|
}
|
||||||
|
|
||||||
|
function createDateString(value: Date) {
|
||||||
|
// without quotes, ISO format
|
||||||
|
return value.toISOString();
|
||||||
|
}
|
||||||
|
|
||||||
|
function createRegExpString(value: RegExp) {
|
||||||
|
return value.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// tslint:disable-next-line:ban-types
|
||||||
|
function createStringWrapperString(value: String) {
|
||||||
|
return `[String: "${value.toString()}"]`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// tslint:disable-next-line:ban-types
|
||||||
|
function createBooleanWrapperString(value: Boolean) {
|
||||||
|
return `[Boolean: ${value.toString()}]`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// tslint:disable-next-line:ban-types
|
||||||
|
function createNumberWrapperString(value: Number) {
|
||||||
|
return `[Number: ${value.toString()}]`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Promise, requires v8 bindings to get info
|
||||||
|
// TODO: Proxy
|
||||||
|
|
||||||
|
function createRawObjectString(
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
value: any,
|
||||||
|
ctx: ConsoleContext,
|
||||||
|
level: number,
|
||||||
|
maxLevel: number
|
||||||
|
): string {
|
||||||
|
if (level >= maxLevel) {
|
||||||
|
return "[Object]";
|
||||||
|
}
|
||||||
|
ctx.add(value);
|
||||||
|
|
||||||
const entries: string[] = [];
|
const entries: string[] = [];
|
||||||
let baseString = "";
|
let baseString = "";
|
||||||
|
|
||||||
|
@ -71,7 +206,7 @@ function createObjectString(
|
||||||
|
|
||||||
for (const key of Object.keys(value)) {
|
for (const key of Object.keys(value)) {
|
||||||
entries.push(
|
entries.push(
|
||||||
`${key}: ${stringifyWithQuotes(ctx, value[key], level + 1, maxLevel)}`
|
`${key}: ${stringifyWithQuotes(value[key], ctx, level + 1, maxLevel)}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,10 +225,54 @@ function createObjectString(
|
||||||
return baseString;
|
return baseString;
|
||||||
}
|
}
|
||||||
|
|
||||||
function stringify(
|
function createObjectString(
|
||||||
ctx: ConsoleContext,
|
|
||||||
// tslint:disable-next-line:no-any
|
// tslint:disable-next-line:no-any
|
||||||
value: any,
|
value: any,
|
||||||
|
...args: [ConsoleContext, number, number]
|
||||||
|
): string {
|
||||||
|
if (value instanceof Error) {
|
||||||
|
return value.stack! || "";
|
||||||
|
} else if (Array.isArray(value)) {
|
||||||
|
return createArrayString(value, ...args);
|
||||||
|
} else if (value instanceof Number) {
|
||||||
|
// tslint:disable-next-line:ban-types
|
||||||
|
return createNumberWrapperString(value as Number);
|
||||||
|
} else if (value instanceof Boolean) {
|
||||||
|
// tslint:disable-next-line:ban-types
|
||||||
|
return createBooleanWrapperString(value as Boolean);
|
||||||
|
} else if (value instanceof String) {
|
||||||
|
// tslint:disable-next-line:ban-types
|
||||||
|
return createStringWrapperString(value as String);
|
||||||
|
} else if (value instanceof RegExp) {
|
||||||
|
return createRegExpString(value as RegExp);
|
||||||
|
} else if (value instanceof Date) {
|
||||||
|
return createDateString(value as Date);
|
||||||
|
} else if (value instanceof Set) {
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
return createSetString(value as Set<any>, ...args);
|
||||||
|
} else if (value instanceof Map) {
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
return createMapString(value as Map<any, any>, ...args);
|
||||||
|
} else if (value instanceof WeakSet) {
|
||||||
|
return createWeakSetString();
|
||||||
|
} else if (value instanceof WeakMap) {
|
||||||
|
return createWeakMapString();
|
||||||
|
} else if (isTypedArray(value)) {
|
||||||
|
return createTypedArrayString(
|
||||||
|
Object.getPrototypeOf(value).constructor.name,
|
||||||
|
value,
|
||||||
|
...args
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Otherwise, default object formatting
|
||||||
|
return createRawObjectString(value, ...args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringify(
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
value: any,
|
||||||
|
ctx: ConsoleContext,
|
||||||
level: number,
|
level: number,
|
||||||
maxLevel: number
|
maxLevel: number
|
||||||
): string {
|
): string {
|
||||||
|
@ -118,20 +297,7 @@ function stringify(
|
||||||
return "[Circular]";
|
return "[Circular]";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (level >= maxLevel) {
|
|
||||||
return `[object]`;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.add(value);
|
|
||||||
|
|
||||||
if (value instanceof Error) {
|
|
||||||
return value.stack! || "";
|
|
||||||
} else if (Array.isArray(value)) {
|
|
||||||
// tslint:disable-next-line:no-any
|
|
||||||
return createArrayString(value as any[], ctx, level, maxLevel);
|
|
||||||
} else {
|
|
||||||
return createObjectString(value, ctx, level, maxLevel);
|
return createObjectString(value, ctx, level, maxLevel);
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
return "[Not Implemented]";
|
return "[Not Implemented]";
|
||||||
}
|
}
|
||||||
|
@ -139,9 +305,9 @@ function stringify(
|
||||||
|
|
||||||
// Print strings when they are inside of arrays or objects with quotes
|
// Print strings when they are inside of arrays or objects with quotes
|
||||||
function stringifyWithQuotes(
|
function stringifyWithQuotes(
|
||||||
ctx: ConsoleContext,
|
|
||||||
// tslint:disable-next-line:no-any
|
// tslint:disable-next-line:no-any
|
||||||
value: any,
|
value: any,
|
||||||
|
ctx: ConsoleContext,
|
||||||
level: number,
|
level: number,
|
||||||
maxLevel: number
|
maxLevel: number
|
||||||
): string {
|
): string {
|
||||||
|
@ -149,7 +315,7 @@ function stringifyWithQuotes(
|
||||||
case "string":
|
case "string":
|
||||||
return `"${value}"`;
|
return `"${value}"`;
|
||||||
default:
|
default:
|
||||||
return stringify(ctx, value, level, maxLevel);
|
return stringify(value, ctx, level, maxLevel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,9 +333,9 @@ export function stringifyArgs(
|
||||||
out.push(
|
out.push(
|
||||||
// use default maximum depth for null or undefined argument
|
// use default maximum depth for null or undefined argument
|
||||||
stringify(
|
stringify(
|
||||||
|
a,
|
||||||
// tslint:disable-next-line:no-any
|
// tslint:disable-next-line:no-any
|
||||||
new Set<any>(),
|
new Set<any>(),
|
||||||
a,
|
|
||||||
0,
|
0,
|
||||||
// tslint:disable-next-line:triple-equals
|
// tslint:disable-next-line:triple-equals
|
||||||
options.depth != undefined ? options.depth : DEFAULT_MAX_DEPTH
|
options.depth != undefined ? options.depth : DEFAULT_MAX_DEPTH
|
||||||
|
|
|
@ -67,12 +67,30 @@ test(function consoleTestStringifyCircular() {
|
||||||
|
|
||||||
nestedObj.o = circularObj;
|
nestedObj.o = circularObj;
|
||||||
// tslint:disable-next-line:max-line-length
|
// tslint:disable-next-line:max-line-length
|
||||||
const nestedObjExpected = `{ num: 1, bool: true, str: "a", method: [Function: method], asyncMethod: [AsyncFunction: asyncMethod], generatorMethod: [GeneratorFunction: generatorMethod], un: undefined, nu: null, arrowFunc: [Function: arrowFunc], extendedClass: Extended { a: 1, b: 2 }, nFunc: [Function], extendedCstr: [Function: Extended], o: { num: 2, bool: false, str: "b", method: [Function: method], un: undefined, nu: null, nested: [Circular], emptyObj: [object], arr: [object], baseClass: [object] } }`;
|
const nestedObjExpected = `{ num: 1, bool: true, str: "a", method: [Function: method], asyncMethod: [AsyncFunction: asyncMethod], generatorMethod: [GeneratorFunction: generatorMethod], un: undefined, nu: null, arrowFunc: [Function: arrowFunc], extendedClass: Extended { a: 1, b: 2 }, nFunc: [Function], extendedCstr: [Function: Extended], o: { num: 2, bool: false, str: "b", method: [Function: method], un: undefined, nu: null, nested: [Circular], emptyObj: {}, arr: [ 1, "s", false, null, [Circular] ], baseClass: Base { a: 1 } } }`;
|
||||||
|
|
||||||
assertEqual(stringify(1), "1");
|
assertEqual(stringify(1), "1");
|
||||||
assertEqual(stringify(1n), "1n");
|
assertEqual(stringify(1n), "1n");
|
||||||
assertEqual(stringify("s"), "s");
|
assertEqual(stringify("s"), "s");
|
||||||
assertEqual(stringify(false), "false");
|
assertEqual(stringify(false), "false");
|
||||||
|
// tslint:disable-next-line:no-construct
|
||||||
|
assertEqual(stringify(new Number(1)), "[Number: 1]");
|
||||||
|
// tslint:disable-next-line:no-construct
|
||||||
|
assertEqual(stringify(new Boolean(true)), "[Boolean: true]");
|
||||||
|
// tslint:disable-next-line:no-construct
|
||||||
|
assertEqual(stringify(new String("deno")), `[String: "deno"]`);
|
||||||
|
assertEqual(stringify(/[0-9]*/), "/[0-9]*/");
|
||||||
|
assertEqual(
|
||||||
|
stringify(new Date("2018-12-10T02:26:59.002Z")),
|
||||||
|
"2018-12-10T02:26:59.002Z"
|
||||||
|
);
|
||||||
|
assertEqual(stringify(new Set([1, 2, 3])), "Set { 1, 2, 3 }");
|
||||||
|
assertEqual(
|
||||||
|
stringify(new Map([[1, "one"], [2, "two"]])),
|
||||||
|
`Map { 1 => "one", 2 => "two" }`
|
||||||
|
);
|
||||||
|
assertEqual(stringify(new WeakSet()), "WeakSet { [items unknown] }");
|
||||||
|
assertEqual(stringify(new WeakMap()), "WeakMap { [items unknown] }");
|
||||||
assertEqual(stringify(Symbol(1)), "Symbol(1)");
|
assertEqual(stringify(Symbol(1)), "Symbol(1)");
|
||||||
assertEqual(stringify(null), "null");
|
assertEqual(stringify(null), "null");
|
||||||
assertEqual(stringify(undefined), "undefined");
|
assertEqual(stringify(undefined), "undefined");
|
||||||
|
@ -84,6 +102,11 @@ test(function consoleTestStringifyCircular() {
|
||||||
stringify(async function* agf() {}),
|
stringify(async function* agf() {}),
|
||||||
"[AsyncGeneratorFunction: agf]"
|
"[AsyncGeneratorFunction: agf]"
|
||||||
);
|
);
|
||||||
|
assertEqual(stringify(new Uint8Array([1, 2, 3])), "Uint8Array [ 1, 2, 3 ]");
|
||||||
|
assertEqual(
|
||||||
|
stringify({ a: { b: { c: { d: new Set([1]) } } } }),
|
||||||
|
"{ a: { b: { c: { d: [Set] } } } }"
|
||||||
|
);
|
||||||
assertEqual(stringify(nestedObj), nestedObjExpected);
|
assertEqual(stringify(nestedObj), nestedObjExpected);
|
||||||
assertEqual(stringify(JSON), "{}");
|
assertEqual(stringify(JSON), "{}");
|
||||||
assertEqual(
|
assertEqual(
|
||||||
|
@ -98,16 +121,16 @@ test(function consoleTestStringifyWithDepth() {
|
||||||
const nestedObj: any = { a: { b: { c: { d: { e: { f: 42 } } } } } };
|
const nestedObj: any = { a: { b: { c: { d: { e: { f: 42 } } } } } };
|
||||||
assertEqual(
|
assertEqual(
|
||||||
stringifyArgs([nestedObj], { depth: 3 }),
|
stringifyArgs([nestedObj], { depth: 3 }),
|
||||||
"{ a: { b: { c: [object] } } }"
|
"{ a: { b: { c: [Object] } } }"
|
||||||
);
|
);
|
||||||
assertEqual(
|
assertEqual(
|
||||||
stringifyArgs([nestedObj], { depth: 4 }),
|
stringifyArgs([nestedObj], { depth: 4 }),
|
||||||
"{ a: { b: { c: { d: [object] } } } }"
|
"{ a: { b: { c: { d: [Object] } } } }"
|
||||||
);
|
);
|
||||||
assertEqual(stringifyArgs([nestedObj], { depth: 0 }), "[object]");
|
assertEqual(stringifyArgs([nestedObj], { depth: 0 }), "[Object]");
|
||||||
assertEqual(
|
assertEqual(
|
||||||
stringifyArgs([nestedObj], { depth: null }),
|
stringifyArgs([nestedObj], { depth: null }),
|
||||||
"{ a: { b: [object] } }"
|
"{ a: { b: { c: { d: [Object] } } } }"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue