1
1
'use strict' ;
2
2
3
3
const {
4
+ ArrayPrototypeMap,
4
5
JSONParse,
5
6
ObjectCreate,
6
7
ObjectKeys,
7
8
ObjectGetOwnPropertyDescriptor,
8
9
ObjectPrototypeHasOwnProperty,
9
10
Map,
10
11
MapPrototypeEntries,
11
- WeakMap,
12
- WeakMapPrototypeGet,
12
+ RegExpPrototypeTest,
13
+ SafeMap,
14
+ StringPrototypeMatch,
15
+ StringPrototypeSplit,
13
16
uncurryThis,
14
17
} = primordials ;
15
18
@@ -27,17 +30,17 @@ let debug = require('internal/util/debuglog').debuglog('source_map', (fn) => {
27
30
} ) ;
28
31
const fs = require ( 'fs' ) ;
29
32
const { getOptionValue } = require ( 'internal/options' ) ;
33
+ const { IterableWeakMap } = require ( 'internal/util/iterable_weak_map' ) ;
30
34
const {
31
35
normalizeReferrerURL,
32
36
} = require ( 'internal/modules/cjs/helpers' ) ;
33
- // For cjs, since Module._cache is exposed to users, we use a WeakMap
34
- // keyed on module, facilitating garbage collection.
35
- const cjsSourceMapCache = new WeakMap ( ) ;
36
- // The esm cache is not exposed to users, so we can use a Map keyed
37
- // on filenames.
38
- const esmSourceMapCache = new Map ( ) ;
39
- const { fileURLToPath, URL } = require ( 'url' ) ;
40
- let Module ;
37
+ // Since the CJS module cache is mutable, which leads to memory leaks when
38
+ // modules are deleted, we use a WeakMap so that the source map cache will
39
+ // be purged automatically:
40
+ const cjsSourceMapCache = new IterableWeakMap ( ) ;
41
+ // The esm cache is not mutable, so we can use a Map without memory concerns:
42
+ const esmSourceMapCache = new SafeMap ( ) ;
43
+ const { fileURLToPath, pathToFileURL, URL } = require ( 'internal/url' ) ;
41
44
let SourceMap ;
42
45
43
46
let sourceMapsEnabled ;
@@ -70,13 +73,14 @@ function maybeCacheSourceMap(filename, content, cjsModuleInstance) {
70
73
debug ( err . stack ) ;
71
74
return ;
72
75
}
73
-
74
- const match = content . match ( / \/ [ * / ] # \s + s o u r c e M a p p i n g U R L = (?< sourceMappingURL > [ ^ \s ] + ) / ) ;
76
+ const match = StringPrototypeMatch (
77
+ content ,
78
+ / \/ [ * / ] # \s + s o u r c e M a p p i n g U R L = (?< sourceMappingURL > [ ^ \s ] + ) /
79
+ ) ;
75
80
if ( match ) {
76
81
const data = dataFromUrl ( filename , match . groups . sourceMappingURL ) ;
77
82
const url = data ? null : match . groups . sourceMappingURL ;
78
83
if ( cjsModuleInstance ) {
79
- if ( ! Module ) Module = require ( 'internal/modules/cjs/loader' ) . Module ;
80
84
cjsSourceMapCache . set ( cjsModuleInstance , {
81
85
filename,
82
86
lineLengths : lineLengths ( content ) ,
@@ -120,7 +124,7 @@ function lineLengths(content) {
120
124
// We purposefully keep \r as part of the line-length calculation, in
121
125
// cases where there is a \r\n separator, so that this can be taken into
122
126
// account in coverage calculations.
123
- return content . split ( / \n | \u2028 | \u2029 / ) . map ( ( line ) => {
127
+ return ArrayPrototypeMap ( StringPrototypeSplit ( content , / \n | \u2028 | \u2029 / ) , ( line ) => {
124
128
return line . length ;
125
129
} ) ;
126
130
}
@@ -139,8 +143,8 @@ function sourceMapFromFile(mapURL) {
139
143
// data:[<mediatype>][;base64],<data> see:
140
144
// https://tools.ietf.org/html/rfc2397#section-2
141
145
function sourceMapFromDataUrl ( sourceURL , url ) {
142
- const [ format , data ] = url . split ( ',' ) ;
143
- const splitFormat = format . split ( ';' ) ;
146
+ const [ format , data ] = StringPrototypeSplit ( url , ',' ) ;
147
+ const splitFormat = StringPrototypeSplit ( format , ';' ) ;
144
148
const contentType = splitFormat [ 0 ] ;
145
149
const base64 = splitFormat [ splitFormat . length - 1 ] === 'base64' ;
146
150
if ( contentType === 'application/json' ) {
@@ -207,48 +211,32 @@ function sourceMapCacheToObject() {
207
211
return obj ;
208
212
}
209
213
210
- // Since WeakMap can't be iterated over, we use Module._cache's
211
- // keys to facilitate Source Map serialization.
212
- //
213
- // TODO(bcoe): this means we don't currently serialize source-maps attached
214
- // to error instances, only module instances.
215
214
function appendCJSCache ( obj ) {
216
- if ( ! Module ) return ;
217
- const cjsModuleCache = ObjectGetValueSafe ( Module , '_cache' ) ;
218
- const cjsModules = ObjectKeys ( cjsModuleCache ) ;
219
- for ( let i = 0 ; i < cjsModules . length ; i ++ ) {
220
- const key = cjsModules [ i ] ;
221
- const module = ObjectGetValueSafe ( cjsModuleCache , key ) ;
222
- const value = WeakMapPrototypeGet ( cjsSourceMapCache , module ) ;
223
- if ( value ) {
224
- // This is okay because `obj` has a null prototype.
225
- obj [ `file://${ key } ` ] = {
226
- lineLengths : ObjectGetValueSafe ( value , 'lineLengths' ) ,
227
- data : ObjectGetValueSafe ( value , 'data' ) ,
228
- url : ObjectGetValueSafe ( value , 'url' )
229
- } ;
230
- }
215
+ for ( const value of cjsSourceMapCache ) {
216
+ obj [ ObjectGetValueSafe ( value , 'filename' ) ] = {
217
+ lineLengths : ObjectGetValueSafe ( value , 'lineLengths' ) ,
218
+ data : ObjectGetValueSafe ( value , 'data' ) ,
219
+ url : ObjectGetValueSafe ( value , 'url' )
220
+ } ;
231
221
}
232
222
}
233
223
234
- // Attempt to lookup a source map, which is either attached to a file URI, or
235
- // keyed on an error instance.
236
- // TODO(bcoe): once WeakRefs are available in Node.js, refactor to drop
237
- // requirement of error parameter.
238
- function findSourceMap ( uri , error ) {
239
- if ( ! Module ) Module = require ( 'internal/modules/cjs/loader' ) . Module ;
224
+ function findSourceMap ( sourceURL ) {
225
+ if ( ! RegExpPrototypeTest ( / ^ \w + : \/ \/ / , sourceURL ) ) {
226
+ sourceURL = pathToFileURL ( sourceURL ) . href ;
227
+ }
240
228
if ( ! SourceMap ) {
241
229
SourceMap = require ( 'internal/source_map/source_map' ) . SourceMap ;
242
230
}
243
- let sourceMap = cjsSourceMapCache . get ( Module . _cache [ uri ] ) ;
244
- if ( ! uri . startsWith ( 'file://' ) ) uri = normalizeReferrerURL ( uri ) ;
245
- if ( sourceMap === undefined ) {
246
- sourceMap = esmSourceMapCache . get ( uri ) ;
247
- }
231
+ let sourceMap = esmSourceMapCache . get ( sourceURL ) ;
248
232
if ( sourceMap === undefined ) {
249
- const candidateSourceMap = cjsSourceMapCache . get ( error ) ;
250
- if ( candidateSourceMap && uri === candidateSourceMap . filename ) {
251
- sourceMap = candidateSourceMap ;
233
+ for ( const value of cjsSourceMapCache ) {
234
+ const filename = ObjectGetValueSafe ( value , 'filename' ) ;
235
+ if ( sourceURL === filename ) {
236
+ sourceMap = {
237
+ data : ObjectGetValueSafe ( value , 'data' )
238
+ } ;
239
+ }
252
240
}
253
241
}
254
242
if ( sourceMap && sourceMap . data ) {
0 commit comments