@@ -10,7 +10,8 @@ const ModuleRequest = require('internal/loader/ModuleRequest');
1010const errors = require ( 'internal/errors' ) ;
1111const debug = require ( 'util' ) . debuglog ( 'esm' ) ;
1212
13- function getBase ( ) {
13+ // Returns a file URL for the current working directory.
14+ function getURLStringForCwd ( ) {
1415 try {
1516 return getURLFromFilePath ( `${ process . cwd ( ) } /` ) . href ;
1617 } catch ( e ) {
@@ -23,22 +24,44 @@ function getBase() {
2324 }
2425}
2526
27+ /* A Loader instance is used as the main entry point for loading ES modules.
28+ * Currently, this is a singleton -- there is only one used for loading
29+ * the main module and everything in its dependency graph. */
2630class Loader {
27- constructor ( base = getBase ( ) ) {
28- this . moduleMap = new ModuleMap ( ) ;
31+ constructor ( base = getURLStringForCwd ( ) ) {
2932 if ( typeof base !== 'string' ) {
3033 throw new errors . TypeError ( 'ERR_INVALID_ARG_TYPE' , 'base' , 'string' ) ;
3134 }
35+
36+ this . moduleMap = new ModuleMap ( ) ;
3237 this . base = base ;
33- this . resolver = ModuleRequest . resolve . bind ( null ) ;
38+ // The resolver has the signature
39+ // (specifier : string, parentURL : string, defaultResolve)
40+ // -> Promise<{ url : string,
41+ // format: anything in Loader.validFormats }>
42+ // where defaultResolve is ModuleRequest.resolve (having the same
43+ // signature itself).
44+ // If `.format` on the returned value is 'dynamic', .dynamicInstantiate
45+ // will be used as described below.
46+ this . resolver = ModuleRequest . resolve ;
47+ // This hook is only called when resolve(...).format is 'dynamic' and has
48+ // the signature
49+ // (url : string) -> Promise<{ exports: { ... }, execute: function }>
50+ // Where `exports` is an object whose property names define the exported
51+ // names of the generated module. `execute` is a function that receives
52+ // an object with the same keys as `exports`, whose values are get/set
53+ // functions for the actual exported values.
3454 this . dynamicInstantiate = undefined ;
3555 }
3656
3757 hook ( { resolve = ModuleRequest . resolve , dynamicInstantiate } ) {
58+ // Use .bind() to avoid giving access to the Loader instance when it is
59+ // called as this.resolver(...);
3860 this . resolver = resolve . bind ( null ) ;
3961 this . dynamicInstantiate = dynamicInstantiate ;
4062 }
4163
64+ // Typechecking wrapper around .resolver().
4265 async resolve ( specifier , parentURL = this . base ) {
4366 if ( typeof parentURL !== 'string' ) {
4467 throw new errors . TypeError ( 'ERR_INVALID_ARG_TYPE' ,
@@ -48,10 +71,11 @@ class Loader {
4871 const { url, format } = await this . resolver ( specifier , parentURL ,
4972 ModuleRequest . resolve ) ;
5073
51- if ( typeof format !== 'string' ) {
74+ if ( ! Loader . validFormats . includes ( format ) ) {
5275 throw new errors . TypeError ( 'ERR_INVALID_ARG_TYPE' , 'format' ,
53- [ 'esm' , 'cjs' , 'builtin' , 'addon' , 'json' ] ) ;
76+ Loader . validFormats ) ;
5477 }
78+
5579 if ( typeof url !== 'string' ) {
5680 throw new errors . TypeError ( 'ERR_INVALID_ARG_TYPE' , 'url' , 'string' ) ;
5781 }
@@ -72,14 +96,20 @@ class Loader {
7296 return { url, format } ;
7397 }
7498
99+ // May create a new ModuleJob instance if one did not already exist.
75100 async getModuleJob ( specifier , parentURL = this . base ) {
76101 const { url, format } = await this . resolve ( specifier , parentURL ) ;
77102 let job = this . moduleMap . get ( url ) ;
78103 if ( job === undefined ) {
79104 let loaderInstance ;
80105 if ( format === 'dynamic' ) {
106+ const { dynamicInstantiate } = this ;
107+ if ( typeof dynamicInstantiate !== 'function' ) {
108+ throw new errors . Error ( 'ERR_MISSING_DYNAMIC_INSTANTIATE_HOOK' ) ;
109+ }
110+
81111 loaderInstance = async ( url ) => {
82- const { exports, execute } = await this . dynamicInstantiate ( url ) ;
112+ const { exports, execute } = await dynamicInstantiate ( url ) ;
83113 return createDynamicModule ( exports , url , ( reflect ) => {
84114 debug ( `Loading custom loader ${ url } ` ) ;
85115 execute ( reflect . exports ) ;
@@ -100,5 +130,6 @@ class Loader {
100130 return module . namespace ( ) ;
101131 }
102132}
133+ Loader . validFormats = [ 'esm' , 'cjs' , 'builtin' , 'addon' , 'json' , 'dynamic' ] ;
103134Object . setPrototypeOf ( Loader . prototype , null ) ;
104135module . exports = Loader ;
0 commit comments