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.

PR-URL: nodejs#22788
Refs: nodejs#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 committed Sep 19, 2018
1 parent 9d71e6a commit b95b0d8
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 3 deletions.
37 changes: 37 additions & 0 deletions 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: REPLACEME
pr-url: https://github.com/nodejs/node/pull/22756
description: The inspection output is now limited to about 128 MB. Data
Expand Down Expand Up @@ -426,6 +429,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 @@ -535,6 +542,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. Its maximum output length is limited to
approximately 128 MB and input values that result in output bigger than that
Expand Down Expand Up @@ -2165,7 +2200,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
18 changes: 15 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 @@ -394,6 +395,8 @@ function debuglog(set) {
function inspect(value, opts) {
// Default options
const ctx = {
budget: {},
indentationLvl: 0,
seen: [],
stylize: stylizeNoColor,
showHidden: inspectDefaultOptions.showHidden,
Expand All @@ -405,9 +408,8 @@ function inspect(value, opts) {
// `maxEntries`.
maxArrayLength: inspectDefaultOptions.maxArrayLength,
breakLength: inspectDefaultOptions.breakLength,
indentationLvl: 0,
compact: inspectDefaultOptions.compact,
budget: {}
sorted: inspectDefaultOptions.sorted
};
if (arguments.length > 1) {
// Legacy...
Expand Down Expand Up @@ -894,6 +896,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);
}
}

const res = reduceToSingleString(ctx, output, base, braces);
const budget = ctx.budget[ctx.indentationLvl] || 0;
const newLength = budget + res.length;
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 @@ -1676,3 +1676,33 @@ assert.strictEqual(inspect(new BigUint64Array([0n])), 'BigUint64Array [ 0n ]');
);
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 b95b0d8

Please sign in to comment.