@@ -60,10 +60,11 @@ const {
6060 StringPrototypeSlice,
6161 StringPrototypeSplit,
6262 StringPrototypeStartsWith,
63+ Symbol,
6364} = primordials ;
6465
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 ( ) ;
6768/**
6869 * Map of already-loaded CJS modules to use.
6970 */
@@ -72,12 +73,15 @@ const cjsExportsCache = new SafeWeakMap();
7273// Set first due to cycle with ESM loader functions.
7374module . exports = {
7475 cjsExportsCache,
75- cjsParseCache ,
76+ cjsSourceCache ,
7677 initializeCJS,
7778 Module,
7879 wrapSafe,
80+ makeRequireWithPolicy,
7981} ;
8082
83+ const is_main_symbol = Symbol ( 'is_main_symbol' ) ;
84+
8185const { BuiltinModule } = require ( 'internal/bootstrap/realm' ) ;
8286const {
8387 maybeCacheSourceMap,
@@ -98,7 +102,6 @@ const {
98102 containsModuleSyntax,
99103 compileFunctionForCJSLoader,
100104} = internalBinding ( 'contextify' ) ;
101-
102105const assert = require ( 'internal/assert' ) ;
103106const fs = require ( 'fs' ) ;
104107const path = require ( 'path' ) ;
@@ -107,7 +110,6 @@ const { safeGetenv } = internalBinding('credentials');
107110const {
108111 privateSymbols : {
109112 require_private_symbol,
110- host_defined_option_symbol,
111113 } ,
112114} = internalBinding ( 'util' ) ;
113115const {
@@ -396,6 +398,10 @@ function initializeCJS() {
396398 // TODO(joyeecheung): deprecate this in favor of a proper hook?
397399 Module . runMain =
398400 require ( 'internal/modules/run_main' ) . executeUserEntryPoint ;
401+
402+ if ( getOptionValue ( '--experimental-require-module' ) ) {
403+ Module . _extensions [ '.mjs' ] = loadESMFromCJS ;
404+ }
399405}
400406
401407// Given a module name, and a list of paths to test, returns the first
@@ -988,7 +994,7 @@ Module._load = function(request, parent, isMain) {
988994 if ( cachedModule !== undefined ) {
989995 updateChildren ( parent , cachedModule , true ) ;
990996 if ( ! cachedModule . loaded ) {
991- const parseCachedModule = cjsParseCache . get ( cachedModule ) ;
997+ const parseCachedModule = cjsSourceCache . get ( cachedModule ) ;
992998 if ( ! parseCachedModule || parseCachedModule . loaded ) {
993999 return getExportsForCircularRequire ( cachedModule ) ;
9941000 }
@@ -1010,6 +1016,9 @@ Module._load = function(request, parent, isMain) {
10101016 setOwnProperty ( process , 'mainModule' , module ) ;
10111017 setOwnProperty ( module . require , 'main' , process . mainModule ) ;
10121018 module . id = '.' ;
1019+ module [ is_main_symbol ] = true ;
1020+ } else {
1021+ module [ is_main_symbol ] = false ;
10131022 }
10141023
10151024 reportModuleToWatchMode ( filename ) ;
@@ -1270,57 +1279,96 @@ function wrapSafe(filename, content, cjsModuleInstance, codeCache) {
12701279 ) ;
12711280
12721281 // 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 ) ;
12751285 }
12761286
1277- return runScriptInThisContext ( script , true , false ) ;
1287+ return {
1288+ __proto__ : null ,
1289+ function : runScriptInThisContext ( script , true , false ) ,
1290+ sourceMapURL,
1291+ retryAsESM : false ,
1292+ } ;
12781293 }
12791294
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 ) ;
12901296
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+ }
12951303
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 ) ;
13031334 }
1335+ return makeRequireFunction ( module , redirects ) ;
13041336}
13051337
13061338/**
13071339 * Run the file contents in the correct scope or sandbox. Expose the correct helper variables (`require`, `module`,
13081340 * `exports`) to the file. Returns exception, if any.
13091341 * @param {string } content The source code of the module
13101342 * @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.
13111344 */
1312- Module . prototype . _compile = function ( content , filename ) {
1345+ Module . prototype . _compile = function ( content , filename , loadAsESM = false ) {
13131346 let moduleURL ;
1314- let redirects ;
13151347 const manifest = policy ( ) ?. manifest ;
13161348 if ( manifest ) {
13171349 moduleURL = pathToFileURL ( filename ) ;
1318- redirects = manifest . getDependencyMapper ( moduleURL ) ;
13191350 manifest . assertIntegrity ( moduleURL , content ) ;
13201351 }
13211352
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+ }
13231361
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.
13241372 let inspectorWrapper = null ;
13251373 if ( getOptionValue ( '--inspect-brk' ) && process . _eval == null ) {
13261374 if ( ! resolvedArgv ) {
@@ -1344,8 +1392,9 @@ Module.prototype._compile = function(content, filename) {
13441392 inspectorWrapper = internalBinding ( 'inspector' ) . callAndPauseOnStart ;
13451393 }
13461394 }
1395+
13471396 const dirname = path . dirname ( filename ) ;
1348- const require = makeRequireFunction ( this , redirects ) ;
1397+ const require = makeRequireWithPolicy ( this , moduleURL ) ;
13491398 let result ;
13501399 const exports = this . exports ;
13511400 const thisValue = exports ;
@@ -1363,25 +1412,37 @@ Module.prototype._compile = function(content, filename) {
13631412 return result ;
13641413} ;
13651414
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 ) ;
13741417 let content ;
13751418 if ( cached ?. source ) {
13761419 content = cached . source ;
13771420 cached . source = undefined ;
13781421 } else {
1422+ // TODO(joyeecheung): read a buffer.
13791423 content = fs . readFileSync ( filename , 'utf8' ) ;
13801424 }
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+
13811437 if ( StringPrototypeEndsWith ( filename , '.js' ) ) {
13821438 const pkg = packageJsonReader . getNearestParentPackageJSON ( filename ) ;
13831439 // Function require shouldn't be used in ES modules.
13841440 if ( pkg ?. data . type === 'module' ) {
1441+ if ( getOptionValue ( '--experimental-require-module' ) ) {
1442+ module . _compile ( content , filename , true ) ;
1443+ return ;
1444+ }
1445+
13851446 // This is an error path because `require` of a `.js` file in a `"type": "module"` scope is not allowed.
13861447 const parent = moduleParentCache . get ( module ) ;
13871448 const parentPath = parent ?. filename ;
@@ -1414,7 +1475,8 @@ Module._extensions['.js'] = function(module, filename) {
14141475 throw err ;
14151476 }
14161477 }
1417- module . _compile ( content , filename ) ;
1478+
1479+ module . _compile ( content , filename , false ) ;
14181480} ;
14191481
14201482/**
0 commit comments