diff options
-rw-r--r-- | doc/api/util.md | 19 | ||||
-rw-r--r-- | lib/internal/util/inspect.js | 37 | ||||
-rw-r--r-- | test/fixtures/node_modules/node_modules/bar.js | 2 | ||||
-rw-r--r-- | test/parallel/test-util-inspect.js | 40 | ||||
-rw-r--r-- | test/pseudo-tty/console_colors.js | 13 | ||||
-rw-r--r-- | test/pseudo-tty/console_colors.out | 35 |
6 files changed, 133 insertions, 13 deletions
diff --git a/doc/api/util.md b/doc/api/util.md index ceaa01d5b0..4d8cfda1d9 100644 --- a/doc/api/util.md +++ b/doc/api/util.md @@ -638,15 +638,16 @@ via the `util.inspect.styles` and `util.inspect.colors` properties. The default styles and associated colors are: - * `number` - `yellow` - * `boolean` - `yellow` - * `string` - `green` - * `date` - `magenta` - * `regexp` - `red` - * `null` - `bold` - * `undefined` - `grey` - * `special` - `cyan` (only applied to functions at this time) - * `name` - (no styling) +* `number` - `yellow` +* `boolean` - `yellow` +* `string` - `green` +* `date` - `magenta` +* `module` - `underline` +* `regexp` - `red` +* `null` - `bold` +* `undefined` - `grey` +* `special` - `cyan` (only applied to functions at this time) +* `name` - (no styling) The predefined color codes are: `white`, `grey`, `black`, `blue`, `cyan`, `green`, `magenta`, `red` and `yellow`. There are also `bold`, `italic`, diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index a31ee5b8eb..08f627db88 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -86,6 +86,8 @@ const { const assert = require('internal/assert'); +const { NativeModule } = require('internal/bootstrap/loaders'); + let hexSlice; const inspectDefaultOptions = Object.seal({ @@ -115,6 +117,9 @@ const strEscapeSequencesReplacerSingle = /[\x00-\x1f\x5c]/g; const keyStrRegExp = /^[a-zA-Z_][a-zA-Z_0-9]*$/; const numberRegExp = /^(0|[1-9][0-9]*)$/; +const coreModuleRegExp = /^ at (?:[^/\\(]+ \(|)((?<![/\\]).+)\.js:\d+:\d+\)?$/; +const nodeModulesRegExp = /[/\\]node_modules[/\\](.+?)(?=[/\\])/g; + const readableRegExps = {}; const kMinLineLength = 16; @@ -253,7 +258,8 @@ inspect.styles = Object.assign(Object.create(null), { symbol: 'green', date: 'magenta', // "name": intentionally not styling - regexp: 'red' + regexp: 'red', + module: 'underline' }); function addQuotes(str, quotes) { @@ -838,10 +844,37 @@ function formatError(err, constructor, tag, ctx) { } } } + // Ignore the error message if it's contained in the stack. + let pos = err.message && stack.indexOf(err.message) || -1; + if (pos !== -1) + pos += err.message.length; // Wrap the error in brackets in case it has no stack trace. - const stackStart = stack.indexOf('\n at'); + const stackStart = stack.indexOf('\n at', pos); if (stackStart === -1) { stack = `[${stack}]`; + } else if (ctx.colors) { + // Highlight userland code and node modules. + let newStack = stack.slice(0, stackStart); + const lines = stack.slice(stackStart + 1).split('\n'); + for (const line of lines) { + const core = line.match(coreModuleRegExp); + if (core !== null && NativeModule.exists(core[1])) { + newStack += `\n${ctx.stylize(line, 'undefined')}`; + } else { + // This adds underscores to all node_modules to quickly identify them. + let nodeModule; + newStack += '\n'; + let pos = 0; + while (nodeModule = nodeModulesRegExp.exec(line)) { + // '/node_modules/'.length === 14 + newStack += line.slice(pos, nodeModule.index + 14); + newStack += ctx.stylize(nodeModule[1], 'module'); + pos = nodeModule.index + nodeModule[0].length; + } + newStack += pos === 0 ? line : line.slice(pos); + } + } + stack = newStack; } // The message and the stack have to be indented as well! if (ctx.indentationLvl !== 0) { diff --git a/test/fixtures/node_modules/node_modules/bar.js b/test/fixtures/node_modules/node_modules/bar.js index 84b74de9d9..a2aab083b7 100644 --- a/test/fixtures/node_modules/node_modules/bar.js +++ b/test/fixtures/node_modules/node_modules/bar.js @@ -19,6 +19,4 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -console.error(__filename); -console.error(module.paths.join('\n') + '\n'); throw new Error('Should not ever get here.'); diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index 140fb22f0d..c1b6e3f98d 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -2311,3 +2311,43 @@ assert.strictEqual( assert.strictEqual(out, expected); } + +{ + // Use a fake stack to verify the expected colored outcome. + const stack = [ + 'TypedError: Wonderful message!', + ' at A.<anonymous> (/test/node_modules/foo/node_modules/bar/baz.js:2:7)', + ' at Module._compile (internal/modules/cjs/loader.js:827:30)', + ' at Fancy (vm.js:697:32)', + // This file is not an actual Node.js core file. + ' at tryModuleLoad (internal/modules/cjs/foo.js:629:12)', + ' at Function.Module._load (internal/modules/cjs/loader.js:621:3)', + // This file is not an actual Node.js core file. + ' at Module.require [as weird/name] (internal/aaaaaa/loader.js:735:19)', + ' at require (internal/modules/cjs/helpers.js:14:16)', + ' at /test/test-util-inspect.js:2239:9', + ' at getActual (assert.js:592:5)' + ]; + const isNodeCoreFile = [ + false, false, true, true, false, true, false, true, false, true + ]; + const err = new TypeError('Wonderful message!'); + err.stack = stack.join('\n'); + util.inspect(err, { colors: true }).split('\n').forEach((line, i) => { + let actual = stack[i].replace(/node_modules\/([a-z]+)/g, (a, m) => { + return `node_modules/\u001b[4m${m}\u001b[24m`; + }); + if (isNodeCoreFile[i]) { + actual = `\u001b[90m${actual}\u001b[39m`; + } + assert.strictEqual(actual, line); + }); +} + +{ + // Cross platform checks. + const err = new Error('foo'); + util.inspect(err, { colors: true }).split('\n').forEach((line, i) => { + assert(i < 2 || line.startsWith('\u001b[90m')); + }); +} diff --git a/test/pseudo-tty/console_colors.js b/test/pseudo-tty/console_colors.js index dd16a0c028..e525bed59c 100644 --- a/test/pseudo-tty/console_colors.js +++ b/test/pseudo-tty/console_colors.js @@ -1,5 +1,6 @@ 'use strict'; require('../common'); +const vm = require('vm'); // Make this test OS-independent by overriding stdio getColorDepth(). process.stdout.getColorDepth = () => 8; process.stderr.getColorDepth = () => 8; @@ -7,3 +8,15 @@ process.stderr.getColorDepth = () => 8; console.log({ foo: 'bar' }); console.log('%s q', 'string'); console.log('%o with object format param', { foo: 'bar' }); + +console.log( + new Error('test\n at abc (../fixtures/node_modules/bar.js:4:4)\nfoobar') +); + +try { + require('../fixtures/node_modules/node_modules/bar.js'); +} catch (err) { + console.log(err); +} + +vm.runInThisContext('console.log(new Error())'); diff --git a/test/pseudo-tty/console_colors.out b/test/pseudo-tty/console_colors.out index f0ee5e42d6..6ee69f645b 100644 --- a/test/pseudo-tty/console_colors.out +++ b/test/pseudo-tty/console_colors.out @@ -1,3 +1,38 @@ { foo: *[32m'bar'*[39m } string q { foo: *[32m'bar'*[39m } with object format param + +Error: test +at abc (../fixtures/node_modules/bar.js:4:4) +foobar +at * (*console_colors.js:*:*) +*[90m at * (internal*:*:*)*[39m +*[90m at *[39m +*[90m at *[39m +*[90m at *[39m +*[90m at *[39m +*[90m at *[39m + +Error: Should not ever get here. +at * (*node_modules*[4m*node_modules*[24m*bar.js:*:*) +*[90m at *[39m +*[90m at *[39m +*[90m at *[39m +*[90m at *[39m +*[90m at *[39m +*[90m at *[39m +at * (*console_colors.js:*:*) +*[90m at *[39m +*[90m at *[39m + +Error +at evalmachine.<anonymous>:*:* +*[90m at Script.runInThisContext (vm.js:*:*)*[39m +*[90m at Object.runInThisContext (vm.js:*:*)*[39m +at * (*console_colors.js:*:*) +*[90m at *[39m +*[90m at *[39m +*[90m at *[39m +*[90m at *[39m +*[90m at *[39m +*[90m at *[39m |