Skip to content

Commit

Permalink
util: add order option to .inspect()
Browse files Browse the repository at this point in the history
The order option can be used to sort the inspected values in case
they do not rely on their order as arrays. That way the output is
stable no matter of the object property inspection order.

Backport-PR-URL: #23039
PR-URL: #22788
Refs: #22763
Reviewed-By: John-David Dalton <john.david.dalton@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
  • Loading branch information
BridgeAR authored and targos committed Sep 24, 2018
1 parent a2a1ebf commit 68eaa87
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 4 deletions.
38 changes: 37 additions & 1 deletion doc/api/util.md
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,9 @@ stream.write('With ES6');
<!-- YAML
added: v0.3.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/22788
description: The `sorted` option is supported now.
- version: v10.6.0
pr-url: https://github.com/nodejs/node/pull/20725
description: Inspecting linked lists and similar objects is now possible
Expand Down Expand Up @@ -420,7 +423,10 @@ changes:
objects the same as arrays. Note that no text will be reduced below 16
characters, no matter the `breakLength` size. For more information, see the
example below. **Default:** `true`.

* `sorted` {boolean|Function} If set to `true` or a function, all properties
of an object and Set and Map entries will be sorted in the returned string.
If set to `true` the [default sort][] is going to be used. If set to a
function, it is used as a [compare function][].
* Returns: {string} The representation of passed object

The `util.inspect()` method returns a string representation of `object` that is
Expand Down Expand Up @@ -534,6 +540,34 @@ console.log(inspect(weakSet, { showHidden: true }));
// WeakSet { { a: 1 }, { b: 2 } }
```

The `sorted` option makes sure the output is identical, no matter of the
properties insertion order:

```js
const { inspect } = require('util');
const assert = require('assert');

const o1 = {
b: [2, 3, 1],
a: '`a` comes before `b`',
c: new Set([2, 3, 1])
};
console.log(inspect(o1, { sorted: true }));
// { a: '`a` comes before `b`', b: [ 2, 3, 1 ], c: Set { 1, 2, 3 } }
console.log(inspect(o1, { sorted: (a, b) => a < b }));
// { c: Set { 3, 2, 1 }, b: [ 2, 3, 1 ], a: '`a` comes before `b`' }

const o2 = {
c: new Set([2, 1, 3]),
a: '`a` comes before `b`',
b: [2, 3, 1]
};
assert.strict.equal(
inspect(o1, { sorted: true }),
inspect(o2, { sorted: true })
);
```

Please note that `util.inspect()` is a synchronous method that is mainly
intended as a debugging tool. Some input values can have a significant
performance overhead that can block the event loop. Use this function
Expand Down Expand Up @@ -2150,7 +2184,9 @@ Deprecated predecessor of `console.log`.
[WHATWG Encoding Standard]: https://encoding.spec.whatwg.org/
[Common System Errors]: errors.html#errors_common_system_errors
[async function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
[compare function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#Parameters
[constructor]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor
[default sort]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
[global symbol registry]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/for
[list of deprecated APIS]: deprecations.html#deprecations_list_of_deprecated_apis
[semantically incompatible]: https://github.com/nodejs/node/issues/4179
Expand Down
19 changes: 16 additions & 3 deletions lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ const inspectDefaultOptions = Object.seal({
showProxy: false,
maxArrayLength: 100,
breakLength: 60,
compact: true
compact: true,
sorted: false
});

const kObjectType = 0;
Expand Down Expand Up @@ -344,6 +345,8 @@ function debuglog(set) {
function inspect(value, opts) {
// Default options
const ctx = {
budget: {},
indentationLvl: 0,
seen: [],
stylize: stylizeNoColor,
showHidden: inspectDefaultOptions.showHidden,
Expand All @@ -355,8 +358,8 @@ function inspect(value, opts) {
// `maxEntries`.
maxArrayLength: inspectDefaultOptions.maxArrayLength,
breakLength: inspectDefaultOptions.breakLength,
indentationLvl: 0,
compact: inspectDefaultOptions.compact
compact: inspectDefaultOptions.compact,
sorted: inspectDefaultOptions.sorted
};
// Legacy...
if (arguments.length > 2) {
Expand Down Expand Up @@ -846,6 +849,16 @@ function formatRaw(ctx, value, recurseTimes) {
}
ctx.seen.pop();

if (ctx.sorted) {
const comparator = ctx.sorted === true ? undefined : ctx.sorted;
if (extrasType === kObjectType) {
output = output.sort(comparator);
} else if (keys.length > 1) {
const sorted = output.slice(output.length - keys.length).sort(comparator);
output.splice(output.length - keys.length, keys.length, ...sorted);
}
}

return reduceToSingleString(ctx, output, base, braces);
}

Expand Down
30 changes: 30 additions & 0 deletions test/parallel/test-util-inspect.js
Original file line number Diff line number Diff line change
Expand Up @@ -1718,3 +1718,33 @@ assert.strictEqual(inspect(Object(13n)), '[BigInt: 13n]');
);
rejection.catch(() => {});
}

assert.strictEqual(
inspect([1, 3, 2], { sorted: true }),
inspect([1, 3, 2])
);
assert.strictEqual(
inspect({ c: 3, a: 1, b: 2 }, { sorted: true }),
'{ a: 1, b: 2, c: 3 }'
);
assert.strictEqual(
inspect(
{ a200: 4, a100: 1, a102: 3, a101: 2 },
{ sorted(a, b) { return a < b; } }
),
'{ a200: 4, a102: 3, a101: 2, a100: 1 }'
);

// Non-indices array properties are sorted as well.
{
const arr = [3, 2, 1];
arr.b = 2;
arr.c = 3;
arr.a = 1;
arr[Symbol('b')] = true;
arr[Symbol('a')] = false;
assert.strictEqual(
inspect(arr, { sorted: true }),
'[ 3, 2, 1, [Symbol(a)]: false, [Symbol(b)]: true, a: 1, b: 2, c: 3 ]'
);
}

0 comments on commit 68eaa87

Please sign in to comment.