11'use strict' ;
22
33const {
4+ ArrayPrototypeMap,
45 JSONParse,
56 ObjectCreate,
67 ObjectKeys,
78 ObjectGetOwnPropertyDescriptor,
89 ObjectPrototypeHasOwnProperty,
910 Map,
1011 MapPrototypeEntries,
11- WeakMap,
12- WeakMapPrototypeGet,
12+ RegExpPrototypeTest,
13+ SafeMap,
14+ StringPrototypeMatch,
15+ StringPrototypeSplit,
1316 uncurryThis,
1417} = primordials ;
1518
@@ -27,17 +30,17 @@ let debug = require('internal/util/debuglog').debuglog('source_map', (fn) => {
2730} ) ;
2831const fs = require ( 'fs' ) ;
2932const { getOptionValue } = require ( 'internal/options' ) ;
33+ const { IterableWeakMap } = require ( 'internal/util/iterable_weak_map' ) ;
3034const {
3135 normalizeReferrerURL,
3236} = 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' ) ;
4144let SourceMap ;
4245
4346let sourceMapsEnabled ;
@@ -70,13 +73,14 @@ function maybeCacheSourceMap(filename, content, cjsModuleInstance) {
7073 debug ( err . stack ) ;
7174 return ;
7275 }
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+ ) ;
7580 if ( match ) {
7681 const data = dataFromUrl ( filename , match . groups . sourceMappingURL ) ;
7782 const url = data ? null : match . groups . sourceMappingURL ;
7883 if ( cjsModuleInstance ) {
79- if ( ! Module ) Module = require ( 'internal/modules/cjs/loader' ) . Module ;
8084 cjsSourceMapCache . set ( cjsModuleInstance , {
8185 filename,
8286 lineLengths : lineLengths ( content ) ,
@@ -120,7 +124,7 @@ function lineLengths(content) {
120124 // We purposefully keep \r as part of the line-length calculation, in
121125 // cases where there is a \r\n separator, so that this can be taken into
122126 // account in coverage calculations.
123- return content . split ( / \n | \u2028 | \u2029 / ) . map ( ( line ) => {
127+ return ArrayPrototypeMap ( StringPrototypeSplit ( content , / \n | \u2028 | \u2029 / ) , ( line ) => {
124128 return line . length ;
125129 } ) ;
126130}
@@ -139,8 +143,8 @@ function sourceMapFromFile(mapURL) {
139143// data:[<mediatype>][;base64],<data> see:
140144// https://tools.ietf.org/html/rfc2397#section-2
141145function 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 , ';' ) ;
144148 const contentType = splitFormat [ 0 ] ;
145149 const base64 = splitFormat [ splitFormat . length - 1 ] === 'base64' ;
146150 if ( contentType === 'application/json' ) {
@@ -207,48 +211,32 @@ function sourceMapCacheToObject() {
207211 return obj ;
208212}
209213
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.
215214function 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+ } ;
231221 }
232222}
233223
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+ }
240228 if ( ! SourceMap ) {
241229 SourceMap = require ( 'internal/source_map/source_map' ) . SourceMap ;
242230 }
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 ) ;
248232 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+ }
252240 }
253241 }
254242 if ( sourceMap && sourceMap . data ) {
0 commit comments