diff options
-rw-r--r-- | doc/api/util.md | 37 | ||||
-rw-r--r-- | lib/util.js | 18 | ||||
-rw-r--r-- | test/parallel/test-util-inspect.js | 30 |
3 files changed, 82 insertions, 3 deletions
diff --git a/doc/api/util.md b/doc/api/util.md index 267bab85ae..b0e942274c 100644 --- a/doc/api/util.md +++ b/doc/api/util.md @@ -361,6 +361,9 @@ stream.write('With ES6'); 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 above that size will not be fully inspected. @@ -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 @@ -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 @@ -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 diff --git a/lib/util.js b/lib/util.js index 89c86cb7fd..d1a483cf17 100644 --- a/lib/util.js +++ b/lib/util.js @@ -99,7 +99,8 @@ const inspectDefaultOptions = Object.seal({ showProxy: false, maxArrayLength: 100, breakLength: 60, - compact: true + compact: true, + sorted: false }); const kObjectType = 0; @@ -394,6 +395,8 @@ function debuglog(set) { function inspect(value, opts) { // Default options const ctx = { + budget: {}, + indentationLvl: 0, seen: [], stylize: stylizeNoColor, showHidden: inspectDefaultOptions.showHidden, @@ -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... @@ -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; diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index 467849cf7a..cd7d08caf0 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -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 ]' + ); +} |