@@ -54,7 +54,6 @@ const {
5454} = require ( 'internal/util' ) ;
5555
5656const {
57- defaultResolve,
5857 throwIfInvalidParentURL,
5958} = require ( 'internal/modules/esm/resolve' ) ;
6059const {
@@ -87,45 +86,40 @@ let importMetaInitializer;
8786// [2] `validate...()`s throw the wrong error
8887
8988class Hooks {
90- #chains = {
91- /**
92- * Prior to ESM loading. These are called once before any modules are started.
93- * @private
94- * @property {KeyedHook[] } globalPreload Last-in-first-out list of preload hooks.
95- */
96- globalPreload : [ ] ,
97-
98- /**
99- * Phase 1 of 2 in ESM loading.
100- * The output of the `resolve` chain of hooks is passed into the `load` chain of hooks.
101- * @private
102- * @property {KeyedHook[] } resolve Last-in-first-out collection of resolve hooks.
103- */
104- resolve : [
105- {
106- fn : defaultResolve ,
107- url : 'node:internal/modules/esm/resolve' ,
108- } ,
109- ] ,
110-
111- /**
112- * Phase 2 of 2 in ESM loading.
113- * @private
114- * @property {KeyedHook[] } load Last-in-first-out collection of loader hooks.
115- */
116- load : [
117- {
118- fn : require ( 'internal/modules/esm/load' ) . defaultLoad ,
119- url : 'node:internal/modules/esm/load' ,
120- } ,
121- ] ,
122- } ;
89+ #loaderInstances = [ ] ;
90+ #chains = { } ;
12391
12492 // Cache URLs we've already validated to avoid repeated validation
12593 #validatedUrls = new SafeSet ( ) ;
12694
12795 allowImportMetaResolve = false ;
12896
97+ constructor ( ) {
98+ const defaultLoader = 'internal/modules/esm/default_loader' ;
99+ this . addCustomLoader ( `node:${ defaultLoader } ` , require ( defaultLoader ) ) ;
100+ }
101+
102+ #rebuildChain( name ) {
103+ const chain = this . #chains[ name ] = [ ] ;
104+ let i = 0 ;
105+ for ( const instance of this . #loaderInstances) {
106+ if ( typeof instance [ name ] !== 'function' ) {
107+ continue ;
108+ }
109+ chain . push ( {
110+ loader : instance ,
111+ fn : instance [ name ] ,
112+ next : chain [ i ++ - 1 ] ,
113+ } ) ;
114+ }
115+ }
116+
117+ #rebuildChains( ) {
118+ this . #rebuildChain( 'globalPreload' ) ;
119+ this . #rebuildChain( 'resolve' ) ;
120+ this . #rebuildChain( 'load' ) ;
121+ }
122+
129123 /**
130124 * Import and register custom/user-defined module loader hook(s).
131125 * @param {string } urlOrSpecifier
@@ -164,25 +158,26 @@ class Hooks {
164158 emitExperimentalWarning (
165159 '`globalPreload` is planned for removal in favor of `initialize`. `globalPreload`' ,
166160 ) ;
167- ArrayPrototypePush ( this . #chains. globalPreload , { __proto__ : null , fn : globalPreload , url } ) ;
168- }
169- if ( resolve ) {
170- const next = this . #chains. resolve [ this . #chains. resolve . length - 1 ] ;
171- ArrayPrototypePush ( this . #chains. resolve , { __proto__ : null , fn : resolve , url, next } ) ;
172161 }
173- if ( load ) {
174- const next = this . #chains. load [ this . #chains. load . length - 1 ] ;
175- ArrayPrototypePush ( this . #chains. load , { __proto__ : null , fn : load , url, next } ) ;
176- }
177- return initialize ?. ( data ) ;
162+ const instance = {
163+ __proto__ : null ,
164+ url,
165+ globalPreload,
166+ initialize,
167+ resolve,
168+ load,
169+ } ;
170+ ArrayPrototypePush ( this . #loaderInstances, instance ) ;
171+ this . #rebuildChains( ) ;
172+ return initialize ?. ( data , { __proto__ : null , id : instance . id , url } ) ;
178173 }
179174
180175 /**
181176 * Initialize `globalPreload` hooks.
182177 */
183178 initializeGlobalPreload ( ) {
184179 const preloadScripts = [ ] ;
185- for ( let i = this . #chains. globalPreload . length - 1 ; i >= 0 ; i -- ) {
180+ for ( const chainEntry of this . #chains. globalPreload ) {
186181 const { MessageChannel } = require ( 'internal/worker/io' ) ;
187182 const channel = new MessageChannel ( ) ;
188183 const {
@@ -193,10 +188,7 @@ class Hooks {
193188 insidePreload . unref ( ) ;
194189 insideLoader . unref ( ) ;
195190
196- const {
197- fn : preload ,
198- url : specifier ,
199- } = this . #chains. globalPreload [ i ] ;
191+ const preload = chainEntry . fn ;
200192
201193 const preloaded = preload ( {
202194 port : insideLoader ,
@@ -207,8 +199,8 @@ class Hooks {
207199 if ( typeof preloaded !== 'string' ) { // [2]
208200 throw new ERR_INVALID_RETURN_VALUE (
209201 'a string' ,
210- `${ specifier } globalPreload` ,
211- preload ,
202+ `${ chainEntry . loader . url } globalPreload` ,
203+ chainEntry . fn ,
212204 ) ;
213205 }
214206
@@ -240,7 +232,6 @@ class Hooks {
240232 ) {
241233 throwIfInvalidParentURL ( parentURL ) ;
242234
243- const chain = this . #chains. resolve ;
244235 const context = {
245236 conditions : getDefaultConditions ( ) ,
246237 importAssertions,
@@ -272,7 +263,11 @@ class Hooks {
272263 }
273264 } ;
274265
275- const nextResolve = nextHookFactory ( chain [ chain . length - 1 ] , meta , { validateArgs, validateOutput } ) ;
266+ const nextResolve = nextHookFactory (
267+ this . #chains. resolve [ this . #chains. resolve . length - 1 ] ,
268+ meta ,
269+ { validateArgs, validateOutput } ,
270+ ) ;
276271
277272 const resolution = await nextResolve ( originalSpecifier , context ) ;
278273 const { hookErrIdentifier } = meta ; // Retrieve the value after all settled
@@ -364,7 +359,6 @@ class Hooks {
364359 * @returns {Promise<{ format: ModuleFormat, source: ModuleSource }> }
365360 */
366361 async load ( url , context = { } ) {
367- const chain = this . #chains. load ;
368362 const meta = {
369363 chainFinished : null ,
370364 context,
@@ -410,7 +404,11 @@ class Hooks {
410404 }
411405 } ;
412406
413- const nextLoad = nextHookFactory ( chain [ chain . length - 1 ] , meta , { validateArgs, validateOutput } ) ;
407+ const nextLoad = nextHookFactory (
408+ this . #chains. load [ this . #chains. load . length - 1 ] ,
409+ meta ,
410+ { validateArgs, validateOutput } ,
411+ ) ;
414412
415413 const loaded = await nextLoad ( url , context ) ;
416414 const { hookErrIdentifier } = meta ; // Retrieve the value after all settled
@@ -789,11 +787,9 @@ function pluckHooks({
789787function nextHookFactory ( current , meta , { validateArgs, validateOutput } ) {
790788 // First, prepare the current
791789 const { hookName } = meta ;
792- const {
793- fn : hook ,
794- url : hookFilePath ,
795- next,
796- } = current ;
790+
791+ const { next, fn : hook , loader } = current ;
792+ const { url : hookFilePath } = loader ;
797793
798794 // ex 'nextResolve'
799795 const nextHookName = `next${
0 commit comments