4
4
require ( 'internal/modules/cjs/loader' ) ;
5
5
6
6
const {
7
- Array,
8
- ArrayIsArray,
7
+ ArrayPrototypeJoin,
8
+ ArrayPrototypeMap,
9
+ ArrayPrototypeSort,
9
10
FunctionPrototypeCall,
11
+ JSONStringify,
12
+ ObjectKeys,
10
13
ObjectSetPrototypeOf,
11
14
PromisePrototypeThen,
12
- SafePromiseAllReturnArrayLike,
15
+ SafeMap,
16
+ PromiseResolve,
13
17
SafeWeakMap,
14
18
} = primordials ;
15
19
@@ -78,6 +82,11 @@ class DefaultModuleLoader {
78
82
*/
79
83
#defaultConditions = getDefaultConditions ( ) ;
80
84
85
+ /**
86
+ * Import cache
87
+ */
88
+ #importCache = new SafeMap ( ) ;
89
+
81
90
/**
82
91
* Map of already-loaded CJS modules to use
83
92
*/
@@ -119,8 +128,8 @@ class DefaultModuleLoader {
119
128
const { setCallbackForWrap } = require ( 'internal/modules/esm/utils' ) ;
120
129
const module = new ModuleWrap ( url , undefined , source , 0 , 0 ) ;
121
130
setCallbackForWrap ( module , {
122
- importModuleDynamically : ( specifier , { url } , importAssertions ) => {
123
- return this . import ( specifier , url , importAssertions ) ;
131
+ importModuleDynamically : ( specifier , { url } , importAttributes ) => {
132
+ return this . import ( specifier , url , importAttributes ) ;
124
133
} ,
125
134
} ) ;
126
135
@@ -147,18 +156,17 @@ class DefaultModuleLoader {
147
156
* @param {string | undefined } parentURL The URL of the module importing this
148
157
* one, unless this is the Node.js entry
149
158
* point.
150
- * @param {Record<string, string> } importAssertions Validations for the
151
- * module import.
159
+ * @param {Record<string, string> } importAttributes The import attributes.
152
160
* @returns {ModuleJob } The (possibly pending) module job
153
161
*/
154
- getModuleJob ( specifier , parentURL , importAssertions ) {
155
- const resolveResult = this . resolve ( specifier , parentURL , importAssertions ) ;
156
- return this . getJobFromResolveResult ( resolveResult , parentURL , importAssertions ) ;
162
+ getModuleJob ( specifier , parentURL , importAttributes ) {
163
+ const resolveResult = this . resolve ( specifier , parentURL , importAttributes ) ;
164
+ return this . getJobFromResolveResult ( resolveResult , parentURL , importAttributes ) ;
157
165
}
158
166
159
- getJobFromResolveResult ( resolveResult , parentURL , importAssertions ) {
167
+ getJobFromResolveResult ( resolveResult , parentURL , importAttributes ) {
160
168
const { url, format } = resolveResult ;
161
- const resolvedImportAssertions = resolveResult . importAssertions ?? importAssertions ;
169
+ const resolvedImportAssertions = resolveResult . importAttributes ?? importAttributes ;
162
170
163
171
let job = this . moduleMap . get ( url , resolvedImportAssertions . type ) ;
164
172
@@ -177,23 +185,23 @@ class DefaultModuleLoader {
177
185
/**
178
186
* Create and cache an object representing a loaded module.
179
187
* @param {string } url The absolute URL that was resolved for this module
180
- * @param {Record<string, string> } importAssertions Validations for the
188
+ * @param {Record<string, string> } importAttributes Validations for the
181
189
* module import.
182
190
* @param {string } [parentURL] The absolute URL of the module importing this
183
191
* one, unless this is the Node.js entry point
184
192
* @param {string } [format] The format hint possibly returned by the
185
193
* `resolve` hook
186
194
* @returns {Promise<ModuleJob> } The (possibly pending) module job
187
195
*/
188
- #createModuleJob( url , importAssertions , parentURL , format ) {
196
+ #createModuleJob( url , importAttributes , parentURL , format ) {
189
197
const moduleProvider = async ( url , isMain ) => {
190
198
const {
191
199
format : finalFormat ,
192
200
responseURL,
193
201
source,
194
202
} = await this . load ( url , {
195
203
format,
196
- importAssertions ,
204
+ importAttributes ,
197
205
} ) ;
198
206
199
207
const translator = getTranslators ( ) . get ( finalFormat ) ;
@@ -218,69 +226,95 @@ class DefaultModuleLoader {
218
226
const job = new ModuleJob (
219
227
this ,
220
228
url ,
221
- importAssertions ,
229
+ importAttributes ,
222
230
moduleProvider ,
223
231
parentURL === undefined ,
224
232
inspectBrk ,
225
233
) ;
226
234
227
- this . moduleMap . set ( url , importAssertions . type , job ) ;
235
+ this . moduleMap . set ( url , importAttributes . type , job ) ;
228
236
229
237
return job ;
230
238
}
231
239
240
+ #serializeCache( specifier , parentURL , importAttributes ) {
241
+ let cache = this . #importCache. get ( parentURL ) ;
242
+ let specifierCache ;
243
+ if ( cache == null ) {
244
+ this . #importCache. set ( parentURL , cache = new SafeMap ( ) ) ;
245
+ } else {
246
+ specifierCache = cache . get ( specifier ) ;
247
+ }
248
+
249
+ if ( specifierCache == null ) {
250
+ cache . set ( specifier , specifierCache = { __proto__ : null } ) ;
251
+ }
252
+
253
+ const serializedAttributes = ArrayPrototypeJoin (
254
+ ArrayPrototypeMap (
255
+ ArrayPrototypeSort ( ObjectKeys ( importAttributes ) ) ,
256
+ ( key ) => JSONStringify ( key ) + JSONStringify ( importAttributes [ key ] ) ) ,
257
+ ',' ) ;
258
+
259
+ return { specifierCache, serializedAttributes } ;
260
+ }
261
+
262
+ cacheStatic ( specifier , parentURL , importAttributes , result ) {
263
+ const { specifierCache, serializedAttributes } = this . #serializeCache( specifier , parentURL , importAttributes ) ;
264
+
265
+ specifierCache [ serializedAttributes ] = result ;
266
+ }
267
+
268
+ async import ( specifier , parentURL , importAttributes ) {
269
+ const { specifierCache, serializedAttributes } = this . #serializeCache( specifier , parentURL , importAttributes ) ;
270
+ const removeCache = ( ) => {
271
+ delete specifierCache [ serializedAttributes ] ;
272
+ } ;
273
+ if ( specifierCache [ serializedAttributes ] != null ) {
274
+ if ( PromiseResolve ( specifierCache [ serializedAttributes ] ) !== specifierCache [ serializedAttributes ] ) {
275
+ const { module } = await specifierCache [ serializedAttributes ] . run ( ) ;
276
+ return module . getNamespace ( ) ;
277
+ }
278
+ const fallback = ( ) => {
279
+ if ( specifierCache [ serializedAttributes ] != null ) {
280
+ return PromisePrototypeThen ( specifierCache [ serializedAttributes ] , undefined , fallback ) ;
281
+ }
282
+ const result = this . #import( specifier , parentURL , importAttributes ) ;
283
+ specifierCache [ serializedAttributes ] = result ;
284
+ PromisePrototypeThen ( result , undefined , removeCache ) ;
285
+ return result ;
286
+ } ;
287
+ return PromisePrototypeThen ( specifierCache [ serializedAttributes ] , undefined , fallback ) ;
288
+ }
289
+ const result = this . #import( specifier , parentURL , importAttributes ) ;
290
+ specifierCache [ serializedAttributes ] = result ;
291
+ PromisePrototypeThen ( result , undefined , removeCache ) ;
292
+ return result ;
293
+ }
294
+
232
295
/**
233
296
* This method is usually called indirectly as part of the loading processes.
234
297
* Internally, it is used directly to add loaders. Use directly with caution.
235
298
*
236
299
* This method must NOT be renamed: it functions as a dynamic import on a
237
300
* loader module.
238
301
*
239
- * @param {string | string[] } specifiers Path(s) to the module .
302
+ * @param {string } specifier The first parameter of an `import()` expression .
240
303
* @param {string } parentURL Path of the parent importing the module.
241
- * @param {Record<string, string> } importAssertions Validations for the
304
+ * @param {Record<string, string> } importAttributes Validations for the
242
305
* module import.
243
- * @returns {Promise<ExportedHooks | KeyedExports[] > }
306
+ * @returns {Promise<ExportedHooks> }
244
307
* A collection of module export(s) or a list of collections of module
245
308
* export(s).
246
309
*/
247
- async import ( specifiers , parentURL , importAssertions ) {
248
- // For loaders, `import` is passed multiple things to process, it returns a
249
- // list pairing the url and exports collected. This is especially useful for
250
- // error messaging, to identity from where an export came. But, in most
251
- // cases, only a single url is being "imported" (ex `import()`), so there is
252
- // only 1 possible url from which the exports were collected and it is
253
- // already known to the caller. Nesting that in a list would only ever
254
- // create redundant work for the caller, so it is later popped off the
255
- // internal list.
256
- const wasArr = ArrayIsArray ( specifiers ) ;
257
- if ( ! wasArr ) { specifiers = [ specifiers ] ; }
258
-
259
- const count = specifiers . length ;
260
- const jobs = new Array ( count ) ;
261
-
262
- for ( let i = 0 ; i < count ; i ++ ) {
263
- jobs [ i ] = PromisePrototypeThen (
264
- this
265
- . getModuleJob ( specifiers [ i ] , parentURL , importAssertions )
266
- . run ( ) ,
267
- ( { module } ) => module . getNamespace ( ) ,
268
- ) ;
269
- }
270
-
271
- const namespaces = await SafePromiseAllReturnArrayLike ( jobs ) ;
310
+ async #import( specifier , parentURL , importAttributes ) {
272
311
273
- if ( ! wasArr ) { return namespaces [ 0 ] ; } // We can skip the pairing below
274
-
275
- for ( let i = 0 ; i < count ; i ++ ) {
276
- namespaces [ i ] = {
277
- __proto__ : null ,
278
- url : specifiers [ i ] ,
279
- exports : namespaces [ i ] ,
280
- } ;
281
- }
312
+ const moduleJob = this . getModuleJob ( specifier , parentURL , importAttributes ) ;
313
+ const { module } = await moduleJob . run ( ) ;
282
314
283
- return namespaces ;
315
+ const { specifierCache, serializedAttributes } = this . #serializeCache( specifier , parentURL , importAttributes ) ;
316
+ specifierCache [ serializedAttributes ] = moduleJob ;
317
+ return module . getNamespace ( ) ;
284
318
}
285
319
286
320
/**
@@ -289,17 +323,17 @@ class DefaultModuleLoader {
289
323
* @param {string } originalSpecifier The specified URL path of the module to
290
324
* be resolved.
291
325
* @param {string } [parentURL] The URL path of the module's parent.
292
- * @param {ImportAssertions } importAssertions Assertions from the import
326
+ * @param {ImportAssertions } importAttributes Assertions from the import
293
327
* statement or expression.
294
328
* @returns {{ format: string, url: URL['href'] } }
295
329
*/
296
- resolve ( originalSpecifier , parentURL , importAssertions ) {
330
+ resolve ( originalSpecifier , parentURL , importAttributes ) {
297
331
defaultResolve ??= require ( 'internal/modules/esm/resolve' ) . defaultResolve ;
298
332
299
333
const context = {
300
334
__proto__ : null ,
301
335
conditions : this . #defaultConditions,
302
- importAssertions ,
336
+ importAttributes ,
303
337
parentURL,
304
338
} ;
305
339
@@ -357,21 +391,21 @@ class CustomizedModuleLoader extends DefaultModuleLoader {
357
391
* @param {string } originalSpecifier The specified URL path of the module to
358
392
* be resolved.
359
393
* @param {string } [parentURL] The URL path of the module's parent.
360
- * @param {ImportAssertions } importAssertions Assertions from the import
394
+ * @param {ImportAssertions } importAttributes Assertions from the import
361
395
* statement or expression.
362
396
* @returns {{ format: string, url: URL['href'] } }
363
397
*/
364
- resolve ( originalSpecifier , parentURL , importAssertions ) {
365
- return hooksProxy . makeSyncRequest ( 'resolve' , originalSpecifier , parentURL , importAssertions ) ;
398
+ resolve ( originalSpecifier , parentURL , importAttributes ) {
399
+ return hooksProxy . makeSyncRequest ( 'resolve' , originalSpecifier , parentURL , importAttributes ) ;
366
400
}
367
401
368
- async #getModuleJob( specifier , parentURL , importAssertions ) {
369
- const resolveResult = await hooksProxy . makeAsyncRequest ( 'resolve' , specifier , parentURL , importAssertions ) ;
402
+ async #getModuleJob( specifier , parentURL , importAttributes ) {
403
+ const resolveResult = await hooksProxy . makeAsyncRequest ( 'resolve' , specifier , parentURL , importAttributes ) ;
370
404
371
- return this . getJobFromResolveResult ( resolveResult , parentURL , importAssertions ) ;
405
+ return this . getJobFromResolveResult ( resolveResult , parentURL , importAttributes ) ;
372
406
}
373
- getModuleJob ( specifier , parentURL , importAssertions ) {
374
- const jobPromise = this . #getModuleJob( specifier , parentURL , importAssertions ) ;
407
+ getModuleJob ( specifier , parentURL , importAttributes ) {
408
+ const jobPromise = this . #getModuleJob( specifier , parentURL , importAttributes ) ;
375
409
376
410
return {
377
411
run ( ) {
0 commit comments