1
+ const fs = require ( 'fs/promises' )
1
2
const { readFileSync } = require ( 'fs' )
3
+ const { fileURLToPath } = require ( 'url' )
2
4
const { findSourceMap } = require ( 'module' )
3
- const { debug } = require ( './common-utils' )
5
+ const { debug, cacheDir, exists } = require ( './common-utils' )
6
+ const path = require ( 'path' )
4
7
5
8
/**
6
9
* @param {string } f
@@ -18,60 +21,178 @@ const lineLengths = (f) =>
18
21
* }} SourceMap
19
22
*/
20
23
24
+ // Matches only the last occurrence of sourceMappingURL
25
+ const innerRegex = / \s * [ # @ ] \s * s o u r c e M a p p i n g U R L \s * = \s * ( [ ^ \s ' " ] * ) \s * /
26
+
27
+ const sourceMappingURLRegex = RegExp (
28
+ '(?:' +
29
+ '/\\*' +
30
+ '(?:\\s*\r?\n(?://)?)?' +
31
+ '(?:' +
32
+ innerRegex . source +
33
+ ')' +
34
+ '\\s*' +
35
+ '\\*/' +
36
+ '|' +
37
+ '//(?:' +
38
+ innerRegex . source +
39
+ ')' +
40
+ ')' +
41
+ '\\s*'
42
+ )
43
+
44
+ /**
45
+ * @see https://github.com/webpack-contrib/source-map-loader/blob/996368547e47a1a840f0d348b556084eb8442301/src/utils.js#L77
46
+ * @param {string } code
47
+ */
48
+ function getSourceMappingURL ( code ) {
49
+ const lines = code . split ( / ^ / m)
50
+ let match
51
+
52
+ for ( let i = lines . length - 1 ; i >= 0 ; i -- ) {
53
+ match = lines [ i ] . match ( sourceMappingURLRegex )
54
+ if ( match ) {
55
+ break
56
+ }
57
+ }
58
+
59
+ const sourceMappingURL = match ? match [ 1 ] || match [ 2 ] || '' : null
60
+
61
+ return {
62
+ sourceMappingURL : sourceMappingURL
63
+ ? decodeURI ( sourceMappingURL )
64
+ : sourceMappingURL ,
65
+ replacementString : match ? match [ 0 ] : null
66
+ }
67
+ }
68
+
69
+ /**
70
+ * @param {string } url
71
+ * @returns
72
+ */
73
+ async function getContentFromUrl ( url ) {
74
+ let code
75
+ let filePath
76
+ if ( / ^ f i l e : / . test ( url ) ) {
77
+ filePath = fileURLToPath ( url )
78
+ code = await ( await fs . readFile ( filePath ) ) . toString ( 'utf-8' )
79
+ } else if ( / ^ h t t p s ? : / . test ( url ) ) {
80
+ const parsedUrl = new URL ( url )
81
+ code = await ( await fetch ( url ) ) . text ( )
82
+ filePath = path . join ( cacheDir , parsedUrl . pathname )
83
+ await fs . writeFile ( filePath , code )
84
+ } else {
85
+ return undefined
86
+ }
87
+ return { code, filePath }
88
+ }
89
+
90
+ const cwd = process . cwd ( )
91
+
21
92
/**
22
- * @param {string } filePath
23
93
* @param {string } url
24
- * @param {Record<string, SourceMap> } sourceMapCache
94
+ * @param {Record<string, string> } hostToProjectMap
95
+ * @param {Record<string, {filePath: string, sources: SourceMap}> } sourceMapCache
25
96
* @returns
26
97
*/
27
- function getSources ( filePath , url , sourceMapCache = { } ) {
98
+ async function getSources ( url , hostToProjectMap , sourceMapCache = { } ) {
28
99
if ( sourceMapCache [ url ] ) {
29
100
return sourceMapCache [ url ]
30
101
}
31
- // debug(`SOURCE MAP: ${url}`)
32
- // see if it has a source map
33
- const s = findSourceMap ( filePath )
34
-
35
- if ( url . includes ( '.next/' ) ) {
36
- console . warn (
37
- {
38
- s,
39
- f : filePath ,
40
- url
41
- } ,
42
- 'SOURCE MAP'
102
+ let projectDir = cwd
103
+
104
+ let filePath
105
+ let code
106
+ let sourceMap
107
+ if ( / ^ f i l e : / . test ( url ) ) {
108
+ filePath = fileURLToPath ( url )
109
+ code = ( await fs . readFile ( filePath ) ) . toString ( 'utf-8' )
110
+
111
+ let { sourceMappingURL } = getSourceMappingURL ( code )
112
+ if ( ! sourceMappingURL ) {
113
+ return
114
+ }
115
+ sourceMappingURL = path . join ( path . dirname ( filePath ) , sourceMappingURL )
116
+ sourceMap = JSON . parse (
117
+ ( await fs . readFile ( sourceMappingURL ) ) . toString ( 'utf-8' )
118
+ )
119
+ } else if ( / ^ h t t p s ? : / . test ( url ) ) {
120
+ code = await ( await fetch ( url ) ) . text ( )
121
+ const parsedUrl = new URL ( url )
122
+
123
+ projectDir =
124
+ hostToProjectMap ?. [ parsedUrl . hostname ] ??
125
+ hostToProjectMap ?. [ parsedUrl . host ] ??
126
+ hostToProjectMap ?. [ parsedUrl . origin ] ??
127
+ projectDir
128
+
129
+ filePath = path . resolve (
130
+ path . join ( cacheDir , parsedUrl . hostname , parsedUrl . pathname )
43
131
)
132
+ if ( ! ( await exists ( path . dirname ( filePath ) ) ) ) {
133
+ await fs . mkdir ( path . dirname ( filePath ) , { recursive : true } )
134
+ }
135
+ await fs . writeFile ( filePath , code )
136
+
137
+ let { sourceMappingURL } = getSourceMappingURL ( code )
138
+ if ( ! sourceMappingURL ) {
139
+ return
140
+ }
141
+ if ( ! sourceMappingURL . startsWith ( 'http' ) ) {
142
+ sourceMappingURL = new URL ( sourceMappingURL , parsedUrl ) . href
143
+ }
144
+ const sourceMapString = await ( await fetch ( sourceMappingURL ) ) . text ( )
145
+ sourceMap = JSON . parse ( sourceMapString )
146
+
147
+ await fs . writeFile ( `${ filePath } .map` , sourceMapString )
148
+ } else {
149
+ return undefined
44
150
}
45
- if ( s ) {
46
- const { payload } = s
47
- /**
48
- * @type {SourceMap }
49
- */
50
- const sources = { source : '' }
51
- /**
52
- * @type {{data: import('module').SourceMapPayload, lineLengths: number[]} }
53
- */
54
- let sourceMapAndLineLengths = Object . assign ( Object . create ( null ) , {
55
- lineLengths : lineLengths ( filePath ) ,
56
- data : payload
57
- } )
151
+ const fileLineLengths = lineLengths ( filePath )
152
+ /**
153
+ * @type {import('module').SourceMapPayload }
154
+ */
155
+ const sourcemap = Object . assign ( Object . create ( null ) , {
156
+ ...sourceMap
157
+ } )
58
158
59
- // See: https://github.com/nodejs/node/pull/34305
60
- if ( sourceMapAndLineLengths ?. data ) {
61
- sources . sourceMap = {
62
- sourcemap : sourceMapAndLineLengths . data
63
- }
64
- if ( sourceMapAndLineLengths . lineLengths ) {
65
- let source = ''
66
- sourceMapAndLineLengths . lineLengths . forEach ( ( length ) => {
67
- source += `${ '' . padEnd ( length , '.' ) } \n`
68
- } )
69
- sources . source = source
159
+ let modified = false
160
+
161
+ Object . assign ( sourcemap , {
162
+ sources : sourceMap . sources . map ( ( sourceFile ) => {
163
+ if ( sourceFile . startsWith ( 'webpack://' ) ) {
164
+ // example: 'webpack://@pepper/order-management/./src/pages/index.page.tsx'
165
+ const res = sourceFile . replace (
166
+ / ^ w e b p a c k : \/ \/ [ ^ . ] + \/ \. / ,
167
+ `${ projectDir } /.`
168
+ )
169
+ if ( res !== sourceFile ) {
170
+ modified = true
171
+ return res
172
+ }
70
173
}
71
- }
72
- sourceMapCache [ url ] = sources
73
- return sources
174
+ return sourceFile
175
+ } )
176
+ } )
177
+
178
+ let source = ''
179
+ if ( fileLineLengths ) {
180
+ fileLineLengths . forEach ( ( length ) => {
181
+ source += `${ '' . padEnd ( length , '.' ) } \n`
182
+ } )
74
183
}
184
+ /**
185
+ * @type {SourceMap }
186
+ */
187
+ const sources = {
188
+ sourceMap : {
189
+ sourcemap : sourceMap
190
+ } ,
191
+ source
192
+ }
193
+
194
+ sourceMapCache [ url ] = { filePath, sources }
195
+ return { filePath, sources }
75
196
}
76
197
77
198
module . exports = {
0 commit comments