Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

util: change inspect compact default #27109

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions doc/api/util.md
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,10 @@ stream.write('With ES6');
<!-- YAML
added: v0.3.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/27109
description: The `compact` options default is changed to `3` and the
`breakLength` options default is changed to `80`.
- version: v11.11.0
pr-url: https://github.com/nodejs/node/pull/26269
description: The `compact` option accepts numbers for a new output mode.
Expand Down Expand Up @@ -463,16 +467,17 @@ changes:
[`TypedArray`][], [`WeakMap`][] and [`WeakSet`][] elements to include when
formatting. Set to `null` or `Infinity` to show all elements. Set to `0` or
negative to show no elements. **Default:** `100`.
* `breakLength` {integer} The length at which an object's keys are split
across multiple lines. Set to `Infinity` to format an object as a single
line. **Default:** `60` for legacy compatibility.
* `breakLength` {integer} The length at which input values are split across
multiple lines. Set to `Infinity` to format the input as a single line
(in combination with `compact` set to `true` or any number >= `1`).
**Default:** `80`.
* `compact` {boolean|integer} Setting this to `false` causes each object key
to be displayed on a new line. It will also add new lines to text that is
longer than `breakLength`. If set to a number, the most `n` inner elements
are united on a single line as long as all properties fit into
`breakLength`. Short array elements are also grouped together. Note that no
text will be reduced below 16 characters, no matter the `breakLength` size.
For more information, see the example below. **Default:** `true`.
For more information, see the example below. **Default:** `3`.
* `sorted` {boolean|Function} If set to `true` or a function, all properties
of an object, and `Set` and `Map` entries are sorted in the resulting
string. If set to `true` the [default sort][] is used. If set to a function,
Expand Down
96 changes: 48 additions & 48 deletions lib/internal/util/inspect.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
});
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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' && ctx.compact >= 1) {
// 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.
Expand Down
6 changes: 4 additions & 2 deletions test/parallel/test-console-group.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
8 changes: 4 additions & 4 deletions test/parallel/test-console.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
2 changes: 1 addition & 1 deletion test/parallel/test-http2-stream-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
10 changes: 5 additions & 5 deletions test/parallel/test-repl-pretty-stack.js
Original file line number Diff line number Diff line change
Expand Up @@ -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' +
Expand All @@ -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'
Expand Down
5 changes: 3 additions & 2 deletions test/parallel/test-repl-underscore.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'",

Expand Down
5 changes: 3 additions & 2 deletions test/parallel/test-repl.js
Original file line number Diff line number Diff line change
Expand Up @@ -534,15 +534,16 @@ const errorTests = [
send: 'require("internal/repl")',
expect: [
'Thrown:',
/^{ Error: Cannot find module 'internal\/repl'/,
/^Error: Cannot find module 'internal\/repl'/,
/^Require stack:/,
/^- <repl>/,
/^ at .*/,
/^ at .*/,
/^ at .*/,
/^ at .*/,
" code: 'MODULE_NOT_FOUND',",
" requireStack: [ '<repl>' ] }"
" requireStack: [ '<repl>' ]",
'}'
]
},
// REPL should handle quotes within regexp literal in multiline mode
Expand Down
95 changes: 55 additions & 40 deletions test/parallel/test-util-format.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
Loading