@@ -100,6 +100,9 @@ const kIsMainSymbol = Symbol('kIsMainSymbol');
100100const kIsCachedByESMLoader = Symbol ( 'kIsCachedByESMLoader' ) ;
101101const kRequiredModuleSymbol = Symbol ( 'kRequiredModuleSymbol' ) ;
102102const kIsExecuting = Symbol ( 'kIsExecuting' ) ;
103+
104+ const kFormat = Symbol ( 'kFormat' ) ;
105+
103106// Set first due to cycle with ESM loader functions.
104107module . exports = {
105108 kModuleSource,
@@ -436,9 +439,8 @@ function initializeCJS() {
436439 Module . _extensions [ '.ts' ] = loadTS ;
437440 }
438441 if ( getOptionValue ( '--experimental-require-module' ) ) {
439- Module . _extensions [ '.mjs' ] = loadESMFromCJS ;
440442 if ( tsEnabled ) {
441- Module . _extensions [ '.mts' ] = loadESMFromCJS ;
443+ Module . _extensions [ '.mts' ] = loadMTS ;
442444 }
443445 }
444446}
@@ -653,8 +655,6 @@ function getDefaultExtensions() {
653655 if ( tsEnabled ) {
654656 // remove .ts and .cts from the default extensions
655657 // to avoid extensionless require of .ts and .cts files.
656- // it behaves similarly to how .mjs is handled when --experimental-require-module
657- // is enabled.
658658 extensions = ArrayPrototypeFilter ( extensions , ( ext ) =>
659659 ( ext !== '.ts' || Module . _extensions [ '.ts' ] !== loadTS ) &&
660660 ( ext !== '.cts' || Module . _extensions [ '.cts' ] !== loadCTS ) ,
@@ -667,14 +667,10 @@ function getDefaultExtensions() {
667667
668668 if ( tsEnabled ) {
669669 extensions = ArrayPrototypeFilter ( extensions , ( ext ) =>
670- ext !== '.mts' || Module . _extensions [ '.mts' ] !== loadESMFromCJS ,
670+ ext !== '.mts' || Module . _extensions [ '.mts' ] !== loadMTS ,
671671 ) ;
672672 }
673- // If the .mjs extension is added by --experimental-require-module,
674- // remove it from the supported default extensions to maintain
675- // compatibility.
676- // TODO(joyeecheung): allow both .mjs and .cjs?
677- return ArrayPrototypeFilter ( extensions , ( ext ) => ext !== '.mjs' || Module . _extensions [ '.mjs' ] !== loadESMFromCJS ) ;
673+ return extensions ;
678674}
679675
680676/**
@@ -1301,10 +1297,6 @@ Module.prototype.load = function(filename) {
13011297 this . paths = Module . _nodeModulePaths ( path . dirname ( filename ) ) ;
13021298
13031299 const extension = findLongestRegisteredExtension ( filename ) ;
1304- // allow .mjs to be overridden
1305- if ( StringPrototypeEndsWith ( filename , '.mjs' ) && ! Module . _extensions [ '.mjs' ] ) {
1306- throw new ERR_REQUIRE_ESM ( filename , true ) ;
1307- }
13081300
13091301 if ( getOptionValue ( '--experimental-strip-types' ) ) {
13101302 if ( StringPrototypeEndsWith ( filename , '.mts' ) && ! Module . _extensions [ '.mts' ] ) {
@@ -1353,12 +1345,10 @@ let hasPausedEntry = false;
13531345 * Resolve and evaluate it synchronously as ESM if it's ESM.
13541346 * @param {Module } mod CJS module instance
13551347 * @param {string } filename Absolute path of the file.
1348+ * @param {string } format Format of the module. If it had types, this would be what it is after type-stripping.
1349+ * @param {string } source Source the module. If it had types, this would have the type stripped.
13561350 */
1357- function loadESMFromCJS ( mod , filename ) {
1358- let source = getMaybeCachedSource ( mod , filename ) ;
1359- if ( getOptionValue ( '--experimental-strip-types' ) && path . extname ( filename ) === '.mts' ) {
1360- source = stripTypeScriptModuleTypes ( source , filename ) ;
1361- }
1351+ function loadESMFromCJS ( mod , filename , format , source ) {
13621352 const cascadedLoader = require ( 'internal/modules/esm/loader' ) . getOrInitializeCascadedLoader ( ) ;
13631353 const isMain = mod [ kIsMainSymbol ] ;
13641354 if ( isMain ) {
@@ -1512,9 +1502,30 @@ function wrapSafe(filename, content, cjsModuleInstance, format) {
15121502 * `exports`) to the file. Returns exception, if any.
15131503 * @param {string } content The source code of the module
15141504 * @param {string } filename The file path of the module
1515- * @param {'module'|'commonjs'|undefined } format Intended format of the module.
1505+ * @param {
1506+ * 'module'|'commonjs'|'commonjs-typescript'|'module-typescript'
1507+ * } format Intended format of the module.
15161508 */
15171509Module . prototype . _compile = function ( content , filename , format ) {
1510+ if ( format === 'commonjs-typescript' || format === 'module-typescript' || format === 'typescript' ) {
1511+ content = stripTypeScriptModuleTypes ( content , filename ) ;
1512+ switch ( format ) {
1513+ case 'commonjs-typescript' : {
1514+ format = 'commonjs' ;
1515+ break ;
1516+ }
1517+ case 'module-typescript' : {
1518+ format = 'module' ;
1519+ break ;
1520+ }
1521+ // If the format is still unknown i.e. 'typescript', detect it in
1522+ // wrapSafe using the type-stripped source.
1523+ default :
1524+ format = undefined ;
1525+ break ;
1526+ }
1527+ }
1528+
15181529 let redirects ;
15191530
15201531 let compiledWrapper ;
@@ -1527,9 +1538,7 @@ Module.prototype._compile = function(content, filename, format) {
15271538 }
15281539
15291540 if ( format === 'module' ) {
1530- // Pass the source into the .mjs extension handler indirectly through the cache.
1531- this [ kModuleSource ] = content ;
1532- loadESMFromCJS ( this , filename ) ;
1541+ loadESMFromCJS ( this , filename , format , content ) ;
15331542 return ;
15341543 }
15351544
@@ -1582,72 +1591,76 @@ Module.prototype._compile = function(content, filename, format) {
15821591
15831592/**
15841593 * Get the source code of a module, using cached ones if it's cached.
1594+ * After this returns, mod[kFormat], mod[kModuleSource] and mod[kURL] will be set.
15851595 * @param {Module } mod Module instance whose source is potentially already cached.
15861596 * @param {string } filename Absolute path to the file of the module.
1587- * @returns {string }
1597+ * @returns {{source: string, format?: string} }
15881598 */
1589- function getMaybeCachedSource ( mod , filename ) {
1590- // If already analyzed the source, then it will be cached.
1591- let content ;
1592- if ( mod [ kModuleSource ] !== undefined ) {
1593- content = mod [ kModuleSource ] ;
1599+ function loadSource ( mod , filename , formatFromNode ) {
1600+ if ( formatFromNode !== undefined ) {
1601+ mod [ kFormat ] = formatFromNode ;
1602+ }
1603+ const format = mod [ kFormat ] ;
1604+
1605+ let source = mod [ kModuleSource ] ;
1606+ if ( source !== undefined ) {
15941607 mod [ kModuleSource ] = undefined ;
15951608 } else {
15961609 // TODO(joyeecheung): we can read a buffer instead to speed up
15971610 // compilation.
1598- content = fs . readFileSync ( filename , 'utf8' ) ;
1611+ source = fs . readFileSync ( filename , 'utf8' ) ;
15991612 }
1600- return content ;
1613+ return { source, format } ;
1614+ }
1615+
1616+ /**
1617+ * Built-in handler for `.mts` files.
1618+ * @param {Module } mod CJS module instance
1619+ * @param {string } filename The file path of the module
1620+ */
1621+ function loadMTS ( mod , filename ) {
1622+ const loadResult = loadSource ( mod , filename , 'module-typescript' ) ;
1623+ mod . _compile ( loadResult . source , filename , loadResult . format ) ;
16011624}
16021625
1626+ /**
1627+ * Built-in handler for `.cts` files.
1628+ * @param {Module } module CJS module instance
1629+ * @param {string } filename The file path of the module
1630+ */
1631+
16031632function loadCTS ( module , filename ) {
1604- const source = getMaybeCachedSource ( module , filename ) ;
1605- const code = stripTypeScriptModuleTypes ( source , filename ) ;
1606- module . _compile ( code , filename , 'commonjs' ) ;
1633+ const loadResult = loadSource ( module , filename , 'commonjs-typescript' ) ;
1634+ module . _compile ( loadResult . source , filename , loadResult . format ) ;
16071635}
16081636
16091637/**
16101638 * Built-in handler for `.ts` files.
1611- * @param {Module } module The module to compile
1639+ * @param {Module } module CJS module instance
16121640 * @param {string } filename The file path of the module
16131641 */
16141642function loadTS ( module , filename ) {
1615- // If already analyzed the source, then it will be cached.
1616- const source = getMaybeCachedSource ( module , filename ) ;
1617- const content = stripTypeScriptModuleTypes ( source , filename ) ;
1618- let format ;
16191643 const pkg = packageJsonReader . getNearestParentPackageJSON ( filename ) ;
1620- // Function require shouldn't be used in ES modules.
1621- if ( pkg ?. data . type === 'module' ) {
1622- if ( getOptionValue ( '--experimental-require-module' ) ) {
1623- module . _compile ( content , filename , 'module' ) ;
1624- return ;
1625- }
1644+ const typeFromPjson = pkg ?. data . type ;
16261645
1627- const parent = module [ kModuleParent ] ;
1628- const parentPath = parent ?. filename ;
1629- const packageJsonPath = pkg . path ;
1630- const usesEsm = containsModuleSyntax ( content , filename ) ;
1631- const err = new ERR_REQUIRE_ESM ( filename , usesEsm , parentPath ,
1632- packageJsonPath ) ;
1633- // Attempt to reconstruct the parent require frame.
1634- if ( Module . _cache [ parentPath ] ) {
1635- let parentSource ;
1636- try {
1637- parentSource = stripTypeScriptModuleTypes ( fs . readFileSync ( parentPath , 'utf8' ) , parentPath ) ;
1638- } catch {
1639- // Continue regardless of error.
1640- }
1641- if ( parentSource ) {
1642- reconstructErrorStack ( err , parentPath , parentSource ) ;
1643- }
1644- }
1646+ let format ;
1647+ if ( typeFromPjson === 'module' ) {
1648+ format = 'module-typescript' ;
1649+ } else if ( typeFromPjson === 'commonjs' ) {
1650+ format = 'commonjs-typescript' ;
1651+ } else {
1652+ format = 'typescript' ;
1653+ }
1654+ const loadResult = loadSource ( module , filename , format ) ;
1655+
1656+ // Function require shouldn't be used in ES modules when require(esm) is disabled.
1657+ if ( typeFromPjson === 'module' && ! getOptionValue ( '--experimental-require-module' ) ) {
1658+ const err = getRequireESMError ( module , pkg , loadResult . source , filename ) ;
16451659 throw err ;
1646- } else if ( pkg ?. data . type === 'commonjs' ) {
1647- format = 'commonjs' ;
16481660 }
16491661
1650- module . _compile ( content , filename , format ) ;
1662+ module [ kFormat ] = loadResult . format ;
1663+ module . _compile ( loadResult . source , filename , loadResult . format ) ;
16511664} ;
16521665
16531666function reconstructErrorStack ( err , parentPath , parentSource ) {
@@ -1663,53 +1676,64 @@ function reconstructErrorStack(err, parentPath, parentSource) {
16631676 }
16641677}
16651678
1679+ /**
1680+ * Generate the legacy ERR_REQUIRE_ESM for the cases where require(esm) is disabled.
1681+ * @param {Module } mod The module being required.
1682+ * @param {undefined|object } pkg Data of the nearest package.json of the module.
1683+ * @param {string } content Source code of the module.
1684+ * @param {string } filename Filename of the module
1685+ * @returns {Error }
1686+ */
1687+ function getRequireESMError ( mod , pkg , content , filename ) {
1688+ // This is an error path because `require` of a `.js` file in a `"type": "module"` scope is not allowed.
1689+ const parent = mod [ kModuleParent ] ;
1690+ const parentPath = parent ?. filename ;
1691+ const packageJsonPath = pkg ?. path ;
1692+ const usesEsm = containsModuleSyntax ( content , filename ) ;
1693+ const err = new ERR_REQUIRE_ESM ( filename , usesEsm , parentPath ,
1694+ packageJsonPath ) ;
1695+ // Attempt to reconstruct the parent require frame.
1696+ const parentModule = Module . _cache [ parentPath ] ;
1697+ if ( parentModule ) {
1698+ let parentSource ;
1699+ try {
1700+ ( { source : parentSource } = loadSource ( parentModule , parentPath ) ) ;
1701+ } catch {
1702+ // Continue regardless of error.
1703+ }
1704+ if ( parentSource ) {
1705+ // TODO(joyeecheung): trim off internal frames from the stack.
1706+ reconstructErrorStack ( err , parentPath , parentSource ) ;
1707+ }
1708+ }
1709+ return err ;
1710+ }
1711+
16661712/**
16671713 * Built-in handler for `.js` files.
16681714 * @param {Module } module The module to compile
16691715 * @param {string } filename The file path of the module
16701716 */
16711717Module . _extensions [ '.js' ] = function ( module , filename ) {
1672- // If already analyzed the source, then it will be cached.
1673- const content = getMaybeCachedSource ( module , filename ) ;
1674-
1675- let format ;
1676- if ( StringPrototypeEndsWith ( filename , '.js' ) ) {
1677- const pkg = packageJsonReader . getNearestParentPackageJSON ( filename ) ;
1678- // Function require shouldn't be used in ES modules.
1679- if ( pkg ?. data . type === 'module' ) {
1680- if ( getOptionValue ( '--experimental-require-module' ) ) {
1681- module . _compile ( content , filename , 'module' ) ;
1682- return ;
1683- }
1684-
1685- // This is an error path because `require` of a `.js` file in a `"type": "module"` scope is not allowed.
1686- const parent = module [ kModuleParent ] ;
1687- const parentPath = parent ?. filename ;
1688- const packageJsonPath = pkg . path ;
1689- const usesEsm = containsModuleSyntax ( content , filename ) ;
1690- const err = new ERR_REQUIRE_ESM ( filename , usesEsm , parentPath ,
1691- packageJsonPath ) ;
1692- // Attempt to reconstruct the parent require frame.
1693- if ( Module . _cache [ parentPath ] ) {
1694- let parentSource ;
1695- try {
1696- parentSource = fs . readFileSync ( parentPath , 'utf8' ) ;
1697- } catch {
1698- // Continue regardless of error.
1699- }
1700- if ( parentSource ) {
1701- reconstructErrorStack ( err , parentPath , parentSource ) ;
1702- }
1703- }
1704- throw err ;
1705- } else if ( pkg ?. data . type === 'commonjs' ) {
1706- format = 'commonjs' ;
1707- }
1708- } else if ( StringPrototypeEndsWith ( filename , '.cjs' ) ) {
1718+ let format , pkg ;
1719+ if ( StringPrototypeEndsWith ( filename , '.cjs' ) ) {
17091720 format = 'commonjs' ;
1721+ } else if ( StringPrototypeEndsWith ( filename , '.mjs' ) ) {
1722+ format = 'module' ;
1723+ } else if ( StringPrototypeEndsWith ( filename , '.js' ) ) {
1724+ pkg = packageJsonReader . getNearestParentPackageJSON ( filename ) ;
1725+ const typeFromPjson = pkg ?. data . type ;
1726+ if ( typeFromPjson === 'module' || typeFromPjson === 'commonjs' || ! typeFromPjson ) {
1727+ format = typeFromPjson ;
1728+ }
17101729 }
1711-
1712- module . _compile ( content , filename , format ) ;
1730+ const { source, format : loadedFormat } = loadSource ( module , filename , format ) ;
1731+ // Function require shouldn't be used in ES modules when require(esm) is disabled.
1732+ if ( loadedFormat === 'module' && ! getOptionValue ( '--experimental-require-module' ) ) {
1733+ const err = getRequireESMError ( module , pkg , source , filename ) ;
1734+ throw err ;
1735+ }
1736+ module . _compile ( source , filename , loadedFormat ) ;
17131737} ;
17141738
17151739/**
0 commit comments