55} = primordials;
66
77const debug = require('internal/util/debuglog').debuglog('source_map');
8+ const { getStringWidth } = require('internal/util/inspect');
9+ const { readFileSync } = require('fs');
810const { findSourceMap } = require('internal/source_map/source_map_cache');
911const {
1012 kNoOverride,
@@ -34,7 +36,17 @@ const prepareStackTrace = (globalThis, error, trace) => {
3436 if (trace.length === 0) {
3537 return errorString;
3638 }
39+
40+ let errorSource = '';
41+ let firstSource;
42+ let firstLine;
43+ let firstColumn;
3744 const preparedTrace = trace.map((t, i) => {
45+ if (i === 0) {
46+ firstLine = t.getLineNumber();
47+ firstColumn = t.getColumnNumber();
48+ firstSource = t.getFileName();
49+ }
3850 let str = i !== 0 ? '\n at ' : '';
3951 str = `${str}${t}`;
4052 try {
@@ -49,18 +61,57 @@ const prepareStackTrace = (globalThis, error, trace) => {
4961 } = sm.findEntry(t.getLineNumber() - 1, t.getColumnNumber() - 1);
5062 if (originalSource && originalLine !== undefined &&
5163 originalColumn !== undefined) {
52- str +=
53- `\n -> ${originalSource.replace('file://', '')}:${originalLine + 1}:${originalColumn + 1}`;
64+ const originalSourceNoScheme = originalSource
65+ .replace(/^file:\/\//, '');
66+ if (i === 0) {
67+ firstLine = originalLine + 1;
68+ firstColumn = originalColumn + 1;
69+ firstSource = originalSourceNoScheme;
70+ // Show error in original source context to help user pinpoint it:
71+ errorSource = getErrorSource(firstSource, firstLine, firstColumn);
72+ }
73+ // Show both original and transpiled stack trace information:
74+ str += `\n -> ${originalSourceNoScheme}:${originalLine + 1}:` +
75+ `${originalColumn + 1}`;
5476 }
5577 }
5678 } catch (err) {
5779 debug(err.stack);
5880 }
5981 return str;
6082 });
61- return `${errorString}\n at ${preparedTrace.join('')}`;
83+ return `${errorSource}${ errorString}\n at ${preparedTrace.join('')}`;
6284};
6385
86+ // Places a snippet of code from where the exception was originally thrown
87+ // above the stack trace. This logic is modeled after GetErrorSource in
88+ // node_errors.cc.
89+ function getErrorSource(firstSource, firstLine, firstColumn) {
90+ let exceptionLine = '';
91+ let source;
92+ try {
93+ source = readFileSync(firstSource, 'utf8');
94+ } catch (err) {
95+ debug(err);
96+ return exceptionLine;
97+ }
98+ const lines = source.split(/\r?\n/, firstLine);
99+ const line = lines[firstLine - 1];
100+ if (!line) return exceptionLine;
101+
102+ // Display ^ in appropriate position, regardless of whether tabs or
103+ // spaces are used:
104+ let prefix = '';
105+ for (const character of line.slice(0, firstColumn)) {
106+ prefix += (character === '\t') ? '\t' :
107+ ' '.repeat(getStringWidth(character));
108+ }
109+ prefix = prefix.slice(0, -1); // The last character is the '^'.
110+
111+ exceptionLine = `${firstSource}:${firstLine}\n${line}\n${prefix}^\n\n`;
112+ return exceptionLine;
113+ }
114+
64115module.exports = {
65116 prepareStackTrace,
66117};
0 commit comments