44require ( 'internal/modules/cjs/loader' ) ;
55
66const {
7- Array,
8- ArrayIsArray,
97 ArrayPrototypeJoin,
108 ArrayPrototypePush,
119 FunctionPrototypeBind,
1210 FunctionPrototypeCall,
1311 ObjectAssign,
1412 ObjectCreate,
1513 ObjectDefineProperty,
14+ ObjectFreeze,
1615 ObjectSetPrototypeOf,
17- PromiseAll,
1816 PromiseResolve,
1917 PromisePrototypeThen,
2018 ReflectApply,
2119 RegExpPrototypeExec,
22- SafeArrayIterator,
2320 SafeWeakMap,
2421 StringPrototypeSlice,
2522 StringPrototypeToUpperCase,
@@ -215,6 +212,8 @@ class ESMLoader {
215212 } ,
216213 ] ;
217214
215+ #preImporters = [ ] ;
216+
218217 /**
219218 * Phase 1 of 2 in ESM loading.
220219 * @private
@@ -276,6 +275,7 @@ class ESMLoader {
276275 */
277276 static pluckHooks ( {
278277 globalPreload,
278+ preImport,
279279 resolve,
280280 load,
281281 // obsolete hooks:
@@ -324,6 +324,9 @@ class ESMLoader {
324324 acceptedHooks . globalPreloader =
325325 FunctionPrototypeBind ( globalPreload , null ) ;
326326 }
327+ if ( preImport ) {
328+ acceptedHooks . preImporter = FunctionPrototypeBind ( preImport , null ) ;
329+ }
327330 if ( resolve ) {
328331 acceptedHooks . resolver = FunctionPrototypeBind ( resolve , null ) ;
329332 }
@@ -351,6 +354,7 @@ class ESMLoader {
351354 } = customLoaders [ i ] ;
352355 const {
353356 globalPreloader,
357+ preImporter,
354358 resolver,
355359 loader,
356360 } = ESMLoader . pluckHooks ( exports ) ;
@@ -364,6 +368,12 @@ class ESMLoader {
364368 } ,
365369 ) ;
366370 }
371+ if ( preImporter ) {
372+ ArrayPrototypePush (
373+ this . #preImporters,
374+ preImporter
375+ ) ;
376+ }
367377 if ( resolver ) {
368378 ArrayPrototypePush (
369379 this . #resolvers,
@@ -398,7 +408,7 @@ class ESMLoader {
398408 const module = new ModuleWrap ( url , undefined , source , 0 , 0 ) ;
399409 callbackMap . set ( module , {
400410 importModuleDynamically : ( specifier , { url } , importAssertions ) => {
401- return this . import ( specifier , url , importAssertions ) ;
411+ return this . import ( specifier , url , importAssertions , true ) ;
402412 }
403413 } ) ;
404414
@@ -517,48 +527,47 @@ class ESMLoader {
517527 * This method must NOT be renamed: it functions as a dynamic import on a
518528 * loader module.
519529 *
520- * @param {string | string[] } specifiers Path(s) to the module.
521- * @param {string } parentURL Path of the parent importing the module.
530+ * @param {string } specifiers Imported specifier
531+ * @param {string } parentURL URL of the parent importing the module.
522532 * @param {Record<string, string> } importAssertions Validations for the
523533 * module import.
524- * @returns {Promise<ExportedHooks | KeyedExports[]> }
525- * A collection of module export(s) or a list of collections of module
526- * export(s).
534+ * @param {boolean } dynamic Whether the import is a dynamic `import()`.
535+ * @returns {Promise<ModuleNamespace> }
527536 */
528- async import ( specifiers , parentURL , importAssertions ) {
529- // For loaders, `import` is passed multiple things to process, it returns a
530- // list pairing the url and exports collected. This is especially useful for
531- // error messaging, to identity from where an export came. But, in most
532- // cases, only a single url is being "imported" (ex `import()`), so there is
533- // only 1 possible url from which the exports were collected and it is
534- // already known to the caller. Nesting that in a list would only ever
535- // create redundant work for the caller, so it is later popped off the
536- // internal list.
537- const wasArr = ArrayIsArray ( specifiers ) ;
538- if ( ! wasArr ) { specifiers = [ specifiers ] ; }
539-
540- const count = specifiers . length ;
541- const jobs = new Array ( count ) ;
542-
543- for ( let i = 0 ; i < count ; i ++ ) {
544- jobs [ i ] = this . getModuleJob ( specifiers [ i ] , parentURL , importAssertions )
545- . then ( ( job ) => job . run ( ) )
546- . then ( ( { module } ) => module . getNamespace ( ) ) ;
547- }
548-
549- const namespaces = await PromiseAll ( new SafeArrayIterator ( jobs ) ) ;
550-
551- if ( ! wasArr ) { return namespaces [ 0 ] ; } // We can skip the pairing below
552-
553- for ( let i = 0 ; i < count ; i ++ ) {
554- const namespace = ObjectCreate ( null ) ;
555- namespace . url = specifiers [ i ] ;
556- namespace . exports = namespaces [ i ] ;
557-
558- namespaces [ i ] = namespace ;
559- }
537+ async import ( specifier , parentURL , importAssertions = ObjectCreate ( null ) , dynamic = false ) {
538+ await this . preImport ( specifier , parentURL , importAssertionsForResolve , dynamic ) ;
539+ const job = await this . getModuleJob ( specifier , parentURL , importAssertions ) ;
540+ this . getModuleJob ( specifier , parentURL , importAssertions ) ;
541+ const { module } = await job . run ( ) ;
542+ return module . getNamespace ( ) ;
543+ }
560544
561- return namespaces ;
545+ /**
546+ * Run the prepare hooks for a new import operation.
547+ *
548+ * Internally, this behaves like a backwards iterator, wherein the stack of
549+ * hooks starts at the top and each call to `nextResolve()` moves down 1 step
550+ * until it reaches the bottom or short-circuits.
551+ *
552+ * @param {string } specifier The import specifier.
553+ * @param {string } parentURL The URL of the module's parent.
554+ * @param {ImportAssertions } [importAssertions] Assertions from the import
555+ * statement or expression.
556+ * @param {boolean } dynamic Whether the import is a dynamic `import()`.
557+ */
558+ async preImport (
559+ specifier ,
560+ parentURL ,
561+ importAssertions ,
562+ dynamic
563+ ) {
564+ const context = ObjectFreeze ( {
565+ conditions : DEFAULT_CONDITIONS ,
566+ dynamic,
567+ importAssertions,
568+ parentURL
569+ } ) ;
570+ await Promise . all ( this . #preImporters. map ( preImport => preImport ( specifier , context ) ) ) ;
562571 }
563572
564573 /**
0 commit comments