@@ -60,10 +60,11 @@ const {
60
60
StringPrototypeSlice,
61
61
StringPrototypeSplit,
62
62
StringPrototypeStartsWith,
63
+ Symbol,
63
64
} = primordials ;
64
65
65
- // Map used to store CJS parsing data.
66
- const cjsParseCache = new SafeWeakMap ( ) ;
66
+ // Map used to store CJS parsing data or for ESM loading .
67
+ const cjsSourceCache = new SafeWeakMap ( ) ;
67
68
/**
68
69
* Map of already-loaded CJS modules to use.
69
70
*/
@@ -72,12 +73,14 @@ const cjsExportsCache = new SafeWeakMap();
72
73
// Set first due to cycle with ESM loader functions.
73
74
module . exports = {
74
75
cjsExportsCache,
75
- cjsParseCache ,
76
+ cjsSourceCache ,
76
77
initializeCJS,
77
78
Module,
78
79
wrapSafe,
79
80
} ;
80
81
82
+ const is_main_symbol = Symbol ( 'is_main_symbol' ) ;
83
+
81
84
const { BuiltinModule } = require ( 'internal/bootstrap/realm' ) ;
82
85
const {
83
86
maybeCacheSourceMap,
@@ -98,7 +101,6 @@ const {
98
101
containsModuleSyntax,
99
102
compileFunctionForCJSLoader,
100
103
} = internalBinding ( 'contextify' ) ;
101
-
102
104
const assert = require ( 'internal/assert' ) ;
103
105
const fs = require ( 'fs' ) ;
104
106
const path = require ( 'path' ) ;
@@ -107,7 +109,6 @@ const { safeGetenv } = internalBinding('credentials');
107
109
const {
108
110
privateSymbols : {
109
111
require_private_symbol,
110
- host_defined_option_symbol,
111
112
} ,
112
113
} = internalBinding ( 'util' ) ;
113
114
const {
@@ -396,6 +397,10 @@ function initializeCJS() {
396
397
// TODO(joyeecheung): deprecate this in favor of a proper hook?
397
398
Module . runMain =
398
399
require ( 'internal/modules/run_main' ) . executeUserEntryPoint ;
400
+
401
+ if ( getOptionValue ( '--experimental-require-module' ) ) {
402
+ Module . _extensions [ '.mjs' ] = loadESMFromCJS ;
403
+ }
399
404
}
400
405
401
406
// Given a module name, and a list of paths to test, returns the first
@@ -988,7 +993,7 @@ Module._load = function(request, parent, isMain) {
988
993
if ( cachedModule !== undefined ) {
989
994
updateChildren ( parent , cachedModule , true ) ;
990
995
if ( ! cachedModule . loaded ) {
991
- const parseCachedModule = cjsParseCache . get ( cachedModule ) ;
996
+ const parseCachedModule = cjsSourceCache . get ( cachedModule ) ;
992
997
if ( ! parseCachedModule || parseCachedModule . loaded ) {
993
998
return getExportsForCircularRequire ( cachedModule ) ;
994
999
}
@@ -1010,6 +1015,9 @@ Module._load = function(request, parent, isMain) {
1010
1015
setOwnProperty ( process , 'mainModule' , module ) ;
1011
1016
setOwnProperty ( module . require , 'main' , process . mainModule ) ;
1012
1017
module . id = '.' ;
1018
+ module [ is_main_symbol ] = true ;
1019
+ } else {
1020
+ module [ is_main_symbol ] = false ;
1013
1021
}
1014
1022
1015
1023
reportModuleToWatchMode ( filename ) ;
@@ -1270,46 +1278,55 @@ function wrapSafe(filename, content, cjsModuleInstance, codeCache) {
1270
1278
) ;
1271
1279
1272
1280
// Cache the source map for the module if present.
1273
- if ( script . sourceMapURL ) {
1274
- maybeCacheSourceMap ( filename , content , this , false , undefined , script . sourceMapURL ) ;
1281
+ const { sourceMapURL } = script ;
1282
+ if ( sourceMapURL ) {
1283
+ maybeCacheSourceMap ( filename , content , this , false , undefined , sourceMapURL ) ;
1275
1284
}
1276
1285
1277
- return runScriptInThisContext ( script , true , false ) ;
1286
+ return {
1287
+ __proto__ : null ,
1288
+ function : runScriptInThisContext ( script , true , false ) ,
1289
+ sourceMapURL,
1290
+ retryAsESM : false ,
1291
+ } ;
1278
1292
}
1279
1293
1280
- try {
1281
- const result = compileFunctionForCJSLoader ( content , filename ) ;
1282
- result . function [ host_defined_option_symbol ] = hostDefinedOptionId ;
1283
-
1284
- // cachedDataRejected is only set for cache coming from SEA.
1285
- if ( codeCache &&
1286
- result . cachedDataRejected !== false &&
1287
- internalBinding ( 'sea' ) . isSea ( ) ) {
1288
- process . emitWarning ( 'Code cache data rejected.' ) ;
1289
- }
1294
+ const result = compileFunctionForCJSLoader ( content , filename ) ;
1290
1295
1291
- // Cache the source map for the module if present.
1292
- if ( result . sourceMapURL ) {
1293
- maybeCacheSourceMap ( filename , content , this , false , undefined , result . sourceMapURL ) ;
1294
- }
1296
+ // cachedDataRejected is only set for cache coming from SEA.
1297
+ if ( codeCache &&
1298
+ result . cachedDataRejected !== false &&
1299
+ internalBinding ( 'sea' ) . isSea ( ) ) {
1300
+ process . emitWarning ( 'Code cache data rejected.' ) ;
1301
+ }
1295
1302
1296
- return result . function ;
1297
- } catch ( err ) {
1298
- if ( process . mainModule === cjsModuleInstance ) {
1299
- const { enrichCJSError } = require ( 'internal/modules/esm/translators' ) ;
1300
- enrichCJSError ( err , content , filename ) ;
1301
- }
1302
- throw err ;
1303
+ // Cache the source map for the module if present.
1304
+ if ( result . sourceMapURL ) {
1305
+ maybeCacheSourceMap ( filename , content , this , false , undefined , result . sourceMapURL ) ;
1303
1306
}
1307
+
1308
+ return result ;
1309
+ }
1310
+
1311
+ // Resolve and evaluate as ESM, synchronously.
1312
+ function loadESMFromCJS ( mod , filename ) {
1313
+ const source = getMaybeCachedSource ( mod , filename ) ;
1314
+ const cascadedLoader = require ( 'internal/modules/esm/loader' ) . getOrInitializeCascadedLoader ( ) ;
1315
+ // We are still using the CJS's resolution here.
1316
+ const url = pathToFileURL ( filename ) . href ;
1317
+ const isMain = mod [ is_main_symbol ] ;
1318
+ // TODO(joyeecheung): maybe we can do some special handling for default here. Maybe we don't.
1319
+ mod . exports = cascadedLoader . importSyncForRequire ( url , source , isMain ) ;
1304
1320
}
1305
1321
1306
1322
/**
1307
1323
* Run the file contents in the correct scope or sandbox. Expose the correct helper variables (`require`, `module`,
1308
1324
* `exports`) to the file. Returns exception, if any.
1309
1325
* @param {string } content The source code of the module
1310
1326
* @param {string } filename The file path of the module
1327
+ * @param {boolean } loadAsESM Whether it's known to be ESM - i.e. suffix is .mjs.
1311
1328
*/
1312
- Module . prototype . _compile = function ( content , filename ) {
1329
+ Module . prototype . _compile = function ( content , filename , loadAsESM = false ) {
1313
1330
let moduleURL ;
1314
1331
let redirects ;
1315
1332
const manifest = policy ( ) ?. manifest ;
@@ -1319,8 +1336,25 @@ Module.prototype._compile = function(content, filename) {
1319
1336
manifest . assertIntegrity ( moduleURL , content ) ;
1320
1337
}
1321
1338
1322
- const compiledWrapper = wrapSafe ( filename , content , this ) ;
1339
+ // TODO(joyeecheung): when the module is the entry point, consider allowing TLA.
1340
+ // Only modules being require()'d really need to avoid TLA.
1341
+ let compiledWrapper ;
1342
+ if ( ! loadAsESM ) {
1343
+ const result = wrapSafe ( filename , content , this ) ;
1344
+ compiledWrapper = result . function ;
1345
+ loadAsESM = result . retryAsESM ;
1346
+ }
1323
1347
1348
+ if ( loadAsESM ) {
1349
+ // Pass the source into the .mjs extension handler indirectly through the cache.
1350
+ cjsSourceCache . set ( this , content ) ;
1351
+ loadESMFromCJS ( this , filename ) ;
1352
+ return ;
1353
+ }
1354
+
1355
+ // TODO(joyeecheung): the detection below is unnecessarily complex. Maybe just
1356
+ // use the is_main_symbol, or a break_on_start_symbol that gets passed from
1357
+ // higher level instead of doing hacky detecion here.
1324
1358
let inspectorWrapper = null ;
1325
1359
if ( getOptionValue ( '--inspect-brk' ) && process . _eval == null ) {
1326
1360
if ( ! resolvedArgv ) {
@@ -1344,6 +1378,7 @@ Module.prototype._compile = function(content, filename) {
1344
1378
inspectorWrapper = internalBinding ( 'inspector' ) . callAndPauseOnStart ;
1345
1379
}
1346
1380
}
1381
+
1347
1382
const dirname = path . dirname ( filename ) ;
1348
1383
const require = makeRequireFunction ( this , redirects ) ;
1349
1384
let result ;
@@ -1363,25 +1398,37 @@ Module.prototype._compile = function(content, filename) {
1363
1398
return result ;
1364
1399
} ;
1365
1400
1366
- /**
1367
- * Native handler for `.js` files.
1368
- * @param {Module } module The module to compile
1369
- * @param {string } filename The file path of the module
1370
- */
1371
- Module . _extensions [ '.js' ] = function ( module , filename ) {
1372
- // If already analyzed the source, then it will be cached.
1373
- const cached = cjsParseCache . get ( module ) ;
1401
+ function getMaybeCachedSource ( mod , filename ) {
1402
+ const cached = cjsSourceCache . get ( mod ) ;
1374
1403
let content ;
1375
1404
if ( cached ?. source ) {
1376
1405
content = cached . source ;
1377
1406
cached . source = undefined ;
1378
1407
} else {
1408
+ // TODO(joyeecheung): read a buffer.
1379
1409
content = fs . readFileSync ( filename , 'utf8' ) ;
1380
1410
}
1411
+ return content ;
1412
+ }
1413
+
1414
+ /**
1415
+ * Native handler for `.js` files.
1416
+ * @param {Module } module The module to compile
1417
+ * @param {string } filename The file path of the module
1418
+ */
1419
+ Module . _extensions [ '.js' ] = function ( module , filename ) {
1420
+ // If already analyzed the source, then it will be cached.
1421
+ const content = getMaybeCachedSource ( module , filename ) ;
1422
+
1381
1423
if ( StringPrototypeEndsWith ( filename , '.js' ) ) {
1382
1424
const pkg = packageJsonReader . getNearestParentPackageJSON ( filename ) ;
1383
1425
// Function require shouldn't be used in ES modules.
1384
1426
if ( pkg ?. data . type === 'module' ) {
1427
+ if ( getOptionValue ( '--experimental-require-module' ) ) {
1428
+ module . _compile ( content , filename , true ) ;
1429
+ return ;
1430
+ }
1431
+
1385
1432
// This is an error path because `require` of a `.js` file in a `"type": "module"` scope is not allowed.
1386
1433
const parent = moduleParentCache . get ( module ) ;
1387
1434
const parentPath = parent ?. filename ;
@@ -1414,7 +1461,8 @@ Module._extensions['.js'] = function(module, filename) {
1414
1461
throw err ;
1415
1462
}
1416
1463
}
1417
- module . _compile ( content , filename ) ;
1464
+
1465
+ module . _compile ( content , filename , false ) ;
1418
1466
} ;
1419
1467
1420
1468
/**
0 commit comments