1
+ import fs from 'fs' ;
2
+ import path from 'path' ;
3
+ import { SourceMapConsumer , RawSourceMap } from 'source-map' ;
4
+
5
+ function retrieveSourceMapURL ( contents : string ) {
6
+ const reversed = contents
7
+ . split ( '\n' )
8
+ . reverse ( )
9
+ . join ( '\n' ) ;
10
+
11
+ const match = / \/ [ / * ] # [ \t ] + s o u r c e M a p p i n g U R L = ( [ ^ \s ' " ] + ?) (?: [ \t ] + | $ ) / gm. exec ( reversed ) ;
12
+ if ( match ) return match [ 1 ] ;
13
+
14
+ return undefined ;
15
+ }
16
+
17
+ const fileCache = new Map < string , string > ( ) ;
18
+
19
+ function getFileContents ( path : string ) {
20
+ if ( fileCache . has ( path ) ) {
21
+ return fileCache . get ( path ) ;
22
+ }
23
+ if ( fs . existsSync ( path ) ) {
24
+ try {
25
+ const data = fs . readFileSync ( path , 'utf8' ) ;
26
+ fileCache . set ( path , data ) ;
27
+ return data ;
28
+ } catch {
29
+ return undefined ;
30
+ }
31
+ }
32
+ }
33
+
34
+ function sourcemapStacktrace ( stack : string ) {
35
+ const replaceFn = ( line : string ) =>
36
+ line . replace (
37
+ / ^ { 4 } a t (?: ( .+ ?) \s + \( ) ? (?: ( .+ ?) : ( \d + ) (?: : ( \d + ) ) ? ) \) ? / ,
38
+ ( input , varName , filePath , line , column ) => {
39
+ if ( ! filePath ) return input ;
40
+
41
+ const contents = getFileContents ( filePath ) ;
42
+ if ( ! contents ) return input ;
43
+
44
+ const srcMapPathOrBase64 = retrieveSourceMapURL ( contents ) ;
45
+ if ( ! srcMapPathOrBase64 ) return input ;
46
+
47
+ let dir = path . dirname ( filePath ) ;
48
+ let srcMapData : string ;
49
+
50
+ if ( / ^ d a t a : a p p l i c a t i o n \/ j s o n [ ^ , ] + b a s e 6 4 , / . test ( srcMapPathOrBase64 ) ) {
51
+ const rawData = srcMapPathOrBase64 . slice ( srcMapPathOrBase64 . indexOf ( ',' ) + 1 ) ;
52
+ try {
53
+ srcMapData = Buffer . from ( rawData , 'base64' ) . toString ( ) ;
54
+ } catch {
55
+ return input ;
56
+ }
57
+ } else {
58
+ const absSrcMapPath = path . resolve ( dir , srcMapPathOrBase64 ) ;
59
+ const data = getFileContents ( absSrcMapPath ) ;
60
+ if ( ! data ) return input ;
61
+
62
+ srcMapData = data ;
63
+ dir = path . dirname ( absSrcMapPath ) ;
64
+ }
65
+
66
+ let rawSourceMap : RawSourceMap ;
67
+ try {
68
+ rawSourceMap = JSON . parse ( srcMapData ) ;
69
+ } catch {
70
+ return input ;
71
+ }
72
+
73
+ const consumer = new SourceMapConsumer ( rawSourceMap ) ;
74
+ const pos = consumer . originalPositionFor ( {
75
+ line : Number ( line ) ,
76
+ column : Number ( column )
77
+ } ) ;
78
+ if ( ! pos . source ) return input ;
79
+
80
+ const absSrcPath = path . resolve ( dir , pos . source ) ;
81
+ const urlPart = `${ absSrcPath } :${ pos . line || 0 } :${ pos . column || 0 } ` ;
82
+
83
+ if ( ! varName ) return ` at ${ urlPart } ` ;
84
+ return ` at ${ varName } (${ urlPart } )` ;
85
+ }
86
+ ) ;
87
+
88
+ fileCache . clear ( ) ;
89
+ return stack
90
+ . split ( '\n' )
91
+ . map ( replaceFn )
92
+ . join ( '\n' ) ;
93
+ }
94
+
95
+ export { sourcemapStacktrace } ;
0 commit comments