@@ -140,168 +140,182 @@ let internalBinding;
140
140
} ;
141
141
}
142
142
143
- // Think of this as module.exports in this file even though it is not
144
- // written in CommonJS style.
145
- const loaderExports = {
146
- internalBinding,
147
- NativeModule,
148
- require : nativeModuleRequire
149
- } ;
150
-
151
143
const loaderId = 'internal/bootstrap/loaders' ;
152
-
153
- // Set up NativeModule.
154
- function NativeModule ( id ) {
155
- this . filename = `${ id } .js` ;
156
- this . id = id ;
157
- this . exports = { } ;
158
- this . module = undefined ;
159
- this . exportKeys = undefined ;
160
- this . loaded = false ;
161
- this . loading = false ;
162
- this . canBeRequiredByUsers = ! id . startsWith ( 'internal/' ) ;
163
- }
164
-
165
- // To be called during pre-execution when --expose-internals is on.
166
- // Enables the user-land module loader to access internal modules.
167
- NativeModule . exposeInternals = function ( ) {
168
- for ( const [ id , mod ] of NativeModule . map ) {
169
- // Do not expose this to user land even with --expose-internals.
170
- if ( id !== loaderId ) {
171
- mod . canBeRequiredByUsers = true ;
172
- }
173
- }
174
- } ;
175
-
176
144
const {
177
145
moduleIds,
178
146
compileFunction
179
147
} = internalBinding ( 'native_module' ) ;
180
148
181
- NativeModule . map = new Map ( ) ;
182
- for ( let i = 0 ; i < moduleIds . length ; ++ i ) {
183
- const id = moduleIds [ i ] ;
184
- const mod = new NativeModule ( id ) ;
185
- NativeModule . map . set ( id , mod ) ;
186
- }
149
+ const getOwn = ( target , property , receiver ) => {
150
+ return ObjectPrototypeHasOwnProperty ( target , property ) ?
151
+ ReflectGet ( target , property , receiver ) :
152
+ undefined ;
153
+ } ;
187
154
188
- function nativeModuleRequire ( id ) {
189
- if ( id === loaderId ) {
190
- return loaderExports ;
155
+ /**
156
+ * An internal abstraction for the built-in JavaScript modules of Node.js.
157
+ * Be careful not to expose this to user land unless --expose-internals is
158
+ * used, in which case there is no compatibility guarantee about this class.
159
+ */
160
+ class NativeModule {
161
+ /**
162
+ * A map from the module IDs to the module instances.
163
+ * @type {Map<string, NativeModule> }
164
+ */
165
+ static map = new Map ( moduleIds . map ( ( id ) => [ id , new NativeModule ( id ) ] ) ) ;
166
+
167
+ constructor ( id ) {
168
+ this . filename = `${ id } .js` ;
169
+ this . id = id ;
170
+ this . canBeRequiredByUsers = ! id . startsWith ( 'internal/' ) ;
171
+
172
+ // The CJS exports object of the module.
173
+ this . exports = { } ;
174
+ // States used to work around circular dependencies.
175
+ this . loaded = false ;
176
+ this . loading = false ;
177
+
178
+ // The following properties are used by the ESM implementation and only
179
+ // initialized when the native module is loaded by users.
180
+ /**
181
+ * The C++ ModuleWrap binding used to interface with the ESM implementation.
182
+ * @type {ModuleWrap|undefined }
183
+ */
184
+ this . module = undefined ;
185
+ /**
186
+ * Exported names for the ESM imports.
187
+ * @type {string[]|undefined }
188
+ */
189
+ this . exportKeys = undefined ;
191
190
}
192
191
193
- const mod = NativeModule . map . get ( id ) ;
194
- // Can't load the internal errors module from here, have to use a raw error.
195
- // eslint-disable-next-line no-restricted-syntax
196
- if ( ! mod ) throw new TypeError ( `Missing internal module '${ id } '` ) ;
197
- return mod . compile ( ) ;
198
- }
192
+ // To be called during pre-execution when --expose-internals is on.
193
+ // Enables the user-land module loader to access internal modules.
194
+ static exposeInternals ( ) {
195
+ for ( const [ id , mod ] of NativeModule . map ) {
196
+ // Do not expose this to user land even with --expose-internals.
197
+ if ( id !== loaderId ) {
198
+ mod . canBeRequiredByUsers = true ;
199
+ }
200
+ }
201
+ }
199
202
200
- NativeModule . exists = function ( id ) {
201
- return NativeModule . map . has ( id ) ;
202
- } ;
203
+ static exists ( id ) {
204
+ return NativeModule . map . has ( id ) ;
205
+ }
203
206
204
- NativeModule . canBeRequiredByUsers = function ( id ) {
205
- const mod = NativeModule . map . get ( id ) ;
206
- return mod && mod . canBeRequiredByUsers ;
207
- } ;
207
+ static canBeRequiredByUsers ( id ) {
208
+ const mod = NativeModule . map . get ( id ) ;
209
+ return mod && mod . canBeRequiredByUsers ;
210
+ }
208
211
209
- // Allow internal modules from dependencies to require
210
- // other modules from dependencies by providing fallbacks.
211
- function requireWithFallbackInDeps ( request ) {
212
- if ( ! NativeModule . map . has ( request ) ) {
213
- request = `internal/deps/${ request } ` ;
212
+ // Used by user-land module loaders to compile and load builtins.
213
+ compileForPublicLoader ( needToSyncExports ) {
214
+ if ( ! this . canBeRequiredByUsers ) {
215
+ // No code because this is an assertion against bugs
216
+ // eslint-disable-next-line no-restricted-syntax
217
+ throw new Error ( `Should not compile ${ this . id } for public use` ) ;
218
+ }
219
+ this . compileForInternalLoader ( ) ;
220
+ if ( needToSyncExports ) {
221
+ if ( ! this . exportKeys ) {
222
+ // When using --expose-internals, we do not want to reflect the named
223
+ // exports from core modules as this can trigger unnecessary getters.
224
+ const internal = this . id . startsWith ( 'internal/' ) ;
225
+ this . exportKeys = internal ? [ ] : ObjectKeys ( this . exports ) ;
226
+ }
227
+ this . getESMFacade ( ) ;
228
+ this . syncExports ( ) ;
229
+ }
230
+ return this . exports ;
214
231
}
215
- return nativeModuleRequire ( request ) ;
216
- }
217
232
218
- // This is exposed for public loaders
219
- NativeModule . prototype . compileForPublicLoader = function ( needToSyncExports ) {
220
- if ( ! this . canBeRequiredByUsers ) {
221
- // No code because this is an assertion against bugs
222
- // eslint-disable-next-line no-restricted-syntax
223
- throw new Error ( `Should not compile ${ this . id } for public use` ) ;
233
+ getESMFacade ( ) {
234
+ if ( this . module ) return this . module ;
235
+ const { ModuleWrap } = internalBinding ( 'module_wrap' ) ;
236
+ const url = `node:${ this . id } ` ;
237
+ const nativeModule = this ;
238
+ this . module = new ModuleWrap (
239
+ url , undefined , [ ...this . exportKeys , 'default' ] ,
240
+ function ( ) {
241
+ nativeModule . syncExports ( ) ;
242
+ this . setExport ( 'default' , nativeModule . exports ) ;
243
+ } ) ;
244
+ // Ensure immediate sync execution to capture exports now
245
+ this . module . instantiate ( ) ;
246
+ this . module . evaluate ( - 1 , false ) ;
247
+ return this . module ;
224
248
}
225
- this . compile ( ) ;
226
- if ( needToSyncExports ) {
227
- if ( ! this . exportKeys ) {
228
- // When using --expose-internals, we do not want to reflect the named
229
- // exports from core modules as this can trigger unnecessary getters.
230
- const internal = this . id . startsWith ( 'internal/' ) ;
231
- this . exportKeys = internal ? [ ] : ObjectKeys ( this . exports ) ;
249
+
250
+ // Provide named exports for all builtin libraries so that the libraries
251
+ // may be imported in a nicer way for ESM users. The default export is left
252
+ // as the entire namespace (module.exports) and updates when this function is
253
+ // called so that APMs and other behavior are supported.
254
+ syncExports ( ) {
255
+ const names = this . exportKeys ;
256
+ if ( this . module ) {
257
+ for ( let i = 0 ; i < names . length ; i ++ ) {
258
+ const exportName = names [ i ] ;
259
+ if ( exportName === 'default' ) continue ;
260
+ this . module . setExport ( exportName ,
261
+ getOwn ( this . exports , exportName , this . exports ) ) ;
262
+ }
232
263
}
233
- this . getESMFacade ( ) ;
234
- this . syncExports ( ) ;
235
264
}
236
- return this . exports ;
237
- } ;
238
265
239
- const getOwn = ( target , property , receiver ) => {
240
- return ObjectPrototypeHasOwnProperty ( target , property ) ?
241
- ReflectGet ( target , property , receiver ) :
242
- undefined ;
243
- } ;
266
+ compileForInternalLoader ( ) {
267
+ if ( this . loaded || this . loading ) {
268
+ return this . exports ;
269
+ }
244
270
245
- NativeModule . prototype . getURL = function ( ) {
246
- return `node:${ this . id } ` ;
247
- } ;
271
+ const id = this . id ;
272
+ this . loading = true ;
248
273
249
- NativeModule . prototype . getESMFacade = function ( ) {
250
- if ( this . module ) return this . module ;
251
- const { ModuleWrap } = internalBinding ( 'module_wrap' ) ;
252
- const url = this . getURL ( ) ;
253
- const nativeModule = this ;
254
- this . module = new ModuleWrap (
255
- url , undefined , [ ...this . exportKeys , 'default' ] ,
256
- function ( ) {
257
- nativeModule . syncExports ( ) ;
258
- this . setExport ( 'default' , nativeModule . exports ) ;
259
- } ) ;
260
- // Ensure immediate sync execution to capture exports now
261
- this . module . instantiate ( ) ;
262
- this . module . evaluate ( - 1 , false ) ;
263
- return this . module ;
264
- } ;
274
+ try {
275
+ const requireFn = this . id . startsWith ( 'internal/deps/' ) ?
276
+ requireWithFallbackInDeps : nativeModuleRequire ;
265
277
266
- // Provide named exports for all builtin libraries so that the libraries
267
- // may be imported in a nicer way for ESM users. The default export is left
268
- // as the entire namespace (module.exports) and updates when this function is
269
- // called so that APMs and other behavior are supported.
270
- NativeModule . prototype . syncExports = function ( ) {
271
- const names = this . exportKeys ;
272
- if ( this . module ) {
273
- for ( let i = 0 ; i < names . length ; i ++ ) {
274
- const exportName = names [ i ] ;
275
- if ( exportName === 'default' ) continue ;
276
- this . module . setExport ( exportName ,
277
- getOwn ( this . exports , exportName , this . exports ) ) ;
278
+ const fn = compileFunction ( id ) ;
279
+ fn ( this . exports , requireFn , this , process , internalBinding , primordials ) ;
280
+
281
+ this . loaded = true ;
282
+ } finally {
283
+ this . loading = false ;
278
284
}
279
- }
280
- } ;
281
285
282
- NativeModule . prototype . compile = function ( ) {
283
- if ( this . loaded || this . loading ) {
286
+ moduleLoadList . push ( `NativeModule ${ id } ` ) ;
284
287
return this . exports ;
285
288
}
289
+ }
286
290
287
- const id = this . id ;
288
- this . loading = true ;
291
+ // Think of this as module.exports in this file even though it is not
292
+ // written in CommonJS style.
293
+ const loaderExports = {
294
+ internalBinding,
295
+ NativeModule,
296
+ require : nativeModuleRequire
297
+ } ;
289
298
290
- try {
291
- const requireFn = this . id . startsWith ( 'internal/deps/' ) ?
292
- requireWithFallbackInDeps : nativeModuleRequire ;
299
+ function nativeModuleRequire ( id ) {
300
+ if ( id === loaderId ) {
301
+ return loaderExports ;
302
+ }
293
303
294
- const fn = compileFunction ( id ) ;
295
- fn ( this . exports , requireFn , this , process , internalBinding , primordials ) ;
304
+ const mod = NativeModule . map . get ( id ) ;
305
+ // Can't load the internal errors module from here, have to use a raw error.
306
+ // eslint-disable-next-line no-restricted-syntax
307
+ if ( ! mod ) throw new TypeError ( `Missing internal module '${ id } '` ) ;
308
+ return mod . compileForInternalLoader ( ) ;
309
+ }
296
310
297
- this . loaded = true ;
298
- } finally {
299
- this . loading = false ;
311
+ // Allow internal modules from dependencies to require
312
+ // other modules from dependencies by providing fallbacks.
313
+ function requireWithFallbackInDeps ( request ) {
314
+ if ( ! NativeModule . map . has ( request ) ) {
315
+ request = `internal/deps/${ request } ` ;
300
316
}
317
+ return nativeModuleRequire ( request ) ;
318
+ }
301
319
302
- moduleLoadList . push ( `NativeModule ${ id } ` ) ;
303
- return this . exports ;
304
- } ;
305
-
306
- // This will be passed to internal/bootstrap/node.js.
320
+ // Pass the exports back to C++ land for C++ internals to use.
307
321
return loaderExports ;
0 commit comments