From 5cf8250a3274c4787c3c4b2586af7c4e48d0c3dd Mon Sep 17 00:00:00 2001 From: Rohith Pariki Date: Sat, 3 Oct 2026 17:11:13 +0530 Subject: [PATCH] util: fix breakLength: Infinity for deep objects When `util.inspect` is called with `breakLength: Infinity`, it should always format the output as a single line. However, the existing logic for compact arrays (`groupArrayElements`) and deep objects ignored this when the depth was large or the array was long. This commit updates `groupArrayElements` to return early if `breakLength === Infinity`, preventing column grouping. It also updates `reduceToSingleString` to bypass the depth checks when `breakLength === Infinity`, ensuring all deep objects are consolidated. Fixes: https://github.com/nodejs/node/issues/60475 Signed-off-by: Rohith Pariki --- lib/internal/util/inspect.js | 6 ++- test/parallel/test-util-inspect.js | 59 +++++++++++++----------------- 2 files changed, 31 insertions(+), 34 deletions(-) diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index 622dab4eae8a..eceab4d1e0e3 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -2052,6 +2052,9 @@ function formatError(err, constructor, tag, ctx, keys) { } function groupArrayElements(ctx, output, value) { + if (ctx.breakLength === Infinity) { + return output; + } let totalLength = 0; let maxLength = 0; let i = 0; @@ -2681,7 +2684,8 @@ function reduceToSingleString( // 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 && + if ((ctx.breakLength === Infinity || + 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 diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index 4e26661a4b49..eb9c64b59e29 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -3053,45 +3053,22 @@ assert.strictEqual( out = util.inspect(obj, { compact: 1, breakLength: Infinity, colors: true }); - expected = [ - '{', - ' a: {', - ' b: { x: \u001b[33m5\u001b[39m, c: \u001b[36m[Object]\u001b[39m }', - ' },', - ' b: [', - " \u001b[32m'foobar'\u001b[39m, \u001b[32m'baz'\u001b[39m,", - " \u001b[32m'foobar'\u001b[39m, \u001b[32m'baz'\u001b[39m,", - " \u001b[32m'foobar'\u001b[39m, \u001b[32m'baz'\u001b[39m,", - " \u001b[32m'foobar'\u001b[39m, \u001b[32m'baz'\u001b[39m,", - " \u001b[32m'foobar'\u001b[39m", - ' ]', - '}', - ].join('\n'); + const inlineArray = [ + "\u001b[32m'foobar'\u001b[39m", "\u001b[32m'baz'\u001b[39m", + "\u001b[32m'foobar'\u001b[39m", "\u001b[32m'baz'\u001b[39m", + "\u001b[32m'foobar'\u001b[39m", "\u001b[32m'baz'\u001b[39m", + "\u001b[32m'foobar'\u001b[39m", "\u001b[32m'baz'\u001b[39m", + "\u001b[32m'foobar'\u001b[39m", + ]; + expected = `{ a: { b: { x: \u001b[33m5\u001b[39m, c: \u001b[36m[Object]\u001b[39m } }, b: [ ${inlineArray.join(', ')} ] }`; assert.strictEqual(out, expected); obj = Array.from({ length: 60 }).map((e, i) => i); out = util.inspect(obj, { compact: 1, breakLength: Infinity, colors: true }); - expected = [ - '[', - ' \u001b[33m0\u001b[39m, \u001b[33m1\u001b[39m, \u001b[33m2\u001b[39m, \u001b[33m3\u001b[39m,', - ' \u001b[33m4\u001b[39m, \u001b[33m5\u001b[39m, \u001b[33m6\u001b[39m, \u001b[33m7\u001b[39m,', - ' \u001b[33m8\u001b[39m, \u001b[33m9\u001b[39m, \u001b[33m10\u001b[39m, \u001b[33m11\u001b[39m,', - ' \u001b[33m12\u001b[39m, \u001b[33m13\u001b[39m, \u001b[33m14\u001b[39m, \u001b[33m15\u001b[39m,', - ' \u001b[33m16\u001b[39m, \u001b[33m17\u001b[39m, \u001b[33m18\u001b[39m, \u001b[33m19\u001b[39m,', - ' \u001b[33m20\u001b[39m, \u001b[33m21\u001b[39m, \u001b[33m22\u001b[39m, \u001b[33m23\u001b[39m,', - ' \u001b[33m24\u001b[39m, \u001b[33m25\u001b[39m, \u001b[33m26\u001b[39m, \u001b[33m27\u001b[39m,', - ' \u001b[33m28\u001b[39m, \u001b[33m29\u001b[39m, \u001b[33m30\u001b[39m, \u001b[33m31\u001b[39m,', - ' \u001b[33m32\u001b[39m, \u001b[33m33\u001b[39m, \u001b[33m34\u001b[39m, \u001b[33m35\u001b[39m,', - ' \u001b[33m36\u001b[39m, \u001b[33m37\u001b[39m, \u001b[33m38\u001b[39m, \u001b[33m39\u001b[39m,', - ' \u001b[33m40\u001b[39m, \u001b[33m41\u001b[39m, \u001b[33m42\u001b[39m, \u001b[33m43\u001b[39m,', - ' \u001b[33m44\u001b[39m, \u001b[33m45\u001b[39m, \u001b[33m46\u001b[39m, \u001b[33m47\u001b[39m,', - ' \u001b[33m48\u001b[39m, \u001b[33m49\u001b[39m, \u001b[33m50\u001b[39m, \u001b[33m51\u001b[39m,', - ' \u001b[33m52\u001b[39m, \u001b[33m53\u001b[39m, \u001b[33m54\u001b[39m, \u001b[33m55\u001b[39m,', - ' \u001b[33m56\u001b[39m, \u001b[33m57\u001b[39m, \u001b[33m58\u001b[39m, \u001b[33m59\u001b[39m', - ']', - ].join('\n'); + const inlineNumbers = Array.from({ length: 60 }, (_, i) => `\u001b[33m${i}\u001b[39m`); + expected = `[ ${inlineNumbers.join(', ')} ]`; assert.strictEqual(out, expected); @@ -4095,6 +4072,22 @@ ${error.stack.split('\n').slice(1).join('\n')}`, assert.strictEqual(util.inspect(obj, { breakLength: Infinity }), expected); } +{ + // Refs: https://github.com/nodejs/node/issues/60475 + // `breakLength: Infinity` should format deeply nested objects on a single line + // regardless of depth limit or array size. + const obj = { a: { b: { c: { d: { e: 1 } } } } }; + const expected = '{ a: { b: { c: { d: { e: 1 } } } } }'; + assert.strictEqual(util.inspect(obj, { breakLength: Infinity, depth: Infinity }), expected); + assert.strictEqual(util.inspect(obj, { breakLength: Infinity, depth: null }), expected); + assert.strictEqual(util.inspect(obj, { breakLength: Infinity, depth: 10 }), expected); + + const arr = Array.from({ length: 60 }, (_, i) => i); + const arrExpected = `[ ${arr.join(', ')} ]`; + assert.strictEqual(util.inspect(arr, { breakLength: Infinity, depth: Infinity }), arrExpected); + assert.strictEqual(util.inspect(arr, { breakLength: Infinity, depth: 2, compact: 3 }), arrExpected); +} + { class Class { get [Symbol.toStringTag]() {