@@ -6,15 +6,14 @@ require('internal/modules/cjs/loader');
6
6
const {
7
7
FunctionPrototypeBind,
8
8
ObjectSetPrototypeOf,
9
- SafeMap,
10
9
} = primordials ;
11
10
12
11
const {
13
12
ERR_INVALID_ARG_VALUE ,
14
13
ERR_INVALID_RETURN_PROPERTY ,
15
14
ERR_INVALID_RETURN_PROPERTY_VALUE ,
16
15
ERR_INVALID_RETURN_VALUE ,
17
- ERR_UNKNOWN_MODULE_FORMAT
16
+ ERR_UNKNOWN_MODULE_FORMAT ,
18
17
} = require ( 'internal/errors' ) . codes ;
19
18
const { URL , pathToFileURL } = require ( 'internal/url' ) ;
20
19
const { validateString } = require ( 'internal/validators' ) ;
@@ -28,11 +27,30 @@ const {
28
27
const { defaultGetFormat } = require ( 'internal/modules/esm/get_format' ) ;
29
28
const { defaultGetSource } = require (
30
29
'internal/modules/esm/get_source' ) ;
31
- const { defaultTransformSource } = require (
32
- 'internal/modules/esm/transform_source' ) ;
33
30
const { translators } = require (
34
31
'internal/modules/esm/translators' ) ;
35
32
const { getOptionValue } = require ( 'internal/options' ) ;
33
+ const {
34
+ isArrayBufferView,
35
+ isAnyArrayBuffer,
36
+ } = require ( 'internal/util/types' ) ;
37
+
38
+ let cwd ; // Initialized in importLoader
39
+
40
+ function validateSource ( source , hookName , allowString ) {
41
+ if ( allowString && typeof source === 'string' ) {
42
+ return ;
43
+ }
44
+ if ( isArrayBufferView ( source ) || isAnyArrayBuffer ( source ) ) {
45
+ return ;
46
+ }
47
+ throw new ERR_INVALID_RETURN_PROPERTY_VALUE (
48
+ `${ allowString ? 'string, ' : '' } array buffer, or typed array` ,
49
+ hookName ,
50
+ 'source' ,
51
+ source ,
52
+ ) ;
53
+ }
36
54
37
55
/* A Loader instance is used as the main entry point for loading ES modules.
38
56
* Currently, this is a singleton -- there is only one used for loading
@@ -46,33 +64,16 @@ class Loader {
46
64
// Registry of loaded modules, akin to `require.cache`
47
65
this . moduleMap = new ModuleMap ( ) ;
48
66
49
- // Map of already-loaded CJS modules to use
50
- this . cjsCache = new SafeMap ( ) ;
51
-
52
- // This hook is called before the first root module is imported. It's a
53
- // function that returns a piece of code that runs as a sloppy-mode script.
54
- // The script may evaluate to a function that can be called with a
55
- // `getBuiltin` helper that can be used to retrieve builtins.
56
- // If the hook returns `null` instead of a source string, it opts out of
57
- // running any preload code.
58
- // The preload code runs as soon as the hook module has finished evaluating.
59
- this . _getGlobalPreloadCode = null ;
60
- // The resolver has the signature
61
- // (specifier : string, parentURL : string, defaultResolve)
62
- // -> Promise<{ url : string }>
63
- // where defaultResolve is ModuleRequest.resolve (having the same
64
- // signature itself).
67
+ // Preload code is provided by loaders to be run after hook initialization.
68
+ this . globalPreloadCode = [ ] ;
69
+ // Loader resolve hook.
65
70
this . _resolve = defaultResolve ;
66
- // This hook is called after the module is resolved but before a translator
67
- // is chosen to load it; the format returned by this function is the name
68
- // of a translator.
71
+ // Loader getFormat hook.
69
72
this . _getFormat = defaultGetFormat ;
70
- // This hook is called just before the source code of an ES module file
71
- // is loaded.
73
+ // Loader getSource hook.
72
74
this . _getSource = defaultGetSource ;
73
- // This hook is called just after the source code of an ES module file
74
- // is loaded, but before anything is done with the string.
75
- this . _transformSource = defaultTransformSource ;
75
+ // Transform source hooks.
76
+ this . transformSourceHooks = [ ] ;
76
77
// The index for assigning unique URLs to anonymous module evaluation
77
78
this . evalIndex = 0 ;
78
79
}
@@ -83,7 +84,7 @@ class Loader {
83
84
validateString ( parentURL , 'parentURL' ) ;
84
85
85
86
const resolveResponse = await this . _resolve (
86
- specifier , { parentURL, conditions : DEFAULT_CONDITIONS } , defaultResolve ) ;
87
+ specifier , { parentURL, conditions : DEFAULT_CONDITIONS } ) ;
87
88
if ( typeof resolveResponse !== 'object' ) {
88
89
throw new ERR_INVALID_RETURN_VALUE (
89
90
'object' , 'loader resolve' , resolveResponse ) ;
@@ -98,8 +99,7 @@ class Loader {
98
99
}
99
100
100
101
async getFormat ( url ) {
101
- const getFormatResponse = await this . _getFormat (
102
- url , { } , defaultGetFormat ) ;
102
+ const getFormatResponse = await this . _getFormat ( url , { } ) ;
103
103
if ( typeof getFormatResponse !== 'object' ) {
104
104
throw new ERR_INVALID_RETURN_VALUE (
105
105
'object' , 'loader getFormat' , getFormatResponse ) ;
@@ -137,6 +137,22 @@ class Loader {
137
137
return format ;
138
138
}
139
139
140
+ async getSource ( url , format ) {
141
+ const { source : originalSource } = await this . _getSource ( url , { format } ) ;
142
+
143
+ const allowString = format !== 'wasm' ;
144
+ validateSource ( originalSource , 'getSource' , allowString ) ;
145
+
146
+ let source = originalSource ;
147
+ for ( let i = 0 ; i < this . transformSourceHooks . length ; i += 1 ) {
148
+ const hook = this . transformSourceHooks [ i ] ;
149
+ ( { source } = await hook ( source , { url, format, originalSource } ) ) ;
150
+ validateSource ( source , 'transformSource' , allowString ) ;
151
+ }
152
+
153
+ return source ;
154
+ }
155
+
140
156
async eval (
141
157
source ,
142
158
url = pathToFileURL ( `${ process . cwd ( ) } /[eval${ ++ this . evalIndex } ]` ) . href
@@ -166,72 +182,84 @@ class Loader {
166
182
return module . getNamespace ( ) ;
167
183
}
168
184
185
+ async importLoader ( specifier ) {
186
+ if ( cwd === undefined ) {
187
+ try {
188
+ // `process.cwd()` can fail.
189
+ cwd = process . cwd ( ) + '/' ;
190
+ } catch {
191
+ cwd = 'file:///' ;
192
+ }
193
+ cwd = pathToFileURL ( cwd ) . href ;
194
+ }
195
+
196
+ const { url } = await defaultResolve ( specifier , cwd ,
197
+ { conditions : DEFAULT_CONDITIONS } ) ;
198
+ const { format } = await defaultGetFormat ( url , { } ) ;
199
+
200
+ // !!! CRITICAL SECTION !!!
201
+ // NO AWAIT OPS BETWEEN HERE AND SETTING JOB IN MODULE MAP!
202
+ // YIELDING CONTROL COULD RESULT IN MAP BEING OVERRIDDEN!
203
+ let job = this . moduleMap . get ( url ) ;
204
+ if ( job === undefined ) {
205
+ if ( ! translators . has ( format ) )
206
+ throw new ERR_UNKNOWN_MODULE_FORMAT ( format ) ;
207
+
208
+ const loaderInstance = translators . get ( format ) ;
209
+
210
+ job = new ModuleJob ( this , url , loaderInstance , false , false ) ;
211
+ this . moduleMap . set ( url , job ) ;
212
+ // !!! END CRITICAL SECTION !!!
213
+ }
214
+
215
+ const { module } = await job . run ( ) ;
216
+ return module . getNamespace ( ) ;
217
+ }
218
+
169
219
hook ( hooks ) {
170
220
const {
171
221
resolve,
172
- dynamicInstantiate,
173
222
getFormat,
174
223
getSource,
175
- transformSource,
176
- getGlobalPreloadCode,
177
224
} = hooks ;
178
225
179
226
// Use .bind() to avoid giving access to the Loader instance when called.
180
227
if ( resolve !== undefined )
181
228
this . _resolve = FunctionPrototypeBind ( resolve , null ) ;
182
- if ( dynamicInstantiate !== undefined ) {
183
- process . emitWarning (
184
- 'The dynamicInstantiate loader hook has been removed.' ) ;
185
- }
186
229
if ( getFormat !== undefined ) {
187
230
this . _getFormat = FunctionPrototypeBind ( getFormat , null ) ;
188
231
}
189
232
if ( getSource !== undefined ) {
190
233
this . _getSource = FunctionPrototypeBind ( getSource , null ) ;
191
234
}
192
- if ( transformSource !== undefined ) {
193
- this . _transformSource = FunctionPrototypeBind ( transformSource , null ) ;
194
- }
195
- if ( getGlobalPreloadCode !== undefined ) {
196
- this . _getGlobalPreloadCode =
197
- FunctionPrototypeBind ( getGlobalPreloadCode , null ) ;
198
- }
199
235
}
200
236
201
237
runGlobalPreloadCode ( ) {
202
- if ( ! this . _getGlobalPreloadCode ) {
203
- return ;
204
- }
205
- const preloadCode = this . _getGlobalPreloadCode ( ) ;
206
- if ( preloadCode === null ) {
207
- return ;
208
- }
238
+ for ( let i = 0 ; i < this . globalPreloadCode . length ; i += 1 ) {
239
+ const preloadCode = this . globalPreloadCode [ i ] ;
209
240
210
- if ( typeof preloadCode !== 'string' ) {
211
- throw new ERR_INVALID_RETURN_VALUE (
212
- 'string' , 'loader getGlobalPreloadCode' , preloadCode ) ;
241
+ const { compileFunction } = require ( 'vm' ) ;
242
+ const preloadInit = compileFunction ( preloadCode , [ 'getBuiltin' ] , {
243
+ filename : '<preload>' ,
244
+ } ) ;
245
+ const { NativeModule } = require ( 'internal/bootstrap/loaders' ) ;
246
+
247
+ preloadInit . call ( globalThis , ( builtinName ) => {
248
+ if ( NativeModule . canBeRequiredByUsers ( builtinName ) ) {
249
+ return require ( builtinName ) ;
250
+ }
251
+ throw new ERR_INVALID_ARG_VALUE ( 'builtinName' , builtinName ) ;
252
+ } ) ;
213
253
}
214
- const { compileFunction } = require ( 'vm' ) ;
215
- const preloadInit = compileFunction ( preloadCode , [ 'getBuiltin' ] , {
216
- filename : '<preload>' ,
217
- } ) ;
218
- const { NativeModule } = require ( 'internal/bootstrap/loaders' ) ;
219
-
220
- preloadInit . call ( globalThis , ( builtinName ) => {
221
- if ( NativeModule . canBeRequiredByUsers ( builtinName ) ) {
222
- return require ( builtinName ) ;
223
- }
224
- throw new ERR_INVALID_ARG_VALUE ( 'builtinName' , builtinName ) ;
225
- } ) ;
226
254
}
227
255
228
256
async getModuleJob ( specifier , parentURL ) {
229
257
const url = await this . resolve ( specifier , parentURL ) ;
230
258
const format = await this . getFormat ( url ) ;
259
+
260
+ // !!! CRITICAL SECTION !!!
261
+ // NO AWAIT OPS BETWEEN HERE AND SETTING JOB IN MODULE MAP
231
262
let job = this . moduleMap . get ( url ) ;
232
- // CommonJS will set functions for lazy job evaluation.
233
- if ( typeof job === 'function' )
234
- this . moduleMap . set ( url , job = job ( ) ) ;
235
263
if ( job !== undefined )
236
264
return job ;
237
265
@@ -245,6 +273,8 @@ class Loader {
245
273
job = new ModuleJob ( this , url , loaderInstance , parentURL === undefined ,
246
274
inspectBrk ) ;
247
275
this . moduleMap . set ( url , job ) ;
276
+ // !!! END CRITICAL SECTION !!!
277
+
248
278
return job ;
249
279
}
250
280
}
0 commit comments