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