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