@@ -101,6 +101,9 @@ const kIsMainSymbol = Symbol('kIsMainSymbol');
101
101
const kIsCachedByESMLoader = Symbol ( 'kIsCachedByESMLoader' ) ;
102
102
const kRequiredModuleSymbol = Symbol ( 'kRequiredModuleSymbol' ) ;
103
103
const kIsExecuting = Symbol ( 'kIsExecuting' ) ;
104
+
105
+ const kFormat = Symbol ( 'kFormat' ) ;
106
+
104
107
// Set first due to cycle with ESM loader functions.
105
108
module . exports = {
106
109
kModuleSource,
@@ -438,10 +441,6 @@ function initializeCJS() {
438
441
// TODO(joyeecheung): deprecate this in favor of a proper hook?
439
442
Module . runMain =
440
443
require ( 'internal/modules/run_main' ) . executeUserEntryPoint ;
441
-
442
- if ( getOptionValue ( '--experimental-require-module' ) ) {
443
- Module . _extensions [ '.mjs' ] = loadESMFromCJS ;
444
- }
445
444
}
446
445
447
446
// Given a module name, and a list of paths to test, returns the first
@@ -651,14 +650,7 @@ function resolveExports(nmPath, request) {
651
650
// We don't cache this in case user extends the extensions.
652
651
function getDefaultExtensions ( ) {
653
652
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 ;
662
654
}
663
655
664
656
/**
@@ -1270,10 +1262,6 @@ Module.prototype.load = function(filename) {
1270
1262
this . paths = Module . _nodeModulePaths ( path . dirname ( filename ) ) ;
1271
1263
1272
1264
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
- }
1277
1265
1278
1266
Module . _extensions [ extension ] ( this , filename ) ;
1279
1267
this . loaded = true ;
@@ -1309,9 +1297,10 @@ let requireModuleWarningMode;
1309
1297
* Resolve and evaluate it synchronously as ESM if it's ESM.
1310
1298
* @param {Module } mod CJS module instance
1311
1299
* @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.
1312
1302
*/
1313
- function loadESMFromCJS ( mod , filename ) {
1314
- const source = getMaybeCachedSource ( mod , filename ) ;
1303
+ function loadESMFromCJS ( mod , filename , format , source ) {
1315
1304
const cascadedLoader = require ( 'internal/modules/esm/loader' ) . getOrInitializeCascadedLoader ( ) ;
1316
1305
const isMain = mod [ kIsMainSymbol ] ;
1317
1306
if ( isMain ) {
@@ -1487,7 +1476,9 @@ function wrapSafe(filename, content, cjsModuleInstance, format) {
1487
1476
* `exports`) to the file. Returns exception, if any.
1488
1477
* @param {string } content The source code of the module
1489
1478
* @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.
1491
1482
*/
1492
1483
Module . prototype . _compile = function ( content , filename , format ) {
1493
1484
let moduleURL ;
@@ -1509,9 +1500,7 @@ Module.prototype._compile = function(content, filename, format) {
1509
1500
}
1510
1501
1511
1502
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 ) ;
1515
1504
return ;
1516
1505
}
1517
1506
@@ -1539,22 +1528,72 @@ Module.prototype._compile = function(content, filename, format) {
1539
1528
1540
1529
/**
1541
1530
* 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.
1542
1532
* @param {Module } mod Module instance whose source is potentially already cached.
1543
1533
* @param {string } filename Absolute path to the file of the module.
1544
- * @returns {string }
1534
+ * @returns {{source: string, format?: string} }
1545
1535
*/
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 ) {
1551
1544
mod [ kModuleSource ] = undefined ;
1552
1545
} else {
1553
1546
// TODO(joyeecheung): we can read a buffer instead to speed up
1554
1547
// 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
+ }
1556
1595
}
1557
- return content ;
1596
+ return err ;
1558
1597
}
1559
1598
1560
1599
/**
@@ -1563,57 +1602,25 @@ function getMaybeCachedSource(mod, filename) {
1563
1602
* @param {string } filename The file path of the module
1564
1603
*/
1565
1604
Module . _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' ) ) {
1613
1607
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
+ }
1614
1616
}
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 ) ;
1617
1624
} ;
1618
1625
1619
1626
/**
0 commit comments