22
33const path = require ( 'path' ) ;
44const { getURLFromFilePath, URL } = require ( 'internal/url' ) ;
5-
6- const {
7- createDynamicModule,
8- setImportModuleDynamicallyCallback
9- } = require ( 'internal/loader/ModuleWrap' ) ;
5+ const errors = require ( 'internal/errors' ) ;
106
117const ModuleMap = require ( 'internal/loader/ModuleMap' ) ;
128const ModuleJob = require ( 'internal/loader/ModuleJob' ) ;
13- const ModuleRequest = require ( 'internal/loader/ModuleRequest' ) ;
14- const errors = require ( 'internal/errors' ) ;
9+ const defaultResolve = require ( 'internal/loader/DefaultResolve' ) ;
10+ const createDynamicModule = require ( 'internal/loader/CreateDynamicModule' ) ;
11+ const translators = require ( 'internal/loader/Translators' ) ;
12+ const { setImportModuleDynamicallyCallback } = internalBinding ( 'module_wrap' ) ;
13+ const FunctionBind = Function . call . bind ( Function . prototype . bind ) ;
14+
1515const debug = require ( 'util' ) . debuglog ( 'esm' ) ;
1616
1717// Returns a file URL for the current working directory.
@@ -40,105 +40,101 @@ function normalizeReferrerURL(referrer) {
4040 * the main module and everything in its dependency graph. */
4141class Loader {
4242 constructor ( base = getURLStringForCwd ( ) ) {
43- if ( typeof base !== 'string' ) {
43+ if ( typeof base !== 'string' )
4444 throw new errors . TypeError ( 'ERR_INVALID_ARG_TYPE' , 'base' , 'string' ) ;
45- }
4645
47- this . moduleMap = new ModuleMap ( ) ;
4846 this . base = base ;
47+
48+ // methods which translate input code or other information
49+ // into es modules
50+ this . translators = translators ;
51+
52+ // registry of loaded modules, akin to `require.cache`
53+ this . moduleMap = new ModuleMap ( ) ;
54+
4955 // The resolver has the signature
5056 // (specifier : string, parentURL : string, defaultResolve)
51- // -> Promise<{ url : string,
52- // format: anything in Loader.validFormats }>
57+ // -> Promise<{ url : string, format: string }>
5358 // where defaultResolve is ModuleRequest.resolve (having the same
5459 // signature itself).
5560 // If `.format` on the returned value is 'dynamic', .dynamicInstantiate
5661 // will be used as described below.
57- this . resolver = ModuleRequest . resolve ;
58- // This hook is only called when resolve(...).format is 'dynamic' and has
59- // the signature
62+ this . _resolve = defaultResolve ;
63+ // This hook is only called when resolve(...).format is 'dynamic' and
64+ // has the signature
6065 // (url : string) -> Promise<{ exports: { ... }, execute: function }>
6166 // Where `exports` is an object whose property names define the exported
6267 // names of the generated module. `execute` is a function that receives
6368 // an object with the same keys as `exports`, whose values are get/set
6469 // functions for the actual exported values.
65- this . dynamicInstantiate = undefined ;
66- }
67-
68- hook ( { resolve = ModuleRequest . resolve , dynamicInstantiate } ) {
69- // Use .bind() to avoid giving access to the Loader instance when it is
70- // called as this.resolver(...);
71- this . resolver = resolve . bind ( null ) ;
72- this . dynamicInstantiate = dynamicInstantiate ;
70+ this . _dynamicInstantiate = undefined ;
7371 }
7472
75- // Typechecking wrapper around .resolver().
7673 async resolve ( specifier , parentURL = this . base ) {
77- if ( typeof parentURL !== 'string' ) {
78- throw new errors . TypeError ( 'ERR_INVALID_ARG_TYPE' ,
79- 'parentURL' , 'string' ) ;
80- }
81-
82- const { url, format } = await this . resolver ( specifier , parentURL ,
83- ModuleRequest . resolve ) ;
74+ if ( typeof parentURL !== 'string' )
75+ throw new errors . TypeError ( 'ERR_INVALID_ARG_TYPE' , 'parentURL' , 'string' ) ;
8476
85- if ( ! Loader . validFormats . includes ( format ) ) {
86- throw new errors . TypeError ( 'ERR_INVALID_ARG_TYPE' , 'format' ,
87- Loader . validFormats ) ;
88- }
77+ const { url, format } =
78+ await this . _resolve ( specifier , parentURL , defaultResolve ) ;
8979
90- if ( typeof url !== 'string' ) {
80+ if ( typeof url !== 'string' )
9181 throw new errors . TypeError ( 'ERR_INVALID_ARG_TYPE' , 'url' , 'string' ) ;
92- }
9382
94- if ( format === 'builtin' ) {
83+ if ( typeof format !== 'string' )
84+ throw new errors . TypeError ( 'ERR_INVALID_ARG_TYPE' , 'format' , 'string' ) ;
85+
86+ if ( format === 'builtin' )
9587 return { url : `node:${ url } ` , format } ;
96- }
9788
98- if ( format !== 'dynamic' ) {
99- if ( ! ModuleRequest . loaders . has ( format ) ) {
100- throw new errors . Error ( 'ERR_UNKNOWN_MODULE_FORMAT' , format ) ;
101- }
102- if ( ! url . startsWith ( 'file:' ) ) {
103- throw new errors . Error ( 'ERR_INVALID_PROTOCOL' , url , 'file:' ) ;
104- }
105- }
89+ if ( format !== 'dynamic' && ! url . startsWith ( 'file:' ) )
90+ throw new errors . Error ( 'ERR_INVALID_PROTOCOL' , url , 'file:' ) ;
10691
10792 return { url, format } ;
10893 }
10994
110- // May create a new ModuleJob instance if one did not already exist.
95+ async import ( specifier , parent = this . base ) {
96+ const job = await this . getModuleJob ( specifier , parent ) ;
97+ const module = await job . run ( ) ;
98+ return module . namespace ( ) ;
99+ }
100+
101+ hook ( { resolve, dynamicInstantiate } ) {
102+ // Use .bind() to avoid giving access to the Loader instance when called.
103+ if ( resolve !== undefined )
104+ this . _resolve = FunctionBind ( resolve , null ) ;
105+ if ( dynamicInstantiate !== undefined )
106+ this . _dynamicInstantiate = FunctionBind ( dynamicInstantiate , null ) ;
107+ }
108+
111109 async getModuleJob ( specifier , parentURL = this . base ) {
112110 const { url, format } = await this . resolve ( specifier , parentURL ) ;
113111 let job = this . moduleMap . get ( url ) ;
114- if ( job === undefined ) {
115- let loaderInstance ;
116- if ( format === 'dynamic' ) {
117- const { dynamicInstantiate } = this ;
118- if ( typeof dynamicInstantiate !== 'function' ) {
119- throw new errors . Error ( 'ERR_MISSING_DYNAMIC_INSTANTIATE_HOOK' ) ;
120- }
121-
122- loaderInstance = async ( url ) => {
123- const { exports, execute } = await dynamicInstantiate ( url ) ;
124- return createDynamicModule ( exports , url , ( reflect ) => {
125- debug ( `Loading custom loader ${ url } ` ) ;
126- execute ( reflect . exports ) ;
127- } ) ;
128- } ;
129- } else {
130- loaderInstance = ModuleRequest . loaders . get ( format ) ;
131- }
132- job = new ModuleJob ( this , url , loaderInstance ) ;
133- this . moduleMap . set ( url , job ) ;
112+ if ( job !== undefined )
113+ return job ;
114+
115+ let loaderInstance ;
116+ if ( format === 'dynamic' ) {
117+ if ( typeof this . _dynamicInstantiate !== 'function' )
118+ throw new errors . Error ( 'ERR_MISSING_DYNAMIC_INTSTANTIATE_HOOK' ) ;
119+
120+ loaderInstance = async ( url ) => {
121+ debug ( `Translating dynamic ${ url } ` ) ;
122+ const { exports, execute } = await this . _dynamicInstantiate ( url ) ;
123+ return createDynamicModule ( exports , url , ( reflect ) => {
124+ debug ( `Loading dynamic ${ url } ` ) ;
125+ execute ( reflect . exports ) ;
126+ } ) ;
127+ } ;
128+ } else {
129+ if ( ! translators . has ( format ) )
130+ throw new errors . RangeError ( 'ERR_UNKNOWN_MODULE_FORMAT' , format ) ;
131+
132+ loaderInstance = translators . get ( format ) ;
134133 }
135- return job ;
136- }
137134
138- async import ( specifier , parentURL = this . base ) {
139- const job = await this . getModuleJob ( specifier , parentURL ) ;
140- const module = await job . run ( ) ;
141- return module . namespace ( ) ;
135+ job = new ModuleJob ( this , url , loaderInstance ) ;
136+ this . moduleMap . set ( url , job ) ;
137+ return job ;
142138 }
143139
144140 static registerImportDynamicallyCallback ( loader ) {
@@ -147,6 +143,6 @@ class Loader {
147143 } ) ;
148144 }
149145}
150- Loader . validFormats = [ 'esm' , 'cjs' , 'builtin' , 'addon' , 'json' , 'dynamic' ] ;
146+
151147Object . setPrototypeOf ( Loader . prototype , null ) ;
152148module . exports = Loader ;
0 commit comments