From bcdbfc00f01a94fed1d3f1d11f8aacc627c5ca36 Mon Sep 17 00:00:00 2001 From: "Yingbo (Max) Wang" Date: Sun, 30 Sep 2018 15:10:20 -0700 Subject: [PATCH] Limit depth of output in console.log for nested objects, and add console.dir (#826) --- js/console.ts | 86 +++++++++++++++++++++++++++++++++++++--------- js/console_test.ts | 19 +++++++++- 2 files changed, 87 insertions(+), 18 deletions(-) diff --git a/js/console.ts b/js/console.ts index d3bd5bb6bb..51798d79d7 100644 --- a/js/console.ts +++ b/js/console.ts @@ -1,5 +1,13 @@ // tslint:disable-next-line:no-any type ConsoleContext = Set; +type ConsoleOptions = Partial<{ + showHidden: boolean; + depth: number; + colors: boolean; +}>; + +// Default depth of logging nested objects +const DEFAULT_MAX_DEPTH = 2; // tslint:disable-next-line:no-any function getClassInstanceName(instance: any): string { @@ -25,11 +33,16 @@ function createFunctionString(value: Function, ctx: ConsoleContext): string { return `[${cstrName}]`; } -// tslint:disable-next-line:no-any -function createArrayString(value: any[], ctx: ConsoleContext): string { +function createArrayString( + // tslint:disable-next-line:no-any + value: any[], + ctx: ConsoleContext, + level: number, + maxLevel: number +): string { const entries: string[] = []; for (const el of value) { - entries.push(stringifyWithQuotes(ctx, el)); + entries.push(stringifyWithQuotes(ctx, el, level + 1, maxLevel)); } ctx.delete(value); if (entries.length === 0) { @@ -38,8 +51,13 @@ function createArrayString(value: any[], ctx: ConsoleContext): string { return `[ ${entries.join(", ")} ]`; } -// tslint:disable-next-line:no-any -function createObjectString(value: any, ctx: ConsoleContext): string { +function createObjectString( + // tslint:disable-next-line:no-any + value: any, + ctx: ConsoleContext, + level: number, + maxLevel: number +): string { const entries: string[] = []; let baseString = ""; @@ -50,7 +68,9 @@ function createObjectString(value: any, ctx: ConsoleContext): string { } for (const key of Object.keys(value)) { - entries.push(`${key}: ${stringifyWithQuotes(ctx, value[key])}`); + entries.push( + `${key}: ${stringifyWithQuotes(ctx, value[key], level + 1, maxLevel)}` + ); } ctx.delete(value); @@ -68,8 +88,13 @@ function createObjectString(value: any, ctx: ConsoleContext): string { return baseString; } -// tslint:disable-next-line:no-any -function stringify(ctx: ConsoleContext, value: any): string { +function stringify( + ctx: ConsoleContext, + // tslint:disable-next-line:no-any + value: any, + level: number, + maxLevel: number +): string { switch (typeof value) { case "string": return value; @@ -88,15 +113,20 @@ function stringify(ctx: ConsoleContext, value: any): string { if (ctx.has(value)) { 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); + return createArrayString(value as any[], ctx, level, maxLevel); } else { - return createObjectString(value, ctx); + return createObjectString(value, ctx, level, maxLevel); } default: return "[Not Implemented]"; @@ -104,25 +134,42 @@ function stringify(ctx: ConsoleContext, value: any): string { } // Print strings when they are inside of arrays or objects with quotes -// tslint:disable-next-line:no-any -function stringifyWithQuotes(ctx: ConsoleContext, value: any): string { +function stringifyWithQuotes( + ctx: ConsoleContext, + // tslint:disable-next-line:no-any + value: any, + level: number, + maxLevel: number +): string { switch (typeof value) { case "string": return `"${value}"`; default: - return stringify(ctx, value); + return stringify(ctx, value, level, maxLevel); } } -// tslint:disable-next-line:no-any -export function stringifyArgs(args: any[]): string { +export function stringifyArgs( + // tslint:disable-next-line:no-any + args: any[], + options: ConsoleOptions = {} +): string { const out: string[] = []; for (const a of args) { if (typeof a === "string") { out.push(a); } else { - // tslint:disable-next-line:no-any - out.push(stringify(new Set(), a)); + out.push( + // use default maximum depth for null or undefined argument + stringify( + // tslint:disable-next-line:no-any + new Set(), + a, + 0, + // tslint:disable-next-line:triple-equals + options.depth != undefined ? options.depth : DEFAULT_MAX_DEPTH + ) + ); } } return out.join(" "); @@ -141,6 +188,11 @@ export class Console { debug = this.log; info = this.log; + // tslint:disable-next-line:no-any + dir(obj: any, options: ConsoleOptions = {}) { + this.printFunc(stringifyArgs([obj], options)); + } + // tslint:disable-next-line:no-any warn(...args: any[]): void { this.printFunc(stringifyArgs(args), true); diff --git a/js/console_test.ts b/js/console_test.ts index 94a627a5ff..9e6a37256c 100644 --- a/js/console_test.ts +++ b/js/console_test.ts @@ -68,7 +68,7 @@ test(function consoleTestStringifyCircular() { nestedObj.o = circularObj; - 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 } } }`; + 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] } }`; assertEqual(stringify(1), "1"); assertEqual(stringify("s"), "s"); @@ -92,6 +92,23 @@ test(function consoleTestStringifyCircular() { ); }); +test(function consoleTestStringifyWithDepth() { + const nestedObj: any = { a: { b: { c: { d: { e: { f: 42 } } } } } }; + assertEqual( + stringifyArgs([nestedObj], { depth: 3 }), + "{ a: { b: { c: [object] } } }" + ); + assertEqual( + stringifyArgs([nestedObj], { depth: 4 }), + "{ a: { b: { c: { d: [object] } } } }" + ); + assertEqual(stringifyArgs([nestedObj], { depth: 0 }), "[object]"); + assertEqual( + stringifyArgs([nestedObj], { depth: null }), + "{ a: { b: [object] } }" + ); +}); + test(function consoleTestError() { class MyError extends Error { constructor(msg: string) {