@@ -25,7 +25,7 @@ const {
25
25
const { BuiltinModule } = require ( 'internal/bootstrap/realm' ) ;
26
26
const assert = require ( 'internal/assert' ) ;
27
27
const { readFileSync } = require ( 'fs' ) ;
28
- const { dirname, extname, isAbsolute } = require ( 'path' ) ;
28
+ const { dirname, extname } = require ( 'path' ) ;
29
29
const {
30
30
assertBufferSource,
31
31
loadBuiltinModule,
@@ -41,6 +41,9 @@ const {
41
41
kModuleSource,
42
42
kModuleExport,
43
43
kModuleExportNames,
44
+ findLongestRegisteredExtension,
45
+ resolveForCJSWithHooks,
46
+ loadSourceForCJSWithHooks,
44
47
} = require ( 'internal/modules/cjs/loader' ) ;
45
48
const { fileURLToPath, pathToFileURL, URL } = require ( 'internal/url' ) ;
46
49
let debug = require ( 'internal/util/debuglog' ) . debuglog ( 'esm' , ( fn ) => {
@@ -170,17 +173,18 @@ const cjsCache = new SafeMap();
170
173
* @param {string } url - The URL of the module.
171
174
* @param {string } source - The source code of the module.
172
175
* @param {boolean } isMain - Whether the module is the main module.
176
+ * @param {string } format - Format of the module.
173
177
* @param {typeof loadCJSModule } [loadCJS=loadCJSModule] - The function to load the CommonJS module.
174
178
* @returns {ModuleWrap } The ModuleWrap object for the CommonJS module.
175
179
*/
176
- function createCJSModuleWrap ( url , source , isMain , loadCJS = loadCJSModule ) {
180
+ function createCJSModuleWrap ( url , source , isMain , format , loadCJS = loadCJSModule ) {
177
181
debug ( `Translating CJSModule ${ url } ` ) ;
178
182
179
183
const filename = urlToFilename ( url ) ;
180
184
// In case the source was not provided by the `load` step, we need fetch it now.
181
185
source = stringify ( source ?? getSource ( new URL ( url ) ) . source ) ;
182
186
183
- const { exportNames, module } = cjsPreparseModuleExports ( filename , source ) ;
187
+ const { exportNames, module } = cjsPreparseModuleExports ( filename , source , isMain , format ) ;
184
188
cjsCache . set ( url , module ) ;
185
189
const namesWithDefault = exportNames . has ( 'default' ) ?
186
190
[ ...exportNames ] : [ 'default' , ...exportNames ] ;
@@ -224,7 +228,7 @@ function createCJSModuleWrap(url, source, isMain, loadCJS = loadCJSModule) {
224
228
translators . set ( 'commonjs-sync' , function requireCommonJS ( url , source , isMain ) {
225
229
initCJSParseSync ( ) ;
226
230
227
- return createCJSModuleWrap ( url , source , isMain , ( module , source , url , filename , isMain ) => {
231
+ return createCJSModuleWrap ( url , source , isMain , 'commonjs' , ( module , source , url , filename , isMain ) => {
228
232
assert ( module === CJSModule . _cache [ filename ] ) ;
229
233
wrapModuleLoad ( filename , null , isMain ) ;
230
234
} ) ;
@@ -236,15 +240,15 @@ translators.set('require-commonjs', (url, source, isMain) => {
236
240
initCJSParseSync ( ) ;
237
241
assert ( cjsParse ) ;
238
242
239
- return createCJSModuleWrap ( url , source ) ;
243
+ return createCJSModuleWrap ( url , source , isMain , 'commonjs' ) ;
240
244
} ) ;
241
245
242
246
// Handle CommonJS modules referenced by `require` calls.
243
247
// This translator function must be sync, as `require` is sync.
244
248
translators . set ( 'require-commonjs-typescript' , ( url , source , isMain ) => {
245
249
assert ( cjsParse ) ;
246
250
const code = stripTypeScriptModuleTypes ( stringify ( source ) , url ) ;
247
- return createCJSModuleWrap ( url , code ) ;
251
+ return createCJSModuleWrap ( url , code , isMain , 'commonjs-typescript' ) ;
248
252
} ) ;
249
253
250
254
// Handle CommonJS modules referenced by `import` statements or expressions,
@@ -268,16 +272,17 @@ translators.set('commonjs', function commonjsStrategy(url, source, isMain) {
268
272
} catch {
269
273
// Continue regardless of error.
270
274
}
271
- return createCJSModuleWrap ( url , source , isMain , cjsLoader ) ;
275
+ return createCJSModuleWrap ( url , source , isMain , 'commonjs' , cjsLoader ) ;
272
276
} ) ;
273
277
274
278
/**
275
279
* Pre-parses a CommonJS module's exports and re-exports.
276
280
* @param {string } filename - The filename of the module.
277
281
* @param {string } [source] - The source code of the module.
282
+ * @param {boolean } isMain - Whether it is pre-parsing for the entry point.
283
+ * @param {string } format
278
284
*/
279
- function cjsPreparseModuleExports ( filename , source ) {
280
- // TODO: Do we want to keep hitting the user mutable CJS loader here?
285
+ function cjsPreparseModuleExports ( filename , source , isMain , format ) {
281
286
let module = CJSModule . _cache [ filename ] ;
282
287
if ( module && module [ kModuleExportNames ] !== undefined ) {
283
288
return { module, exportNames : module [ kModuleExportNames ] } ;
@@ -288,10 +293,15 @@ function cjsPreparseModuleExports(filename, source) {
288
293
module . filename = filename ;
289
294
module . paths = CJSModule . _nodeModulePaths ( module . path ) ;
290
295
module [ kIsCachedByESMLoader ] = true ;
291
- module [ kModuleSource ] = source ;
292
296
CJSModule . _cache [ filename ] = module ;
293
297
}
294
298
299
+ if ( source === undefined ) {
300
+ ( { source } = loadSourceForCJSWithHooks ( module , filename , format ) ) ;
301
+ }
302
+ module [ kModuleSource ] = source ;
303
+
304
+ debug ( `Preparsing exports of ${ filename } ` ) ;
295
305
let exports , reexports ;
296
306
try {
297
307
( { exports, reexports } = cjsParse ( source || '' ) ) ;
@@ -305,34 +315,27 @@ function cjsPreparseModuleExports(filename, source) {
305
315
// Set first for cycles.
306
316
module [ kModuleExportNames ] = exportNames ;
307
317
318
+ // If there are any re-exports e.g. `module.exports = { ...require(...) }`,
319
+ // pre-parse the dependencies to find transitively exported names.
308
320
if ( reexports . length ) {
309
- module . filename = filename ;
310
- module . paths = CJSModule . _nodeModulePaths ( module . path ) ;
321
+ module . filename ??= filename ;
322
+ module . paths ??= CJSModule . _nodeModulePaths ( dirname ( filename ) ) ;
323
+
311
324
for ( let i = 0 ; i < reexports . length ; i ++ ) {
325
+ debug ( `Preparsing re-exports of '${ filename } '` ) ;
312
326
const reexport = reexports [ i ] ;
313
327
let resolved ;
328
+ let format ;
314
329
try {
315
- // TODO: this should be calling the `resolve` hook chain instead.
316
- // Doing so would mean dropping support for CJS in the loader thread, as
317
- // this call needs to be sync from the perspective of the main thread,
318
- // which we can do via HooksProxy and Atomics, but we can't do within
319
- // the loaders thread. Until this is done, the lexer will use the
320
- // monkey-patchable CJS loader to get the path to the module file to
321
- // load (which may or may not be aligned with the URL that the `resolve`
322
- // hook have returned).
323
- resolved = CJSModule . _resolveFilename ( reexport , module ) ;
324
- } catch {
330
+ ( { format, filename : resolved } = resolveForCJSWithHooks ( reexport , module , false ) ) ;
331
+ } catch ( e ) {
332
+ debug ( `Failed to resolve '${ reexport } ', skipping` , e ) ;
325
333
continue ;
326
334
}
327
- // TODO: this should be calling the `load` hook chain and check if it returns
328
- // `format: 'commonjs'` instead of relying on file extensions.
329
- const ext = extname ( resolved ) ;
330
- if ( ( ext === '.js' || ext === '.cjs' || ! CJSModule . _extensions [ ext ] ) &&
331
- isAbsolute ( resolved ) ) {
332
- // TODO: this should be calling the `load` hook chain to get the source
333
- // (and fallback to reading the FS only if the source is nullish).
334
- const source = readFileSync ( resolved , 'utf-8' ) ;
335
- const { exportNames : reexportNames } = cjsPreparseModuleExports ( resolved , source ) ;
335
+
336
+ if ( format === 'commonjs' ||
337
+ ( ! BuiltinModule . normalizeRequirableId ( resolved ) && findLongestRegisteredExtension ( resolved ) === '.js' ) ) {
338
+ const { exportNames : reexportNames } = cjsPreparseModuleExports ( resolved , undefined , false , format ) ;
336
339
for ( const name of reexportNames ) {
337
340
exportNames . add ( name ) ;
338
341
}
0 commit comments