From d09d2287eb772a924100e6df9165db947916fa80 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 29 Nov 2021 17:36:49 +0100 Subject: [PATCH] util: pass through the inspect function to custom inspect functions This allows to use more portable custom inspect functions. Fixes: https://github.com/nodejs/node/issues/35956 Signed-off-by: Ruben Bridgewater --- doc/api/util.md | 35 ++++++++++++++++++++---------- lib/internal/util/inspect.js | 9 ++++++-- test/parallel/test-util-inspect.js | 7 +++--- 3 files changed, 35 insertions(+), 16 deletions(-) diff --git a/doc/api/util.md b/doc/api/util.md index 3810b1145cd7cc..86bee91614e305 100644 --- a/doc/api/util.md +++ b/doc/api/util.md @@ -576,7 +576,7 @@ changes: codes. Colors are customizable. See [Customizing `util.inspect` colors][]. **Default:** `false`. * `customInspect` {boolean} If `false`, - `[util.inspect.custom](depth, opts)` functions are not invoked. + `[util.inspect.custom](depth, opts, inspect)` functions are not invoked. **Default:** `true`. * `showProxy` {boolean} If `true`, `Proxy` inspection includes the [`target` and `handler`][] objects. **Default:** `false`. @@ -853,10 +853,18 @@ ignored, if not supported. + + Objects may also define their own -[`[util.inspect.custom](depth, opts)`][util.inspect.custom] function, +[`[util.inspect.custom](depth, opts, inspect)`][util.inspect.custom] function, which `util.inspect()` will invoke and use the result of when inspecting -the object: +the object. ```js const util = require('util'); @@ -866,7 +874,7 @@ class Box { this.value = value; } - [util.inspect.custom](depth, options) { + [util.inspect.custom](depth, options, inspect) { if (depth < 0) { return options.stylize('[Box]', 'special'); } @@ -877,8 +885,8 @@ class Box { // Five space padding because that's the size of "Box< ". const padding = ' '.repeat(5); - const inner = util.inspect(this.value, newOptions) - .replace(/\n/g, `\n${padding}`); + const inner = inspect(this.value, newOptions) + .replace(/\n/g, `\n${padding}`); return `${options.stylize('Box', 'special')}< ${inner} >`; } } @@ -889,9 +897,9 @@ util.inspect(box); // Returns: "Box< true >" ``` -Custom `[util.inspect.custom](depth, opts)` functions typically return a string -but may return a value of any type that will be formatted accordingly by -`util.inspect()`. +Custom `[util.inspect.custom](depth, opts, inspect)` functions typically return +a string but may return a value of any type that will be formatted accordingly +by `util.inspect()`. ```js const util = require('util'); @@ -921,8 +929,13 @@ In addition to being accessible through `util.inspect.custom`, this symbol is [registered globally][global symbol registry] and can be accessed in any environment as `Symbol.for('nodejs.util.inspect.custom')`. +Using this allows code to be written in a portable fashion, so that the custom +inspect function is used in an Node.js environment and ignored in the browser. +The `util.inspect()` function itself is passed as third argument to the custom +inspect function to allow further portability. + ```js -const inspect = Symbol.for('nodejs.util.inspect.custom'); +const customInspectSymbol = Symbol.for('nodejs.util.inspect.custom'); class Password { constructor(value) { @@ -933,7 +946,7 @@ class Password { return 'xxxxxxxx'; } - [inspect]() { + [customInspectSymbol](depth, inspectOptions, inspect) { return `Password <${this.toString()}>`; } } diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index 4936ab761ddccb..498b16d6015b04 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -761,7 +761,12 @@ function formatValue(ctx, value, recurseTimes, typedArray) { const isCrossContext = proxy !== undefined || !(context instanceof Object); const ret = FunctionPrototypeCall( - maybeCustom, context, depth, getUserOptions(ctx, isCrossContext)); + maybeCustom, + context, + depth, + getUserOptions(ctx, isCrossContext), + inspect + ); // If the custom inspection method returned `this`, don't go into // infinite recursion. if (ret !== context) { @@ -1126,7 +1131,7 @@ function getClassBase(value, constructor, tag) { function getFunctionBase(value, constructor, tag) { const stringified = FunctionPrototypeToString(value); - if (stringified.slice(0, 5) === 'class' && stringified.endsWith('}')) { + if (stringified.startsWith('class') && stringified.endsWith('}')) { const slice = stringified.slice(5, -1); const bracketIndex = slice.indexOf('{'); if (bracketIndex !== -1 && diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index fe01c4c0f92858..33c3ca7a6ef1b8 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -932,7 +932,7 @@ util.inspect({ hasOwnProperty: null }); assert.strictEqual(util.inspect(subject), "{ foo: 'bar' }"); - subject[util.inspect.custom] = common.mustCall((depth, opts) => { + subject[util.inspect.custom] = common.mustCall((depth, opts, inspect) => { const clone = { ...opts }; // This might change at some point but for now we keep the stylize function. // The function should either be documented or an alternative should be @@ -942,12 +942,13 @@ util.inspect({ hasOwnProperty: null }); assert.strictEqual(opts.budget, undefined); assert.strictEqual(opts.indentationLvl, undefined); assert.strictEqual(opts.showHidden, false); + assert.strictEqual(inspect, util.inspect); assert.deepStrictEqual( - new Set(Object.keys(util.inspect.defaultOptions).concat(['stylize'])), + new Set(Object.keys(inspect.defaultOptions).concat(['stylize'])), new Set(Object.keys(opts)) ); opts.showHidden = true; - return { [util.inspect.custom]: common.mustCall((depth, opts2) => { + return { [inspect.custom]: common.mustCall((depth, opts2) => { assert.deepStrictEqual(clone, opts2); }) }; });