44require ( 'internal/modules/cjs/loader' ) ;
55
66const {
7+ ArrayPrototypeJoin,
8+ ArrayPrototypeMap,
9+ ArrayPrototypeSort,
710 FunctionPrototypeCall,
11+ JSONStringify,
12+ ObjectKeys,
813 ObjectSetPrototypeOf,
914 PromisePrototypeThen,
15+ SafeMap,
16+ PromiseResolve,
1017 SafeWeakMap,
1118} = primordials ;
1219
@@ -75,6 +82,11 @@ class DefaultModuleLoader {
7582 */
7683 #defaultConditions = getDefaultConditions ( ) ;
7784
85+ /**
86+ * Import cache
87+ */
88+ #importCache = new SafeMap ( ) ;
89+
7890 /**
7991 * Map of already-loaded CJS modules to use
8092 */
@@ -145,8 +157,7 @@ class DefaultModuleLoader {
145157 * @param {string | undefined } parentURL The URL of the module importing this
146158 * one, unless this is the Node.js entry
147159 * point.
148- * @param {Record<string, string> } importAssertions Validations for the
149- * module import.
160+ * @param {Record<string, string> } importAssertions The import attributes.
150161 * @returns {ModuleJob } The (possibly pending) module job
151162 */
152163 getModuleJob ( specifier , parentURL , importAssertions ) {
@@ -227,6 +238,34 @@ class DefaultModuleLoader {
227238 return job ;
228239 }
229240
241+ #serializeCache( specifier , parentURL , importAssertions ) {
242+ let cache = this . #importCache. get ( parentURL ) ;
243+ let specifierCache ;
244+ if ( cache == null ) {
245+ this . #importCache. set ( parentURL , cache = new SafeMap ( ) ) ;
246+ } else {
247+ specifierCache = cache . get ( specifier ) ;
248+ }
249+
250+ if ( specifierCache == null ) {
251+ cache . set ( specifier , specifierCache = { __proto__ : null } ) ;
252+ }
253+
254+ const serializedAttributes = ArrayPrototypeJoin (
255+ ArrayPrototypeMap (
256+ ArrayPrototypeSort ( ObjectKeys ( importAssertions ) ) ,
257+ ( key ) => JSONStringify ( key ) + JSONStringify ( importAssertions [ key ] ) ) ,
258+ ',' ) ;
259+
260+ return { specifierCache, serializedAttributes } ;
261+ }
262+
263+ cacheStatic ( specifier , parentURL , importAssertions , result ) {
264+ const { specifierCache, serializedAttributes } = this . #serializeCache( specifier , parentURL , importAssertions ) ;
265+
266+ specifierCache [ serializedAttributes ] = result ;
267+ }
268+
230269 /**
231270 * This method is usually called indirectly as part of the loading processes.
232271 * Internally, it is used directly to add loaders. Use directly with caution.
@@ -237,13 +276,43 @@ class DefaultModuleLoader {
237276 * @param {string } parentURL Path of the parent importing the module.
238277 * @param {Record<string, string> } importAssertions Validations for the
239278 * module import.
240- * @returns {Promise<ExportedHooks | KeyedExports[] > }
279+ * @returns {Promise<ExportedHooks> }
241280 * A collection of module export(s) or a list of collections of module
242281 * export(s).
243282 */
244283 async import ( specifier , parentURL , importAssertions ) {
284+ const { specifierCache, serializedAttributes } = this . #serializeCache( specifier , parentURL , importAssertions ) ;
285+ const removeCache = ( ) => {
286+ delete specifierCache [ serializedAttributes ] ;
287+ } ;
288+ if ( specifierCache [ serializedAttributes ] != null ) {
289+ if ( PromiseResolve ( specifierCache [ serializedAttributes ] ) !== specifierCache [ serializedAttributes ] ) {
290+ const { module } = await specifierCache [ serializedAttributes ] . run ( ) ;
291+ return module . getNamespace ( ) ;
292+ }
293+ const fallback = ( ) => {
294+ if ( specifierCache [ serializedAttributes ] != null ) {
295+ return PromisePrototypeThen ( specifierCache [ serializedAttributes ] , undefined , fallback ) ;
296+ }
297+ const result = this . #import( specifier , parentURL , importAssertions ) ;
298+ specifierCache [ serializedAttributes ] = result ;
299+ PromisePrototypeThen ( result , undefined , removeCache ) ;
300+ return result ;
301+ } ;
302+ return PromisePrototypeThen ( specifierCache [ serializedAttributes ] , undefined , fallback ) ;
303+ }
304+ const result = this . #import( specifier , parentURL , importAssertions ) ;
305+ specifierCache [ serializedAttributes ] = result ;
306+ PromisePrototypeThen ( result , undefined , removeCache ) ;
307+ return result ;
308+ }
309+
310+ async #import( specifier , parentURL , importAssertions ) {
245311 const moduleJob = this . getModuleJob ( specifier , parentURL , importAssertions ) ;
246312 const { module } = await moduleJob . run ( ) ;
313+
314+ const { specifierCache, serializedAttributes } = this . #serializeCache( specifier , parentURL , importAssertions ) ;
315+ specifierCache [ serializedAttributes ] = moduleJob ;
247316 return module . getNamespace ( ) ;
248317 }
249318
0 commit comments