Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nodejs/node.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbcoe <bencoe@google.com>2020-05-25 02:00:46 +0300
committerBenjamin Coe <bencoe@google.com>2020-05-25 08:03:34 +0300
commit458677f5ef2bd35da920246bb266502ea76bb66c (patch)
treee1809f38c6bbbf898287513a062ad588ba32e43d /lib/internal/source_map
parent8f10bb2da5bcf166fa1b414055f03352bbdb8126 (diff)
errors: print original exception context
When --enable-source-maps is set, the error context displayed above the stack trace now shows original source rather than transpiled. PR-URL: https://github.com/nodejs/node/pull/33491 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Diffstat (limited to 'lib/internal/source_map')
-rw-r--r--lib/internal/source_map/prepare_stack_trace.js57
-rw-r--r--lib/internal/source_map/source_map_cache.js27
2 files changed, 76 insertions, 8 deletions
diff --git a/lib/internal/source_map/prepare_stack_trace.js b/lib/internal/source_map/prepare_stack_trace.js
index 037a8dc53e0..df0d79a204b 100644
--- a/lib/internal/source_map/prepare_stack_trace.js
+++ b/lib/internal/source_map/prepare_stack_trace.js
@@ -5,6 +5,8 @@ const {
} = primordials;
const debug = require('internal/util/debuglog').debuglog('source_map');
+const { getStringWidth } = require('internal/util/inspect');
+const { readFileSync } = require('fs');
const { findSourceMap } = require('internal/source_map/source_map_cache');
const {
kNoOverride,
@@ -34,7 +36,17 @@ const prepareStackTrace = (globalThis, error, trace) => {
if (trace.length === 0) {
return errorString;
}
+
+ let errorSource = '';
+ let firstSource;
+ let firstLine;
+ let firstColumn;
const preparedTrace = trace.map((t, i) => {
+ if (i === 0) {
+ firstLine = t.getLineNumber();
+ firstColumn = t.getColumnNumber();
+ firstSource = t.getFileName();
+ }
let str = i !== 0 ? '\n at ' : '';
str = `${str}${t}`;
try {
@@ -49,8 +61,18 @@ const prepareStackTrace = (globalThis, error, trace) => {
} = sm.findEntry(t.getLineNumber() - 1, t.getColumnNumber() - 1);
if (originalSource && originalLine !== undefined &&
originalColumn !== undefined) {
- str +=
-`\n -> ${originalSource.replace('file://', '')}:${originalLine + 1}:${originalColumn + 1}`;
+ const originalSourceNoScheme = originalSource
+ .replace(/^file:\/\//, '');
+ if (i === 0) {
+ firstLine = originalLine + 1;
+ firstColumn = originalColumn + 1;
+ firstSource = originalSourceNoScheme;
+ // Show error in original source context to help user pinpoint it:
+ errorSource = getErrorSource(firstSource, firstLine, firstColumn);
+ }
+ // Show both original and transpiled stack trace information:
+ str += `\n -> ${originalSourceNoScheme}:${originalLine + 1}:` +
+ `${originalColumn + 1}`;
}
}
} catch (err) {
@@ -58,9 +80,38 @@ const prepareStackTrace = (globalThis, error, trace) => {
}
return str;
});
- return `${errorString}\n at ${preparedTrace.join('')}`;
+ return `${errorSource}${errorString}\n at ${preparedTrace.join('')}`;
};
+// Places a snippet of code from where the exception was originally thrown
+// above the stack trace. This logic is modeled after GetErrorSource in
+// node_errors.cc.
+function getErrorSource(firstSource, firstLine, firstColumn) {
+ let exceptionLine = '';
+ let source;
+ try {
+ source = readFileSync(firstSource, 'utf8');
+ } catch (err) {
+ debug(err);
+ return exceptionLine;
+ }
+ const lines = source.split(/\r?\n/, firstLine);
+ const line = lines[firstLine - 1];
+ if (!line) return exceptionLine;
+
+ // Display ^ in appropriate position, regardless of whether tabs or
+ // spaces are used:
+ let prefix = '';
+ for (const character of line.slice(0, firstColumn)) {
+ prefix += (character === '\t') ? '\t' :
+ ' '.repeat(getStringWidth(character));
+ }
+ prefix = prefix.slice(0, -1); // The last character is the '^'.
+
+ exceptionLine = `${firstSource}:${firstLine}\n${line}\n${prefix}^\n\n`;
+ return exceptionLine;
+}
+
module.exports = {
prepareStackTrace,
};
diff --git a/lib/internal/source_map/source_map_cache.js b/lib/internal/source_map/source_map_cache.js
index 06b1a2a5f52..04926acfc1b 100644
--- a/lib/internal/source_map/source_map_cache.js
+++ b/lib/internal/source_map/source_map_cache.js
@@ -39,12 +39,28 @@ const { fileURLToPath, URL } = require('url');
let Module;
let SourceMap;
-let experimentalSourceMaps;
-function maybeCacheSourceMap(filename, content, cjsModuleInstance) {
- if (experimentalSourceMaps === undefined) {
- experimentalSourceMaps = getOptionValue('--enable-source-maps');
+let sourceMapsEnabled;
+function getSourceMapsEnabled() {
+ if (sourceMapsEnabled === undefined) {
+ sourceMapsEnabled = getOptionValue('--enable-source-maps');
+ if (sourceMapsEnabled) {
+ const {
+ enableSourceMaps,
+ setPrepareStackTraceCallback
+ } = internalBinding('errors');
+ const {
+ prepareStackTrace
+ } = require('internal/source_map/prepare_stack_trace');
+ setPrepareStackTraceCallback(prepareStackTrace);
+ enableSourceMaps();
+ }
}
- if (!(process.env.NODE_V8_COVERAGE || experimentalSourceMaps)) return;
+ return sourceMapsEnabled;
+}
+
+function maybeCacheSourceMap(filename, content, cjsModuleInstance) {
+ const sourceMapsEnabled = getSourceMapsEnabled();
+ if (!(process.env.NODE_V8_COVERAGE || sourceMapsEnabled)) return;
let basePath;
try {
filename = normalizeReferrerURL(filename);
@@ -248,6 +264,7 @@ function findSourceMap(uri, error) {
module.exports = {
findSourceMap,
+ getSourceMapsEnabled,
maybeCacheSourceMap,
rekeySourceMap,
sourceMapCacheToObject,