@@ -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,
@@ -434,10 +437,6 @@ function initializeCJS() {
434437 // TODO(joyeecheung): deprecate this in favor of a proper hook?
435438 Module . runMain =
436439 require ( 'internal/modules/run_main' ) . executeUserEntryPoint ;
437-
438- if ( getOptionValue ( '--experimental-require-module' ) ) {
439- Module . _extensions [ '.mjs' ] = loadESMFromCJS ;
440- }
441440}
442441
443442// Given a module name, and a list of paths to test, returns the first
@@ -647,14 +646,7 @@ function resolveExports(nmPath, request) {
647646// We don't cache this in case user extends the extensions.
648647function getDefaultExtensions ( ) {
649648 const extensions = ObjectKeys ( Module . _extensions ) ;
650- if ( ! getOptionValue ( '--experimental-require-module' ) ) {
651- return extensions ;
652- }
653- // If the .mjs extension is added by --experimental-require-module,
654- // remove it from the supported default extensions to maintain
655- // compatibility.
656- // TODO(joyeecheung): allow both .mjs and .cjs?
657- return ArrayPrototypeFilter ( extensions , ( ext ) => ext !== '.mjs' || Module . _extensions [ '.mjs' ] !== loadESMFromCJS ) ;
649+ return extensions ;
658650}
659651
660652/**
@@ -1263,10 +1255,6 @@ Module.prototype.load = function(filename) {
12631255 this . paths = Module . _nodeModulePaths ( path . dirname ( filename ) ) ;
12641256
12651257 const extension = findLongestRegisteredExtension ( filename ) ;
1266- // allow .mjs to be overridden
1267- if ( StringPrototypeEndsWith ( filename , '.mjs' ) && ! Module . _extensions [ '.mjs' ] ) {
1268- throw new ERR_REQUIRE_ESM ( filename , true ) ;
1269- }
12701258
12711259 Module . _extensions [ extension ] ( this , filename ) ;
12721260 this . loaded = true ;
@@ -1302,9 +1290,10 @@ let requireModuleWarningMode;
13021290 * Resolve and evaluate it synchronously as ESM if it's ESM.
13031291 * @param {Module } mod CJS module instance
13041292 * @param {string } filename Absolute path of the file.
1293+ * @param {string } format Format of the module. If it had types, this would be what it is after type-stripping.
1294+ * @param {string } source Source the module. If it had types, this would have the type stripped.
13051295 */
1306- function loadESMFromCJS ( mod , filename ) {
1307- const source = getMaybeCachedSource ( mod , filename ) ;
1296+ function loadESMFromCJS ( mod , filename , format , source ) {
13081297 const cascadedLoader = require ( 'internal/modules/esm/loader' ) . getOrInitializeCascadedLoader ( ) ;
13091298 const isMain = mod [ kIsMainSymbol ] ;
13101299 if ( isMain ) {
@@ -1480,7 +1469,9 @@ function wrapSafe(filename, content, cjsModuleInstance, format) {
14801469 * `exports`) to the file. Returns exception, if any.
14811470 * @param {string } content The source code of the module
14821471 * @param {string } filename The file path of the module
1483- * @param {'module'|'commonjs'|undefined } format Intended format of the module.
1472+ * @param {
1473+ * 'module'|'commonjs'|'commonjs-typescript'|'module-typescript'
1474+ * } format Intended format of the module.
14841475 */
14851476Module . prototype . _compile = function ( content , filename , format ) {
14861477 let moduleURL ;
@@ -1502,9 +1493,7 @@ Module.prototype._compile = function(content, filename, format) {
15021493 }
15031494
15041495 if ( format === 'module' ) {
1505- // Pass the source into the .mjs extension handler indirectly through the cache.
1506- this [ kModuleSource ] = content ;
1507- loadESMFromCJS ( this , filename ) ;
1496+ loadESMFromCJS ( this , filename , format , content ) ;
15081497 return ;
15091498 }
15101499
@@ -1532,22 +1521,72 @@ Module.prototype._compile = function(content, filename, format) {
15321521
15331522/**
15341523 * Get the source code of a module, using cached ones if it's cached.
1524+ * After this returns, mod[kFormat], mod[kModuleSource] and mod[kURL] will be set.
15351525 * @param {Module } mod Module instance whose source is potentially already cached.
15361526 * @param {string } filename Absolute path to the file of the module.
1537- * @returns {string }
1527+ * @returns {{source: string, format?: string} }
15381528 */
1539- function getMaybeCachedSource ( mod , filename ) {
1540- // If already analyzed the source, then it will be cached.
1541- let content ;
1542- if ( mod [ kModuleSource ] !== undefined ) {
1543- content = mod [ kModuleSource ] ;
1529+ function loadSource ( mod , filename , formatFromNode ) {
1530+ if ( formatFromNode !== undefined ) {
1531+ mod [ kFormat ] = formatFromNode ;
1532+ }
1533+ const format = mod [ kFormat ] ;
1534+
1535+ let source = mod [ kModuleSource ] ;
1536+ if ( source !== undefined ) {
15441537 mod [ kModuleSource ] = undefined ;
15451538 } else {
15461539 // TODO(joyeecheung): we can read a buffer instead to speed up
15471540 // compilation.
1548- content = fs . readFileSync ( filename , 'utf8' ) ;
1541+ source = fs . readFileSync ( filename , 'utf8' ) ;
1542+ }
1543+ return { source, format } ;
1544+ }
1545+
1546+ function reconstructErrorStack ( err , parentPath , parentSource ) {
1547+ const errLine = StringPrototypeSplit (
1548+ StringPrototypeSlice ( err . stack , StringPrototypeIndexOf (
1549+ err . stack , ' at ' ) ) , '\n' , 1 ) [ 0 ] ;
1550+ const { 1 : line , 2 : col } =
1551+ RegExpPrototypeExec ( / ( \d + ) : ( \d + ) \) / , errLine ) || [ ] ;
1552+ if ( line && col ) {
1553+ const srcLine = StringPrototypeSplit ( parentSource , '\n' ) [ line - 1 ] ;
1554+ const frame = `${ parentPath } :${ line } \n${ srcLine } \n${ StringPrototypeRepeat ( ' ' , col - 1 ) } ^\n` ;
1555+ setArrowMessage ( err , frame ) ;
1556+ }
1557+ }
1558+
1559+ /**
1560+ * Generate the legacy ERR_REQUIRE_ESM for the cases where require(esm) is disabled.
1561+ * @param {Module } mod The module being required.
1562+ * @param {undefined|object } pkg Data of the nearest package.json of the module.
1563+ * @param {string } content Source code of the module.
1564+ * @param {string } filename Filename of the module
1565+ * @returns {Error }
1566+ */
1567+ function getRequireESMError ( mod , pkg , content , filename ) {
1568+ // This is an error path because `require` of a `.js` file in a `"type": "module"` scope is not allowed.
1569+ const parent = mod [ kModuleParent ] ;
1570+ const parentPath = parent ?. filename ;
1571+ const packageJsonPath = pkg ?. path ? path . resolve ( pkg . path , 'package.json' ) : null ;
1572+ const usesEsm = containsModuleSyntax ( content , filename ) ;
1573+ const err = new ERR_REQUIRE_ESM ( filename , usesEsm , parentPath ,
1574+ packageJsonPath ) ;
1575+ // Attempt to reconstruct the parent require frame.
1576+ const parentModule = Module . _cache [ parentPath ] ;
1577+ if ( parentModule ) {
1578+ let parentSource ;
1579+ try {
1580+ ( { source : parentSource } = loadSource ( parentModule , parentPath ) ) ;
1581+ } catch {
1582+ // Continue regardless of error.
1583+ }
1584+ if ( parentSource ) {
1585+ // TODO(joyeecheung): trim off internal frames from the stack.
1586+ reconstructErrorStack ( err , parentPath , parentSource ) ;
1587+ }
15491588 }
1550- return content ;
1589+ return err ;
15511590}
15521591
15531592/**
@@ -1556,57 +1595,25 @@ function getMaybeCachedSource(mod, filename) {
15561595 * @param {string } filename The file path of the module
15571596 */
15581597Module . _extensions [ '.js' ] = function ( module , filename ) {
1559- // If already analyzed the source, then it will be cached.
1560- const content = getMaybeCachedSource ( module , filename ) ;
1561-
1562- let format ;
1563- if ( StringPrototypeEndsWith ( filename , '.js' ) ) {
1564- const pkg = packageJsonReader . readPackageScope ( filename ) || { __proto__ : null } ;
1565- // Function require shouldn't be used in ES modules.
1566- if ( pkg . data ?. type === 'module' ) {
1567- if ( getOptionValue ( '--experimental-require-module' ) ) {
1568- module . _compile ( content , filename , 'module' ) ;
1569- return ;
1570- }
1571-
1572- // This is an error path because `require` of a `.js` file in a `"type": "module"` scope is not allowed.
1573- const parent = module [ kModuleParent ] ;
1574- const parentPath = parent ?. filename ;
1575- const packageJsonPath = path . resolve ( pkg . path , 'package.json' ) ;
1576- const usesEsm = containsModuleSyntax ( content , filename ) ;
1577- const err = new ERR_REQUIRE_ESM ( filename , usesEsm , parentPath ,
1578- packageJsonPath ) ;
1579- // Attempt to reconstruct the parent require frame.
1580- if ( Module . _cache [ parentPath ] ) {
1581- let parentSource ;
1582- try {
1583- parentSource = fs . readFileSync ( parentPath , 'utf8' ) ;
1584- } catch {
1585- // Continue regardless of error.
1586- }
1587- if ( parentSource ) {
1588- const errLine = StringPrototypeSplit (
1589- StringPrototypeSlice ( err . stack , StringPrototypeIndexOf (
1590- err . stack , ' at ' ) ) , '\n' , 1 ) [ 0 ] ;
1591- const { 1 : line , 2 : col } =
1592- RegExpPrototypeExec ( / ( \d + ) : ( \d + ) \) / , errLine ) || [ ] ;
1593- if ( line && col ) {
1594- const srcLine = StringPrototypeSplit ( parentSource , '\n' ) [ line - 1 ] ;
1595- const frame = `${ parentPath } :${ line } \n${ srcLine } \n${
1596- StringPrototypeRepeat ( ' ' , col - 1 ) } ^\n`;
1597- setArrowMessage ( err , frame ) ;
1598- }
1599- }
1600- }
1601- throw err ;
1602- } else if ( pkg . data ?. type === 'commonjs' ) {
1603- format = 'commonjs' ;
1604- }
1605- } else if ( StringPrototypeEndsWith ( filename , '.cjs' ) ) {
1598+ let format , pkg ;
1599+ if ( StringPrototypeEndsWith ( filename , '.cjs' ) ) {
16061600 format = 'commonjs' ;
1601+ } else if ( StringPrototypeEndsWith ( filename , '.mjs' ) ) {
1602+ format = 'module' ;
1603+ } else if ( StringPrototypeEndsWith ( filename , '.js' ) ) {
1604+ pkg = packageJsonReader . readPackageScope ( filename ) || { __proto__ : null } ;
1605+ const typeFromPjson = pkg . data ?. type ;
1606+ if ( typeFromPjson === 'module' || typeFromPjson === 'commonjs' || ! typeFromPjson ) {
1607+ format = typeFromPjson ;
1608+ }
16071609 }
1608-
1609- module . _compile ( content , filename , format ) ;
1610+ const { source, format : loadedFormat } = loadSource ( module , filename , format ) ;
1611+ // Function require shouldn't be used in ES modules when require(esm) is disabled.
1612+ if ( loadedFormat === 'module' && ! getOptionValue ( '--experimental-require-module' ) ) {
1613+ const err = getRequireESMError ( module , pkg , source , filename ) ;
1614+ throw err ;
1615+ }
1616+ module . _compile ( source , filename , loadedFormat ) ;
16101617} ;
16111618
16121619/**
0 commit comments