2
2
3
3
const path = require ( 'path' ) ;
4
4
const { getURLFromFilePath, URL } = require ( 'internal/url' ) ;
5
-
6
- const {
7
- createDynamicModule,
8
- setImportModuleDynamicallyCallback
9
- } = require ( 'internal/loader/ModuleWrap' ) ;
5
+ const errors = require ( 'internal/errors' ) ;
10
6
11
7
const ModuleMap = require ( 'internal/loader/ModuleMap' ) ;
12
8
const 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
+
15
15
const debug = require ( 'util' ) . debuglog ( 'esm' ) ;
16
16
17
17
// Returns a file URL for the current working directory.
@@ -40,105 +40,101 @@ function normalizeReferrerURL(referrer) {
40
40
* the main module and everything in its dependency graph. */
41
41
class Loader {
42
42
constructor ( base = getURLStringForCwd ( ) ) {
43
- if ( typeof base !== 'string' ) {
43
+ if ( typeof base !== 'string' )
44
44
throw new errors . TypeError ( 'ERR_INVALID_ARG_TYPE' , 'base' , 'string' ) ;
45
- }
46
45
47
- this . moduleMap = new ModuleMap ( ) ;
48
46
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
+
49
55
// The resolver has the signature
50
56
// (specifier : string, parentURL : string, defaultResolve)
51
- // -> Promise<{ url : string,
52
- // format: anything in Loader.validFormats }>
57
+ // -> Promise<{ url : string, format: string }>
53
58
// where defaultResolve is ModuleRequest.resolve (having the same
54
59
// signature itself).
55
60
// If `.format` on the returned value is 'dynamic', .dynamicInstantiate
56
61
// 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
60
65
// (url : string) -> Promise<{ exports: { ... }, execute: function }>
61
66
// Where `exports` is an object whose property names define the exported
62
67
// names of the generated module. `execute` is a function that receives
63
68
// an object with the same keys as `exports`, whose values are get/set
64
69
// 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 ;
73
71
}
74
72
75
- // Typechecking wrapper around .resolver().
76
73
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' ) ;
84
76
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 ) ;
89
79
90
- if ( typeof url !== 'string' ) {
80
+ if ( typeof url !== 'string' )
91
81
throw new errors . TypeError ( 'ERR_INVALID_ARG_TYPE' , 'url' , 'string' ) ;
92
- }
93
82
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' )
95
87
return { url : `node:${ url } ` , format } ;
96
- }
97
88
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:' ) ;
106
91
107
92
return { url, format } ;
108
93
}
109
94
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
+
111
109
async getModuleJob ( specifier , parentURL = this . base ) {
112
110
const { url, format } = await this . resolve ( specifier , parentURL ) ;
113
111
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 ) ;
134
133
}
135
- return job ;
136
- }
137
134
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 ;
142
138
}
143
139
144
140
static registerImportDynamicallyCallback ( loader ) {
@@ -147,6 +143,6 @@ class Loader {
147
143
} ) ;
148
144
}
149
145
}
150
- Loader . validFormats = [ 'esm' , 'cjs' , 'builtin' , 'addon' , 'json' , 'dynamic' ] ;
146
+
151
147
Object . setPrototypeOf ( Loader . prototype , null ) ;
152
148
module . exports = Loader ;
0 commit comments