1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-25 15:29:32 -05:00

Add more console types formatting support (#1299)

This commit is contained in:
Kevin (Kun) "Kassimo" Qian 2018-12-10 12:01:02 -05:00 committed by Ryan Dahl
parent c427c2df42
commit 1548792fb3
2 changed files with 225 additions and 36 deletions

View file

@ -1,4 +1,5 @@
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
import { isTypedArray } from "./util";
// tslint:disable-next-line:no-any
type ConsoleContext = Set<any>;
@ -9,7 +10,7 @@ type ConsoleOptions = Partial<{
}>;
// Default depth of logging nested objects
const DEFAULT_MAX_DEPTH = 2;
const DEFAULT_MAX_DEPTH = 4;
// tslint:disable-next-line:no-any
function getClassInstanceName(instance: any): string {
@ -35,6 +36,42 @@ function createFunctionString(value: Function, ctx: ConsoleContext): string {
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(
// tslint:disable-next-line:no-any
value: any[],
@ -42,24 +79,122 @@ function createArrayString(
level: number,
maxLevel: number
): string {
const entries: string[] = [];
for (const el of value) {
entries.push(stringifyWithQuotes(ctx, el, level + 1, maxLevel));
}
ctx.delete(value);
if (entries.length === 0) {
return "[]";
}
return `[ ${entries.join(", ")} ]`;
const printConfig: IterablePrintConfig = {
typeName: "Array",
displayName: "",
delims: ["[", "]"],
entryHandler: (el, ctx, level, maxLevel) =>
stringifyWithQuotes(el, ctx, level + 1, maxLevel)
};
return createIterableString(value, ctx, level, maxLevel, printConfig);
}
function createObjectString(
function createTypedArrayString(
typedArrayName: string,
// tslint:disable-next-line:no-any
value: any,
ctx: ConsoleContext,
level: number,
maxLevel: number
): 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[] = [];
let baseString = "";
@ -71,7 +206,7 @@ function createObjectString(
for (const key of Object.keys(value)) {
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;
}
function stringify(
ctx: ConsoleContext,
function createObjectString(
// tslint:disable-next-line:no-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,
maxLevel: number
): string {
@ -118,20 +297,7 @@ function stringify(
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:
return "[Not Implemented]";
}
@ -139,9 +305,9 @@ function stringify(
// Print strings when they are inside of arrays or objects with quotes
function stringifyWithQuotes(
ctx: ConsoleContext,
// tslint:disable-next-line:no-any
value: any,
ctx: ConsoleContext,
level: number,
maxLevel: number
): string {
@ -149,7 +315,7 @@ function stringifyWithQuotes(
case "string":
return `"${value}"`;
default:
return stringify(ctx, value, level, maxLevel);
return stringify(value, ctx, level, maxLevel);
}
}
@ -167,9 +333,9 @@ export function stringifyArgs(
out.push(
// use default maximum depth for null or undefined argument
stringify(
a,
// tslint:disable-next-line:no-any
new Set<any>(),
a,
0,
// tslint:disable-next-line:triple-equals
options.depth != undefined ? options.depth : DEFAULT_MAX_DEPTH

View file

@ -67,12 +67,30 @@ test(function consoleTestStringifyCircular() {
nestedObj.o = circularObj;
// 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(1n), "1n");
assertEqual(stringify("s"), "s");
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(null), "null");
assertEqual(stringify(undefined), "undefined");
@ -84,6 +102,11 @@ test(function consoleTestStringifyCircular() {
stringify(async function* 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(JSON), "{}");
assertEqual(
@ -98,16 +121,16 @@ test(function consoleTestStringifyWithDepth() {
const nestedObj: any = { a: { b: { c: { d: { e: { f: 42 } } } } } };
assertEqual(
stringifyArgs([nestedObj], { depth: 3 }),
"{ a: { b: { c: [object] } } }"
"{ a: { b: { c: [Object] } } }"
);
assertEqual(
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(
stringifyArgs([nestedObj], { depth: null }),
"{ a: { b: [object] } }"
"{ a: { b: { c: { d: [Object] } } } }"
);
});