7
7
let debug = require ( 'internal/util/debuglog' ) . debuglog ( 'source_map' , ( fn ) => {
8
8
debug = fn ;
9
9
} ) ;
10
+ const { getStringWidth } = require ( 'internal/util/inspect' ) ;
11
+ const { readFileSync } = require ( 'fs' ) ;
10
12
const { findSourceMap } = require ( 'internal/source_map/source_map_cache' ) ;
11
13
const {
12
14
kNoOverride,
@@ -36,7 +38,17 @@ const prepareStackTrace = (globalThis, error, trace) => {
36
38
if ( trace . length === 0 ) {
37
39
return errorString ;
38
40
}
41
+
42
+ let errorSource = '' ;
43
+ let firstSource ;
44
+ let firstLine ;
45
+ let firstColumn ;
39
46
const preparedTrace = trace . map ( ( t , i ) => {
47
+ if ( i === 0 ) {
48
+ firstLine = t . getLineNumber ( ) ;
49
+ firstColumn = t . getColumnNumber ( ) ;
50
+ firstSource = t . getFileName ( ) ;
51
+ }
40
52
let str = i !== 0 ? '\n at ' : '' ;
41
53
str = `${ str } ${ t } ` ;
42
54
try {
@@ -51,18 +63,57 @@ const prepareStackTrace = (globalThis, error, trace) => {
51
63
} = sm . findEntry ( t . getLineNumber ( ) - 1 , t . getColumnNumber ( ) - 1 ) ;
52
64
if ( originalSource && originalLine !== undefined &&
53
65
originalColumn !== undefined ) {
54
- str +=
55
- `\n -> ${ originalSource . replace ( 'file://' , '' ) } :${ originalLine + 1 } :${ originalColumn + 1 } ` ;
66
+ const originalSourceNoScheme = originalSource
67
+ . replace ( / ^ f i l e : \/ \/ / , '' ) ;
68
+ if ( i === 0 ) {
69
+ firstLine = originalLine + 1 ;
70
+ firstColumn = originalColumn + 1 ;
71
+ firstSource = originalSourceNoScheme ;
72
+ // Show error in original source context to help user pinpoint it:
73
+ errorSource = getErrorSource ( firstSource , firstLine , firstColumn ) ;
74
+ }
75
+ // Show both original and transpiled stack trace information:
76
+ str += `\n -> ${ originalSourceNoScheme } :${ originalLine + 1 } :` +
77
+ `${ originalColumn + 1 } ` ;
56
78
}
57
79
}
58
80
} catch ( err ) {
59
81
debug ( err . stack ) ;
60
82
}
61
83
return str ;
62
84
} ) ;
63
- return `${ errorString } \n at ${ preparedTrace . join ( '' ) } ` ;
85
+ return `${ errorSource } ${ errorString } \n at ${ preparedTrace . join ( '' ) } ` ;
64
86
} ;
65
87
88
+ // Places a snippet of code from where the exception was originally thrown
89
+ // above the stack trace. This logic is modeled after GetErrorSource in
90
+ // node_errors.cc.
91
+ function getErrorSource ( firstSource , firstLine , firstColumn ) {
92
+ let exceptionLine = '' ;
93
+ let source ;
94
+ try {
95
+ source = readFileSync ( firstSource , 'utf8' ) ;
96
+ } catch ( err ) {
97
+ debug ( err ) ;
98
+ return exceptionLine ;
99
+ }
100
+ const lines = source . split ( / \r ? \n / , firstLine ) ;
101
+ const line = lines [ firstLine - 1 ] ;
102
+ if ( ! line ) return exceptionLine ;
103
+
104
+ // Display ^ in appropriate position, regardless of whether tabs or
105
+ // spaces are used:
106
+ let prefix = '' ;
107
+ for ( const character of line . slice ( 0 , firstColumn ) ) {
108
+ prefix += ( character === '\t' ) ? '\t' :
109
+ ' ' . repeat ( getStringWidth ( character ) ) ;
110
+ }
111
+ prefix = prefix . slice ( 0 , - 1 ) ; // The last character is the '^'.
112
+
113
+ exceptionLine = `${ firstSource } :${ firstLine } \n${ line } \n${ prefix } ^\n\n` ;
114
+ return exceptionLine ;
115
+ }
116
+
66
117
module . exports = {
67
118
prepareStackTrace,
68
119
} ;
0 commit comments