@@ -63,24 +63,33 @@ 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
Module,
79
86
wrapSafe,
87
+ kIsMainSymbol,
88
+ kIsCachedByESMLoader,
89
+ kRequiredModuleSymbol,
90
+ kIsExecuting,
80
91
} ;
81
92
82
- const kIsMainSymbol = Symbol ( 'kIsMainSymbol' ) ;
83
-
84
93
const { BuiltinModule } = require ( 'internal/bootstrap/realm' ) ;
85
94
const {
86
95
maybeCacheSourceMap,
@@ -137,6 +146,7 @@ const {
137
146
codes : {
138
147
ERR_INVALID_ARG_VALUE ,
139
148
ERR_INVALID_MODULE_SPECIFIER ,
149
+ ERR_REQUIRE_CYCLE_MODULE ,
140
150
ERR_REQUIRE_ESM ,
141
151
ERR_UNKNOWN_BUILTIN_MODULE ,
142
152
} ,
@@ -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
/**
@@ -1366,7 +1399,7 @@ Module.prototype._compile = function(content, filename, loadAsESM = false) {
1366
1399
// Only modules being require()'d really need to avoid TLA.
1367
1400
if ( loadAsESM ) {
1368
1401
// Pass the source into the .mjs extension handler indirectly through the cache.
1369
- cjsSourceCache . set ( this , { source : content } ) ;
1402
+ requiredESMSourceCache . set ( this , content ) ;
1370
1403
loadESMFromCJS ( this , filename ) ;
1371
1404
return ;
1372
1405
}
@@ -1407,13 +1440,15 @@ Module.prototype._compile = function(content, filename, loadAsESM = false) {
1407
1440
const module = this ;
1408
1441
if ( requireDepth === 0 ) { statCache = new SafeMap ( ) ; }
1409
1442
setHasStartedUserCJSExecution ( ) ;
1443
+ this [ kIsExecuting ] = true ;
1410
1444
if ( inspectorWrapper ) {
1411
1445
result = inspectorWrapper ( compiledWrapper , thisValue , exports ,
1412
1446
require , module , filename , dirname ) ;
1413
1447
} else {
1414
1448
result = ReflectApply ( compiledWrapper , thisValue ,
1415
1449
[ exports , require , module , filename , dirname ] ) ;
1416
1450
}
1451
+ this [ kIsExecuting ] = false ;
1417
1452
if ( requireDepth === 0 ) { statCache = null ; }
1418
1453
return result ;
1419
1454
} ;
@@ -1425,15 +1460,15 @@ Module.prototype._compile = function(content, filename, loadAsESM = false) {
1425
1460
* @returns {string }
1426
1461
*/
1427
1462
function getMaybeCachedSource ( mod , filename ) {
1428
- const cached = cjsSourceCache . get ( mod ) ;
1463
+ const cached = importedCJSCache . get ( mod ) ;
1429
1464
let content ;
1430
1465
if ( cached ?. source ) {
1431
1466
content = cached . source ;
1432
1467
cached . source = undefined ;
1433
1468
} else {
1434
1469
// TODO(joyeecheung): we can read a buffer instead to speed up
1435
1470
// compilation.
1436
- content = fs . readFileSync ( filename , 'utf8' ) ;
1471
+ content = requiredESMSourceCache . get ( mod ) ?? fs . readFileSync ( filename , 'utf8' ) ;
1437
1472
}
1438
1473
return content ;
1439
1474
}
0 commit comments