From a9bf6652b5353f2098d4c0cd0eb77d17e02e164d Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 26 Mar 2019 15:53:11 +0100 Subject: [PATCH] util: use minimal object inspection with %s specifier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This improves `util.format()` by returning more meaningful results when using `%s` as specifier and any object as value. Besides that `BigInt` will also be represented with an `n` at the end to indicate that it's of type `BigInt`. PR-URL: https://github.com/nodejs/node/pull/26927 Reviewed-By: Michaƫl Zasso Reviewed-By: Yongsheng Zhang --- doc/api/util.md | 20 ++++++++++--------- lib/internal/util/inspect.js | 15 +++++++++++++- .../parallel/test-http-response-statuscode.js | 4 ++-- ...stream-writable-change-default-encoding.js | 2 +- test/parallel/test-util-format.js | 14 +++++++++++++ 5 files changed, 42 insertions(+), 13 deletions(-) diff --git a/doc/api/util.md b/doc/api/util.md index beb79ae0f85616..6a8b30ef7ae1b6 100644 --- a/doc/api/util.md +++ b/doc/api/util.md @@ -220,15 +220,17 @@ as a `printf`-like format string which can contain zero or more format specifiers. Each specifier is replaced with the converted value from the corresponding argument. Supported specifiers are: -* `%s` - `String`. -* `%d` - `Number` (integer or floating point value) or `BigInt`. -* `%i` - Integer or `BigInt`. -* `%f` - Floating point value. -* `%j` - JSON. Replaced with the string `'[Circular]'` if the argument -contains circular references. -* `%o` - `Object`. A string representation of an object - with generic JavaScript object formatting. - Similar to `util.inspect()` with options +* `%s` - `String` will be used to convert all values except `BigInt` and + `Object`. `BigInt` values will be represented with an `n` and Objects are + inspected using `util.inspect()` with options + `{ depth: 0, colors: false, compact: 3 }`. +* `%d` - `Number` will be used to convert all values except `BigInt`. +* `%i` - `parseInt(value, 10)` is used for all values except `BigInt`. +* `%f` - `parseFloat(value)` is used for all values. +* `%j` - JSON. Replaced with the string `'[Circular]'` if the argument contains + circular references. +* `%o` - `Object`. A string representation of an object with generic JavaScript + object formatting. Similar to `util.inspect()` with options `{ showHidden: true, showProxy: true }`. This will show the full object including non-enumerable properties and proxies. * `%O` - `Object`. A string representation of an object with generic JavaScript diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index 52924a4f706945..f25c2eaca7c454 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -1446,7 +1446,20 @@ function formatWithOptions(inspectOptions, ...args) { if (a + 1 !== args.length) { switch (nextChar) { case 115: // 's' - tempStr = String(args[++a]); + const tempArg = args[++a]; + if (typeof tempArg === 'object' && tempArg !== null) { + tempStr = inspect(tempArg, { + ...inspectOptions, + compact: 3, + colors: false, + depth: 0 + }); + // eslint-disable-next-line valid-typeof + } else if (typeof tempArg === 'bigint') { + tempStr = `${tempArg}n`; + } else { + tempStr = String(tempArg); + } break; case 106: // 'j' tempStr = tryStringify(args[++a]); diff --git a/test/parallel/test-http-response-statuscode.js b/test/parallel/test-http-response-statuscode.js index 7e93ad10a9f9c8..680fa37cf6f9f4 100644 --- a/test/parallel/test-http-response-statuscode.js +++ b/test/parallel/test-http-response-statuscode.js @@ -29,7 +29,7 @@ const server = http.Server(common.mustCall(function(req, res) { test(res, NaN, 'NaN'); break; case 3: - test(res, {}, '[object Object]'); + test(res, {}, '{}'); break; case 4: test(res, 99, '99'); @@ -47,7 +47,7 @@ const server = http.Server(common.mustCall(function(req, res) { test(res, true, 'true'); break; case 9: - test(res, [], ''); + test(res, [], '[]'); break; case 10: test(res, 'this is not valid', 'this is not valid'); diff --git a/test/parallel/test-stream-writable-change-default-encoding.js b/test/parallel/test-stream-writable-change-default-encoding.js index 4fc1970a9d05db..d8b774134ea714 100644 --- a/test/parallel/test-stream-writable-change-default-encoding.js +++ b/test/parallel/test-stream-writable-change-default-encoding.js @@ -63,7 +63,7 @@ common.expectsError(function changeDefaultEncodingToInvalidValue() { }, { type: TypeError, code: 'ERR_UNKNOWN_ENCODING', - message: 'Unknown encoding: [object Object]' + message: 'Unknown encoding: {}' }); (function checkVairableCaseEncoding() { diff --git a/test/parallel/test-util-format.js b/test/parallel/test-util-format.js index 5dab0177565c10..da8e0cea8b23aa 100644 --- a/test/parallel/test-util-format.js +++ b/test/parallel/test-util-format.js @@ -123,11 +123,17 @@ assert.strictEqual(util.format('%f %f', 42), '42 %f'); // String format specifier assert.strictEqual(util.format('%s'), '%s'); assert.strictEqual(util.format('%s', undefined), 'undefined'); +assert.strictEqual(util.format('%s', null), 'null'); assert.strictEqual(util.format('%s', 'foo'), 'foo'); assert.strictEqual(util.format('%s', 42), '42'); assert.strictEqual(util.format('%s', '42'), '42'); assert.strictEqual(util.format('%s %s', 42, 43), '42 43'); assert.strictEqual(util.format('%s %s', 42), '42 %s'); +assert.strictEqual(util.format('%s', 42n), '42n'); +assert.strictEqual(util.format('%s', Symbol('foo')), 'Symbol(foo)'); +assert.strictEqual(util.format('%s', true), 'true'); +assert.strictEqual(util.format('%s', { a: [1, 2, 3] }), '{ a: [Array] }'); +assert.strictEqual(util.format('%s', () => 5), '() => 5'); // JSON format specifier assert.strictEqual(util.format('%j'), '%j'); @@ -346,3 +352,11 @@ assert.strictEqual( util.format(new SharedArrayBuffer(4)), 'SharedArrayBuffer { [Uint8Contents]: <00 00 00 00>, byteLength: 4 }' ); + +assert.strictEqual( + util.formatWithOptions( + { colors: true, compact: 3 }, + '%s', [ 1, { a: true }] + ), + '[ 1, [Object] ]' +);