@@ -101,6 +101,9 @@ const kIsMainSymbol = Symbol('kIsMainSymbol');
101101const kIsCachedByESMLoader = Symbol ( 'kIsCachedByESMLoader' ) ;
102102const kRequiredModuleSymbol = Symbol ( 'kRequiredModuleSymbol' ) ;
103103const kIsExecuting = Symbol ( 'kIsExecuting' ) ;
104+
105+ const kFormat = Symbol ( 'kFormat' ) ;
106+
104107// Set first due to cycle with ESM loader functions.
105108module . exports = {
106109 kModuleSource,
@@ -438,10 +441,6 @@ function initializeCJS() {
438441 // TODO(joyeecheung): deprecate this in favor of a proper hook?
439442 Module . runMain =
440443 require ( 'internal/modules/run_main' ) . executeUserEntryPoint ;
441-
442- if ( getOptionValue ( '--experimental-require-module' ) ) {
443- Module . _extensions [ '.mjs' ] = loadESMFromCJS ;
444- }
445444}
446445
447446// Given a module name, and a list of paths to test, returns the first
@@ -651,14 +650,7 @@ function resolveExports(nmPath, request) {
651650// We don't cache this in case user extends the extensions.
652651function getDefaultExtensions ( ) {
653652 const extensions = ObjectKeys ( Module . _extensions ) ;
654- if ( ! getOptionValue ( '--experimental-require-module' ) ) {
655- return extensions ;
656- }
657- // If the .mjs extension is added by --experimental-require-module,
658- // remove it from the supported default extensions to maintain
659- // compatibility.
660- // TODO(joyeecheung): allow both .mjs and .cjs?
661- return ArrayPrototypeFilter ( extensions , ( ext ) => ext !== '.mjs' || Module . _extensions [ '.mjs' ] !== loadESMFromCJS ) ;
653+ return extensions ;
662654}
663655
664656/**
@@ -1270,10 +1262,6 @@ Module.prototype.load = function(filename) {
12701262 this . paths = Module . _nodeModulePaths ( path . dirname ( filename ) ) ;
12711263
12721264 const extension = findLongestRegisteredExtension ( filename ) ;
1273- // allow .mjs to be overridden
1274- if ( StringPrototypeEndsWith ( filename , '.mjs' ) && ! Module . _extensions [ '.mjs' ] ) {
1275- throw new ERR_REQUIRE_ESM ( filename , true ) ;
1276- }
12771265
12781266 Module . _extensions [ extension ] ( this , filename ) ;
12791267 this . loaded = true ;
@@ -1309,9 +1297,10 @@ let requireModuleWarningMode;
13091297 * Resolve and evaluate it synchronously as ESM if it's ESM.
13101298 * @param {Module } mod CJS module instance
13111299 * @param {string } filename Absolute path of the file.
1300+ * @param {string } format Format of the module. If it had types, this would be what it is after type-stripping.
1301+ * @param {string } source Source the module. If it had types, this would have the type stripped.
13121302 */
1313- function loadESMFromCJS ( mod , filename ) {
1314- const source = getMaybeCachedSource ( mod , filename ) ;
1303+ function loadESMFromCJS ( mod , filename , format , source ) {
13151304 const cascadedLoader = require ( 'internal/modules/esm/loader' ) . getOrInitializeCascadedLoader ( ) ;
13161305 const isMain = mod [ kIsMainSymbol ] ;
13171306 if ( isMain ) {
@@ -1487,7 +1476,9 @@ function wrapSafe(filename, content, cjsModuleInstance, format) {
14871476 * `exports`) to the file. Returns exception, if any.
14881477 * @param {string } content The source code of the module
14891478 * @param {string } filename The file path of the module
1490- * @param {'module'|'commonjs'|undefined } format Intended format of the module.
1479+ * @param {
1480+ * 'module'|'commonjs'|'commonjs-typescript'|'module-typescript'
1481+ * } format Intended format of the module.
14911482 */
14921483Module . prototype . _compile = function ( content , filename , format ) {
14931484 let moduleURL ;
@@ -1509,9 +1500,7 @@ Module.prototype._compile = function(content, filename, format) {
15091500 }
15101501
15111502 if ( format === 'module' ) {
1512- // Pass the source into the .mjs extension handler indirectly through the cache.
1513- this [ kModuleSource ] = content ;
1514- loadESMFromCJS ( this , filename ) ;
1503+ loadESMFromCJS ( this , filename , format , content ) ;
15151504 return ;
15161505 }
15171506
@@ -1539,22 +1528,72 @@ Module.prototype._compile = function(content, filename, format) {
15391528
15401529/**
15411530 * Get the source code of a module, using cached ones if it's cached.
1531+ * After this returns, mod[kFormat], mod[kModuleSource] and mod[kURL] will be set.
15421532 * @param {Module } mod Module instance whose source is potentially already cached.
15431533 * @param {string } filename Absolute path to the file of the module.
1544- * @returns {string }
1534+ * @returns {{source: string, format?: string} }
15451535 */
1546- function getMaybeCachedSource ( mod , filename ) {
1547- // If already analyzed the source, then it will be cached.
1548- let content ;
1549- if ( mod [ kModuleSource ] !== undefined ) {
1550- content = mod [ kModuleSource ] ;
1536+ function loadSource ( mod , filename , formatFromNode ) {
1537+ if ( formatFromNode !== undefined ) {
1538+ mod [ kFormat ] = formatFromNode ;
1539+ }
1540+ const format = mod [ kFormat ] ;
1541+
1542+ let source = mod [ kModuleSource ] ;
1543+ if ( source !== undefined ) {
15511544 mod [ kModuleSource ] = undefined ;
15521545 } else {
15531546 // TODO(joyeecheung): we can read a buffer instead to speed up
15541547 // compilation.
1555- content = fs . readFileSync ( filename , 'utf8' ) ;
1548+ source = fs . readFileSync ( filename , 'utf8' ) ;
1549+ }
1550+ return { source, format } ;
1551+ }
1552+
1553+ function reconstructErrorStack ( err , parentPath , parentSource ) {
1554+ const errLine = StringPrototypeSplit (
1555+ StringPrototypeSlice ( err . stack , StringPrototypeIndexOf (
1556+ err . stack , ' at ' ) ) , '\n' , 1 ) [ 0 ] ;
1557+ const { 1 : line , 2 : col } =
1558+ RegExpPrototypeExec ( / ( \d + ) : ( \d + ) \) / , errLine ) || [ ] ;
1559+ if ( line && col ) {
1560+ const srcLine = StringPrototypeSplit ( parentSource , '\n' ) [ line - 1 ] ;
1561+ const frame = `${ parentPath } :${ line } \n${ srcLine } \n${ StringPrototypeRepeat ( ' ' , col - 1 ) } ^\n` ;
1562+ setArrowMessage ( err , frame ) ;
1563+ }
1564+ }
1565+
1566+ /**
1567+ * Generate the legacy ERR_REQUIRE_ESM for the cases where require(esm) is disabled.
1568+ * @param {Module } mod The module being required.
1569+ * @param {undefined|object } pkg Data of the nearest package.json of the module.
1570+ * @param {string } content Source code of the module.
1571+ * @param {string } filename Filename of the module
1572+ * @returns {Error }
1573+ */
1574+ function getRequireESMError ( mod , pkg , content , filename ) {
1575+ // This is an error path because `require` of a `.js` file in a `"type": "module"` scope is not allowed.
1576+ const parent = mod [ kModuleParent ] ;
1577+ const parentPath = parent ?. filename ;
1578+ const packageJsonPath = pkg ?. path ? path . resolve ( pkg . path , 'package.json' ) : null ;
1579+ const usesEsm = containsModuleSyntax ( content , filename ) ;
1580+ const err = new ERR_REQUIRE_ESM ( filename , usesEsm , parentPath ,
1581+ packageJsonPath ) ;
1582+ // Attempt to reconstruct the parent require frame.
1583+ const parentModule = Module . _cache [ parentPath ] ;
1584+ if ( parentModule ) {
1585+ let parentSource ;
1586+ try {
1587+ ( { source : parentSource } = loadSource ( parentModule , parentPath ) ) ;
1588+ } catch {
1589+ // Continue regardless of error.
1590+ }
1591+ if ( parentSource ) {
1592+ // TODO(joyeecheung): trim off internal frames from the stack.
1593+ reconstructErrorStack ( err , parentPath , parentSource ) ;
1594+ }
15561595 }
1557- return content ;
1596+ return err ;
15581597}
15591598
15601599/**
@@ -1563,57 +1602,25 @@ function getMaybeCachedSource(mod, filename) {
15631602 * @param {string } filename The file path of the module
15641603 */
15651604Module . _extensions [ '.js' ] = function ( module , filename ) {
1566- // If already analyzed the source, then it will be cached.
1567- const content = getMaybeCachedSource ( module , filename ) ;
1568-
1569- let format ;
1570- if ( StringPrototypeEndsWith ( filename , '.js' ) ) {
1571- const pkg = packageJsonReader . readPackageScope ( filename ) || { __proto__ : null } ;
1572- // Function require shouldn't be used in ES modules.
1573- if ( pkg . data ?. type === 'module' ) {
1574- if ( getOptionValue ( '--experimental-require-module' ) ) {
1575- module . _compile ( content , filename , 'module' ) ;
1576- return ;
1577- }
1578-
1579- // This is an error path because `require` of a `.js` file in a `"type": "module"` scope is not allowed.
1580- const parent = module [ kModuleParent ] ;
1581- const parentPath = parent ?. filename ;
1582- const packageJsonPath = path . resolve ( pkg . path , 'package.json' ) ;
1583- const usesEsm = containsModuleSyntax ( content , filename ) ;
1584- const err = new ERR_REQUIRE_ESM ( filename , usesEsm , parentPath ,
1585- packageJsonPath ) ;
1586- // Attempt to reconstruct the parent require frame.
1587- if ( Module . _cache [ parentPath ] ) {
1588- let parentSource ;
1589- try {
1590- parentSource = fs . readFileSync ( parentPath , 'utf8' ) ;
1591- } catch {
1592- // Continue regardless of error.
1593- }
1594- if ( parentSource ) {
1595- const errLine = StringPrototypeSplit (
1596- StringPrototypeSlice ( err . stack , StringPrototypeIndexOf (
1597- err . stack , ' at ' ) ) , '\n' , 1 ) [ 0 ] ;
1598- const { 1 : line , 2 : col } =
1599- RegExpPrototypeExec ( / ( \d + ) : ( \d + ) \) / , errLine ) || [ ] ;
1600- if ( line && col ) {
1601- const srcLine = StringPrototypeSplit ( parentSource , '\n' ) [ line - 1 ] ;
1602- const frame = `${ parentPath } :${ line } \n${ srcLine } \n${
1603- StringPrototypeRepeat ( ' ' , col - 1 ) } ^\n`;
1604- setArrowMessage ( err , frame ) ;
1605- }
1606- }
1607- }
1608- throw err ;
1609- } else if ( pkg . data ?. type === 'commonjs' ) {
1610- format = 'commonjs' ;
1611- }
1612- } else if ( StringPrototypeEndsWith ( filename , '.cjs' ) ) {
1605+ let format , pkg ;
1606+ if ( StringPrototypeEndsWith ( filename , '.cjs' ) ) {
16131607 format = 'commonjs' ;
1608+ } else if ( StringPrototypeEndsWith ( filename , '.mjs' ) ) {
1609+ format = 'module' ;
1610+ } else if ( StringPrototypeEndsWith ( filename , '.js' ) ) {
1611+ pkg = packageJsonReader . readPackageScope ( filename ) || { __proto__ : null } ;
1612+ const typeFromPjson = pkg . data ?. type ;
1613+ if ( typeFromPjson === 'module' || typeFromPjson === 'commonjs' || ! typeFromPjson ) {
1614+ format = typeFromPjson ;
1615+ }
16141616 }
1615-
1616- module . _compile ( content , filename , format ) ;
1617+ const { source, format : loadedFormat } = loadSource ( module , filename , format ) ;
1618+ // Function require shouldn't be used in ES modules when require(esm) is disabled.
1619+ if ( loadedFormat === 'module' && ! getOptionValue ( '--experimental-require-module' ) ) {
1620+ const err = getRequireESMError ( module , pkg , source , filename ) ;
1621+ throw err ;
1622+ }
1623+ module . _compile ( source , filename , loadedFormat ) ;
16171624} ;
16181625
16191626/**
0 commit comments