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