From 38bc5fbd6b919a61bc83f5136ba42d71823cb699 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Thu, 17 May 2018 03:19:56 +0200 Subject: [PATCH] util: remove erroneous whitespace When inspecting nested objects some times a whitespace was added at the end of a line. This fixes this erroneous space. Besides that the `breakLength` was not followed if a single property was longer than the breakLength. It will now break a single property into the key and value in such cases. PR-URL: https://github.com/nodejs/node/pull/20802 Refs: https://github.com/nodejs/node/issues/20253 Reviewed-By: Anna Henningsen Reviewed-By: James M Snell Reviewed-By: Matteo Collina Reviewed-By: Trivikram Kamat --- lib/util.js | 82 ++++++++++++------------ test/parallel/test-util-format.js | 16 ++--- test/parallel/test-util-inspect.js | 2 +- test/parallel/test-whatwg-url-inspect.js | 8 ++- 4 files changed, 56 insertions(+), 52 deletions(-) diff --git a/lib/util.js b/lib/util.js index ac98590464a3fb..676f43499e830c 100644 --- a/lib/util.js +++ b/lib/util.js @@ -416,7 +416,7 @@ function getPrefix(constructor, tag) { return ''; } -function formatValue(ctx, value, recurseTimes, ln) { +function formatValue(ctx, value, recurseTimes) { // Primitive types cannot have properties if (typeof value !== 'object' && typeof value !== 'function') { return formatPrimitive(ctx.stylize, value, ctx); @@ -592,7 +592,7 @@ function formatValue(ctx, value, recurseTimes, ln) { return ctx.stylize(dateToISOString.call(value), 'date'); } // Make dates with properties first say the date - base = `${dateToISOString.call(value)}`; + base = dateToISOString.call(value); } else if (isError(value)) { // Make error with message first say the error base = formatError(value); @@ -693,7 +693,7 @@ function formatValue(ctx, value, recurseTimes, ln) { } ctx.seen.pop(); - return reduceToSingleString(ctx, output, base, braces, ln); + return reduceToSingleString(ctx, output, base, braces); } function formatNumber(fn, value) { @@ -768,7 +768,23 @@ function formatNamespaceObject(ctx, value, recurseTimes, keys) { const len = keys.length; const output = new Array(len); for (var i = 0; i < len; i++) { - output[i] = formatNamespaceProperty(ctx, value, recurseTimes, keys[i]); + try { + output[i] = formatProperty(ctx, value, recurseTimes, keys[i], 0); + } catch (err) { + if (!(err instanceof ReferenceError)) { + throw err; + } + // Use the existing functionality. This makes sure the indentation and + // line breaks are always correct. Otherwise it is very difficult to keep + // this aligned, even though this is a hacky way of dealing with this. + const tmp = { [keys[i]]: '' }; + output[i] = formatProperty(ctx, tmp, recurseTimes, keys[i], 0); + const pos = output[i].lastIndexOf(' '); + // We have to find the last whitespace and have to replace that value as + // it will be visualized as a regular string. + output[i] = output[i].slice(0, pos + 1) + + ctx.stylize('', 'special'); + } } return output; } @@ -996,42 +1012,21 @@ function formatPromise(ctx, value, recurseTimes, keys) { return output; } -function formatKey(ctx, key, enumerable) { - if (typeof key === 'symbol') { - return `[${ctx.stylize(key.toString(), 'symbol')}]`; - } - if (enumerable === false) { - return `[${key}]`; - } - if (keyStrRegExp.test(key)) { - return ctx.stylize(key, 'name'); - } - return ctx.stylize(strEscape(key), 'string'); -} - -function formatNamespaceProperty(ctx, ns, recurseTimes, key) { - let value; - try { - value = formatValue(ctx, ns[key], recurseTimes, true); - } catch (err) { - if (err instanceof ReferenceError) { - value = ctx.stylize('', 'special'); - } else { - throw err; - } - } - - return `${formatKey(ctx, key)}: ${value}`; -} - function formatProperty(ctx, value, recurseTimes, key, array) { - let str; + let name, str; + let extra = ' '; const desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key], enumerable: true }; if (desc.value !== undefined) { const diff = array !== 0 || ctx.compact === false ? 2 : 3; ctx.indentationLvl += diff; - str = formatValue(ctx, desc.value, recurseTimes, array === 0); + str = formatValue(ctx, desc.value, recurseTimes); + if (diff === 3) { + const len = ctx.colors ? removeColors(str).length : str.length; + if (ctx.breakLength < len) { + extra = `\n${' '.repeat(ctx.indentationLvl)}`; + } + } ctx.indentationLvl -= diff; } else if (desc.get !== undefined) { if (desc.set !== undefined) { @@ -1047,11 +1042,19 @@ function formatProperty(ctx, value, recurseTimes, key, array) { if (array === 1) { return str; } - - return `${formatKey(ctx, key, desc.enumerable)}: ${str}`; + if (typeof key === 'symbol') { + name = `[${ctx.stylize(key.toString(), 'symbol')}]`; + } else if (desc.enumerable === false) { + name = `[${key}]`; + } else if (keyStrRegExp.test(key)) { + name = ctx.stylize(key, 'name'); + } else { + name = ctx.stylize(strEscape(key), 'string'); + } + return `${name}:${extra}${str}`; } -function reduceToSingleString(ctx, output, base, braces, addLn) { +function reduceToSingleString(ctx, output, base, braces) { const breakLength = ctx.breakLength; let i = 0; if (ctx.compact === false) { @@ -1080,11 +1083,10 @@ function reduceToSingleString(ctx, output, base, braces, addLn) { // we need to force the first item to be on the next line or the // items will not line up correctly. const indentation = ' '.repeat(ctx.indentationLvl); - const extraLn = addLn === true ? `\n${indentation}` : ''; const ln = base === '' && braces[0].length === 1 ? - ' ' : `${base ? ` ${base}` : base}\n${indentation} `; + ' ' : `${base ? ` ${base}` : ''}\n${indentation} `; const str = join(output, `,\n${indentation} `); - return `${extraLn}${braces[0]}${ln}${str} ${braces[1]}`; + return `${braces[0]}${ln}${str} ${braces[1]}`; } function isBoolean(arg) { diff --git a/test/parallel/test-util-format.js b/test/parallel/test-util-format.js index fd686cb6d355ad..f99f85b78c853b 100644 --- a/test/parallel/test-util-format.js +++ b/test/parallel/test-util-format.js @@ -126,7 +126,7 @@ assert.strictEqual( util.format('%o', obj), '{ foo: \'bar\',\n' + ' foobar: 1,\n' + - ' func: \n' + + ' func:\n' + ' { [Function: func]\n' + ' [length]: 0,\n' + ' [name]: \'func\',\n' + @@ -135,8 +135,8 @@ assert.strictEqual( util.format('%o', nestedObj2), '{ foo: \'bar\',\n' + ' foobar: 1,\n' + - ' func: \n' + - ' [ { a: \n' + + ' func:\n' + + ' [ { a:\n' + ' { [Function: a]\n' + ' [length]: 0,\n' + ' [name]: \'a\',\n' + @@ -145,9 +145,9 @@ assert.strictEqual( assert.strictEqual( util.format('%o', nestedObj), '{ foo: \'bar\',\n' + - ' foobar: \n' + + ' foobar:\n' + ' { foo: \'bar\',\n' + - ' func: \n' + + ' func:\n' + ' { [Function: func]\n' + ' [length]: 0,\n' + ' [name]: \'func\',\n' + @@ -156,14 +156,14 @@ assert.strictEqual( util.format('%o %o', obj, obj), '{ foo: \'bar\',\n' + ' foobar: 1,\n' + - ' func: \n' + + ' func:\n' + ' { [Function: func]\n' + ' [length]: 0,\n' + ' [name]: \'func\',\n' + ' [prototype]: func { [constructor]: [Circular] } } }' + ' { foo: \'bar\',\n' + ' foobar: 1,\n' + - ' func: \n' + + ' func:\n' + ' { [Function: func]\n' + ' [length]: 0,\n' + ' [name]: \'func\',\n' + @@ -172,7 +172,7 @@ assert.strictEqual( util.format('%o %o', obj), '{ foo: \'bar\',\n' + ' foobar: 1,\n' + - ' func: \n' + + ' func:\n' + ' { [Function: func]\n' + ' [length]: 0,\n' + ' [name]: \'func\',\n' + diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index 0ea2fd702fd37d..b7aa752952ce4c 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -1237,7 +1237,7 @@ util.inspect(process); let out = util.inspect(o, { compact: true, depth: 5, breakLength: 80 }); let expect = [ - '{ a: ', + '{ a:', ' [ 1,', ' 2,', " [ [ 'Lorem ipsum dolor\\nsit amet,\\tconsectetur adipiscing elit, " + diff --git a/test/parallel/test-whatwg-url-inspect.js b/test/parallel/test-whatwg-url-inspect.js index 5758b39b8af83d..1265428d4ca811 100644 --- a/test/parallel/test-whatwg-url-inspect.js +++ b/test/parallel/test-whatwg-url-inspect.js @@ -16,7 +16,8 @@ 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', @@ -32,7 +33,8 @@ assert.strictEqual( 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', @@ -46,7 +48,7 @@ assert.strictEqual( hash: '#hash', cannotBeBase: false, special: true, - [Symbol(context)]:\x20 + [Symbol(context)]: URLContext { flags: 2032, scheme: 'https:',