@@ -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,
@@ -437,9 +440,8 @@ function initializeCJS() {
437440 Module . _extensions [ '.ts' ] = loadTS ;
438441 }
439442 if ( getOptionValue ( '--experimental-require-module' ) ) {
440- Module . _extensions [ '.mjs' ] = loadESMFromCJS ;
441443 if ( tsEnabled ) {
442- Module . _extensions [ '.mts' ] = loadESMFromCJS ;
444+ Module . _extensions [ '.mts' ] = loadMTS ;
443445 }
444446 }
445447}
@@ -654,8 +656,6 @@ function getDefaultExtensions() {
654656 if ( tsEnabled ) {
655657 // remove .ts and .cts from the default extensions
656658 // to avoid extensionless require of .ts and .cts files.
657- // it behaves similarly to how .mjs is handled when --experimental-require-module
658- // is enabled.
659659 extensions = ArrayPrototypeFilter ( extensions , ( ext ) =>
660660 ( ext !== '.ts' || Module . _extensions [ '.ts' ] !== loadTS ) &&
661661 ( ext !== '.cts' || Module . _extensions [ '.cts' ] !== loadCTS ) ,
@@ -668,14 +668,10 @@ function getDefaultExtensions() {
668668
669669 if ( tsEnabled ) {
670670 extensions = ArrayPrototypeFilter ( extensions , ( ext ) =>
671- ext !== '.mts' || Module . _extensions [ '.mts' ] !== loadESMFromCJS ,
671+ ext !== '.mts' || Module . _extensions [ '.mts' ] !== loadMTS ,
672672 ) ;
673673 }
674- // If the .mjs extension is added by --experimental-require-module,
675- // remove it from the supported default extensions to maintain
676- // compatibility.
677- // TODO(joyeecheung): allow both .mjs and .cjs?
678- return ArrayPrototypeFilter ( extensions , ( ext ) => ext !== '.mjs' || Module . _extensions [ '.mjs' ] !== loadESMFromCJS ) ;
674+ return extensions ;
679675}
680676
681677/**
@@ -1284,10 +1280,6 @@ Module.prototype.load = function(filename) {
12841280 this . paths = Module . _nodeModulePaths ( path . dirname ( filename ) ) ;
12851281
12861282 const extension = findLongestRegisteredExtension ( filename ) ;
1287- // allow .mjs to be overridden
1288- if ( StringPrototypeEndsWith ( filename , '.mjs' ) && ! Module . _extensions [ '.mjs' ] ) {
1289- throw new ERR_REQUIRE_ESM ( filename , true ) ;
1290- }
12911283
12921284 if ( getOptionValue ( '--experimental-strip-types' ) ) {
12931285 if ( StringPrototypeEndsWith ( filename , '.mts' ) && ! Module . _extensions [ '.mts' ] ) {
@@ -1328,12 +1320,10 @@ let requireModuleWarningMode;
13281320 * Resolve and evaluate it synchronously as ESM if it's ESM.
13291321 * @param {Module } mod CJS module instance
13301322 * @param {string } filename Absolute path of the file.
1323+ * @param {string } format Format of the module. If it had types, this would be what it is after type-stripping.
1324+ * @param {string } source Source the module. If it had types, this would have the type stripped.
13311325 */
1332- function loadESMFromCJS ( mod , filename ) {
1333- let source = getMaybeCachedSource ( mod , filename ) ;
1334- if ( getOptionValue ( '--experimental-strip-types' ) && path . extname ( filename ) === '.mts' ) {
1335- source = stripTypeScriptModuleTypes ( source , filename ) ;
1336- }
1326+ function loadESMFromCJS ( mod , filename , format , source ) {
13371327 const cascadedLoader = require ( 'internal/modules/esm/loader' ) . getOrInitializeCascadedLoader ( ) ;
13381328 const isMain = mod [ kIsMainSymbol ] ;
13391329 if ( isMain ) {
@@ -1509,9 +1499,30 @@ function wrapSafe(filename, content, cjsModuleInstance, format) {
15091499 * `exports`) to the file. Returns exception, if any.
15101500 * @param {string } content The source code of the module
15111501 * @param {string } filename The file path of the module
1512- * @param {'module'|'commonjs'|undefined } format Intended format of the module.
1502+ * @param {
1503+ * 'module'|'commonjs'|'commonjs-typescript'|'module-typescript'
1504+ * } format Intended format of the module.
15131505 */
15141506Module . prototype . _compile = function ( content , filename , format ) {
1507+ if ( format === 'commonjs-typescript' || format === 'module-typescript' || format === 'typescript' ) {
1508+ content = stripTypeScriptModuleTypes ( content , filename ) ;
1509+ switch ( format ) {
1510+ case 'commonjs-typescript' : {
1511+ format = 'commonjs' ;
1512+ break ;
1513+ }
1514+ case 'module-typescript' : {
1515+ format = 'module' ;
1516+ break ;
1517+ }
1518+ // If the format is still unknown i.e. 'typescript', detect it in
1519+ // wrapSafe using the type-stripped source.
1520+ default :
1521+ format = undefined ;
1522+ break ;
1523+ }
1524+ }
1525+
15151526 let redirects ;
15161527
15171528 let compiledWrapper ;
@@ -1524,9 +1535,7 @@ Module.prototype._compile = function(content, filename, format) {
15241535 }
15251536
15261537 if ( format === 'module' ) {
1527- // Pass the source into the .mjs extension handler indirectly through the cache.
1528- this [ kModuleSource ] = content ;
1529- loadESMFromCJS ( this , filename ) ;
1538+ loadESMFromCJS ( this , filename , format , content ) ;
15301539 return ;
15311540 }
15321541
@@ -1554,72 +1563,76 @@ Module.prototype._compile = function(content, filename, format) {
15541563
15551564/**
15561565 * Get the source code of a module, using cached ones if it's cached.
1566+ * After this returns, mod[kFormat], mod[kModuleSource] and mod[kURL] will be set.
15571567 * @param {Module } mod Module instance whose source is potentially already cached.
15581568 * @param {string } filename Absolute path to the file of the module.
1559- * @returns {string }
1569+ * @returns {{source: string, format?: string} }
15601570 */
1561- function getMaybeCachedSource ( mod , filename ) {
1562- // If already analyzed the source, then it will be cached.
1563- let content ;
1564- if ( mod [ kModuleSource ] !== undefined ) {
1565- content = mod [ kModuleSource ] ;
1571+ function loadSource ( mod , filename , formatFromNode ) {
1572+ if ( formatFromNode !== undefined ) {
1573+ mod [ kFormat ] = formatFromNode ;
1574+ }
1575+ const format = mod [ kFormat ] ;
1576+
1577+ let source = mod [ kModuleSource ] ;
1578+ if ( source !== undefined ) {
15661579 mod [ kModuleSource ] = undefined ;
15671580 } else {
15681581 // TODO(joyeecheung): we can read a buffer instead to speed up
15691582 // compilation.
1570- content = fs . readFileSync ( filename , 'utf8' ) ;
1583+ source = fs . readFileSync ( filename , 'utf8' ) ;
15711584 }
1572- return content ;
1585+ return { source , format } ;
15731586}
15741587
1588+ /**
1589+ * Built-in handler for `.mts` files.
1590+ * @param {Module } mod CJS module instance
1591+ * @param {string } filename The file path of the module
1592+ */
1593+ function loadMTS ( mod , filename ) {
1594+ const loadResult = loadSource ( mod , filename , 'module-typescript' ) ;
1595+ mod . _compile ( loadResult . source , filename , loadResult . format ) ;
1596+ }
1597+
1598+ /**
1599+ * Built-in handler for `.cts` files.
1600+ * @param {Module } module CJS module instance
1601+ * @param {string } filename The file path of the module
1602+ */
1603+
15751604function loadCTS ( module , filename ) {
1576- const source = getMaybeCachedSource ( module , filename ) ;
1577- const code = stripTypeScriptModuleTypes ( source , filename ) ;
1578- module . _compile ( code , filename , 'commonjs' ) ;
1605+ const loadResult = loadSource ( module , filename , 'commonjs-typescript' ) ;
1606+ module . _compile ( loadResult . source , filename , loadResult . format ) ;
15791607}
15801608
15811609/**
15821610 * Built-in handler for `.ts` files.
1583- * @param {Module } module The module to compile
1611+ * @param {Module } module CJS module instance
15841612 * @param {string } filename The file path of the module
15851613 */
15861614function loadTS ( module , filename ) {
1587- // If already analyzed the source, then it will be cached.
1588- const source = getMaybeCachedSource ( module , filename ) ;
1589- const content = stripTypeScriptModuleTypes ( source , filename ) ;
1590- let format ;
15911615 const pkg = packageJsonReader . getNearestParentPackageJSON ( filename ) ;
1592- // Function require shouldn't be used in ES modules.
1593- if ( pkg ?. data . type === 'module' ) {
1594- if ( getOptionValue ( '--experimental-require-module' ) ) {
1595- module . _compile ( content , filename , 'module' ) ;
1596- return ;
1597- }
1616+ const typeFromPjson = pkg ?. data . type ;
15981617
1599- const parent = module [ kModuleParent ] ;
1600- const parentPath = parent ?. filename ;
1601- const packageJsonPath = pkg . path ;
1602- const usesEsm = containsModuleSyntax ( content , filename ) ;
1603- const err = new ERR_REQUIRE_ESM ( filename , usesEsm , parentPath ,
1604- packageJsonPath ) ;
1605- // Attempt to reconstruct the parent require frame.
1606- if ( Module . _cache [ parentPath ] ) {
1607- let parentSource ;
1608- try {
1609- parentSource = stripTypeScriptModuleTypes ( fs . readFileSync ( parentPath , 'utf8' ) , parentPath ) ;
1610- } catch {
1611- // Continue regardless of error.
1612- }
1613- if ( parentSource ) {
1614- reconstructErrorStack ( err , parentPath , parentSource ) ;
1615- }
1616- }
1618+ let format ;
1619+ if ( typeFromPjson === 'module' ) {
1620+ format = 'module-typescript' ;
1621+ } else if ( typeFromPjson === 'commonjs' ) {
1622+ format = 'commonjs-typescript' ;
1623+ } else {
1624+ format = 'typescript' ;
1625+ }
1626+ const loadResult = loadSource ( module , filename , format ) ;
1627+
1628+ // Function require shouldn't be used in ES modules when require(esm) is disabled.
1629+ if ( typeFromPjson === 'module' && ! getOptionValue ( '--experimental-require-module' ) ) {
1630+ const err = getRequireESMError ( module , pkg , loadResult . source , filename ) ;
16171631 throw err ;
1618- } else if ( pkg ?. data . type === 'commonjs' ) {
1619- format = 'commonjs' ;
16201632 }
16211633
1622- module . _compile ( content , filename , format ) ;
1634+ module [ kFormat ] = loadResult . format ;
1635+ module . _compile ( loadResult . source , filename , loadResult . format ) ;
16231636} ;
16241637
16251638function reconstructErrorStack ( err , parentPath , parentSource ) {
@@ -1635,53 +1648,64 @@ function reconstructErrorStack(err, parentPath, parentSource) {
16351648 }
16361649}
16371650
1651+ /**
1652+ * Generate the legacy ERR_REQUIRE_ESM for the cases where require(esm) is disabled.
1653+ * @param {Module } mod The module being required.
1654+ * @param {undefined|object } pkg Data of the nearest package.json of the module.
1655+ * @param {string } content Source code of the module.
1656+ * @param {string } filename Filename of the module
1657+ * @returns {Error }
1658+ */
1659+ function getRequireESMError ( mod , pkg , content , filename ) {
1660+ // This is an error path because `require` of a `.js` file in a `"type": "module"` scope is not allowed.
1661+ const parent = mod [ kModuleParent ] ;
1662+ const parentPath = parent ?. filename ;
1663+ const packageJsonPath = pkg ?. path ;
1664+ const usesEsm = containsModuleSyntax ( content , filename ) ;
1665+ const err = new ERR_REQUIRE_ESM ( filename , usesEsm , parentPath ,
1666+ packageJsonPath ) ;
1667+ // Attempt to reconstruct the parent require frame.
1668+ const parentModule = Module . _cache [ parentPath ] ;
1669+ if ( parentModule ) {
1670+ let parentSource ;
1671+ try {
1672+ ( { source : parentSource } = loadSource ( parentModule , parentPath ) ) ;
1673+ } catch {
1674+ // Continue regardless of error.
1675+ }
1676+ if ( parentSource ) {
1677+ // TODO(joyeecheung): trim off internal frames from the stack.
1678+ reconstructErrorStack ( err , parentPath , parentSource ) ;
1679+ }
1680+ }
1681+ return err ;
1682+ }
1683+
16381684/**
16391685 * Built-in handler for `.js` files.
16401686 * @param {Module } module The module to compile
16411687 * @param {string } filename The file path of the module
16421688 */
16431689Module . _extensions [ '.js' ] = function ( module , filename ) {
1644- // If already analyzed the source, then it will be cached.
1645- const content = getMaybeCachedSource ( module , filename ) ;
1646-
1647- let format ;
1648- if ( StringPrototypeEndsWith ( filename , '.js' ) ) {
1649- const pkg = packageJsonReader . getNearestParentPackageJSON ( filename ) ;
1650- // Function require shouldn't be used in ES modules.
1651- if ( pkg ?. data . type === 'module' ) {
1652- if ( getOptionValue ( '--experimental-require-module' ) ) {
1653- module . _compile ( content , filename , 'module' ) ;
1654- return ;
1655- }
1656-
1657- // This is an error path because `require` of a `.js` file in a `"type": "module"` scope is not allowed.
1658- const parent = module [ kModuleParent ] ;
1659- const parentPath = parent ?. filename ;
1660- const packageJsonPath = pkg . path ;
1661- const usesEsm = containsModuleSyntax ( content , filename ) ;
1662- const err = new ERR_REQUIRE_ESM ( filename , usesEsm , parentPath ,
1663- packageJsonPath ) ;
1664- // Attempt to reconstruct the parent require frame.
1665- if ( Module . _cache [ parentPath ] ) {
1666- let parentSource ;
1667- try {
1668- parentSource = fs . readFileSync ( parentPath , 'utf8' ) ;
1669- } catch {
1670- // Continue regardless of error.
1671- }
1672- if ( parentSource ) {
1673- reconstructErrorStack ( err , parentPath , parentSource ) ;
1674- }
1675- }
1676- throw err ;
1677- } else if ( pkg ?. data . type === 'commonjs' ) {
1678- format = 'commonjs' ;
1679- }
1680- } else if ( StringPrototypeEndsWith ( filename , '.cjs' ) ) {
1690+ let format , pkg ;
1691+ if ( StringPrototypeEndsWith ( filename , '.cjs' ) ) {
16811692 format = 'commonjs' ;
1693+ } else if ( StringPrototypeEndsWith ( filename , '.mjs' ) ) {
1694+ format = 'module' ;
1695+ } else if ( StringPrototypeEndsWith ( filename , '.js' ) ) {
1696+ pkg = packageJsonReader . getNearestParentPackageJSON ( filename ) ;
1697+ const typeFromPjson = pkg ?. data . type ;
1698+ if ( typeFromPjson === 'module' || typeFromPjson === 'commonjs' || ! typeFromPjson ) {
1699+ format = typeFromPjson ;
1700+ }
16821701 }
1683-
1684- module . _compile ( content , filename , format ) ;
1702+ const { source, format : loadedFormat } = loadSource ( module , filename , format ) ;
1703+ // Function require shouldn't be used in ES modules when require(esm) is disabled.
1704+ if ( loadedFormat === 'module' && ! getOptionValue ( '--experimental-require-module' ) ) {
1705+ const err = getRequireESMError ( module , pkg , source , filename ) ;
1706+ throw err ;
1707+ }
1708+ module . _compile ( source , filename , loadedFormat ) ;
16851709} ;
16861710
16871711/**
0 commit comments