diff --git a/lib/util.js b/lib/util.js index 45d98de194c836..e81f9a2673c2b1 100644 --- a/lib/util.js +++ b/lib/util.js @@ -638,6 +638,9 @@ function formatValue(ctx, value, recurseTimes, ln) { } else { extra = '[items unknown]'; } + } else if (types.isModuleNamespaceObject(value)) { + braces[0] = `[${tag}] {`; + formatter = formatNamespaceObject; } else { // Check boxed primitives other than string with valueOf() // NOTE: `Date` has to be checked first! @@ -766,6 +769,15 @@ function formatObject(ctx, value, recurseTimes, keys) { return output; } +function formatNamespaceObject(ctx, value, recurseTimes, keys) { + const len = keys.length; + const output = new Array(len); + for (var i = 0; i < len; i++) { + output[i] = formatNamespaceProperty(ctx, value, recurseTimes, keys[i]); + } + return output; +} + // The array is sparse and/or has extra keys function formatSpecialArray(ctx, value, recurseTimes, keys, maxLength, valLen) { const output = []; @@ -993,8 +1005,36 @@ function formatPromise(ctx, value, recurseTimes, keys) { return output; } +function formatKey(ctx, key, enumerable) { + if (typeof key === 'symbol') { + return `[${ctx.stylize(key.toString(), 'symbol')}]`; + } + if (enumerable === false) { + return `[${key}]`; + } + if (keyStrRegExp.test(key)) { + return ctx.stylize(key, 'name'); + } + return ctx.stylize(strEscape(key), 'string'); +} + +function formatNamespaceProperty(ctx, ns, recurseTimes, key) { + let value; + try { + value = formatValue(ctx, ns[key], recurseTimes, true); + } catch (err) { + if (err instanceof ReferenceError) { + value = ctx.stylize('', 'special'); + } else { + throw err; + } + } + + return `${formatKey(ctx, key)}: ${value}`; +} + function formatProperty(ctx, value, recurseTimes, key, array) { - let name, str; + let str; const desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key], enumerable: true }; if (desc.value !== undefined) { @@ -1016,17 +1056,8 @@ function formatProperty(ctx, value, recurseTimes, key, array) { if (array === 1) { return str; } - if (typeof key === 'symbol') { - name = `[${ctx.stylize(key.toString(), 'symbol')}]`; - } else if (desc.enumerable === false) { - name = `[${key}]`; - } else if (keyStrRegExp.test(key)) { - name = ctx.stylize(key, 'name'); - } else { - name = ctx.stylize(strEscape(key), 'string'); - } - return `${name}: ${str}`; + return `${formatKey(ctx, key, desc.enumerable)}: ${str}`; } function reduceToSingleString(ctx, output, base, braces, addLn) { diff --git a/test/parallel/test-util-inspect-namespace.js b/test/parallel/test-util-inspect-namespace.js new file mode 100644 index 00000000000000..fddbcdb34697d9 --- /dev/null +++ b/test/parallel/test-util-inspect-namespace.js @@ -0,0 +1,22 @@ +'use strict'; + +// Flags: --experimental-vm-modules + +const common = require('../common'); +const assert = require('assert'); + +common.crashOnUnhandledRejection(); + +const { Module } = require('vm'); +const { inspect } = require('util'); + +(async () => { + const m = new Module('export const a = 1; export var b = 2'); + await m.link(() => 0); + m.instantiate(); + assert.strictEqual( + inspect(m.namespace), + '[Module] { a: , b: undefined }'); + await m.evaluate(); + assert.strictEqual(inspect(m.namespace), '[Module] { a: 1, b: 2 }'); +})();