@@ -47,45 +47,48 @@ const prepareStackTrace = (globalThis, error, trace) => {
4747  } 
4848
4949  let  errorSource  =  '' ; 
50-   let  firstLine ; 
51-   let  firstColumn ; 
50+   let  lastSourceMap ; 
51+   let  lastFileName ; 
5252  const  preparedTrace  =  ArrayPrototypeJoin ( ArrayPrototypeMap ( trace ,  ( t ,  i )  =>  { 
53-     if  ( i  ===  0 )  { 
54-       firstLine  =  t . getLineNumber ( ) ; 
55-       firstColumn  =  t . getColumnNumber ( ) ; 
56-     } 
5753    let  str  =  i  !==  0  ? '\n    at '  : '' ; 
5854    str  =  `${ str } ${ t }  ; 
5955    try  { 
60-       const  sm  =  findSourceMap ( t . getFileName ( ) ) ; 
56+       // A stack trace will often have several call sites in a row within the 
57+       // same file, cache the source map and file content accordingly: 
58+       const  fileName  =  t . getFileName ( ) ; 
59+       const  sm  =  fileName  ===  lastFileName  ?
60+         lastSourceMap  :
61+         findSourceMap ( fileName ) ; 
62+       lastSourceMap  =  sm ; 
63+       lastFileName  =  fileName ; 
6164      if  ( sm )  { 
62-         // Source Map V3 lines/columns use zero-based offsets  whereas, in  
63-         // stack traces, they  start at 1/1.  
65+         // Source Map V3 lines/columns start at 0/0  whereas stack traces  
66+         // start at 1/1:  
6467        const  { 
6568          originalLine, 
6669          originalColumn, 
67-           originalSource
70+           originalSource, 
6871        }  =  sm . findEntry ( t . getLineNumber ( )  -  1 ,  t . getColumnNumber ( )  -  1 ) ; 
6972        if  ( originalSource  &&  originalLine  !==  undefined  && 
7073            originalColumn  !==  undefined )  { 
74+           const  name  =  getOriginalSymbolName ( sm ,  trace ,  i ) ; 
7175          if  ( i  ===  0 )  { 
72-             firstLine  =  originalLine  +  1 ; 
73-             firstColumn  =  originalColumn  +  1 ; 
74- 
75-             // Show error in original source context to help user pinpoint it: 
7676            errorSource  =  getErrorSource ( 
77-               sm . payload , 
77+               sm , 
7878              originalSource , 
79-               firstLine , 
80-               firstColumn 
79+               originalLine , 
80+               originalColumn 
8181            ) ; 
8282          } 
8383          // Show both original and transpiled stack trace information: 
84+           const  prefix  =  ( name  &&  name  !==  t . getFunctionName ( ) )  ?
85+             `\n        -> at ${ name }   :
86+             '\n        ->' ; 
8487          const  originalSourceNoScheme  = 
8588            StringPrototypeStartsWith ( originalSource ,  'file://' )  ?
8689              fileURLToPath ( originalSource )  : originalSource ; 
87-           str  +=  `\n        ->  ${ originalSourceNoScheme } ${ originalLine  +  1 }   + 
88-             `${ originalColumn  +  1 }  ; 
90+           str  +=  `${ prefix }  ( ${ originalSourceNoScheme } ${ originalLine  +  1 }   + 
91+             `${ originalColumn  +  1 } ) ` ; 
8992        } 
9093      } 
9194    }  catch  ( err )  { 
@@ -96,18 +99,69 @@ const prepareStackTrace = (globalThis, error, trace) => {
9699  return  `${ errorSource } ${ errorString } ${ preparedTrace }  ; 
97100} ; 
98101
102+ // Transpilers may have removed the original symbol name used in the stack 
103+ // trace, if possible restore it from the names field of the source map: 
104+ function  getOriginalSymbolName ( sourceMap ,  trace ,  curIndex )  { 
105+   // First check for a symbol name associated with the enclosing function: 
106+   const  enclosingEntry  =  sourceMap . findEntry ( 
107+     trace [ curIndex ] . getEnclosingLineNumber ( )  -  1 , 
108+     trace [ curIndex ] . getEnclosingColumnNumber ( )  -  1 
109+   ) ; 
110+   if  ( enclosingEntry . name )  return  enclosingEntry . name ; 
111+   // Fallback to using the symbol name attached to the next stack frame: 
112+   const  currentFileName  =  trace [ curIndex ] . getFileName ( ) ; 
113+   const  nextCallSite  =  trace [ curIndex  +  1 ] ; 
114+   if  ( nextCallSite  &&  currentFileName  ===  nextCallSite . getFileName ( ) )  { 
115+     const  {  name }  =  sourceMap . findEntry ( 
116+       nextCallSite . getLineNumber ( )  -  1 , 
117+       nextCallSite . getColumnNumber ( )  -  1 
118+     ) ; 
119+     return  name ; 
120+   } 
121+ } 
122+ 
99123// Places a snippet of code from where the exception was originally thrown 
100124// above the stack trace. This logic is modeled after GetErrorSource in 
101125// node_errors.cc. 
102- function  getErrorSource ( payload ,  originalSource ,  firstLine ,  firstColumn )  { 
126+ function  getErrorSource ( 
127+   sourceMap , 
128+   originalSourcePath , 
129+   originalLine , 
130+   originalColumn 
131+ )  { 
103132  let  exceptionLine  =  '' ; 
104-   const  originalSourceNoScheme  = 
105-     StringPrototypeStartsWith ( originalSource ,  'file://' )  ?
106-       fileURLToPath ( originalSource )  : originalSource ; 
133+   const  originalSourcePathNoScheme  = 
134+     StringPrototypeStartsWith ( originalSourcePath ,  'file://' )  ?
135+       fileURLToPath ( originalSourcePath )  : originalSourcePath ; 
136+   const  source  =  getOriginalSource ( 
137+     sourceMap . payload , 
138+     originalSourcePathNoScheme 
139+   ) ; 
140+   const  lines  =  StringPrototypeSplit ( source ,  / \r ? \n / ,  originalLine  +  1 ) ; 
141+   const  line  =  lines [ originalLine ] ; 
142+   if  ( ! line )  return  exceptionLine ; 
143+ 
144+   // Display ^ in appropriate position, regardless of whether tabs or 
145+   // spaces are used: 
146+   let  prefix  =  '' ; 
147+   for  ( const  character  of  StringPrototypeSlice ( line ,  0 ,  originalColumn  +  1 ) )  { 
148+     prefix  +=  ( character  ===  '\t' )  ? '\t'  :
149+       StringPrototypeRepeat ( ' ' ,  getStringWidth ( character ) ) ; 
150+   } 
151+   prefix  =  StringPrototypeSlice ( prefix ,  0 ,  - 1 ) ;  // The last character is '^'. 
107152
153+   exceptionLine  = 
154+    `${ originalSourcePathNoScheme } ${ originalLine  +  1 } ${ line } ${ prefix }  ; 
155+   return  exceptionLine ; 
156+ } 
157+ 
158+ function  getOriginalSource ( payload ,  originalSourcePath )  { 
108159  let  source ; 
160+   const  originalSourcePathNoScheme  = 
161+     StringPrototypeStartsWith ( originalSourcePath ,  'file://' )  ?
162+       fileURLToPath ( originalSourcePath )  : originalSourcePath ; 
109163  const  sourceContentIndex  = 
110-     ArrayPrototypeIndexOf ( payload . sources ,  originalSource ) ; 
164+     ArrayPrototypeIndexOf ( payload . sources ,  originalSourcePath ) ; 
111165  if  ( payload . sourcesContent ?. [ sourceContentIndex ] )  { 
112166    // First we check if the original source content was provided in the 
113167    // source map itself: 
@@ -116,29 +170,13 @@ function getErrorSource(payload, originalSource, firstLine, firstColumn) {
116170    // If no sourcesContent was found, attempt to load the original source 
117171    // from disk: 
118172    try  { 
119-       source  =  readFileSync ( originalSourceNoScheme ,  'utf8' ) ; 
173+       source  =  readFileSync ( originalSourcePathNoScheme ,  'utf8' ) ; 
120174    }  catch  ( err )  { 
121175      debug ( err ) ; 
122-       return  '' ; 
176+       source   =  '' ; 
123177    } 
124178  } 
125- 
126-   const  lines  =  StringPrototypeSplit ( source ,  / \r ? \n / ,  firstLine ) ; 
127-   const  line  =  lines [ firstLine  -  1 ] ; 
128-   if  ( ! line )  return  exceptionLine ; 
129- 
130-   // Display ^ in appropriate position, regardless of whether tabs or 
131-   // spaces are used: 
132-   let  prefix  =  '' ; 
133-   for  ( const  character  of  StringPrototypeSlice ( line ,  0 ,  firstColumn ) )  { 
134-     prefix  +=  ( character  ===  '\t' )  ? '\t'  :
135-       StringPrototypeRepeat ( ' ' ,  getStringWidth ( character ) ) ; 
136-   } 
137-   prefix  =  StringPrototypeSlice ( prefix ,  0 ,  - 1 ) ;  // The last character is '^'. 
138- 
139-   exceptionLine  = 
140-               `${ originalSourceNoScheme } ${ firstLine } ${ line } ${ prefix }  ; 
141-   return  exceptionLine ; 
179+   return  source ; 
142180} 
143181
144182module . exports  =  { 
0 commit comments