From 2a69b0f4ed09fcbd01031fdeec758f3204bc5ca6 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 5 Apr 2019 22:02:57 +0200 Subject: [PATCH 1/6] util: improve inspect edge cases This makes sure `compact` number mode causes small proxies and map entries to be printed on a single line. It also fixed the line break calculation for `compact` mode when not set to `true`. It now also adds the additional whitespace, comma and quotes to the formula to prevent exceeding the `breakLength`. --- lib/internal/util/inspect.js | 92 +++++++++++++++--------------- test/parallel/test-util-inspect.js | 2 +- 2 files changed, 47 insertions(+), 47 deletions(-) diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index f25c2eaca7c454..274bcc99b94fc6 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -425,7 +425,8 @@ function formatProxy(ctx, proxy, recurseTimes) { formatValue(ctx, proxy[1], recurseTimes) ]; ctx.indentationLvl -= 2; - return reduceToSingleString(ctx, res, '', ['Proxy [', ']']); + return reduceToSingleString( + ctx, res, '', ['Proxy [', ']'], kArrayExtrasType, recurseTimes); } function findTypedConstructor(value) { @@ -783,37 +784,8 @@ function formatRaw(ctx, value, recurseTimes, typedArray) { } } - let combine = false; - if (typeof ctx.compact === 'number') { - // Memorize the original output length. In case the the output is grouped, - // prevent lining up the entries on a single line. - const entries = output.length; - // Group array elements together if the array contains at least six separate - // entries. - if (extrasType === kArrayExtrasType && output.length > 6) { - output = groupArrayElements(ctx, output); - } - // `ctx.currentDepth` is set to the most inner depth of the currently - // inspected object part while `recurseTimes` is the actual current depth - // that is inspected. - // - // Example: - // - // const a = { first: [ 1, 2, 3 ], second: { inner: [ 1, 2, 3 ] } } - // - // The deepest depth of `a` is 2 (a.second.inner) and `a.first` has a max - // depth of 1. - // - // Consolidate all entries of the local most inner depth up to - // `ctx.compact`, as long as the properties are smaller than - // `ctx.breakLength`. - if (ctx.currentDepth - recurseTimes < ctx.compact && - entries === output.length) { - combine = true; - } - } - - const res = reduceToSingleString(ctx, output, base, braces, combine); + const res = reduceToSingleString( + ctx, output, base, braces, extrasType, recurseTimes); const budget = ctx.budget[ctx.indentationLvl] || 0; const newLength = budget + res.length; ctx.budget[ctx.indentationLvl] = newLength; @@ -981,9 +953,10 @@ function formatBigInt(fn, value) { function formatPrimitive(fn, value, ctx) { if (typeof value === 'string') { if (ctx.compact !== true && - ctx.indentationLvl + value.length > ctx.breakLength && - value.length > kMinLineLength) { - const rawMaxLineLength = ctx.breakLength - ctx.indentationLvl; + ctx.indentationLvl + value.length + 4 > ctx.breakLength && + value.length > kMinLineLength) { + // Subtract the potential quotes, the space and the plus as well (4). + const rawMaxLineLength = ctx.breakLength - ctx.indentationLvl - 4; const maxLineLength = Math.max(rawMaxLineLength, kMinLineLength); const lines = Math.ceil(value.length / maxLineLength); const averageLineLength = Math.ceil(value.length / lines); @@ -1228,7 +1201,8 @@ function formatMapIterInner(ctx, recurseTimes, entries, state) { formatValue(ctx, entries[pos], recurseTimes), formatValue(ctx, entries[pos + 1], recurseTimes) ]; - output[i] = reduceToSingleString(ctx, res, '', ['[', ']']); + output[i] = reduceToSingleString( + ctx, res, '', ['[', ']'], kArrayExtrasType, recurseTimes); } } ctx.indentationLvl -= 2; @@ -1365,17 +1339,43 @@ function isBelowBreakLength(ctx, output, start, base) { return base === '' || !base.includes('\n'); } -function reduceToSingleString(ctx, output, base, braces, combine = false) { +function reduceToSingleString( + ctx, output, base, braces, extrasType, recurseTimes) { if (ctx.compact !== true) { - if (combine) { - // Line up all entries on a single line in case the entries do not exceed - // `breakLength`. Add 10 as constant to start next to all other factors - // that may reduce `breakLength`. - const start = output.length + ctx.indentationLvl + - braces[0].length + base.length + 10; - if (isBelowBreakLength(ctx, output, start, base)) { - return `${base ? `${base} ` : ''}${braces[0]} ${join(output, ', ')} ` + - braces[1]; + if (typeof ctx.compact === 'number') { + // Memorize the original output length. In case the the output is grouped, + // prevent lining up the entries on a single line. + const entries = output.length; + // Group array elements together if the array contains at least six + // separate entries. + if (extrasType === kArrayExtrasType && entries > 6) { + output = groupArrayElements(ctx, output); + } + // `ctx.currentDepth` is set to the most inner depth of the currently + // inspected object part while `recurseTimes` is the actual current depth + // that is inspected. + // + // Example: + // + // const a = { first: [ 1, 2, 3 ], second: { inner: [ 1, 2, 3 ] } } + // + // The deepest depth of `a` is 2 (a.second.inner) and `a.first` has a max + // depth of 1. + // + // Consolidate all entries of the local most inner depth up to + // `ctx.compact`, as long as the properties are smaller than + // `ctx.breakLength`. + if (ctx.currentDepth - recurseTimes < ctx.compact && + entries === output.length) { + // Line up all entries on a single line in case the entries do not + // exceed `breakLength`. Add 10 as constant to start next to all other + // factors that may reduce `breakLength`. + const start = output.length + ctx.indentationLvl + + braces[0].length + base.length + 10; + if (isBelowBreakLength(ctx, output, start, base)) { + return `${base ? `${base} ` : ''}${braces[0]} ${join(output, ', ')}` + + ` ${braces[1]}`; + } } } // Line up each entry on an individual line. diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index 1de812286a2c28..ee3c5588933a6c 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -1674,7 +1674,7 @@ assert.strictEqual(util.inspect('"\'${a}'), "'\"\\'${a}'"); return 'BazError'; } }, undefined] -].forEach(([Class, message, messages], i) => { +].forEach(([Class, message], i) => { console.log('Test %i', i); const foo = new Class(message); const name = foo.name; From 3b0b59b997c12f38debd42c859faa72b891096d2 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Fri, 5 Apr 2019 22:18:16 +0200 Subject: [PATCH 2/6] util: change inspect compact and breakLength default This changes the `compact` default from `true` to `3`. That mode changes arrays to be grouped together, it alignes multiple small entries on a single line in similar to `compact` true but only for the most inner three depth levels and the closing brackets are always on the same indentation as the openeing of the object instead of at the same line as another property. Big strings will be naturally broken into multiple lines instead of having one huge line that is not well readable. The output size mainly stays the same that way while it will be smaller in case of big arrays. Increasing the `breakLength` to 80 adjusts for most terminals that support at least 80 characters in a single line and improves the general output that way. A lot of calculations use the `breakLength` to determine the concrete behavior. --- lib/internal/util/inspect.js | 4 +- test/parallel/test-console-group.js | 6 +- test/parallel/test-console.js | 8 +- test/parallel/test-http2-stream-client.js | 2 +- test/parallel/test-repl-pretty-stack.js | 10 +- test/parallel/test-repl-underscore.js | 5 +- test/parallel/test-repl.js | 5 +- test/parallel/test-util-format.js | 95 ++++++----- test/parallel/test-util-inspect-proxy.js | 17 +- test/parallel/test-util-inspect.js | 153 +++++++++--------- ...test-whatwg-encoding-custom-textdecoder.js | 2 +- .../test-whatwg-url-custom-inspect.js | 32 ++-- 12 files changed, 185 insertions(+), 154 deletions(-) diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index 274bcc99b94fc6..c54ecbc7482bb5 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -96,8 +96,8 @@ const inspectDefaultOptions = Object.seal({ customInspect: true, showProxy: false, maxArrayLength: 100, - breakLength: 60, - compact: true, + breakLength: 80, + compact: 3, sorted: false, getters: false }); diff --git a/test/parallel/test-console-group.js b/test/parallel/test-console-group.js index 9ab6c9db7e8c16..c3c70bfa248715 100644 --- a/test/parallel/test-console-group.js +++ b/test/parallel/test-console-group.js @@ -122,11 +122,13 @@ function teardown() { const expectedOut = 'not indented\n' + ' indented\n' + ' also indented\n' + - " { also: 'a',\n" + + ' {\n' + + " also: 'a',\n" + " multiline: 'object',\n" + " should: 'be',\n" + " indented: 'properly',\n" + - " kthx: 'bai' }\n"; + " kthx: 'bai'\n" + + ' }\n'; const expectedErr = ''; c.log('not indented'); diff --git a/test/parallel/test-console.js b/test/parallel/test-console.js index 8f259b7c940b76..e2c5291d915c1d 100644 --- a/test/parallel/test-console.js +++ b/test/parallel/test-console.js @@ -234,11 +234,11 @@ for (const expected of expectedStrings) { } assert.strictEqual(strings.shift(), - "{ foo: 'bar',\n [Symbol(nodejs.util.inspect.custom)]: " + - '[Function: [nodejs.util.inspect.custom]] }\n'); + "{\n foo: 'bar',\n [Symbol(nodejs.util.inspect.custom)]:" + + ' [Function: [nodejs.util.inspect.custom]]\n}\n'); assert.strictEqual(strings.shift(), - "{ foo: 'bar',\n [Symbol(nodejs.util.inspect.custom)]: " + - '[Function: [nodejs.util.inspect.custom]] }\n'); + "{\n foo: 'bar',\n [Symbol(nodejs.util.inspect.custom)]:" + + ' [Function: [nodejs.util.inspect.custom]]\n}\n'); assert.ok(strings.shift().includes('foo: [Object]')); assert.strictEqual(strings.shift().includes('baz'), false); assert.strictEqual(strings.shift(), 'inspect inspect\n'); diff --git a/test/parallel/test-http2-stream-client.js b/test/parallel/test-http2-stream-client.js index 3e6c6b2a8a1b5e..59e88ece98d2b0 100644 --- a/test/parallel/test-http2-stream-client.js +++ b/test/parallel/test-http2-stream-client.js @@ -11,7 +11,7 @@ const server = http2.createServer(); server.on('stream', common.mustCall((stream) => { assert.strictEqual(stream.aborted, false); const insp = util.inspect(stream); - assert.ok(/Http2Stream { id/.test(insp)); + assert.ok(/Http2Stream {/.test(insp)); assert.ok(/ state:/.test(insp)); assert.ok(/ readableState:/.test(insp)); assert.ok(/ writableState:/.test(insp)); diff --git a/test/parallel/test-repl-pretty-stack.js b/test/parallel/test-repl-pretty-stack.js index 454c9026481cb5..8beb48fd689dc7 100644 --- a/test/parallel/test-repl-pretty-stack.js +++ b/test/parallel/test-repl-pretty-stack.js @@ -30,7 +30,7 @@ function run({ command, expected, ...extraREPLOptions }) { const tests = [ { - // test .load for a file that throws + // Test .load for a file that throws. command: `.load ${fixtures.path('repl-pretty-stack.js')}`, expected: 'Thrown:\nError: Whoops!\n at repl:9:24\n' + ' at d (repl:12:3)\n at c (repl:9:3)\n' + @@ -48,20 +48,20 @@ const tests = [ { command: '(() => { const err = Error(\'Whoops!\'); ' + 'err.foo = \'bar\'; throw err; })()', - expected: 'Thrown:\n{ Error: Whoops!\n at repl:1:22\n foo: \'bar\' }\n', + expected: "Thrown:\nError: Whoops!\n at repl:1:22 {\n foo: 'bar'\n}\n", }, { command: '(() => { const err = Error(\'Whoops!\'); ' + 'err.foo = \'bar\'; throw err; })()', - expected: 'Thrown:\n{ Error: Whoops!\n at repl:1:22\n foo: ' + - "\u001b[32m'bar'\u001b[39m }\n", + expected: 'Thrown:\nError: Whoops!\n at repl:1:22 {\n foo: ' + + "\u001b[32m'bar'\u001b[39m\n}\n", useColors: true }, { command: 'foo = bar;', expected: 'Thrown:\nReferenceError: bar is not defined\n' }, - // test anonymous IIFE + // Test anonymous IIFE. { command: '(function() { throw new Error(\'Whoops!\'); })()', expected: 'Thrown:\nError: Whoops!\n at repl:1:21\n' diff --git a/test/parallel/test-repl-underscore.js b/test/parallel/test-repl-underscore.js index a63b7501e68bf6..2e0865f5a75b16 100644 --- a/test/parallel/test-repl-underscore.js +++ b/test/parallel/test-repl-underscore.js @@ -179,12 +179,13 @@ function testError() { // The sync error, with individual property echoes 'Thrown:', - /^{ Error: ENOENT: no such file or directory, scandir '.*nonexistent.*'/, + /^Error: ENOENT: no such file or directory, scandir '.*nonexistent\?'/, /Object\.readdirSync/, /^ errno: -(2|4058),$/, " syscall: 'scandir',", " code: 'ENOENT',", - " path: '/nonexistent?' }", + " path: '/nonexistent?'", + '}', "'ENOENT'", "'scandir'", diff --git a/test/parallel/test-repl.js b/test/parallel/test-repl.js index 3cfc105201a9e3..f9f5d196aa3e6b 100644 --- a/test/parallel/test-repl.js +++ b/test/parallel/test-repl.js @@ -534,7 +534,7 @@ const errorTests = [ send: 'require("internal/repl")', expect: [ 'Thrown:', - /^{ Error: Cannot find module 'internal\/repl'/, + /^Error: Cannot find module 'internal\/repl'/, /^Require stack:/, /^- /, /^ at .*/, @@ -542,7 +542,8 @@ const errorTests = [ /^ at .*/, /^ at .*/, " code: 'MODULE_NOT_FOUND',", - " requireStack: [ '' ] }" + " requireStack: [ '' ]", + '}' ] }, // REPL should handle quotes within regexp literal in multiline mode diff --git a/test/parallel/test-util-format.js b/test/parallel/test-util-format.js index da8e0cea8b23aa..206b66e17c3be0 100644 --- a/test/parallel/test-util-format.js +++ b/test/parallel/test-util-format.js @@ -165,59 +165,74 @@ assert.strictEqual(util.format('%o', 42), '42'); assert.strictEqual(util.format('%o', 'foo'), '\'foo\''); assert.strictEqual( util.format('%o', obj), - '{ foo: \'bar\',\n' + + '{\n' + + ' foo: \'bar\',\n' + ' foobar: 1,\n' + - ' func:\n' + - ' { [Function: func]\n' + - ' [length]: 0,\n' + - ' [name]: \'func\',\n' + - ' [prototype]: func { [constructor]: [Circular] } } }'); + ' func: [Function: func] {\n' + + ' [length]: 0,\n' + + ' [name]: \'func\',\n' + + ' [prototype]: func { [constructor]: [Circular] }\n' + + ' }\n' + + '}'); assert.strictEqual( util.format('%o', nestedObj2), - '{ foo: \'bar\',\n' + + '{\n' + + ' foo: \'bar\',\n' + ' foobar: 1,\n' + - ' func:\n' + - ' [ { a:\n' + - ' { [Function: a]\n' + - ' [length]: 0,\n' + - ' [name]: \'a\',\n' + - ' [prototype]: a { [constructor]: [Circular] } } },\n' + - ' [length]: 1 ] }'); + ' func: [\n' + + ' {\n' + + ' a: [Function: a] {\n' + + ' [length]: 0,\n' + + ' [name]: \'a\',\n' + + ' [prototype]: a { [constructor]: [Circular] }\n' + + ' }\n' + + ' },\n' + + ' [length]: 1\n' + + ' ]\n' + + '}'); assert.strictEqual( util.format('%o', nestedObj), - '{ foo: \'bar\',\n' + - ' foobar:\n' + - ' { foo: \'bar\',\n' + - ' func:\n' + - ' { [Function: func]\n' + - ' [length]: 0,\n' + - ' [name]: \'func\',\n' + - ' [prototype]: func { [constructor]: [Circular] } } } }'); + '{\n' + + ' foo: \'bar\',\n' + + ' foobar: {\n' + + ' foo: \'bar\',\n' + + ' func: [Function: func] {\n' + + ' [length]: 0,\n' + + ' [name]: \'func\',\n' + + ' [prototype]: func { [constructor]: [Circular] }\n' + + ' }\n' + + ' }\n' + + '}'); assert.strictEqual( util.format('%o %o', obj, obj), - '{ foo: \'bar\',\n' + + '{\n' + + ' foo: \'bar\',\n' + ' foobar: 1,\n' + - ' func:\n' + - ' { [Function: func]\n' + - ' [length]: 0,\n' + - ' [name]: \'func\',\n' + - ' [prototype]: func { [constructor]: [Circular] } } }' + - ' { foo: \'bar\',\n' + + ' func: [Function: func] {\n' + + ' [length]: 0,\n' + + ' [name]: \'func\',\n' + + ' [prototype]: func { [constructor]: [Circular] }\n' + + ' }\n' + + '} {\n' + + ' foo: \'bar\',\n' + ' foobar: 1,\n' + - ' func:\n' + - ' { [Function: func]\n' + - ' [length]: 0,\n' + - ' [name]: \'func\',\n' + - ' [prototype]: func { [constructor]: [Circular] } } }'); + ' func: [Function: func] {\n' + + ' [length]: 0,\n' + + ' [name]: \'func\',\n' + + ' [prototype]: func { [constructor]: [Circular] }\n' + + ' }\n' + + '}'); assert.strictEqual( util.format('%o %o', obj), - '{ foo: \'bar\',\n' + + '{\n' + + ' foo: \'bar\',\n' + ' foobar: 1,\n' + - ' func:\n' + - ' { [Function: func]\n' + - ' [length]: 0,\n' + - ' [name]: \'func\',\n' + - ' [prototype]: func { [constructor]: [Circular] } } } %o'); + ' func: [Function: func] {\n' + + ' [length]: 0,\n' + + ' [name]: \'func\',\n' + + ' [prototype]: func { [constructor]: [Circular] }\n' + + ' }\n' + + '} %o'); assert.strictEqual(util.format('%O'), '%O'); assert.strictEqual(util.format('%O', 42), '42'); diff --git a/test/parallel/test-util-inspect-proxy.js b/test/parallel/test-util-inspect-proxy.js index 97d2006408acdc..c20af7450a426e 100644 --- a/test/parallel/test-util-inspect-proxy.js +++ b/test/parallel/test-util-inspect-proxy.js @@ -51,7 +51,8 @@ assert.strictEqual( util.inspect(proxyObj, opts), 'Proxy [\n' + ' [ 1, 2, 3 ],\n' + - ' { getPrototypeOf: [Function: getPrototypeOf],\n' + + ' {\n' + + ' getPrototypeOf: [Function: getPrototypeOf],\n' + ' setPrototypeOf: [Function: setPrototypeOf],\n' + ' isExtensible: [Function: isExtensible],\n' + ' preventExtensions: [Function: preventExtensions],\n' + @@ -63,7 +64,9 @@ assert.strictEqual( ' deleteProperty: [Function: deleteProperty],\n' + ' ownKeys: [Function: ownKeys],\n' + ' apply: [Function: apply],\n' + - ' construct: [Function: construct] } ]' + ' construct: [Function: construct]\n' + + ' }\n' + + ']' ); // Using getProxyDetails with non-proxy returns undefined @@ -89,14 +92,18 @@ const expected3 = 'Proxy [ Proxy [ Proxy [ {}, {} ], {} ], Proxy [ {}, {} ] ]'; const expected4 = 'Proxy [ Proxy [ {}, {} ], Proxy [ Proxy [ {}, {} ], {} ] ]'; const expected5 = 'Proxy [\n ' + 'Proxy [ Proxy [ Proxy [Array], {} ], Proxy [ {}, {} ] ],\n' + - ' Proxy [ Proxy [ {}, {} ], Proxy [ Proxy [Array], {} ] ] ]'; + ' Proxy [ Proxy [ {}, {} ], Proxy [ Proxy [Array], {} ] ]' + + '\n]'; const expected6 = 'Proxy [\n' + ' Proxy [\n' + ' Proxy [ Proxy [Array], Proxy [Array] ],\n' + - ' Proxy [ Proxy [Array], Proxy [Array] ] ],\n' + + ' Proxy [ Proxy [Array], Proxy [Array] ]\n' + + ' ],\n' + ' Proxy [\n' + ' Proxy [ Proxy [Array], Proxy [Array] ],\n' + - ' Proxy [ Proxy [Array], Proxy [Array] ] ] ]'; + ' Proxy [ Proxy [Array], Proxy [Array] ]\n' + + ' ]\n' + + ']'; assert.strictEqual( util.inspect(proxy1, { showProxy: true, depth: null }), expected1); diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index ee3c5588933a6c..140fb22f0dfd48 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -49,7 +49,7 @@ assert.strictEqual(util.inspect(new Date('')), (new Date('')).toString()); assert.strictEqual(util.inspect('\n\u0001'), "'\\n\\u0001'"); assert.strictEqual( util.inspect(`${Array(75).fill(1)}'\n\u001d\n\u0003`), - `"${Array(75).fill(1)}'\\n\\u001d\\n\\u0003"` + `"${Array(75).fill(1)}'\\n" +\n '\\u001d\\n\\u0003'` ); assert.strictEqual(util.inspect([]), '[]'); assert.strictEqual(util.inspect(Object.create([])), 'Array {}'); @@ -72,7 +72,7 @@ assert.strictEqual(util.inspect({ 'a': { 'b': { 'c': { 'd': 2 } } } }), '{ a: { b: { c: [Object] } } }'); assert.strictEqual( util.inspect({ 'a': { 'b': { 'c': { 'd': 2 } } } }, false, null), - '{ a: { b: { c: { d: 2 } } } }'); + '{\n a: { b: { c: { d: 2 } } }\n}'); assert.strictEqual(util.inspect([1, 2, 3], true), '[ 1, 2, 3, [length]: 3 ]'); assert.strictEqual(util.inspect({ 'a': { 'b': { 'c': 2 } } }, false, 0), '{ a: [Object] }'); @@ -96,7 +96,7 @@ assert.strictEqual( Object.assign(new String('hello'), { [Symbol('foo')]: 123 }), { showHidden: true } ), - "{ [String: 'hello'] [length]: 5, [Symbol(foo)]: 123 }" + "[String: 'hello'] { [length]: 5, [Symbol(foo)]: 123 }" ); assert.strictEqual(util.inspect((new JSStream())._externalStream), @@ -127,9 +127,8 @@ assert(!/Object/.test( 'DataView {\n' + ' byteLength: 2,\n' + ' byteOffset: 1,\n' + - ' buffer:\n' + - ' ArrayBuffer { [Uint8Contents]: ' + - '<01 02 03 04>, byteLength: 4 } }'); + ' buffer: ArrayBuffer {' + + ' [Uint8Contents]: <01 02 03 04>, byteLength: 4 }\n}'); assert.strictEqual( util.inspect(ab, showHidden), 'ArrayBuffer { [Uint8Contents]: <01 02 03 04>, byteLength: 4 }' @@ -138,9 +137,8 @@ assert(!/Object/.test( 'DataView {\n' + ' byteLength: 2,\n' + ' byteOffset: 1,\n' + - ' buffer:\n' + - ' ArrayBuffer { [Uint8Contents]: ' + - '<01 02 03 04>, byteLength: 4 } }'); + ' buffer: ArrayBuffer { [Uint8Contents]: ' + + '<01 02 03 04>, byteLength: 4 }\n}'); ab.x = 42; dv.y = 1337; assert.strictEqual(util.inspect(ab, showHidden), @@ -150,10 +148,9 @@ assert(!/Object/.test( 'DataView {\n' + ' byteLength: 2,\n' + ' byteOffset: 1,\n' + - ' buffer:\n' + - ' ArrayBuffer { [Uint8Contents]: <01 02 03 04>, ' + - 'byteLength: 4, x: 42 },\n' + - ' y: 1337 }'); + ' buffer: ArrayBuffer { [Uint8Contents]: <01 02 03 04>,' + + ' byteLength: 4, x: 42 },\n' + + ' y: 1337\n}'); } // Now do the same checks but from a different context. @@ -169,9 +166,8 @@ assert(!/Object/.test( 'DataView {\n' + ' byteLength: 2,\n' + ' byteOffset: 1,\n' + - ' buffer:\n' + - ' ArrayBuffer { [Uint8Contents]: <00 00 00 00>, ' + - 'byteLength: 4 } }'); + ' buffer: ArrayBuffer { [Uint8Contents]: <00 00 00 00>,' + + ' byteLength: 4 }\n}'); assert.strictEqual( util.inspect(ab, showHidden), 'ArrayBuffer { [Uint8Contents]: <00 00 00 00>, byteLength: 4 }' @@ -180,9 +176,8 @@ assert(!/Object/.test( 'DataView {\n' + ' byteLength: 2,\n' + ' byteOffset: 1,\n' + - ' buffer:\n' + - ' ArrayBuffer { [Uint8Contents]: <00 00 00 00>, ' + - 'byteLength: 4 } }'); + ' buffer: ArrayBuffer { [Uint8Contents]: <00 00 00 00>,' + + ' byteLength: 4 }\n}'); ab.x = 42; dv.y = 1337; assert.strictEqual(util.inspect(ab, showHidden), @@ -192,10 +187,9 @@ assert(!/Object/.test( 'DataView {\n' + ' byteLength: 2,\n' + ' byteOffset: 1,\n' + - ' buffer:\n' + - ' ArrayBuffer { [Uint8Contents]: <00 00 00 00>,' + + ' buffer: ArrayBuffer { [Uint8Contents]: <00 00 00 00>,' + ' byteLength: 4, x: 42 },\n' + - ' y: 1337 }'); + ' y: 1337\n}'); } [ Float32Array, @@ -221,7 +215,7 @@ assert(!/Object/.test( ` [length]: ${length},\n` + ` [byteLength]: ${byteLength},\n` + ' [byteOffset]: 0,\n' + - ` [buffer]: ArrayBuffer { byteLength: ${byteLength} } ]`); + ` [buffer]: ArrayBuffer { byteLength: ${byteLength} }\n]`); assert.strictEqual( util.inspect(array, false), `${constructor.name} [ 65, 97 ]` @@ -255,7 +249,7 @@ assert(!/Object/.test( ` [length]: ${length},\n` + ` [byteLength]: ${byteLength},\n` + ' [byteOffset]: 0,\n' + - ` [buffer]: ArrayBuffer { byteLength: ${byteLength} } ]`); + ` [buffer]: ArrayBuffer { byteLength: ${byteLength} }\n]`); assert.strictEqual( util.inspect(array, false), `${constructor.name} [ 65, 97 ]` @@ -397,11 +391,12 @@ assert.strictEqual( delete arr[0]; delete arr[10]; assert.strictEqual(util.inspect(arr), - ['[ <4294967294 empty items>,', + ['[', + '<4294967294 empty items>,', 'true,', "'4294967296': true,", "'4294967295': true,", - "'4294967297': true ]" + "'4294967297': true\n]" ].join('\n ')); } @@ -409,21 +404,21 @@ assert.strictEqual( { const value = () => {}; value.aprop = 42; - assert.strictEqual(util.inspect(value), '{ [Function: value] aprop: 42 }'); + assert.strictEqual(util.inspect(value), '[Function: value] { aprop: 42 }'); } // Anonymous function with properties. { const value = (() => function() {})(); value.aprop = 42; - assert.strictEqual(util.inspect(value), '{ [Function] aprop: 42 }'); + assert.strictEqual(util.inspect(value), '[Function] { aprop: 42 }'); } // Regular expressions with properties. { const value = /123/ig; value.aprop = 42; - assert.strictEqual(util.inspect(value), '{ /123/gi aprop: 42 }'); + assert.strictEqual(util.inspect(value), '/123/gi { aprop: 42 }'); } // Dates with properties. @@ -431,7 +426,7 @@ assert.strictEqual( const value = new Date('Sun, 14 Feb 2010 11:48:40 GMT'); value.aprop = 42; assert.strictEqual(util.inspect(value), - '{ 2010-02-14T11:48:40.000Z aprop: 42 }'); + '2010-02-14T11:48:40.000Z { aprop: 42 }'); } // Test the internal isDate implementation. @@ -649,7 +644,7 @@ assert.strictEqual(util.inspect(Object.create(Date.prototype)), 'Date {}'); { const x = { [util.inspect.custom]: util.inspect }; assert(util.inspect(x).includes( - '[Symbol(nodejs.util.inspect.custom)]:\n { [Function: inspect]')); + '[Symbol(nodejs.util.inspect.custom)]: [Function: inspect] {\n')); } // `util.inspect` should display the escaped value of a key. @@ -814,8 +809,10 @@ util.inspect({ hasOwnProperty: null }); // Returning `this` from a custom inspection function works. const subject = { a: 123, [util.inspect.custom]() { return this; } }; const UIC = 'nodejs.util.inspect.custom'; - assert.strictEqual(util.inspect(subject), - `{ a: 123,\n [Symbol(${UIC})]: [Function: [${UIC}]] }`); + assert.strictEqual( + util.inspect(subject), + `{\n a: 123,\n [Symbol(${UIC})]: [Function: [${UIC}]]\n}` + ); } // Verify that it's possible to use the stylize function to manipulate input. @@ -866,23 +863,23 @@ assert.strictEqual(util.inspect(new Number(13.37)), '[Number: 13.37]'); { const str = new String('baz'); str.foo = 'bar'; - assert.strictEqual(util.inspect(str), "{ [String: 'baz'] foo: 'bar' }"); + assert.strictEqual(util.inspect(str), "[String: 'baz'] { foo: 'bar' }"); const bool = new Boolean(true); bool.foo = 'bar'; - assert.strictEqual(util.inspect(bool), "{ [Boolean: true] foo: 'bar' }"); + assert.strictEqual(util.inspect(bool), "[Boolean: true] { foo: 'bar' }"); const num = new Number(13.37); num.foo = 'bar'; - assert.strictEqual(util.inspect(num), "{ [Number: 13.37] foo: 'bar' }"); + assert.strictEqual(util.inspect(num), "[Number: 13.37] { foo: 'bar' }"); const sym = Object(Symbol('foo')); sym.foo = 'bar'; - assert.strictEqual(util.inspect(sym), "{ [Symbol: Symbol(foo)] foo: 'bar' }"); + assert.strictEqual(util.inspect(sym), "[Symbol: Symbol(foo)] { foo: 'bar' }"); const big = Object(BigInt(55)); big.foo = 'bar'; - assert.strictEqual(util.inspect(big), "{ [BigInt: 55n] foo: 'bar' }"); + assert.strictEqual(util.inspect(big), "[BigInt: 55n] { foo: 'bar' }"); } // Test es6 Symbol. @@ -1041,17 +1038,18 @@ if (typeof Symbol !== 'undefined') { // Test alignment of items in container. // Assumes that the first numeric character is the start of an item. { - function checkAlignment(container) { + function checkAlignment(container, start, lineX, end) { const lines = util.inspect(container).split('\n'); - const numRE = /\d/; - let pos; - lines.forEach((line) => { - const npos = line.search(numRE); - if (npos !== -1) { - if (pos !== undefined) { - assert.strictEqual(pos, npos); - } - pos = npos; + lines.forEach((line, i) => { + if (i === 0) { + assert.strictEqual(line, start); + } else if (i === lines.length - 1) { + assert.strictEqual(line, end); + } else { + let expected = lineX.replace('X', i - 1); + if (i !== lines.length - 2) + expected += ','; + assert.strictEqual(line, expected); } }); } @@ -1066,10 +1064,12 @@ if (typeof Symbol !== 'undefined') { obj[prop] = null; }); - checkAlignment(bigArray); - checkAlignment(obj); - checkAlignment(new Set(bigArray)); - checkAlignment(new Map(bigArray.map((number) => [number, null]))); + checkAlignment(obj, '{', " 'X': null", '}'); + checkAlignment(new Set(bigArray), 'Set {', ' X', '}'); + checkAlignment( + new Map(bigArray.map((number) => [number, null])), + 'Map {', ' X => null', '}' + ); } @@ -1151,8 +1151,8 @@ if (typeof Symbol !== 'undefined') { // https://github.com/nodejs/node/pull/6334 is backported. { const x = new Array(101).fill(); - assert(util.inspect(x).endsWith('1 more item ]')); - assert(!util.inspect(x, { maxArrayLength: 101 }).endsWith('1 more item ]')); + assert(util.inspect(x).endsWith('1 more item\n]')); + assert(!util.inspect(x, { maxArrayLength: 101 }).endsWith('1 more item\n]')); assert.strictEqual( util.inspect(x, { maxArrayLength: -1 }), '[ ... 101 more items ]' @@ -1165,7 +1165,7 @@ if (typeof Symbol !== 'undefined') { const x = Array(101); assert.strictEqual(util.inspect(x, { maxArrayLength: 0 }), '[ ... 101 more items ]'); - assert(!util.inspect(x, { maxArrayLength: null }).endsWith('1 more item ]')); + assert(!util.inspect(x, { maxArrayLength: null }).endsWith('1 more item\n]')); assert(!util.inspect( x, { maxArrayLength: Infinity } ).endsWith('1 more item ]')); @@ -1173,12 +1173,12 @@ if (typeof Symbol !== 'undefined') { { const x = new Uint8Array(101); - assert(util.inspect(x).endsWith('1 more item ]')); - assert(!util.inspect(x, { maxArrayLength: 101 }).endsWith('1 more item ]')); + assert(util.inspect(x).endsWith('1 more item\n]')); + assert(!util.inspect(x, { maxArrayLength: 101 }).endsWith('1 more item\n]')); assert.strictEqual(util.inspect(x, { maxArrayLength: 0 }), 'Uint8Array [ ... 101 more items ]'); - assert(!util.inspect(x, { maxArrayLength: null }).endsWith('1 more item ]')); - assert(util.inspect(x, { maxArrayLength: Infinity }).endsWith(' 0 ]')); + assert(!util.inspect(x, { maxArrayLength: null }).endsWith('1 more item\n]')); + assert(util.inspect(x, { maxArrayLength: Infinity }).endsWith(' 0, 0\n]')); } { @@ -1190,9 +1190,11 @@ if (typeof Symbol !== 'undefined') { const twoLines = util.inspect(obj, { breakLength: breakpoint }); assert.strictEqual(oneLine, "{ foo: 'abc', bar: 'xyz' }"); - assert.strictEqual(oneLine, - util.inspect(obj, { breakLength: breakpoint + 1 })); - assert.strictEqual(twoLines, "{ foo: 'abc',\n bar: 'xyz' }"); + assert.strictEqual( + util.inspect(obj, { breakLength: breakpoint + 1 }), + twoLines + ); + assert.strictEqual(twoLines, "{\n foo: 'abc',\n bar: 'xyz'\n}"); } // util.inspect.defaultOptions tests. @@ -1545,7 +1547,9 @@ util.inspect(process); assert.strict.equal(out, expected); - out = util.inspect(map, { showHidden: true, depth: 9, breakLength: 4 }); + out = util.inspect(map, { + showHidden: true, depth: 9, breakLength: 4, compact: true + }); expected = [ 'Map {', ' Promise {', @@ -1712,18 +1716,18 @@ assert.strictEqual(util.inspect('"\'${a}'), "'\"\\'${a}'"); delete foo[Symbol.toStringTag]; assert( util.inspect(foo).startsWith( - `{ [${name}: null prototype]${message ? `: ${message}` : '\n'}`), + `[${name}: null prototype]${message ? `: ${message}` : '\n'}`), util.inspect(foo) ); foo.stack = 'This is a stack'; assert.strictEqual( util.inspect(foo), - '{ [[Error: null prototype]: This is a stack] bar: true }' + '[[Error: null prototype]: This is a stack] { bar: true }' ); foo.stack = stack.split('\n')[0]; assert.strictEqual( util.inspect(foo), - `{ [[${name}: null prototype]${message ? `: ${message}` : ''}] bar: true }` + `[[${name}: null prototype]${message ? `: ${message}` : ''}] { bar: true }` ); }); @@ -1789,12 +1793,12 @@ assert.strictEqual(util.inspect('"\'${a}'), "'\"\\'${a}'"); [new BigUint64Array(2), '[BigUint64Array: null prototype] [ 0n, 0n ]'], [new ArrayBuffer(16), '[ArrayBuffer: null prototype] {\n' + ' [Uint8Contents]: <00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00>,\n' + - ' byteLength: undefined }'], + ' byteLength: undefined\n}'], [new DataView(new ArrayBuffer(16)), '[DataView: null prototype] {\n byteLength: undefined,\n ' + - 'byteOffset: undefined,\n buffer: undefined }'], + 'byteOffset: undefined,\n buffer: undefined\n}'], [new SharedArrayBuffer(2), '[SharedArrayBuffer: null prototype] ' + - '{ [Uint8Contents]: <00 00>, byteLength: undefined }'], + '{\n [Uint8Contents]: <00 00>,\n byteLength: undefined\n}'], [/foobar/, '[RegExp: null prototype] /foobar/'], [new Date('Sun, 14 Feb 2010 11:48:40 GMT'), '[Date: null prototype] 2010-02-14T11:48:40.000Z'] @@ -1815,7 +1819,9 @@ assert.strictEqual(util.inspect('"\'${a}'), "'\"\\'${a}'"); [RegExp, ['foobar', 'g'], '/foobar/g'], [WeakSet, [[{}]], '{ }'], [WeakMap, [[[{}, {}]]], '{ }'], - [BigInt64Array, [10], '[ 0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n ]'], + [BigInt64Array, + [10], + '[\n 0n, 0n, 0n,\n 0n, 0n, 0n,\n 0n, 0n, 0n,\n 0n\n]'], [Date, ['Sun, 14 Feb 2010 11:48:40 GMT'], '2010-02-14T11:48:40.000Z'], [Date, ['invalid_date'], 'Invalid Date'] ].forEach(([base, input, rawExpected]) => { @@ -1996,8 +2002,8 @@ assert.strictEqual( getset.foo = new Set([[{ a: true }, 2, {}], 'foobar', { x: 1 }]); assert.strictEqual( inspect(getset, { getters: true }), - '{ foo: [Getter/Setter] Set { [ [Object], 2, {} ], ' + - "'foobar', { x: 1 } },\n inc: [Getter: NaN] }"); + '{\n foo: [Getter/Setter] Set { [ [Object], 2, {} ], ' + + "'foobar', { x: 1 } },\n inc: [Getter: NaN]\n}"); } // Check compact number mode. @@ -2029,8 +2035,7 @@ assert.strictEqual( long: Array(9).fill('This text is too long for grouping!') }; - let out = util.inspect(obj, { compact: 3, depth: 10 }); - + let out = util.inspect(obj, { compact: 3, depth: 10, breakLength: 60 }); let expected = [ '{', ' a: {', diff --git a/test/parallel/test-whatwg-encoding-custom-textdecoder.js b/test/parallel/test-whatwg-encoding-custom-textdecoder.js index 63abd2506a5701..d20f687ddd3534 100644 --- a/test/parallel/test-whatwg-encoding-custom-textdecoder.js +++ b/test/parallel/test-whatwg-encoding-custom-textdecoder.js @@ -108,7 +108,7 @@ if (common.hasIntl) { assert.strictEqual( util.inspect(dec, { showHidden: true }), 'TextDecoder {\n encoding: \'utf-8\',\n fatal: false,\n ' + - 'ignoreBOM: true,\n [Symbol(flags)]: 4,\n [Symbol(handle)]: {} }' + 'ignoreBOM: true,\n [Symbol(flags)]: 4,\n [Symbol(handle)]: {}\n}' ); } else { assert.strictEqual( diff --git a/test/parallel/test-whatwg-url-custom-inspect.js b/test/parallel/test-whatwg-url-custom-inspect.js index 21c13582254e94..318b8b66d5672a 100644 --- a/test/parallel/test-whatwg-url-custom-inspect.js +++ b/test/parallel/test-whatwg-url-custom-inspect.js @@ -17,8 +17,7 @@ const url = new URL('https://username:password@host.name:8080/path/name/?que=ry# assert.strictEqual( util.inspect(url), `URL { - href: - 'https://username:password@host.name:8080/path/name/?que=ry#hash', + href: 'https://username:password@host.name:8080/path/name/?que=ry#hash', origin: 'https://host.name:8080', protocol: 'https:', username: 'username', @@ -29,13 +28,13 @@ assert.strictEqual( pathname: '/path/name/', search: '?que=ry', searchParams: URLSearchParams { 'que' => 'ry' }, - hash: '#hash' }`); + hash: '#hash' +}`); assert.strictEqual( util.inspect(url, { showHidden: true }), `URL { - href: - 'https://username:password@host.name:8080/path/name/?que=ry#hash', + href: 'https://username:password@host.name:8080/path/name/?que=ry#hash', origin: 'https://host.name:8080', protocol: 'https:', username: 'username', @@ -49,17 +48,18 @@ assert.strictEqual( hash: '#hash', cannotBeBase: false, special: true, - [Symbol(context)]: - URLContext { - flags: 2032, - scheme: 'https:', - username: 'username', - password: 'password', - host: 'host.name', - port: 8080, - path: [ 'path', 'name', '', [length]: 3 ], - query: 'que=ry', - fragment: 'hash' } }`); + [Symbol(context)]: URLContext { + flags: 2032, + scheme: 'https:', + username: 'username', + password: 'password', + host: 'host.name', + port: 8080, + path: [ 'path', 'name', '', [length]: 3 ], + query: 'que=ry', + fragment: 'hash' + } +}`); assert.strictEqual( util.inspect({ a: url }, { depth: 0 }), From fe71ce1f069d64100c1894a79755fbcba9a98e97 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 6 Apr 2019 01:51:02 +0200 Subject: [PATCH 3/6] fixup! util: change inspect compact and breakLength default --- test/parallel/test-whatwg-encoding-custom-textdecoder.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/parallel/test-whatwg-encoding-custom-textdecoder.js b/test/parallel/test-whatwg-encoding-custom-textdecoder.js index d20f687ddd3534..e3779a945de4a9 100644 --- a/test/parallel/test-whatwg-encoding-custom-textdecoder.js +++ b/test/parallel/test-whatwg-encoding-custom-textdecoder.js @@ -113,10 +113,10 @@ if (common.hasIntl) { } else { assert.strictEqual( util.inspect(dec, { showHidden: true }), - 'TextDecoder {\n encoding: \'utf-8\',\n fatal: false,\n ' + - 'ignoreBOM: true,\n [Symbol(flags)]: 4,\n [Symbol(handle)]:\n ' + - 'StringDecoder {\n encoding: \'utf8\',\n ' + - '[Symbol(kNativeDecoder)]: } }' + "TextDecoder {\n encoding: 'utf-8',\n fatal: false,\n " + + 'ignoreBOM: true,\n [Symbol(flags)]: 4,\n [Symbol(handle)]: ' + + "StringDecoder {\n encoding: 'utf8',\n " + + '[Symbol(kNativeDecoder)]: \n }\n}' ); } } From be24b748e1be584c7b7c73cdbb7c31f74574b67f Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sat, 6 Apr 2019 02:36:40 +0200 Subject: [PATCH 4/6] fixup! util: change inspect compact and breakLength default --- doc/api/util.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/doc/api/util.md b/doc/api/util.md index 6a8b30ef7ae1b6..051dbc1570a18e 100644 --- a/doc/api/util.md +++ b/doc/api/util.md @@ -390,6 +390,10 @@ stream.write('With ES6');