From 2dc1ba63ccb6f8080412f269952e23817c62c217 Mon Sep 17 00:00:00 2001 From: Greg Alexander Date: Mon, 31 Jul 2017 17:08:44 -0500 Subject: [PATCH 1/5] util: implement %o as formatting specifier Implementing the %o and %O as formatting specifier for util.format. Based on discussion in issue, this specifier should just call util.inspect to format passed in value. Fixes: https://github.com/nodejs/node/issues/14545 --- lib/util.js | 6 ++++ test/parallel/test-util-format.js | 51 +++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/lib/util.js b/lib/util.js index 86be8612b97131..37756064ac462e 100644 --- a/lib/util.js +++ b/lib/util.js @@ -137,6 +137,12 @@ function format(f) { str += f.slice(lastPos, i); str += String(arguments[a++]); break; + case 79: // 'O' + case 111: // 'o' + if (lastPos < i) + str += f.slice(lastPos, i); + str += inspect(arguments[a++]); + break; case 37: // '%' if (lastPos < i) str += f.slice(lastPos, i); diff --git a/test/parallel/test-util-format.js b/test/parallel/test-util-format.js index 867cd57b62d543..c56a696e566bfe 100644 --- a/test/parallel/test-util-format.js +++ b/test/parallel/test-util-format.js @@ -101,6 +101,53 @@ assert.strictEqual(util.format('%j', '42'), '"42"'); assert.strictEqual(util.format('%j %j', 42, 43), '42 43'); assert.strictEqual(util.format('%j %j', 42), '42 %j'); +// Object format specifier +const obj = { + foo: 'bar', + foobar: 1, + func: function() {} +}; +const nestedObj = { + foo: 'bar', + foobar: { + foo: 'bar', + func: function() {} + } +}; +assert.strictEqual(util.format('%o'), '%o'); +assert.strictEqual(util.format('%o', 42), '42'); +assert.strictEqual(util.format('%o', 'foo'), '\'foo\''); +assert.strictEqual( + util.format('%o', obj), + '{ foo: \'bar\', foobar: 1, func: [Function: func] }'); +assert.strictEqual( + util.format('%o', nestedObj), + '{ foo: \'bar\', foobar: { foo: \'bar\', func: [Function: func] } }'); +assert.strictEqual( + util.format('%o %o', obj, obj), + '{ foo: \'bar\', foobar: 1, func: [Function: func] } ' + + '{ foo: \'bar\', foobar: 1, func: [Function: func] }'); +assert.strictEqual( + util.format('%o %o', obj), + '{ foo: \'bar\', foobar: 1, func: [Function: func] } %o'); + +assert.strictEqual(util.format('%O'), '%O'); +assert.strictEqual(util.format('%O', 42), '42'); +assert.strictEqual(util.format('%O', 'foo'), '\'foo\''); +assert.strictEqual( + util.format('%O', obj), + '{ foo: \'bar\', foobar: 1, func: [Function: func] }'); +assert.strictEqual( + util.format('%O', nestedObj), + '{ foo: \'bar\', foobar: { foo: \'bar\', func: [Function: func] } }'); +assert.strictEqual( + util.format('%O %O', obj, obj), + '{ foo: \'bar\', foobar: 1, func: [Function: func] } ' + + '{ foo: \'bar\', foobar: 1, func: [Function: func] }'); +assert.strictEqual( + util.format('%O %O', obj), + '{ foo: \'bar\', foobar: 1, func: [Function: func] } %O'); + // Various format specifiers assert.strictEqual(util.format('%%s%s', 'foo'), '%sfoo'); assert.strictEqual(util.format('%s:%s'), '%s:%s'); @@ -125,6 +172,10 @@ assert.strictEqual(util.format('%f:%f'), '%f:%f'); assert.strictEqual(util.format('o: %j, a: %j', {}, []), 'o: {}, a: []'); assert.strictEqual(util.format('o: %j, a: %j', {}), 'o: {}, a: %j'); assert.strictEqual(util.format('o: %j, a: %j'), 'o: %j, a: %j'); +assert.strictEqual(util.format('o: %o, a: %O', {}, []), 'o: {}, a: []'); +assert.strictEqual(util.format('o: %o, a: %o', {}), 'o: {}, a: %o'); +assert.strictEqual(util.format('o: %O, a: %O'), 'o: %O, a: %O'); + // Invalid format specifiers assert.strictEqual(util.format('a% b', 'x'), 'a% b x'); From c0826e78892f95c16f36ce7b8cfcc851490f99b6 Mon Sep 17 00:00:00 2001 From: Greg Alexander Date: Tue, 1 Aug 2017 11:03:28 -0500 Subject: [PATCH 2/5] util: implement %o as formatting specifier Doc update --- doc/api/util.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/api/util.md b/doc/api/util.md index 27bf540915cecb..140653dabcf201 100644 --- a/doc/api/util.md +++ b/doc/api/util.md @@ -167,6 +167,9 @@ corresponding argument. Supported placeholders are: * `%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. +Similar to `util.inspect()` without options. +* `%O` - Object. Same as `%o`. * `%%` - single percent sign (`'%'`). This does not consume an argument. If the placeholder does not have a corresponding argument, the placeholder is From 0b373b4d0f96cd348af9f1aec929c07eeedb1744 Mon Sep 17 00:00:00 2001 From: Greg Alexander Date: Tue, 1 Aug 2017 15:05:37 -0500 Subject: [PATCH 3/5] util: implement %o as formatting specifier. Nit fix and changing behavior of %o --- doc/api/util.md | 8 +++++--- lib/util.js | 6 +++++- test/parallel/test-util-format.js | 11 +++++------ 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/doc/api/util.md b/doc/api/util.md index 140653dabcf201..9d7471d56fda31 100644 --- a/doc/api/util.md +++ b/doc/api/util.md @@ -167,9 +167,11 @@ corresponding argument. Supported placeholders are: * `%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. -Similar to `util.inspect()` without options. -* `%O` - Object. Same as `%o`. +* `%o` - Object. A string representation of an object + with optimally useful formatting. +* `%O` - Object. A string representation of an object + with generic JavaScript object formatting. + Similar to `util.inspect()` without options. * `%%` - single percent sign (`'%'`). This does not consume an argument. If the placeholder does not have a corresponding argument, the placeholder is diff --git a/lib/util.js b/lib/util.js index 37756064ac462e..ecbf4642b258c1 100644 --- a/lib/util.js +++ b/lib/util.js @@ -138,11 +138,15 @@ function format(f) { str += String(arguments[a++]); break; case 79: // 'O' - case 111: // 'o' if (lastPos < i) str += f.slice(lastPos, i); str += inspect(arguments[a++]); break; + case 111: // 'o' + if (lastPos < i) + str += f.slice(lastPos, i); + str += tryStringify(arguments[a++], null, ' '); + break; case 37: // '%' if (lastPos < i) str += f.slice(lastPos, i); diff --git a/test/parallel/test-util-format.js b/test/parallel/test-util-format.js index c56a696e566bfe..b481748e592114 100644 --- a/test/parallel/test-util-format.js +++ b/test/parallel/test-util-format.js @@ -116,20 +116,19 @@ const nestedObj = { }; assert.strictEqual(util.format('%o'), '%o'); assert.strictEqual(util.format('%o', 42), '42'); -assert.strictEqual(util.format('%o', 'foo'), '\'foo\''); +assert.strictEqual(util.format('%o', 'foo'), '"foo"'); assert.strictEqual( util.format('%o', obj), - '{ foo: \'bar\', foobar: 1, func: [Function: func] }'); + '{"foo":"bar","foobar":1}'); assert.strictEqual( util.format('%o', nestedObj), - '{ foo: \'bar\', foobar: { foo: \'bar\', func: [Function: func] } }'); + '{"foo":"bar","foobar":{"foo":"bar"}}'); assert.strictEqual( util.format('%o %o', obj, obj), - '{ foo: \'bar\', foobar: 1, func: [Function: func] } ' + - '{ foo: \'bar\', foobar: 1, func: [Function: func] }'); + '{"foo":"bar","foobar":1} {"foo":"bar","foobar":1}'); assert.strictEqual( util.format('%o %o', obj), - '{ foo: \'bar\', foobar: 1, func: [Function: func] } %o'); + '{"foo":"bar","foobar":1} %o'); assert.strictEqual(util.format('%O'), '%O'); assert.strictEqual(util.format('%O', 42), '42'); From 436765e90a4fe75d6111f9930d0b7d6ce7e1111e Mon Sep 17 00:00:00 2001 From: Greg Alexander Date: Tue, 1 Aug 2017 16:34:47 -0500 Subject: [PATCH 4/5] util: implement %o as formatting specifier. doc fix --- doc/api/util.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/api/util.md b/doc/api/util.md index 9d7471d56fda31..abe019ba6f4f81 100644 --- a/doc/api/util.md +++ b/doc/api/util.md @@ -169,9 +169,13 @@ corresponding argument. Supported placeholders are: contains circular references. * `%o` - Object. A string representation of an object with optimally useful formatting. + This is equivalent to `JSON.stringify` with no replacer + and 1 space. This is not full representation of the object. + Functions, for example, will not be displayed. * `%O` - Object. A string representation of an object with generic JavaScript object formatting. Similar to `util.inspect()` without options. + This is a full representation of the object. * `%%` - single percent sign (`'%'`). This does not consume an argument. If the placeholder does not have a corresponding argument, the placeholder is From 6bffe782afa1dcc4da972ab5d338ed915b58ff1a Mon Sep 17 00:00:00 2001 From: Greg Alexander Date: Wed, 2 Aug 2017 16:18:52 -0500 Subject: [PATCH 5/5] util: implement %o as formatting specifier Fixes after discussion. --- doc/api/util.md | 11 ++++---- lib/util.js | 3 ++- test/parallel/test-util-format.js | 42 +++++++++++++++++++++++++++---- 3 files changed, 44 insertions(+), 12 deletions(-) diff --git a/doc/api/util.md b/doc/api/util.md index abe019ba6f4f81..4e2ea9579f905e 100644 --- a/doc/api/util.md +++ b/doc/api/util.md @@ -167,15 +167,14 @@ corresponding argument. Supported placeholders are: * `%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 optimally useful formatting. - This is equivalent to `JSON.stringify` with no replacer - and 1 space. This is not full representation of the object. - Functions, for example, will not be displayed. +* `%o` - Object. A string representation of an object + with generic JavaScript object formatting. + Similar to `util.inspect()` with options `{ showHidden: true, depth: 4, showProxy: true }`. + This will show the full object including non-enumerable symbols and properties. * `%O` - Object. A string representation of an object with generic JavaScript object formatting. Similar to `util.inspect()` without options. - This is a full representation of the object. + This will show the full object not including non-enumerable symbols and properties. * `%%` - single percent sign (`'%'`). This does not consume an argument. If the placeholder does not have a corresponding argument, the placeholder is diff --git a/lib/util.js b/lib/util.js index ecbf4642b258c1..a9f267bd416ddc 100644 --- a/lib/util.js +++ b/lib/util.js @@ -145,7 +145,8 @@ function format(f) { case 111: // 'o' if (lastPos < i) str += f.slice(lastPos, i); - str += tryStringify(arguments[a++], null, ' '); + str += inspect(arguments[a++], + { showHidden: true, depth: 4, showProxy: true }); break; case 37: // '%' if (lastPos < i) diff --git a/test/parallel/test-util-format.js b/test/parallel/test-util-format.js index b481748e592114..27e3b62476211c 100644 --- a/test/parallel/test-util-format.js +++ b/test/parallel/test-util-format.js @@ -116,19 +116,51 @@ const nestedObj = { }; assert.strictEqual(util.format('%o'), '%o'); assert.strictEqual(util.format('%o', 42), '42'); -assert.strictEqual(util.format('%o', 'foo'), '"foo"'); +assert.strictEqual(util.format('%o', 'foo'), '\'foo\''); assert.strictEqual( util.format('%o', obj), - '{"foo":"bar","foobar":1}'); + '{ foo: \'bar\',\n' + + ' foobar: 1,\n' + + ' func: \n' + + ' { [Function: func]\n' + + ' [length]: 0,\n' + + ' [name]: \'func\',\n' + + ' [prototype]: func { [constructor]: [Circular] } } }'); assert.strictEqual( util.format('%o', nestedObj), - '{"foo":"bar","foobar":{"foo":"bar"}}'); + '{ foo: \'bar\',\n' + + ' foobar: \n' + + ' { foo: \'bar\',\n' + + ' func: \n' + + ' { [Function: func]\n' + + ' [length]: 0,\n' + + ' [name]: \'func\',\n' + + ' [prototype]: func { [constructor]: [Circular] } } } }'); assert.strictEqual( util.format('%o %o', obj, obj), - '{"foo":"bar","foobar":1} {"foo":"bar","foobar":1}'); + '{ foo: \'bar\',\n' + + ' foobar: 1,\n' + + ' func: \n' + + ' { [Function: func]\n' + + ' [length]: 0,\n' + + ' [name]: \'func\',\n' + + ' [prototype]: func { [constructor]: [Circular] } } }' + + ' { foo: \'bar\',\n' + + ' foobar: 1,\n' + + ' func: \n' + + ' { [Function: func]\n' + + ' [length]: 0,\n' + + ' [name]: \'func\',\n' + + ' [prototype]: func { [constructor]: [Circular] } } }'); assert.strictEqual( util.format('%o %o', obj), - '{"foo":"bar","foobar":1} %o'); + '{ foo: \'bar\',\n' + + ' foobar: 1,\n' + + ' func: \n' + + ' { [Function: func]\n' + + ' [length]: 0,\n' + + ' [name]: \'func\',\n' + + ' [prototype]: func { [constructor]: [Circular] } } } %o'); assert.strictEqual(util.format('%O'), '%O'); assert.strictEqual(util.format('%O', 42), '42');