@@ -25,7 +25,7 @@ const {
2525const { BuiltinModule } = require ( 'internal/bootstrap/realm' ) ;
2626const assert = require ( 'internal/assert' ) ;
2727const { readFileSync } = require ( 'fs' ) ;
28- const { dirname, extname, isAbsolute } = require ( 'path' ) ;
28+ const { dirname, extname } = require ( 'path' ) ;
2929const {
3030 assertBufferSource,
3131 loadBuiltinModule,
@@ -41,6 +41,9 @@ const {
4141 kModuleSource,
4242 kModuleExport,
4343 kModuleExportNames,
44+ findLongestRegisteredExtension,
45+ resolveForCJSWithHooks,
46+ loadSourceForCJSWithHooks,
4447} = require ( 'internal/modules/cjs/loader' ) ;
4548const { fileURLToPath, pathToFileURL, URL } = require ( 'internal/url' ) ;
4649let debug = require ( 'internal/util/debuglog' ) . debuglog ( 'esm' , ( fn ) => {
@@ -170,17 +173,18 @@ const cjsCache = new SafeMap();
170173 * @param {string } url - The URL of the module.
171174 * @param {string } source - The source code of the module.
172175 * @param {boolean } isMain - Whether the module is the main module.
176+ * @param {string } format - Format of the module.
173177 * @param {typeof loadCJSModule } [loadCJS=loadCJSModule] - The function to load the CommonJS module.
174178 * @returns {ModuleWrap } The ModuleWrap object for the CommonJS module.
175179 */
176- function createCJSModuleWrap ( url , source , isMain , loadCJS = loadCJSModule ) {
180+ function createCJSModuleWrap ( url , source , isMain , format , loadCJS = loadCJSModule ) {
177181 debug ( `Translating CJSModule ${ url } ` ) ;
178182
179183 const filename = urlToFilename ( url ) ;
180184 // In case the source was not provided by the `load` step, we need fetch it now.
181185 source = stringify ( source ?? getSource ( new URL ( url ) ) . source ) ;
182186
183- const { exportNames, module } = cjsPreparseModuleExports ( filename , source ) ;
187+ const { exportNames, module } = cjsPreparseModuleExports ( filename , source , isMain , format ) ;
184188 cjsCache . set ( url , module ) ;
185189 const namesWithDefault = exportNames . has ( 'default' ) ?
186190 [ ...exportNames ] : [ 'default' , ...exportNames ] ;
@@ -224,7 +228,7 @@ function createCJSModuleWrap(url, source, isMain, loadCJS = loadCJSModule) {
224228translators . set ( 'commonjs-sync' , function requireCommonJS ( url , source , isMain ) {
225229 initCJSParseSync ( ) ;
226230
227- return createCJSModuleWrap ( url , source , isMain , ( module , source , url , filename , isMain ) => {
231+ return createCJSModuleWrap ( url , source , isMain , 'commonjs' , ( module , source , url , filename , isMain ) => {
228232 assert ( module === CJSModule . _cache [ filename ] ) ;
229233 wrapModuleLoad ( filename , null , isMain ) ;
230234 } ) ;
@@ -236,15 +240,15 @@ translators.set('require-commonjs', (url, source, isMain) => {
236240 initCJSParseSync ( ) ;
237241 assert ( cjsParse ) ;
238242
239- return createCJSModuleWrap ( url , source ) ;
243+ return createCJSModuleWrap ( url , source , isMain , 'commonjs' ) ;
240244} ) ;
241245
242246// Handle CommonJS modules referenced by `require` calls.
243247// This translator function must be sync, as `require` is sync.
244248translators . set ( 'require-commonjs-typescript' , ( url , source , isMain ) => {
245249 assert ( cjsParse ) ;
246250 const code = stripTypeScriptModuleTypes ( stringify ( source ) , url ) ;
247- return createCJSModuleWrap ( url , code ) ;
251+ return createCJSModuleWrap ( url , code , isMain , 'commonjs-typescript' ) ;
248252} ) ;
249253
250254// Handle CommonJS modules referenced by `import` statements or expressions,
@@ -268,16 +272,17 @@ translators.set('commonjs', function commonjsStrategy(url, source, isMain) {
268272 } catch {
269273 // Continue regardless of error.
270274 }
271- return createCJSModuleWrap ( url , source , isMain , cjsLoader ) ;
275+ return createCJSModuleWrap ( url , source , isMain , 'commonjs' , cjsLoader ) ;
272276} ) ;
273277
274278/**
275279 * Pre-parses a CommonJS module's exports and re-exports.
276280 * @param {string } filename - The filename of the module.
277281 * @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
278284 */
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 ) {
281286 let module = CJSModule . _cache [ filename ] ;
282287 if ( module && module [ kModuleExportNames ] !== undefined ) {
283288 return { module, exportNames : module [ kModuleExportNames ] } ;
@@ -288,10 +293,15 @@ function cjsPreparseModuleExports(filename, source) {
288293 module . filename = filename ;
289294 module . paths = CJSModule . _nodeModulePaths ( module . path ) ;
290295 module [ kIsCachedByESMLoader ] = true ;
291- module [ kModuleSource ] = source ;
292296 CJSModule . _cache [ filename ] = module ;
293297 }
294298
299+ if ( source === undefined ) {
300+ ( { source } = loadSourceForCJSWithHooks ( module , filename , format ) ) ;
301+ }
302+ module [ kModuleSource ] = source ;
303+
304+ debug ( `Preparsing exports of ${ filename } ` ) ;
295305 let exports , reexports ;
296306 try {
297307 ( { exports, reexports } = cjsParse ( source || '' ) ) ;
@@ -305,34 +315,27 @@ function cjsPreparseModuleExports(filename, source) {
305315 // Set first for cycles.
306316 module [ kModuleExportNames ] = exportNames ;
307317
318+ // If there are any re-exports e.g. `module.exports = { ...require(...) }`,
319+ // pre-parse the dependencies to find transitively exported names.
308320 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+
311324 for ( let i = 0 ; i < reexports . length ; i ++ ) {
325+ debug ( `Preparsing re-exports of '${ filename } '` ) ;
312326 const reexport = reexports [ i ] ;
313327 let resolved ;
328+ let format ;
314329 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 ) ;
325333 continue ;
326334 }
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 ) ;
336339 for ( const name of reexportNames ) {
337340 exportNames . add ( name ) ;
338341 }
0 commit comments