@@ -60,6 +60,7 @@ const {
60
60
StringPrototypeSlice,
61
61
StringPrototypeSplit,
62
62
StringPrototypeStartsWith,
63
+ Symbol,
63
64
} = primordials ;
64
65
65
66
// Map used to store CJS parsing data.
@@ -76,6 +77,7 @@ module.exports = {
76
77
initializeCJS,
77
78
Module,
78
79
wrapSafe,
80
+ makeRequireWithPolicy,
79
81
} ;
80
82
81
83
const { BuiltinModule } = require ( 'internal/bootstrap/realm' ) ;
@@ -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 {
@@ -161,6 +162,8 @@ let requireDepth = 0;
161
162
let isPreloading = false ;
162
163
let statCache = null ;
163
164
165
+ const is_main_symbol = Symbol ( 'is-main-module' ) ;
166
+
164
167
/**
165
168
* Our internal implementation of `require`.
166
169
* @param {Module } module Parent module of what is being required
@@ -271,6 +274,7 @@ function Module(id = '', parent) {
271
274
setOwnProperty ( this . __proto__ , 'require' , makeRequireFunction ( this , redirects ) ) ;
272
275
}
273
276
this [ require_private_symbol ] = internalRequire ;
277
+ this [ is_main_symbol ] = false ; // Set to true by the entry point handler.
274
278
}
275
279
276
280
/** @type {Record<string, Module> } */
@@ -396,6 +400,10 @@ function initializeCJS() {
396
400
// TODO(joyeecheung): deprecate this in favor of a proper hook?
397
401
Module . runMain =
398
402
require ( 'internal/modules/run_main' ) . executeUserEntryPoint ;
403
+
404
+ if ( getOptionValue ( '--experimental-require-module' ) ) {
405
+ Module . _extensions [ '.mjs' ] = loadESMFromCJS ;
406
+ }
399
407
}
400
408
401
409
// Given a module name, and a list of paths to test, returns the first
@@ -1010,6 +1018,7 @@ Module._load = function(request, parent, isMain) {
1010
1018
setOwnProperty ( process , 'mainModule' , module ) ;
1011
1019
setOwnProperty ( module . require , 'main' , process . mainModule ) ;
1012
1020
module . id = '.' ;
1021
+ module [ is_main_symbol ] = true ;
1013
1022
}
1014
1023
1015
1024
reportModuleToWatchMode ( filename ) ;
@@ -1270,57 +1279,95 @@ 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 ) ;
1303
1307
}
1308
+
1309
+ return result ;
1310
+ }
1311
+
1312
+ // Resolve and evaluate as ESM, synchronously.
1313
+ function loadESMFromCJS ( mod , filename ) {
1314
+ const cascadedLoader = require ( 'internal/modules/esm/loader' ) . getOrInitializeCascadedLoader ( ) ;
1315
+ // Note that we are still using the CJS's path resolution here.
1316
+ const parent = moduleParentCache . get ( mod ) ?. filename ;
1317
+ const base = parent ? pathToFileURL ( parent ) : parent ;
1318
+ // console.log('loadESMFromCJS', mod, filename, base);
1319
+ const specifier = mod [ is_main_symbol ] ? pathToFileURL ( mod . filename ) : mod . id ;
1320
+ const job = cascadedLoader . getModuleJobSync ( specifier , base , kEmptyObject , 'from-cjs-error' ) ;
1321
+ const { namespace } = job . runSync ( ) ;
1322
+ // TODO(joyeecheung): maybe we can do some special handling for default here. Maybe we don't.
1323
+ mod . exports = namespace ;
1324
+ }
1325
+
1326
+ /**
1327
+ * Create a require function for this module, apply policy if necessary.
1328
+ * @param {Module } module
1329
+ * @param {string } moduleURL
1330
+ * @returns {Function }
1331
+ */
1332
+ function makeRequireWithPolicy ( module , moduleURL ) {
1333
+ const manifest = policy ( ) ?. manifest ;
1334
+ let redirects ;
1335
+ if ( manifest ) {
1336
+ redirects = manifest . getDependencyMapper ( moduleURL ) ;
1337
+ }
1338
+ return makeRequireFunction ( module , redirects ) ;
1304
1339
}
1305
1340
1306
1341
/**
1307
1342
* Run the file contents in the correct scope or sandbox. Expose the correct helper variables (`require`, `module`,
1308
1343
* `exports`) to the file. Returns exception, if any.
1309
1344
* @param {string } content The source code of the module
1310
1345
* @param {string } filename The file path of the module
1346
+ * @param {boolean } loadAsESM Whether it's known to be ESM - i.e. suffix is .mjs.
1311
1347
*/
1312
- Module . prototype . _compile = function ( content , filename ) {
1348
+ Module . prototype . _compile = function ( content , filename , loadAsESM = false ) {
1313
1349
let moduleURL ;
1314
- let redirects ;
1315
1350
const manifest = policy ( ) ?. manifest ;
1316
1351
if ( manifest ) {
1317
1352
moduleURL = pathToFileURL ( filename ) ;
1318
- redirects = manifest . getDependencyMapper ( moduleURL ) ;
1319
1353
manifest . assertIntegrity ( moduleURL , content ) ;
1320
1354
}
1321
1355
1322
- const compiledWrapper = wrapSafe ( filename , content , this ) ;
1356
+ let compiledWrapper ;
1357
+ if ( ! loadAsESM ) {
1358
+ const result = wrapSafe ( filename , content , this ) ;
1359
+ compiledWrapper = result . function ;
1360
+ loadAsESM = result . retryAsESM ;
1361
+ }
1323
1362
1363
+ if ( loadAsESM ) {
1364
+ loadESMFromCJS ( this ) ;
1365
+ return ;
1366
+ }
1367
+
1368
+ // TODO(joyeecheung): the detection below is unnecessarily complex. Maybe just
1369
+ // use the is_main_symbol, or a break_on_start_symbol that gets passed from
1370
+ // higher level instead of doing hacky detecion here.
1324
1371
let inspectorWrapper = null ;
1325
1372
if ( getOptionValue ( '--inspect-brk' ) && process . _eval == null ) {
1326
1373
if ( ! resolvedArgv ) {
@@ -1344,8 +1391,9 @@ Module.prototype._compile = function(content, filename) {
1344
1391
inspectorWrapper = internalBinding ( 'inspector' ) . callAndPauseOnStart ;
1345
1392
}
1346
1393
}
1394
+
1347
1395
const dirname = path . dirname ( filename ) ;
1348
- const require = makeRequireFunction ( this , redirects ) ;
1396
+ const require = makeRequireWithPolicy ( this , moduleURL ) ;
1349
1397
let result ;
1350
1398
const exports = this . exports ;
1351
1399
const thisValue = exports ;
@@ -1370,6 +1418,7 @@ Module.prototype._compile = function(content, filename) {
1370
1418
*/
1371
1419
Module . _extensions [ '.js' ] = function ( module , filename ) {
1372
1420
// If already analyzed the source, then it will be cached.
1421
+ // TODO(joyeecheung): pass as buffer.
1373
1422
const cached = cjsParseCache . get ( module ) ;
1374
1423
let content ;
1375
1424
if ( cached ?. source ) {
@@ -1378,7 +1427,8 @@ Module._extensions['.js'] = function(module, filename) {
1378
1427
} else {
1379
1428
content = fs . readFileSync ( filename , 'utf8' ) ;
1380
1429
}
1381
- if ( StringPrototypeEndsWith ( filename , '.js' ) ) {
1430
+ if ( ! getOptionValue ( '--experimental-require-module' ) &&
1431
+ StringPrototypeEndsWith ( filename , '.js' ) ) {
1382
1432
const pkg = packageJsonReader . getNearestParentPackageJSON ( filename ) ;
1383
1433
// Function require shouldn't be used in ES modules.
1384
1434
if ( pkg ?. data . type === 'module' ) {
@@ -1414,7 +1464,8 @@ Module._extensions['.js'] = function(module, filename) {
1414
1464
throw err ;
1415
1465
}
1416
1466
}
1417
- module . _compile ( content , filename ) ;
1467
+
1468
+ module . _compile ( content , filename , false ) ;
1418
1469
} ;
1419
1470
1420
1471
/**
0 commit comments