Skip to content

Commit 211df3f

Browse files
gla5001tniessen
authored andcommitted
util: implement %o and %O as formatting specifiers
Implementing the %o and %O formatting specifiers for util.format. Based on discussion in issue, this specifier should just call util.inspect to format the value. PR-URL: #14558 Fixes: #14545 Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: Roman Reiss <[email protected]> Reviewed-By: Vse Mozhet Byt <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Evan Lucas <[email protected]> Reviewed-By: Timothy Gu <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Tobias Nießen <[email protected]> Reviewed-By: Yuta Hiroto <[email protected]>
1 parent 753b68f commit 211df3f

File tree

3 files changed

+101
-0
lines changed

3 files changed

+101
-0
lines changed

doc/api/util.md

+8
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,14 @@ corresponding argument. Supported placeholders are:
167167
* `%f` - Floating point value.
168168
* `%j` - JSON. Replaced with the string `'[Circular]'` if the argument
169169
contains circular references.
170+
* `%o` - Object. A string representation of an object
171+
with generic JavaScript object formatting.
172+
Similar to `util.inspect()` with options `{ showHidden: true, depth: 4, showProxy: true }`.
173+
This will show the full object including non-enumerable symbols and properties.
174+
* `%O` - Object. A string representation of an object
175+
with generic JavaScript object formatting.
176+
Similar to `util.inspect()` without options.
177+
This will show the full object not including non-enumerable symbols and properties.
170178
* `%%` - single percent sign (`'%'`). This does not consume an argument.
171179

172180
If the placeholder does not have a corresponding argument, the placeholder is

lib/util.js

+11
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,17 @@ function format(f) {
137137
str += f.slice(lastPos, i);
138138
str += String(arguments[a++]);
139139
break;
140+
case 79: // 'O'
141+
if (lastPos < i)
142+
str += f.slice(lastPos, i);
143+
str += inspect(arguments[a++]);
144+
break;
145+
case 111: // 'o'
146+
if (lastPos < i)
147+
str += f.slice(lastPos, i);
148+
str += inspect(arguments[a++],
149+
{ showHidden: true, depth: 4, showProxy: true });
150+
break;
140151
case 37: // '%'
141152
if (lastPos < i)
142153
str += f.slice(lastPos, i);

test/parallel/test-util-format.js

+82
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,84 @@ assert.strictEqual(util.format('%j', '42'), '"42"');
101101
assert.strictEqual(util.format('%j %j', 42, 43), '42 43');
102102
assert.strictEqual(util.format('%j %j', 42), '42 %j');
103103

104+
// Object format specifier
105+
const obj = {
106+
foo: 'bar',
107+
foobar: 1,
108+
func: function() {}
109+
};
110+
const nestedObj = {
111+
foo: 'bar',
112+
foobar: {
113+
foo: 'bar',
114+
func: function() {}
115+
}
116+
};
117+
assert.strictEqual(util.format('%o'), '%o');
118+
assert.strictEqual(util.format('%o', 42), '42');
119+
assert.strictEqual(util.format('%o', 'foo'), '\'foo\'');
120+
assert.strictEqual(
121+
util.format('%o', obj),
122+
'{ foo: \'bar\',\n' +
123+
' foobar: 1,\n' +
124+
' func: \n' +
125+
' { [Function: func]\n' +
126+
' [length]: 0,\n' +
127+
' [name]: \'func\',\n' +
128+
' [prototype]: func { [constructor]: [Circular] } } }');
129+
assert.strictEqual(
130+
util.format('%o', nestedObj),
131+
'{ foo: \'bar\',\n' +
132+
' foobar: \n' +
133+
' { foo: \'bar\',\n' +
134+
' func: \n' +
135+
' { [Function: func]\n' +
136+
' [length]: 0,\n' +
137+
' [name]: \'func\',\n' +
138+
' [prototype]: func { [constructor]: [Circular] } } } }');
139+
assert.strictEqual(
140+
util.format('%o %o', obj, obj),
141+
'{ foo: \'bar\',\n' +
142+
' foobar: 1,\n' +
143+
' func: \n' +
144+
' { [Function: func]\n' +
145+
' [length]: 0,\n' +
146+
' [name]: \'func\',\n' +
147+
' [prototype]: func { [constructor]: [Circular] } } }' +
148+
' { foo: \'bar\',\n' +
149+
' foobar: 1,\n' +
150+
' func: \n' +
151+
' { [Function: func]\n' +
152+
' [length]: 0,\n' +
153+
' [name]: \'func\',\n' +
154+
' [prototype]: func { [constructor]: [Circular] } } }');
155+
assert.strictEqual(
156+
util.format('%o %o', obj),
157+
'{ foo: \'bar\',\n' +
158+
' foobar: 1,\n' +
159+
' func: \n' +
160+
' { [Function: func]\n' +
161+
' [length]: 0,\n' +
162+
' [name]: \'func\',\n' +
163+
' [prototype]: func { [constructor]: [Circular] } } } %o');
164+
165+
assert.strictEqual(util.format('%O'), '%O');
166+
assert.strictEqual(util.format('%O', 42), '42');
167+
assert.strictEqual(util.format('%O', 'foo'), '\'foo\'');
168+
assert.strictEqual(
169+
util.format('%O', obj),
170+
'{ foo: \'bar\', foobar: 1, func: [Function: func] }');
171+
assert.strictEqual(
172+
util.format('%O', nestedObj),
173+
'{ foo: \'bar\', foobar: { foo: \'bar\', func: [Function: func] } }');
174+
assert.strictEqual(
175+
util.format('%O %O', obj, obj),
176+
'{ foo: \'bar\', foobar: 1, func: [Function: func] } ' +
177+
'{ foo: \'bar\', foobar: 1, func: [Function: func] }');
178+
assert.strictEqual(
179+
util.format('%O %O', obj),
180+
'{ foo: \'bar\', foobar: 1, func: [Function: func] } %O');
181+
104182
// Various format specifiers
105183
assert.strictEqual(util.format('%%s%s', 'foo'), '%sfoo');
106184
assert.strictEqual(util.format('%s:%s'), '%s:%s');
@@ -125,6 +203,10 @@ assert.strictEqual(util.format('%f:%f'), '%f:%f');
125203
assert.strictEqual(util.format('o: %j, a: %j', {}, []), 'o: {}, a: []');
126204
assert.strictEqual(util.format('o: %j, a: %j', {}), 'o: {}, a: %j');
127205
assert.strictEqual(util.format('o: %j, a: %j'), 'o: %j, a: %j');
206+
assert.strictEqual(util.format('o: %o, a: %O', {}, []), 'o: {}, a: []');
207+
assert.strictEqual(util.format('o: %o, a: %o', {}), 'o: {}, a: %o');
208+
assert.strictEqual(util.format('o: %O, a: %O'), 'o: %O, a: %O');
209+
128210

129211
// Invalid format specifiers
130212
assert.strictEqual(util.format('a% b', 'x'), 'a% b x');

0 commit comments

Comments
 (0)