3
3
const {
4
4
ArrayPrototypePush,
5
5
JSONParse,
6
- ObjectKeys,
7
6
RegExpPrototypeExec,
8
7
SafeMap,
9
8
StringPrototypeCodePointAt,
@@ -25,17 +24,15 @@ const {
25
24
} = require ( 'internal/errors' ) ;
26
25
const { getLazy } = require ( 'internal/util' ) ;
27
26
28
- // Since the CJS module cache is mutable, which leads to memory leaks when
29
- // modules are deleted, we use a WeakMap so that the source map cache will
30
- // be purged automatically:
31
- const getCjsSourceMapCache = getLazy ( ( ) => {
32
- const { IterableWeakMap } = require ( 'internal/util/iterable_weak_map' ) ;
33
- return new IterableWeakMap ( ) ;
27
+ const getModuleSourceMapCache = getLazy ( ( ) => {
28
+ const { SourceMapCacheMap } = require ( 'internal/source_map/source_map_cache_map' ) ;
29
+ return new SourceMapCacheMap ( ) ;
34
30
} ) ;
35
31
36
- // The esm cache is not mutable, so we can use a Map without memory concerns:
37
- const esmSourceMapCache = new SafeMap ( ) ;
38
- // The generated sources is not mutable, so we can use a Map without memory concerns:
32
+ // The generated source module/script instance is not accessible, so we can use
33
+ // a Map without memory concerns. Separate generated source entries with the module
34
+ // source entries to avoid overriding the module source entries with arbitrary
35
+ // source url magic comments.
39
36
const generatedSourceMapCache = new SafeMap ( ) ;
40
37
const kLeadingProtocol = / ^ \w + : \/ \/ / ;
41
38
const kSourceMappingURLMagicComment = / \/ [ * / ] # \s + s o u r c e M a p p i n g U R L = (?< sourceMappingURL > [ ^ \s ] + ) / g;
@@ -52,6 +49,10 @@ function getSourceMapsEnabled() {
52
49
return sourceMapsEnabled ;
53
50
}
54
51
52
+ /**
53
+ * Enables or disables source maps programmatically.
54
+ * @param {boolean } val
55
+ */
55
56
function setSourceMapsEnabled ( val ) {
56
57
validateBoolean ( val , 'val' ) ;
57
58
@@ -72,6 +73,14 @@ function setSourceMapsEnabled(val) {
72
73
sourceMapsEnabled = val ;
73
74
}
74
75
76
+ /**
77
+ * Extracts the source url from the content if present. For example
78
+ * //# sourceURL=file:///path/to/file
79
+ *
80
+ * Read more at: https://tc39.es/source-map-spec/#linking-evald-code-to-named-generated-code
81
+ * @param {string } content - source content
82
+ * @returns {string | null } source url or null if not present
83
+ */
75
84
function extractSourceURLMagicComment ( content ) {
76
85
let match ;
77
86
let matchSourceURL ;
@@ -90,6 +99,14 @@ function extractSourceURLMagicComment(content) {
90
99
return sourceURL ;
91
100
}
92
101
102
+ /**
103
+ * Extracts the source map url from the content if present. For example
104
+ * //# sourceMappingURL=file:///path/to/file
105
+ *
106
+ * Read more at: https://tc39.es/source-map-spec/#linking-generated-code
107
+ * @param {string } content - source content
108
+ * @returns {string | null } source map url or null if not present
109
+ */
93
110
function extractSourceMapURLMagicComment ( content ) {
94
111
let match ;
95
112
let lastMatch ;
@@ -104,7 +121,17 @@ function extractSourceMapURLMagicComment(content) {
104
121
return lastMatch . groups . sourceMappingURL ;
105
122
}
106
123
107
- function maybeCacheSourceMap ( filename , content , cjsModuleInstance , isGeneratedSource , sourceURL , sourceMapURL ) {
124
+ /**
125
+ * Caches the source map if it is present in the content, with the given filename, moduleInstance, and sourceURL.
126
+ * @param {string } filename - the actual filename
127
+ * @param {string } content - the actual source content
128
+ * @param {import('internal/modules/cjs/loader').Module | ModuleWrap } moduleInstance - a module instance that
129
+ * associated with the source, once this is reclaimed, the source map entry will be removed from the cache
130
+ * @param {boolean } isGeneratedSource - if the source was generated and evaluated with the global eval
131
+ * @param {string | undefined } sourceURL - the source url
132
+ * @param {string | undefined } sourceMapURL - the source map url
133
+ */
134
+ function maybeCacheSourceMap ( filename , content , moduleInstance , isGeneratedSource , sourceURL , sourceMapURL ) {
108
135
const sourceMapsEnabled = getSourceMapsEnabled ( ) ;
109
136
if ( ! ( process . env . NODE_V8_COVERAGE || sourceMapsEnabled ) ) return ;
110
137
const { normalizeReferrerURL } = require ( 'internal/modules/helpers' ) ;
@@ -130,45 +157,32 @@ function maybeCacheSourceMap(filename, content, cjsModuleInstance, isGeneratedSo
130
157
}
131
158
132
159
const data = dataFromUrl ( filename , sourceMapURL ) ;
133
- const url = data ? null : sourceMapURL ;
134
- if ( cjsModuleInstance ) {
135
- getCjsSourceMapCache ( ) . set ( cjsModuleInstance , {
136
- __proto__ : null ,
137
- filename,
138
- lineLengths : lineLengths ( content ) ,
139
- data,
140
- url,
141
- sourceURL,
142
- } ) ;
143
- } else if ( isGeneratedSource ) {
144
- const entry = {
145
- __proto__ : null ,
146
- lineLengths : lineLengths ( content ) ,
147
- data,
148
- url,
149
- sourceURL,
150
- } ;
160
+ const entry = {
161
+ __proto__ : null ,
162
+ lineLengths : lineLengths ( content ) ,
163
+ data,
164
+ // Save the source map url if it is not a data url.
165
+ sourceMapURL : data ? null : sourceMapURL ,
166
+ sourceURL,
167
+ } ;
168
+
169
+ if ( isGeneratedSource ) {
151
170
generatedSourceMapCache . set ( filename , entry ) ;
152
171
if ( sourceURL ) {
153
172
generatedSourceMapCache . set ( sourceURL , entry ) ;
154
173
}
155
- } else {
156
- // If there is no cjsModuleInstance and is not generated source assume we are in a
157
- // "modules/esm" context.
158
- const entry = {
159
- __proto__ : null ,
160
- lineLengths : lineLengths ( content ) ,
161
- data,
162
- url,
163
- sourceURL,
164
- } ;
165
- esmSourceMapCache . set ( filename , entry ) ;
166
- if ( sourceURL ) {
167
- esmSourceMapCache . set ( sourceURL , entry ) ;
168
- }
174
+ return ;
169
175
}
176
+ // If it is not a generated source, we assume we are in a "cjs/esm"
177
+ // context.
178
+ const keys = sourceURL ? [ filename , sourceURL ] : [ filename ] ;
179
+ getModuleSourceMapCache ( ) . set ( keys , entry , moduleInstance ) ;
170
180
}
171
181
182
+ /**
183
+ * Caches the source map if it is present in the eval'd source.
184
+ * @param {string } content - the eval'd source code
185
+ */
172
186
function maybeCacheGeneratedSourceMap ( content ) {
173
187
const sourceMapsEnabled = getSourceMapsEnabled ( ) ;
174
188
if ( ! ( process . env . NODE_V8_COVERAGE || sourceMapsEnabled ) ) return ;
@@ -186,6 +200,14 @@ function maybeCacheGeneratedSourceMap(content) {
186
200
}
187
201
}
188
202
203
+ /**
204
+ * Resolves source map payload data from the source url and source map url.
205
+ * If the source map url is a data url, the data is returned.
206
+ * Otherwise the source map url is resolved to a file path and the file is read.
207
+ * @param {string } sourceURL - url of the source file
208
+ * @param {string } sourceMappingURL - url of the source map
209
+ * @returns {object } deserialized source map JSON object
210
+ */
189
211
function dataFromUrl ( sourceURL , sourceMappingURL ) {
190
212
try {
191
213
const url = new URL ( sourceMappingURL ) ;
@@ -227,7 +249,11 @@ function lineLengths(content) {
227
249
return output ;
228
250
}
229
251
230
-
252
+ /**
253
+ * Read source map from file.
254
+ * @param {string } mapURL - file url of the source map
255
+ * @returns {object } deserialized source map JSON object
256
+ */
231
257
function sourceMapFromFile ( mapURL ) {
232
258
try {
233
259
const fs = require ( 'fs' ) ;
@@ -281,56 +307,44 @@ function sourcesToAbsolute(baseURL, data) {
281
307
return data ;
282
308
}
283
309
284
- // WARNING: The `sourceMapCacheToObject` and `appendCJSCache` run during
285
- // shutdown. In particular, they also run when Workers are terminated, making
286
- // it important that they do not call out to any user-provided code, including
287
- // built-in prototypes that might have been tampered with.
310
+ // WARNING: The `sourceMapCacheToObject` runs during shutdown. In particular,
311
+ // it also runs when Workers are terminated, making it important that it does
312
+ // not call out to any user-provided code, including built-in prototypes that
313
+ // might have been tampered with.
288
314
289
315
// Get serialized representation of source-map cache, this is used
290
316
// to persist a cache of source-maps to disk when NODE_V8_COVERAGE is enabled.
291
317
function sourceMapCacheToObject ( ) {
292
- const obj = { __proto__ : null } ;
293
-
294
- for ( const { 0 : k , 1 : v } of esmSourceMapCache ) {
295
- obj [ k ] = v ;
296
- }
297
-
298
- appendCJSCache ( obj ) ;
299
-
300
- if ( ObjectKeys ( obj ) . length === 0 ) {
318
+ const moduleSourceMapCache = getModuleSourceMapCache ( ) ;
319
+ if ( moduleSourceMapCache . size === 0 ) {
301
320
return undefined ;
302
321
}
303
- return obj ;
304
- }
305
322
306
- function appendCJSCache ( obj ) {
307
- for ( const value of getCjsSourceMapCache ( ) ) {
308
- obj [ value . filename ] = {
323
+ const obj = { __proto__ : null } ;
324
+ for ( const { 0 : k , 1 : v } of moduleSourceMapCache ) {
325
+ obj [ k ] = {
309
326
__proto__ : null ,
310
- lineLengths : value . lineLengths ,
311
- data : value . data ,
312
- url : value . url ,
327
+ lineLengths : v . lineLengths ,
328
+ data : v . data ,
329
+ url : v . sourceMapURL ,
313
330
} ;
314
331
}
332
+ return obj ;
315
333
}
316
334
335
+ /**
336
+ * Find a source map for a given actual source URL or path.
337
+ * @param {string } sourceURL - actual source URL or path
338
+ * @returns {import('internal/source_map/source_map').SourceMap | undefined } a source map or undefined if not found
339
+ */
317
340
function findSourceMap ( sourceURL ) {
318
341
if ( RegExpPrototypeExec ( kLeadingProtocol , sourceURL ) === null ) {
319
342
sourceURL = pathToFileURL ( sourceURL ) . href ;
320
343
}
321
344
if ( ! SourceMap ) {
322
345
SourceMap = require ( 'internal/source_map/source_map' ) . SourceMap ;
323
346
}
324
- let entry = esmSourceMapCache . get ( sourceURL ) ?? generatedSourceMapCache . get ( sourceURL ) ;
325
- if ( entry === undefined ) {
326
- for ( const value of getCjsSourceMapCache ( ) ) {
327
- const filename = value . filename ;
328
- const cachedSourceURL = value . sourceURL ;
329
- if ( sourceURL === filename || sourceURL === cachedSourceURL ) {
330
- entry = value ;
331
- }
332
- }
333
- }
347
+ const entry = getModuleSourceMapCache ( ) . get ( sourceURL ) ?? generatedSourceMapCache . get ( sourceURL ) ;
334
348
if ( entry === undefined ) {
335
349
return undefined ;
336
350
}
0 commit comments