77 Array,
88 ArrayIsArray,
99 FunctionPrototypeCall,
10+ JSONStringify,
1011 ObjectSetPrototypeOf,
12+ PromisePrototypeThen,
13+ SafeMap,
1114 SafePromiseAllReturnArrayLike,
1215 SafeWeakMap,
1316} = primordials ;
@@ -71,6 +74,8 @@ class ESMLoader {
7174 #defaultLoad;
7275 #importMetaInitializer;
7376
77+ #dynamicImportCache = new SafeMap ( ) ;
78+
7479 /**
7580 * The conditions for resolving packages if `--conditions` is not used.
7681 */
@@ -124,7 +129,7 @@ class ESMLoader {
124129 const module = new ModuleWrap ( url , undefined , source , 0 , 0 ) ;
125130 setCallbackForWrap ( module , {
126131 importModuleDynamically : ( specifier , { url } , importAssertions ) => {
127- return this . import ( specifier , url , importAssertions ) ;
132+ return this . dynamicImport ( specifier , url , importAssertions ) ;
128133 }
129134 } ) ;
130135
@@ -242,6 +247,41 @@ class ESMLoader {
242247 return job ;
243248 }
244249
250+ async dynamicImport ( specifier , parentURL , importAssertions ) {
251+ let cache = this . #dynamicImportCache. get ( parentURL ) ;
252+ let specifierCache ;
253+ if ( cache == null ) {
254+ this . #dynamicImportCache. set ( parentURL , cache = new SafeMap ( ) ) ;
255+ } else {
256+ specifierCache = cache . get ( specifier ) ;
257+ }
258+
259+ if ( specifierCache == null ) {
260+ cache . set ( specifier , specifierCache = { __proto__ : null } ) ;
261+ }
262+
263+ const assertions = JSONStringify ( importAssertions ) ;
264+ const removeCache = ( ) => {
265+ delete specifierCache [ assertions ] ;
266+ } ;
267+ if ( specifierCache [ assertions ] != null ) {
268+ const fallback = ( ) => {
269+ if ( specifierCache [ assertions ] != null ) {
270+ return PromisePrototypeThen ( specifierCache [ assertions ] , undefined , fallback ) ;
271+ }
272+ const result = this . import ( specifier , parentURL , importAssertions ) ;
273+ specifierCache [ assertions ] = result ;
274+ PromisePrototypeThen ( result , undefined , removeCache ) ;
275+ return result ;
276+ } ;
277+ return PromisePrototypeThen ( specifierCache [ assertions ] , undefined , fallback ) ;
278+ }
279+ const result = this . import ( specifier , parentURL , importAssertions ) ;
280+ specifierCache [ assertions ] = result ;
281+ PromisePrototypeThen ( result , undefined , removeCache ) ;
282+ return result ;
283+ }
284+
245285 /**
246286 * This method is usually called indirectly as part of the loading processes.
247287 * Internally, it is used directly to add loaders. Use directly with caution.
@@ -267,7 +307,9 @@ class ESMLoader {
267307 // create redundant work for the caller, so it is later popped off the
268308 // internal list.
269309 const wasArr = ArrayIsArray ( specifiers ) ;
270- if ( ! wasArr ) { specifiers = [ specifiers ] ; }
310+ if ( ! wasArr ) {
311+ specifiers = [ specifiers ] ;
312+ }
271313
272314 const count = specifiers . length ;
273315 const jobs = new Array ( count ) ;
0 commit comments