8000
We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
There was an error while loading. Please reload this page.
1 parent d2a7057 commit 12e5535Copy full SHA for 12e5535
lib/internal/errors/error_source.js
@@ -8,9 +8,15 @@ const {
8
const {
9
getErrorSourcePositions,
10
} = internalBinding('errors');
11
+const {
12
+ getSourceMapsSupport,
13
+ findSourceMap,
14
+ getSourceLine,
15
+} = require('internal/source_map/source_map_cache');
16
17
/**
- * Get the source location of an error.
18
+ * Get the source location of an error. If source map is enabled, resolve the source location
19
+ * based on the source map.
20
*
21
* The `error.stack` must not have been accessed. The resolution is based on the structured
22
* error stack data.
@@ -21,10 +27,35 @@ function getErrorSourceLocation(error) {
27
const pos = getErrorSourcePositions(error);
28
23
29
sourceLine,
30
+ scriptResourceName,
31
+ lineNumber,
24
32
startColumn,
25
33
} = pos;
26
34
- return { sourceLine, startColumn };
35
+ // Source map is not enabled. Return the source line directly.
36
+ if (!getSourceMapsSupport().enabled) {
37
+ return { sourceLine, startColumn };
38
+ }
39
+
40
+ const sm = findSourceMap(scriptResourceName);
41
+ if (sm === undefined) {
42
+ return;
43
44
+ const {
45
+ originalLine,
46
+ originalColumn,
47
+ originalSource,
48
+ } = sm.findEntry(lineNumber - 1, startColumn);
49
+ const originalSourceLine = getSourceLine(sm, originalSource, originalLine, originalColumn);
50
51
+ if (!originalSourceLine) {
52
53
54
55
+ return {
56
+ sourceLine: originalSourceLine,
57
+ startColumn: originalColumn,
58
+ };
59
}
60
61
const memberAccessTokens = [ '.', '?.', '[', ']' ];
@@ -111,7 +142,8 @@ function getFirstExpression(code, startColumn) {
111
142
112
143
113
144
114
- * Get the source expression of an error.
145
+ * Get the source expression of an error. If source map is enabled, resolve the source location
146
115
147
116
148
* The `error.stack` must not have been accessed, or the source location may be incorrect. The
117
149
* resolution is based on the structured error stack data.
lib/internal/source_map/prepare_stack_trace.js
@@ -1,11 +1,9 @@
1
'use strict';
2
3
4
- ArrayPrototypeIndexOf,
5
ArrayPrototypeJoin,
6
ArrayPrototypeMap,
7
ErrorPrototypeToString,
- RegExpPrototypeSymbolSplit,
SafeStringIterator,
StringPrototypeRepeat,
StringPrototypeSlice,
@@ -16,8 +14,7 @@ let debug = require('internal/util/debuglog').debuglog('source_map', (fn) => {
debug = fn;
});
const { getStringWidth } = require('internal/util/inspect');
-const { readFileSync } = require('fs');
-const { findSourceMap } = require('internal/source_map/source_map_cache');
+const { findSourceMap, getSourceLine } = require('internal/source_map/source_map_cache');
kIsNodeError,
} = require('internal/errors');
@@ -155,21 +152,13 @@ function getErrorSource(
155
152
originalLine,
156
153
originalColumn,
157
154
) {
158
- const originalSourcePathNoScheme =
159
- StringPrototypeStartsWith(originalSourcePath, 'file://') ?
160
- fileURLToPath(originalSourcePath) : originalSourcePath;
161
- const source = getOriginalSource(
162
- sourceMap.payload,
163
- originalSourcePath,
164
- );
165
- if (typeof source !== 'string') {
166
- return;
167
- }
168
- const lines = RegExpPrototypeSymbolSplit(/\r?\n/, source, originalLine + 1);
169
- const line = lines[originalLine];
+ const line = getSourceLine(sourceMap, originalSourcePath, originalLine);
170
if (!line) {
171
return;
172
+ const originalSourcePathNoScheme =
+ StringPrototypeStartsWith(originalSourcePath, 'file://') ?
+ fileURLToPath(originalSourcePath) : originalSourcePath;
173
174
// Display ^ in appropriate position, regardless of whether tabs or
175
// spaces are used:
@@ -182,39 +171,10 @@ function getErrorSource(
182
prefix = StringPrototypeSlice(prefix, 0, -1); // The last character is '^'.
183
184
const exceptionLine =
185
- `${originalSourcePathNoScheme}:${originalLine + 1}\n${line}\n${prefix}^\n\n`;
+ `${originalSourcePathNoScheme}:${originalLine + 1}\n${line}\n${prefix}^\n`;
186
return exceptionLine;
187
176
188
177
189
-/**
190
- * Retrieve the original source code from the source map's `sources` list or disk.
191
- * @param {import('internal/source_map/source_map').SourceMap.payload} payload
192
- * @param {string} originalSourcePath - path or url of the original source
193
- * @returns {string | undefined} - the source content or undefined if file not found
194
- */
195
-function getOriginalSource(payload, originalSourcePath) {
196
- let source;
197
- // payload.sources has been normalized to be an array of absolute urls.
198
- const sourceContentIndex =
199
- ArrayPrototypeIndexOf(payload.sources, originalSourcePath);
200
- if (payload.sourcesContent?.[sourceContentIndex]) {
201
- // First we check if the original source content was provided in the
202
- // source map itself:
203
- source = payload.sourcesContent[sourceContentIndex];
204
- } else if (StringPrototypeStartsWith(originalSourcePath, 'file://')) {
205
- // If no sourcesContent was found, attempt to load the original source
206
- // from disk:
207
- debug(`read source of ${originalSourcePath} from filesystem`);
208
- const originalSourcePathNoScheme = fileURLToPath(originalSourcePath);
209
- try {
210
- source = readFileSync(originalSourcePathNoScheme, 'utf8');
211
- } catch (err) {
212
- debug(err);
213
214
215
- return source;
216
-}
217
-
218
178
219
179
* Retrieve exact line in the original source code from the source map's `sources` list or disk.
220
180
* @param {string} fileName - actual file name
lib/internal/source_map/source_map_cache.js
@@ -1,13 +1,16 @@
+ ArrayPrototypeIndexOf,
ArrayPrototypePush,
JSONParse,
ObjectFreeze,
RegExpPrototypeExec,
+ RegExpPrototypeSymbolSplit,
SafeMap,
StringPrototypeCodePointAt,
StringPrototypeSplit,
+ StringPrototypeStartsWith,
} = primordials;
// See https://tc39.es/ecma426/ for SourceMap V3 specification.
@@ -16,6 +19,7 @@ let debug = require('internal/util/debuglog').debuglog('source_map', (fn) => {
+const { readFileSync } = require('fs');
const { validateBoolean, validateObject } = require('internal/validators');
setSourceMapsEnabled: setSourceMapsNative,
@@ -277,8 +281,7 @@ function lineLengths(content) {
277
281
*/
278
282
function sourceMapFromFile(mapURL) {
279
283
try {
280
- const fs = require('fs');
- const content = fs.readFileSync(fileURLToPath(mapURL), 'utf8');
284
+ const content = readFileSync(fileURLToPath(mapURL), 'utf8');
285
const data = JSONParse(content);
286
return sourcesToAbsolute(mapURL, data);
287
} catch (err) {
@@ -400,8 +403,62 @@ function findSourceMap(sourceURL) {
400
403
401
404
402
405
406
+/**
407
+ * Retrieve the original source code from the source map's `sources` list or disk.
408
+ * @param {import('internal/source_map/source_map').SourceMap.payload} payload
409
+ * @param {string} originalSourcePath - path or url of the original source
410
+ * @returns {string | undefined} - the source content or undefined if file not found
411
+ */
412
+function getOriginalSource(payload, originalSourcePath) {
413
+ let source;
414
+ // payload.sources has been normalized to be an array of absolute urls.
415
+ const sourceContentIndex =
416
+ ArrayPrototypeIndexOf(payload.sources, originalSourcePath);
417
+ if (payload.sourcesContent?.[sourceContentIndex]) {
418
+ // First we check if the original source content was provided in the
419
+ // source map itself:
420
+ source = payload.sourcesContent[sourceContentIndex];
421
+ } else if (StringPrototypeStartsWith(originalSourcePath, 'file://')) {
422
+ // If no sourcesContent was found, attempt to load the original source
423
+ // from disk:
424
+ debug(`read source of ${originalSourcePath} from filesystem`);
425
+ const originalSourcePathNoScheme = fileURLToPath(originalSourcePath);
426
+ try {
427
+ source = readFileSync(originalSourcePathNoScheme, 'utf8');
428
+ } catch (err) {
429
+ debug(err);
430
431
432
+ return source;
433
+}
434
435
436
+ * Get the line of source in the source map.
437
+ * @param {import('internal/source_map/source_map').SourceMap} sourceMap
438
+ * @param {string} originalSourcePath path or url of the original source
439
+ * @param {number} originalLine line number in the original source
440
+ * @returns {string|undefined} source line if found
441
442
+function getSourceLine(
443
+ sourceMap,
444
+ originalSourcePath,
445
446
+) {
447
+ const source = getOriginalSource(
448
+ sourceMap.payload,
449
450
+ );
451
+ if (typeof source !== 'string') {
452
453
454
+ const lines = RegExpPrototypeSymbolSplit(/\r?\n/, source, originalLine + 1);
455
+ const line = lines[originalLine];
456
+ return line;
457
458
459
module.exports = {
460
findSourceMap,
461
462
getSourceMapsSupport,
463
setSourceMapsSupport,
464
maybeCacheSourceMap,
test/fixtures/source-map/output/source_map_assert_source_line.snapshot
@@ -0,0 +1,20 @@
+AssertionError [ERR_ASSERTION]: The expression evaluated to a falsy value:
+ assert(false)
+ at Object.<anonymous> (*/test/fixtures/source-map/output/source_map_assert_source_line.ts:11:3)
+ *
+ at TracingChannel.traceSync (node:diagnostics_channel:322:14)
+ generatedMessage: true,
+ code: 'ERR_ASSERTION',
+ actual: false,
+ expected: true,
+ operator: '==',
+ diff: 'simple'
test/fixtures/source-map/output/source_map_assert_source_line.ts
@@ -0,0 +1,14 @@
+// Flags: --enable-source-maps --experimental-transform-types --no-warnings
+require('../../../common');
+const assert = require('node:assert');
+enum Bar {
+ makeSureTransformTypes,
+try {
+ assert(false);
+} catch (e) {
+ console.log(e);
test/fixtures/source-map/output/source_map_enclosing_function.snapshot
@@ -2,7 +2,6 @@
throw err
^
Error: an error!
at functionD (*/test/fixtures/source-map/enclosing-call-site.js:16:17)
at functionC (*/test/fixtures/source-map/enclosing-call-site.js:10:3)
test/fixtures/source-map/output/source_map_eval.snapshot
alert "I knew it!"
ReferenceError: alert is not defined
at Object.eval (*/synthesized/workspace/tabs-source-url.coffee:26:2)
at eval (*/synthesized/workspace/tabs-source-url.coffee:1:14)
test/fixtures/source-map/output/source_map_reference_error_tabs.snapshot
at Object.<anonymous> (*/test/fixtures/source-map/tabs.coffee:26:2)
at Object.<anonymous> (*/test/fixtures/source-map/tabs.coffee:1:14)
test/fixtures/source-map/output/source_map_throw_async_stack_trace.snapshot
throw new Error('message')
Error: message
at Throw (*/test/fixtures/source-map/output/source_map_throw_async_stack_trace.mts:13:9)
at async Promise.all (index 3)
test/fixtures/source-map/output/source_map_throw_construct.snapshot
throw new Error('message');
at new Foo (*/test/fixtures/source-map/output/source_map_throw_construct.mts:13:11)
at <anonymous> (*/test/fixtures/source-map/output/source_map_throw_construct.mts:17:1)