@@ -100,6 +100,9 @@ const kIsMainSymbol = Symbol('kIsMainSymbol');
100
100
const kIsCachedByESMLoader = Symbol ( 'kIsCachedByESMLoader' ) ;
101
101
const kRequiredModuleSymbol = Symbol ( 'kRequiredModuleSymbol' ) ;
102
102
const kIsExecuting = Symbol ( 'kIsExecuting' ) ;
103
+
104
+ const kFormat = Symbol ( 'kFormat' ) ;
105
+
103
106
// Set first due to cycle with ESM loader functions.
104
107
module . exports = {
105
108
kModuleSource,
@@ -436,9 +439,8 @@ function initializeCJS() {
436
439
Module . _extensions [ '.ts' ] = loadTS ;
437
440
}
438
441
if ( getOptionValue ( '--experimental-require-module' ) ) {
439
- Module . _extensions [ '.mjs' ] = loadESMFromCJS ;
440
442
if ( tsEnabled ) {
441
- Module . _extensions [ '.mts' ] = loadESMFromCJS ;
443
+ Module . _extensions [ '.mts' ] = loadMTS ;
442
444
}
443
445
}
444
446
}
@@ -653,8 +655,6 @@ function getDefaultExtensions() {
653
655
if ( tsEnabled ) {
654
656
// remove .ts and .cts from the default extensions
655
657
// 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.
658
658
extensions = ArrayPrototypeFilter ( extensions , ( ext ) =>
659
659
( ext !== '.ts' || Module . _extensions [ '.ts' ] !== loadTS ) &&
660
660
( ext !== '.cts' || Module . _extensions [ '.cts' ] !== loadCTS ) ,
@@ -667,14 +667,10 @@ function getDefaultExtensions() {
667
667
668
668
if ( tsEnabled ) {
669
669
extensions = ArrayPrototypeFilter ( extensions , ( ext ) =>
670
- ext !== '.mts' || Module . _extensions [ '.mts' ] !== loadESMFromCJS ,
670
+ ext !== '.mts' || Module . _extensions [ '.mts' ] !== loadMTS ,
671
671
) ;
672
672
}
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 ;
678
674
}
679
675
680
676
/**
@@ -1301,10 +1297,6 @@ Module.prototype.load = function(filename) {
1301
1297
this . paths = Module . _nodeModulePaths ( path . dirname ( filename ) ) ;
1302
1298
1303
1299
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
- }
1308
1300
1309
1301
if ( getOptionValue ( '--experimental-strip-types' ) ) {
1310
1302
if ( StringPrototypeEndsWith ( filename , '.mts' ) && ! Module . _extensions [ '.mts' ] ) {
@@ -1353,12 +1345,10 @@ let hasPausedEntry = false;
1353
1345
* Resolve and evaluate it synchronously as ESM if it's ESM.
1354
1346
* @param {Module } mod CJS module instance
1355
1347
* @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.
1356
1350
*/
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 ) {
1362
1352
const cascadedLoader = require ( 'internal/modules/esm/loader' ) . getOrInitializeCascadedLoader ( ) ;
1363
1353
const isMain = mod [ kIsMainSymbol ] ;
1364
1354
if ( isMain ) {
@@ -1512,9 +1502,30 @@ function wrapSafe(filename, content, cjsModuleInstance, format) {
1512
1502
* `exports`) to the file. Returns exception, if any.
1513
1503
* @param {string } content The source code of the module
1514
1504
* @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.
1516
1508
*/
1517
1509
Module . 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
+
1518
1529
let redirects ;
1519
1530
1520
1531
let compiledWrapper ;
@@ -1527,9 +1538,7 @@ Module.prototype._compile = function(content, filename, format) {
1527
1538
}
1528
1539
1529
1540
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 ) ;
1533
1542
return ;
1534
1543
}
1535
1544
@@ -1582,72 +1591,76 @@ Module.prototype._compile = function(content, filename, format) {
1582
1591
1583
1592
/**
1584
1593
* 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.
1585
1595
* @param {Module } mod Module instance whose source is potentially already cached.
1586
1596
* @param {string } filename Absolute path to the file of the module.
1587
- * @returns {string }
1597
+ * @returns {{source: string, format?: string} }
1588
1598
*/
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 ) {
1594
1607
mod [ kModuleSource ] = undefined ;
1595
1608
} else {
1596
1609
// TODO(joyeecheung): we can read a buffer instead to speed up
1597
1610
// compilation.
1598
- content = fs . readFileSync ( filename , 'utf8' ) ;
1611
+ source = fs . readFileSync ( filename , 'utf8' ) ;
1599
1612
}
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 ) ;
1601
1624
}
1602
1625
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
+
1603
1632
function 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 ) ;
1607
1635
}
1608
1636
1609
1637
/**
1610
1638
* Built-in handler for `.ts` files.
1611
- * @param {Module } module The module to compile
1639
+ * @param {Module } module CJS module instance
1612
1640
* @param {string } filename The file path of the module
1613
1641
*/
1614
1642
function 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 ;
1619
1643
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 ;
1626
1645
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 ) ;
1645
1659
throw err ;
1646
- } else if ( pkg ?. data . type === 'commonjs' ) {
1647
- format = 'commonjs' ;
1648
1660
}
1649
1661
1650
- module . _compile ( content , filename , format ) ;
1662
+ module [ kFormat ] = loadResult . format ;
1663
+ module . _compile ( loadResult . source , filename , loadResult . format ) ;
1651
1664
} ;
1652
1665
1653
1666
function reconstructErrorStack ( err , parentPath , parentSource ) {
@@ -1663,53 +1676,64 @@ function reconstructErrorStack(err, parentPath, parentSource) {
1663
1676
}
1664
1677
}
1665
1678
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
+
1666
1712
/**
1667
1713
* Built-in handler for `.js` files.
1668
1714
* @param {Module } module The module to compile
1669
1715
* @param {string } filename The file path of the module
1670
1716
*/
1671
1717
Module . _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' ) ) {
1709
1720
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
+ }
1710
1729
}
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 ) ;
1713
1737
} ;
1714
1738
1715
1739
/**
0 commit comments