@@ -63,25 +63,34 @@ const {
63
63
Symbol,
64
64
} = primordials ;
65
65
66
+ const { kEvaluated } = internalBinding ( 'module_wrap' ) ;
67
+
66
68
// Map used to store CJS parsing data or for ESM loading.
67
- const cjsSourceCache = new SafeWeakMap ( ) ;
69
+ const importedCJSCache = new SafeWeakMap ( ) ;
68
70
/**
69
71
* Map of already-loaded CJS modules to use.
70
72
*/
71
73
const cjsExportsCache = new SafeWeakMap ( ) ;
74
+ const requiredESMSourceCache = new SafeWeakMap ( ) ;
72
75
76
+ const kIsMainSymbol = Symbol ( 'kIsMainSymbol' ) ;
77
+ const kIsCachedByESMLoader = Symbol ( 'kIsCachedByESMLoader' ) ;
78
+ const kRequiredModuleSymbol = Symbol ( 'kRequiredModuleSymbol' ) ;
79
+ const kIsExecuting = Symbol ( 'kIsExecuting' ) ;
73
80
// Set first due to cycle with ESM loader functions.
74
81
module . exports = {
75
82
cjsExportsCache,
76
- cjsSourceCache ,
83
+ importedCJSCache ,
77
84
initializeCJS,
78
85
entryPointSource : undefined , // Set below.
79
86
Module,
80
87
wrapSafe,
88
+ kIsMainSymbol,
89
+ kIsCachedByESMLoader,
90
+ kRequiredModuleSymbol,
91
+ kIsExecuting,
81
92
} ;
82
93
83
- const kIsMainSymbol = Symbol ( 'kIsMainSymbol' ) ;
84
-
85
94
const { BuiltinModule } = require ( 'internal/bootstrap/realm' ) ;
86
95
const {
87
96
maybeCacheSourceMap,
@@ -138,6 +147,7 @@ const {
138
147
codes : {
139
148
ERR_INVALID_ARG_VALUE ,
140
149
ERR_INVALID_MODULE_SPECIFIER ,
150
+ ERR_REQUIRE_CYCLE_MODULE ,
141
151
ERR_REQUIRE_ESM ,
142
152
ERR_UNKNOWN_BUILTIN_MODULE ,
143
153
} ,
@@ -942,6 +952,16 @@ const CircularRequirePrototypeWarningProxy = new Proxy({}, {
942
952
* @param {Module } module The module instance
943
953
*/
944
954
function 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
+
945
965
if ( module . exports &&
946
966
! isProxy ( module . exports ) &&
947
967
ObjectGetPrototypeOf ( module . exports ) === ObjectPrototype &&
@@ -1009,11 +1029,21 @@ Module._load = function(request, parent, isMain) {
1009
1029
if ( cachedModule !== undefined ) {
1010
1030
updateChildren ( parent , cachedModule , true ) ;
1011
1031
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 ] ) {
1014
1036
return getExportsForCircularRequire ( cachedModule ) ;
1015
1037
}
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 ;
1017
1047
} else {
1018
1048
return cachedModule . exports ;
1019
1049
}
@@ -1027,18 +1057,21 @@ Module._load = function(request, parent, isMain) {
1027
1057
// Don't call updateChildren(), Module constructor already does.
1028
1058
const module = cachedModule || new Module ( filename , parent ) ;
1029
1059
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
+ }
1038
1069
1039
- reportModuleToWatchMode ( filename ) ;
1070
+ reportModuleToWatchMode ( filename ) ;
1071
+ Module . _cache [ filename ] = module ;
1072
+ module [ kIsCachedByESMLoader ] = false ;
1073
+ }
1040
1074
1041
- Module . _cache [ filename ] = module ;
1042
1075
if ( parent !== undefined ) {
1043
1076
relativeResolveCache [ relResolveCacheIdentifier ] = filename ;
1044
1077
}
@@ -1280,7 +1313,7 @@ function loadESMFromCJS(mod, filename) {
1280
1313
const isMain = mod [ kIsMainSymbol ] ;
1281
1314
// TODO(joyeecheung): we may want to invent optional special handling for default exports here.
1282
1315
// 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 ) ) ;
1284
1317
}
1285
1318
1286
1319
/**
@@ -1373,7 +1406,7 @@ Module.prototype._compile = function(content, filename, loadAsESM = false) {
1373
1406
// Only modules being require()'d really need to avoid TLA.
1374
1407
if ( loadAsESM ) {
1375
1408
// Pass the source into the .mjs extension handler indirectly through the cache.
1376
- cjsSourceCache . set ( this , { source : content } ) ;
1409
+ requiredESMSourceCache . set ( this , content ) ;
1377
1410
loadESMFromCJS ( this , filename ) ;
1378
1411
return ;
1379
1412
}
@@ -1414,13 +1447,15 @@ Module.prototype._compile = function(content, filename, loadAsESM = false) {
1414
1447
const module = this ;
1415
1448
if ( requireDepth === 0 ) { statCache = new SafeMap ( ) ; }
1416
1449
setHasStartedUserCJSExecution ( ) ;
1450
+ this [ kIsExecuting ] = true ;
1417
1451
if ( inspectorWrapper ) {
1418
1452
result = inspectorWrapper ( compiledWrapper , thisValue , exports ,
1419
1453
require , module , filename , dirname ) ;
1420
1454
} else {
1421
1455
result = ReflectApply ( compiledWrapper , thisValue ,
1422
1456
[ exports , require , module , filename , dirname ] ) ;
1423
1457
}
1458
+ this [ kIsExecuting ] = false ;
1424
1459
if ( requireDepth === 0 ) { statCache = null ; }
1425
1460
return result ;
1426
1461
} ;
@@ -1432,15 +1467,15 @@ Module.prototype._compile = function(content, filename, loadAsESM = false) {
1432
1467
* @returns {string }
1433
1468
*/
1434
1469
function getMaybeCachedSource ( mod , filename ) {
1435
- const cached = cjsSourceCache . get ( mod ) ;
1470
+ const cached = importedCJSCache . get ( mod ) ;
1436
1471
let content ;
1437
1472
if ( cached ?. source ) {
1438
1473
content = cached . source ;
1439
1474
cached . source = undefined ;
1440
1475
} else {
1441
1476
// TODO(joyeecheung): we can read a buffer instead to speed up
1442
1477
// compilation.
1443
- content = fs . readFileSync ( filename , 'utf8' ) ;
1478
+ content = requiredESMSourceCache . get ( mod ) ?? fs . readFileSync ( filename , 'utf8' ) ;
1444
1479
}
1445
1480
return content ;
1446
1481
}
0 commit comments