@@ -54,7 +54,6 @@ const {
5454}  =  require ( 'internal/modules/esm/resolve' ) ; 
5555const  { 
5656  getDefaultConditions, 
57-   loaderWorkerId, 
5857}  =  require ( 'internal/modules/esm/utils' ) ; 
5958const  {  deserializeError }  =  require ( 'internal/error_serdes' ) ; 
6059const  { 
@@ -105,7 +104,39 @@ function defineImportAssertionAlias(context) {
105104
106105// [2] `validate...()`s throw the wrong error 
107106
108- class  Hooks  { 
107+ /** 
108+  * @typedef  {{ format: ModuleFormat, source: ModuleSource } } LoadResult 
109+  */ 
110+ 
111+ /** 
112+  * @typedef  {{ format: ModuleFormat, url: string, importAttributes: Record<string, string> } } ResolveResult 
113+  */ 
114+ 
115+ /** 
116+  * Interface for classes that implement asynchronous loader hooks that can be attached to the ModuleLoader 
117+  * via `ModuleLoader.#setAsyncLoaderHooks()`. 
118+  * @typedef  {object } AsyncLoaderHooks 
119+  * @property  {boolean } allowImportMetaResolve Whether to allow the use of `import.meta.resolve`. 
120+  * @property  {(url: string, context: object, defaultLoad: Function) => Promise<LoadResult> } load 
121+  *   Calling the asynchronous `load` hook asynchronously. 
122+  * @property  {(url: string, context: object, defaultLoad: Function) => LoadResult } loadSync 
123+  *   Calling the asynchronous `load` hook synchronously. 
124+  * @property  {(originalSpecifier: string, parentURL: string, 
125+  *             importAttributes: Record<string, string>) => Promise<ResolveResult>} resolve 
126+  *   Calling the asynchronous `resolve` hook asynchronously. 
127+  * @property  {(originalSpecifier: string, parentURL: string, 
128+  *            importAttributes: Record<string, string>) => ResolveResult} resolveSync 
129+  *   Calling the asynchronous `resolve` hook synchronously. 
130+  * @property  {(specifier: string, parentURL: string) => any } register Register asynchronous loader hooks 
131+  * @property  {() => void } waitForLoaderHookInitialization Force loading of hooks. 
132+  */ 
133+ 
134+ /** 
135+  * @implements  {AsyncLoaderHooks} 
136+  * Instances of this class run directly on the loader hook worker thread and customize the module 
137+  * loading of the hooks worker itself. 
138+  */ 
139+ class  AsyncLoaderHooksOnLoaderHookWorker  { 
109140  #chains =  { 
110141    /** 
111142     * Phase 1 of 2 in ESM loading. 
@@ -452,7 +483,7 @@ class Hooks {
452483    } ; 
453484  } 
454485
455-   forceLoadHooks ( )  { 
486+   waitForLoaderHookInitialization ( )  { 
456487    // No-op 
457488  } 
458489
@@ -462,14 +493,20 @@ class Hooks {
462493    return  meta ; 
463494  } 
464495} 
465- ObjectSetPrototypeOf ( Hooks . prototype ,  null ) ; 
496+ ObjectSetPrototypeOf ( AsyncLoaderHooksOnLoaderHookWorker . prototype ,  null ) ; 
466497
467498/** 
468-  * There may be multiple instances of Hooks/HooksProxy, but there is only 1 Internal worker, so 
469-  * there is only 1 MessageChannel. 
499+  * There is only one loader hook thread for each non-loader-hook worker thread 
500+  * (i.e. the non-loader-hook thread and any worker threads that are not loader hook workers themselves), 
501+  * so there is only 1 MessageChannel. 
470502 */ 
471503let  MessageChannel ; 
472- class  HooksProxy  { 
504+ 
505+ /** 
506+  * Abstraction over a worker thread that runs the asynchronous module loader hooks. 
507+  * Instances of this class run on the non-loader-hook thread and communicate with the loader hooks worker thread. 
508+  */ 
509+ class  AsyncLoaderHookWorker  { 
473510  /** 
474511   * Shared memory. Always use Atomics method to read or write to it. 
475512   * @type  {Int32Array } 
@@ -503,7 +540,7 @@ class HooksProxy {
503540    const  lock  =  new  SharedArrayBuffer ( SHARED_MEMORY_BYTE_LENGTH ) ; 
504541    this . #lock =  new  Int32Array ( lock ) ; 
505542
506-     this . #worker =  new  InternalWorker ( loaderWorkerId ,  { 
543+     this . #worker =  new  InternalWorker ( 'internal/modules/esm/worker' ,  { 
507544      stderr : false , 
508545      stdin : false , 
509546      stdout : false , 
@@ -644,7 +681,7 @@ class HooksProxy {
644681    this . #importMetaInitializer( meta ,  context ,  loader ) ; 
645682  } 
646683} 
647- ObjectSetPrototypeOf ( HooksProxy . prototype ,  null ) ; 
684+ ObjectSetPrototypeOf ( AsyncLoaderHookWorker . prototype ,  null ) ; 
648685
649686// TODO(JakobJingleheimer): Remove this when loaders go "stable". 
650687let  globalPreloadWarningWasEmitted  =  false ; 
@@ -757,6 +794,95 @@ function nextHookFactory(current, meta, { validateArgs, validateOutput }) {
757794  ) ; 
758795} 
759796
797+ /** 
798+  * @type  {AsyncLoaderHookWorker } 
799+  * Worker instance used to run async loader hooks in a separate thread. This is a singleton for each 
800+  * non-loader-hook worker thread (i.e. the main thread and any worker threads that are not 
801+  * loader hook workers themselves). 
802+  */ 
803+ let  asyncLoaderHookWorker ; 
804+ /** 
805+  * Get the AsyncLoaderHookWorker instance. If it is not defined, then create a new one. 
806+  * @returns  {AsyncLoaderHookWorker } 
807+  */ 
808+ function  getAsyncLoaderHookWorker ( )  { 
809+   asyncLoaderHookWorker  ??=  new  AsyncLoaderHookWorker ( ) ; 
810+   return  asyncLoaderHookWorker ; 
811+ } 
812+ 
813+ /** 
814+  * @implements  {AsyncLoaderHooks} 
815+  * Instances of this class are created in the non-loader-hook thread and communicate with the worker thread 
816+  * spawned to run the async loader hooks. 
817+  */ 
818+ class  AsyncLoaderHooksProxiedToLoaderHookWorker  { 
819+ 
820+   allowImportMetaResolve  =  true ; 
821+ 
822+   /** 
823+    * Instantiate a module loader that uses user-provided custom loader hooks. 
824+    */ 
825+   constructor ( )  { 
826+     getAsyncLoaderHookWorker ( ) ; 
827+   } 
828+ 
829+   /** 
830+    * Register some loader specifier. 
831+    * @param  {string } originalSpecifier The specified URL path of the loader to 
832+    *   be registered. 
833+    * @param  {string } parentURL The parent URL from where the loader will be 
834+    *   registered if using it package name as specifier 
835+    * @param  {any } [data] Arbitrary data to be passed from the custom loader 
836+    *   (user-land) to the worker. 
837+    * @param  {any[] } [transferList] Objects in `data` that are changing ownership 
838+    * @param  {boolean } [isInternal] For internal loaders that should not be publicly exposed. 
839+    * @returns  {{ format: string, url: URL['href'] } } 
840+    */ 
841+   register ( originalSpecifier ,  parentURL ,  data ,  transferList ,  isInternal )  { 
842+     return  asyncLoaderHookWorker . makeSyncRequest ( 'register' ,  transferList ,  originalSpecifier ,  parentURL , 
843+                                                  data ,  isInternal ) ; 
844+   } 
845+ 
846+   /** 
847+    * Resolve the location of the module. 
848+    * @param  {string } originalSpecifier The specified URL path of the module to 
849+    *   be resolved. 
850+    * @param  {string } [parentURL] The URL path of the module's parent. 
851+    * @param  {ImportAttributes } importAttributes Attributes from the import 
852+    *   statement or expression. 
853+    * @returns  {{ format: string, url: URL['href'] } } 
854+    */ 
855+   resolve ( originalSpecifier ,  parentURL ,  importAttributes )  { 
856+     return  asyncLoaderHookWorker . makeAsyncRequest ( 'resolve' ,  undefined ,  originalSpecifier ,  parentURL ,  importAttributes ) ; 
857+   } 
858+ 
859+   resolveSync ( originalSpecifier ,  parentURL ,  importAttributes )  { 
860+     // This happens only as a result of `import.meta.resolve` calls, which must be sync per spec. 
861+     return  asyncLoaderHookWorker . makeSyncRequest ( 'resolve' ,  undefined ,  originalSpecifier ,  parentURL ,  importAttributes ) ; 
862+   } 
863+ 
864+   /** 
865+    * Provide source that is understood by one of Node's translators. 
866+    * @param  {URL['href'] } url The URL/path of the module to be loaded 
867+    * @param  {object } [context] Metadata about the module 
868+    * @returns  {Promise<{ format: ModuleFormat, source: ModuleSource }> } 
869+    */ 
870+   load ( url ,  context )  { 
871+     return  asyncLoaderHookWorker . makeAsyncRequest ( 'load' ,  undefined ,  url ,  context ) ; 
872+   } 
873+   loadSync ( url ,  context )  { 
874+     return  asyncLoaderHookWorker . makeSyncRequest ( 'load' ,  undefined ,  url ,  context ) ; 
875+   } 
876+ 
877+   importMetaInitialize ( meta ,  context ,  loader )  { 
878+     asyncLoaderHookWorker . importMetaInitialize ( meta ,  context ,  loader ) ; 
879+   } 
880+ 
881+   waitForLoaderHookInitialization ( )  { 
882+     asyncLoaderHookWorker . waitForWorker ( ) ; 
883+   } 
884+ } 
760885
761- exports . Hooks  =  Hooks ; 
762- exports . HooksProxy  =  HooksProxy ; 
886+ exports . AsyncLoaderHooksProxiedToLoaderHookWorker  =  AsyncLoaderHooksProxiedToLoaderHookWorker ; 
887+ exports . AsyncLoaderHooksOnLoaderHookWorker  =  AsyncLoaderHooksOnLoaderHookWorker ; 
888+ exports . AsyncLoaderHookWorker  =  AsyncLoaderHookWorker ; 
0 commit comments