@@ -31,6 +31,7 @@ const {
3131 ERR_INVALID_RETURN_PROPERTY_VALUE ,
3232 ERR_INVALID_RETURN_VALUE ,
3333 ERR_LOADER_CHAIN_INCOMPLETE ,
34+ ERR_METHOD_NOT_IMPLEMENTED ,
3435 ERR_UNKNOWN_BUILTIN_MODULE ,
3536 ERR_WORKER_UNSERIALIZABLE_ERROR ,
3637} = require ( 'internal/errors' ) . codes ;
@@ -65,7 +66,7 @@ const {
6566let debug = require ( 'internal/util/debuglog' ) . debuglog ( 'esm' , ( fn ) => {
6667 debug = fn ;
6768} ) ;
68-
69+ let importMetaInitializer ;
6970
7071/**
7172 * @typedef {object } ExportedHooks
@@ -82,7 +83,6 @@ let debug = require('internal/util/debuglog').debuglog('esm', (fn) => {
8283
8384// [2] `validate...()`s throw the wrong error
8485
85-
8686class Hooks {
8787 #chains = {
8888 /**
@@ -121,20 +121,20 @@ class Hooks {
121121 // Cache URLs we've already validated to avoid repeated validation
122122 #validatedUrls = new SafeSet ( ) ;
123123
124+ allowImportMetaResolve = false ;
125+
124126 /**
125127 * Import and register custom/user-defined module loader hook(s).
126128 * @param {string } urlOrSpecifier
127129 * @param {string } parentURL
128130 */
129131 async register ( urlOrSpecifier , parentURL ) {
130132 const moduleLoader = require ( 'internal/process/esm_loader' ) . esmLoader ;
131-
132133 const keyedExports = await moduleLoader . import (
133134 urlOrSpecifier ,
134135 parentURL ,
135136 kEmptyObject ,
136137 ) ;
137-
138138 this . addCustomLoader ( urlOrSpecifier , keyedExports ) ;
139139 }
140140
@@ -152,13 +152,15 @@ class Hooks {
152152 } = pluckHooks ( exports ) ;
153153
154154 if ( globalPreload ) {
155- ArrayPrototypePush ( this . #chains. globalPreload , { fn : globalPreload , url } ) ;
155+ ArrayPrototypePush ( this . #chains. globalPreload , { __proto__ : null , fn : globalPreload , url } ) ;
156156 }
157157 if ( resolve ) {
158- ArrayPrototypePush ( this . #chains. resolve , { fn : resolve , url } ) ;
158+ const next = this . #chains. resolve [ this . #chains. resolve . length - 1 ] ;
159+ ArrayPrototypePush ( this . #chains. resolve , { __proto__ : null , fn : resolve , url, next } ) ;
159160 }
160161 if ( load ) {
161- ArrayPrototypePush ( this . #chains. load , { fn : load , url } ) ;
162+ const next = this . #chains. load [ this . #chains. load . length - 1 ] ;
163+ ArrayPrototypePush ( this . #chains. load , { __proto__ : null , fn : load , url, next } ) ;
162164 }
163165 }
164166
@@ -235,7 +237,6 @@ class Hooks {
235237 chainFinished : null ,
236238 context,
237239 hookErrIdentifier : '' ,
238- hookIndex : chain . length - 1 ,
239240 hookName : 'resolve' ,
240241 shortCircuited : false ,
241242 } ;
@@ -258,7 +259,7 @@ class Hooks {
258259 }
259260 } ;
260261
261- const nextResolve = nextHookFactory ( chain , meta , { validateArgs, validateOutput } ) ;
262+ const nextResolve = nextHookFactory ( chain [ chain . length - 1 ] , meta , { validateArgs, validateOutput } ) ;
262263
263264 const resolution = await nextResolve ( originalSpecifier , context ) ;
264265 const { hookErrIdentifier } = meta ; // Retrieve the value after all settled
@@ -335,6 +336,10 @@ class Hooks {
335336 } ;
336337 }
337338
339+ resolveSync ( _originalSpecifier , _parentURL , _importAssertions ) {
340+ throw new ERR_METHOD_NOT_IMPLEMENTED ( 'resolveSync()' ) ;
341+ }
342+
338343 /**
339344 * Provide source that is understood by one of Node's translators.
340345 *
@@ -351,7 +356,6 @@ class Hooks {
351356 chainFinished : null ,
352357 context,
353358 hookErrIdentifier : '' ,
354- hookIndex : chain . length - 1 ,
355359 hookName : 'load' ,
356360 shortCircuited : false ,
357361 } ;
@@ -393,7 +397,7 @@ class Hooks {
393397 }
394398 } ;
395399
396- const nextLoad = nextHookFactory ( chain , meta , { validateArgs, validateOutput } ) ;
400+ const nextLoad = nextHookFactory ( chain [ chain . length - 1 ] , meta , { validateArgs, validateOutput } ) ;
397401
398402 const loaded = await nextLoad ( url , context ) ;
399403 const { hookErrIdentifier } = meta ; // Retrieve the value after all settled
@@ -468,6 +472,16 @@ class Hooks {
468472 source,
469473 } ;
470474 }
475+
476+ forceLoadHooks ( ) {
477+ // No-op
478+ }
479+
480+ importMetaInitialize ( meta , context , loader ) {
481+ importMetaInitializer ??= require ( 'internal/modules/esm/initialize_import_meta' ) . initializeImportMeta ;
482+ meta = importMetaInitializer ( meta , context , loader ) ;
483+ return meta ;
484+ }
471485}
472486ObjectSetPrototypeOf ( Hooks . prototype , null ) ;
473487
@@ -717,46 +731,39 @@ function pluckHooks({
717731 * A utility function to iterate through a hook chain, track advancement in the
718732 * chain, and generate and supply the `next<HookName>` argument to the custom
719733 * hook.
720- * @param {KeyedHook[] } chain The whole hook chain.
734+ * @param {Hook } current The (currently) first hook in the chain (this shifts
735+ * on every call).
721736 * @param {object } meta Properties that change as the current hook advances
722737 * along the chain.
723738 * @param {boolean } meta.chainFinished Whether the end of the chain has been
724739 * reached AND invoked.
725740 * @param {string } meta.hookErrIdentifier A user-facing identifier to help
726741 * pinpoint where an error occurred. Ex "file:///foo.mjs 'resolve'".
727- * @param {number } meta.hookIndex A non-negative integer tracking the current
728- * position in the hook chain.
729742 * @param {string } meta.hookName The kind of hook the chain is (ex 'resolve')
730743 * @param {boolean } meta.shortCircuited Whether a hook signaled a short-circuit.
731744 * @param {(hookErrIdentifier, hookArgs) => void } validate A wrapper function
732745 * containing all validation of a custom loader hook's intermediary output. Any
733746 * validation within MUST throw.
734747 * @returns {function next<HookName>(...hookArgs) } The next hook in the chain.
735748 */
736- function nextHookFactory ( chain , meta , { validateArgs, validateOutput } ) {
749+ function nextHookFactory ( current , meta , { validateArgs, validateOutput } ) {
737750 // First, prepare the current
738751 const { hookName } = meta ;
739752 const {
740753 fn : hook ,
741754 url : hookFilePath ,
742- } = chain [ meta . hookIndex ] ;
755+ next,
756+ } = current ;
743757
744758 // ex 'nextResolve'
745759 const nextHookName = `next${
746760 StringPrototypeToUpperCase ( hookName [ 0 ] ) +
747761 StringPrototypeSlice ( hookName , 1 )
748762 } `;
749763
750- // When hookIndex is 0, it's reached the default, which does not call next()
751- // so feed it a noop that blows up if called, so the problem is obvious.
752- const generatedHookIndex = meta . hookIndex ;
753764 let nextNextHook ;
754- if ( meta . hookIndex > 0 ) {
755- // Now, prepare the next: decrement the pointer so the next call to the
756- // factory generates the next link in the chain.
757- meta . hookIndex -- ;
758-
759- nextNextHook = nextHookFactory ( chain , meta , { validateArgs, validateOutput } ) ;
765+ if ( next ) {
766+ nextNextHook = nextHookFactory ( next , meta , { validateArgs, validateOutput } ) ;
760767 } else {
761768 // eslint-disable-next-line func-name-matching
762769 nextNextHook = function chainAdvancedTooFar ( ) {
@@ -773,17 +780,16 @@ function nextHookFactory(chain, meta, { validateArgs, validateOutput }) {
773780
774781 validateArgs ( `${ meta . hookErrIdentifier } hook's ${ nextHookName } ()` , arg0 , context ) ;
775782
776- const outputErrIdentifier = `${ chain [ generatedHookIndex ] . url } '${ hookName } ' hook's ${ nextHookName } ()` ;
783+ const outputErrIdentifier = `${ hookFilePath } '${ hookName } ' hook's ${ nextHookName } ()` ;
777784
778785 // Set when next<HookName> is actually called, not just generated.
779- if ( generatedHookIndex === 0 ) { meta . chainFinished = true ; }
786+ if ( ! next ) { meta . chainFinished = true ; }
780787
781788 if ( context ) { // `context` has already been validated, so no fancy check needed.
782789 ObjectAssign ( meta . context , context ) ;
783790 }
784791
785792 const output = await hook ( arg0 , meta . context , nextNextHook ) ;
786-
787793 validateOutput ( outputErrIdentifier , output ) ;
788794
789795 if ( output ?. shortCircuit === true ) { meta . shortCircuited = true ; }
0 commit comments