@@ -63,25 +63,34 @@ const {
6363 Symbol,
6464} = primordials ;
6565
66+ const { kEvaluated } = internalBinding ( 'module_wrap' ) ;
67+
6668// Map used to store CJS parsing data or for ESM loading.
67- const cjsSourceCache = new SafeWeakMap ( ) ;
69+ const importedCJSCache = new SafeWeakMap ( ) ;
6870/**
6971 * Map of already-loaded CJS modules to use.
7072 */
7173const cjsExportsCache = new SafeWeakMap ( ) ;
74+ const requiredESMSourceCache = new SafeWeakMap ( ) ;
7275
76+ const kIsMainSymbol = Symbol ( 'kIsMainSymbol' ) ;
77+ const kIsCachedByESMLoader = Symbol ( 'kIsCachedByESMLoader' ) ;
78+ const kRequiredModuleSymbol = Symbol ( 'kRequiredModuleSymbol' ) ;
79+ const kIsExecuting = Symbol ( 'kIsExecuting' ) ;
7380// Set first due to cycle with ESM loader functions.
7481module . exports = {
7582 cjsExportsCache,
76- cjsSourceCache ,
83+ importedCJSCache ,
7784 initializeCJS,
7885 entryPointSource : undefined , // Set below.
7986 Module,
8087 wrapSafe,
88+ kIsMainSymbol,
89+ kIsCachedByESMLoader,
90+ kRequiredModuleSymbol,
91+ kIsExecuting,
8192} ;
8293
83- const kIsMainSymbol = Symbol ( 'kIsMainSymbol' ) ;
84-
8594const { BuiltinModule } = require ( 'internal/bootstrap/realm' ) ;
8695const {
8796 maybeCacheSourceMap,
@@ -138,6 +147,7 @@ const {
138147 codes : {
139148 ERR_INVALID_ARG_VALUE ,
140149 ERR_INVALID_MODULE_SPECIFIER ,
150+ ERR_REQUIRE_CYCLE_MODULE ,
141151 ERR_REQUIRE_ESM ,
142152 ERR_UNKNOWN_BUILTIN_MODULE ,
143153 } ,
@@ -942,6 +952,16 @@ const CircularRequirePrototypeWarningProxy = new Proxy({}, {
942952 * @param {Module } module The module instance
943953 */
944954function getExportsForCircularRequire ( module ) {
955+ const requiredESM = module [ kRequiredModuleSymbol ] ;
956+ if ( requiredESM && requiredESM . getStatus ( ) !== kEvaluated ) {
957+ let message = `Cannot require() ES Module ${ module . id } in a cycle.` ;
958+ const parent = moduleParentCache . get ( module ) ;
959+ if ( parent ) {
960+ message += ` (from ${ parent . filename } )` ;
961+ }
962+ throw new ERR_REQUIRE_CYCLE_MODULE ( message ) ;
963+ }
964+
945965 if ( module . exports &&
946966 ! isProxy ( module . exports ) &&
947967 ObjectGetPrototypeOf ( module . exports ) === ObjectPrototype &&
@@ -1009,11 +1029,21 @@ Module._load = function(request, parent, isMain) {
10091029 if ( cachedModule !== undefined ) {
10101030 updateChildren ( parent , cachedModule , true ) ;
10111031 if ( ! cachedModule . loaded ) {
1012- const parseCachedModule = cjsSourceCache . get ( cachedModule ) ;
1013- if ( ! parseCachedModule || parseCachedModule . loaded ) {
1032+ // If it's not cached by the ESM loader, the loading request
1033+ // comes from required CJS, and we can consider it a circular
1034+ // dependency when it's cached.
1035+ if ( ! cachedModule [ kIsCachedByESMLoader ] ) {
10141036 return getExportsForCircularRequire ( cachedModule ) ;
10151037 }
1016- parseCachedModule . loaded = true ;
1038+ // If it's cached by the ESM loader as a way to indirectly pass
1039+ // the module in to avoid creating it twice, the loading request
1040+ // come from imported CJS. In that case use the importedCJSCache
1041+ // to determine if it's loading or not.
1042+ const importedCJSMetadata = importedCJSCache . get ( cachedModule ) ;
1043+ if ( importedCJSMetadata . loading ) {
1044+ return getExportsForCircularRequire ( cachedModule ) ;
1045+ }
1046+ importedCJSMetadata . loading = true ;
10171047 } else {
10181048 return cachedModule . exports ;
10191049 }
@@ -1027,18 +1057,21 @@ Module._load = function(request, parent, isMain) {
10271057 // Don't call updateChildren(), Module constructor already does.
10281058 const module = cachedModule || new Module ( filename , parent ) ;
10291059
1030- if ( isMain ) {
1031- setOwnProperty ( process , 'mainModule' , module ) ;
1032- setOwnProperty ( module . require , 'main' , process . mainModule ) ;
1033- module . id = '.' ;
1034- module [ kIsMainSymbol ] = true ;
1035- } else {
1036- module [ kIsMainSymbol ] = false ;
1037- }
1060+ if ( ! cachedModule ) {
1061+ if ( isMain ) {
1062+ setOwnProperty ( process , 'mainModule' , module ) ;
1063+ setOwnProperty ( module . require , 'main' , process . mainModule ) ;
1064+ module . id = '.' ;
1065+ module [ kIsMainSymbol ] = true ;
1066+ } else {
1067+ module [ kIsMainSymbol ] = false ;
1068+ }
10381069
1039- reportModuleToWatchMode ( filename ) ;
1070+ reportModuleToWatchMode ( filename ) ;
1071+ Module . _cache [ filename ] = module ;
1072+ module [ kIsCachedByESMLoader ] = false ;
1073+ }
10401074
1041- Module . _cache [ filename ] = module ;
10421075 if ( parent !== undefined ) {
10431076 relativeResolveCache [ relResolveCacheIdentifier ] = filename ;
10441077 }
@@ -1280,7 +1313,7 @@ function loadESMFromCJS(mod, filename) {
12801313 const isMain = mod [ kIsMainSymbol ] ;
12811314 // TODO(joyeecheung): we may want to invent optional special handling for default exports here.
12821315 // For now, it's good enough to be identical to what `import()` returns.
1283- mod . exports = cascadedLoader . importSyncForRequire ( filename , source , isMain ) ;
1316+ mod . exports = cascadedLoader . importSyncForRequire ( mod , filename , source , isMain , moduleParentCache . get ( mod ) ) ;
12841317}
12851318
12861319/**
@@ -1373,7 +1406,7 @@ Module.prototype._compile = function(content, filename, loadAsESM = false) {
13731406 // Only modules being require()'d really need to avoid TLA.
13741407 if ( loadAsESM ) {
13751408 // Pass the source into the .mjs extension handler indirectly through the cache.
1376- cjsSourceCache . set ( this , { source : content } ) ;
1409+ requiredESMSourceCache . set ( this , content ) ;
13771410 loadESMFromCJS ( this , filename ) ;
13781411 return ;
13791412 }
@@ -1414,13 +1447,15 @@ Module.prototype._compile = function(content, filename, loadAsESM = false) {
14141447 const module = this ;
14151448 if ( requireDepth === 0 ) { statCache = new SafeMap ( ) ; }
14161449 setHasStartedUserCJSExecution ( ) ;
1450+ this [ kIsExecuting ] = true ;
14171451 if ( inspectorWrapper ) {
14181452 result = inspectorWrapper ( compiledWrapper , thisValue , exports ,
14191453 require , module , filename , dirname ) ;
14201454 } else {
14211455 result = ReflectApply ( compiledWrapper , thisValue ,
14221456 [ exports , require , module , filename , dirname ] ) ;
14231457 }
1458+ this [ kIsExecuting ] = false ;
14241459 if ( requireDepth === 0 ) { statCache = null ; }
14251460 return result ;
14261461} ;
@@ -1432,15 +1467,15 @@ Module.prototype._compile = function(content, filename, loadAsESM = false) {
14321467 * @returns {string }
14331468 */
14341469function getMaybeCachedSource ( mod , filename ) {
1435- const cached = cjsSourceCache . get ( mod ) ;
1470+ const cached = importedCJSCache . get ( mod ) ;
14361471 let content ;
14371472 if ( cached ?. source ) {
14381473 content = cached . source ;
14391474 cached . source = undefined ;
14401475 } else {
14411476 // TODO(joyeecheung): we can read a buffer instead to speed up
14421477 // compilation.
1443- content = fs . readFileSync ( filename , 'utf8' ) ;
1478+ content = requiredESMSourceCache . get ( mod ) ?? fs . readFileSync ( filename , 'utf8' ) ;
14441479 }
14451480 return content ;
14461481}
0 commit comments