summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/api/util.md19
-rw-r--r--lib/internal/util/inspect.js37
-rw-r--r--test/fixtures/node_modules/node_modules/bar.js2
-rw-r--r--test/parallel/test-util-inspect.js40
-rw-r--r--test/pseudo-tty/console_colors.js13
-rw-r--r--test/pseudo-tty/console_colors.out35
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