44require ( 'internal/modules/cjs/loader' ) ;
55
66const {
7- Array,
8- ArrayIsArray,
97 ArrayPrototypeJoin,
108 ArrayPrototypePush,
119 FunctionPrototypeBind,
1210 FunctionPrototypeCall,
1311 ObjectAssign,
1412 ObjectCreate,
13+ ObjectFreeze,
1514 ObjectSetPrototypeOf,
16- PromiseAll,
1715 RegExpPrototypeExec,
18- SafeArrayIterator,
1916 SafeWeakMap,
2017 StringPrototypeStartsWith,
2118 globalThis,
@@ -59,7 +56,7 @@ const {
5956 fetchModule,
6057 inFetchCache,
6158} = require ( 'internal/modules/esm/fetch_module' ) ;
62-
59+ const { strictEqual } = require ( 'internal/assert' ) ;
6360
6461/**
6562 * @typedef {object } ExportedHooks
@@ -123,6 +120,8 @@ class ESMLoader {
123120 } ,
124121 ] ;
125122
123+ #preImporters = [ ] ;
124+
126125 /**
127126 * Phase 1 of 2 in ESM loading.
128127 * @private
@@ -184,6 +183,7 @@ class ESMLoader {
184183 */
185184 static pluckHooks ( {
186185 globalPreload,
186+ preImport,
187187 resolve,
188188 load,
189189 // obsolete hooks:
@@ -232,6 +232,9 @@ class ESMLoader {
232232 acceptedHooks . globalPreloader =
233233 FunctionPrototypeBind ( globalPreload , null ) ;
234234 }
235+ if ( preImport ) {
236+ acceptedHooks . preImporter = FunctionPrototypeBind ( preImport , null ) ;
237+ }
235238 if ( resolve ) {
236239 acceptedHooks . resolver = FunctionPrototypeBind ( resolve , null ) ;
237240 }
@@ -259,6 +262,7 @@ class ESMLoader {
259262 } = customLoaders [ i ] ;
260263 const {
261264 globalPreloader,
265+ preImporter,
262266 resolver,
263267 loader,
264268 } = ESMLoader . pluckHooks ( exports ) ;
@@ -272,6 +276,12 @@ class ESMLoader {
272276 } ,
273277 ) ;
274278 }
279+ if ( preImporter ) {
280+ ArrayPrototypePush (
281+ this . #preImporters,
282+ preImporter
283+ ) ;
284+ }
275285 if ( resolver ) {
276286 ArrayPrototypePush (
277287 this . #resolvers,
@@ -308,7 +318,7 @@ class ESMLoader {
308318 importModuleDynamically : ( specifier , { url } , importAssertions ) => {
309319 return this . import ( specifier ,
310320 this . getBaseURL ( url ) ,
311- importAssertions ) ;
321+ importAssertions , true ) ;
312322 }
313323 } ) ;
314324
@@ -472,48 +482,47 @@ class ESMLoader {
472482 * This method must NOT be renamed: it functions as a dynamic import on a
473483 * loader module.
474484 *
475- * @param {string | string[] } specifiers Path(s) to the module.
476- * @param {string } parentURL Path of the parent importing the module.
485+ * @param {string } specifiers Imported specifier
486+ * @param {string } parentURL URL of the parent importing the module.
477487 * @param {Record<string, string> } importAssertions Validations for the
478488 * module import.
479- * @returns {Promise<ExportedHooks | KeyedExports[]> }
480- * A collection of module export(s) or a list of collections of module
481- * export(s).
489+ * @param {boolean } dynamic Whether the import is a dynamic `import()`.
490+ * @returns {Promise<ModuleNamespace> }
482491 */
483- async import ( specifiers , parentURL , importAssertions ) {
484- // For loaders, `import` is passed multiple things to process, it returns a
485- // list pairing the url and exports collected. This is especially useful for
486- // error messaging, to identity from where an export came. But, in most
487- // cases, only a single url is being "imported" (ex `import()`), so there is
488- // only 1 possible url from which the exports were collected and it is
489- // already known to the caller. Nesting that in a list would only ever
490- // create redundant work for the caller, so it is later popped off the
491- // internal list.
492- const wasArr = ArrayIsArray ( specifiers ) ;
493- if ( ! wasArr ) { specifiers = [ specifiers ] ; }
494-
495- const count = specifiers . length ;
496- const jobs = new Array ( count ) ;
497-
498- for ( let i = 0 ; i < count ; i ++ ) {
499- jobs [ i ] = this . getModuleJob ( specifiers [ i ] , parentURL , importAssertions )
500- . then ( ( job ) => job . run ( ) )
501- . then ( ( { module } ) => module . getNamespace ( ) ) ;
502- }
503-
504- const namespaces = await PromiseAll ( new SafeArrayIterator ( jobs ) ) ;
505-
506- if ( ! wasArr ) { return namespaces [ 0 ] ; } // We can skip the pairing below
507-
508- for ( let i = 0 ; i < count ; i ++ ) {
509- const namespace = ObjectCreate ( null ) ;
510- namespace . url = specifiers [ i ] ;
511- namespace . exports = namespaces [ i ] ;
512-
513- namespaces [ i ] = namespace ;
514- }
492+ async import ( specifier , parentURL , importAssertions = ObjectCreate ( null ) , dynamic = false ) {
493+ await this . preImport ( specifier , parentURL , importAssertionsForResolve , dynamic ) ;
494+ const job = await this . getModuleJob ( specifier , parentURL , importAssertions ) ;
495+ this . getModuleJob ( specifier , parentURL , importAssertions ) ;
496+ const { module } = await job . run ( ) ;
497+ return module . getNamespace ( ) ;
498+ }
515499
516- return namespaces ;
500+ /**
501+ * Run the prepare hooks for a new import operation.
502+ *
503+ * Internally, this behaves like a backwards iterator, wherein the stack of
504+ * hooks starts at the top and each call to `nextResolve()` moves down 1 step
505+ * until it reaches the bottom or short-circuits.
506+ *
507+ * @param {string } specifier The import specifier.
508+ * @param {string } parentURL The URL of the module's parent.
509+ * @param {ImportAssertions } [importAssertions] Assertions from the import
510+ * statement or expression.
511+ * @param {boolean } dynamic Whether the import is a dynamic `import()`.
512+ */
513+ async preImport (
514+ specifier ,
515+ parentURL ,
516+ importAssertions ,
517+ dynamic
518+ ) {
519+ const context = ObjectFreeze ( {
520+ conditions : DEFAULT_CONDITIONS ,
521+ dynamic,
522+ importAssertions,
523+ parentURL
524+ } ) ;
525+ await Promise . all ( this . #preImporters. map ( preImport => preImport ( specifier , context ) ) ) ;
517526 }
518527
519528 /**
0 commit comments