@@ -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,
@@ -433,10 +436,6 @@ function initializeCJS() {
433436 // TODO(joyeecheung): deprecate this in favor of a proper hook?
434437 Module . runMain =
435438 require ( 'internal/modules/run_main' ) . executeUserEntryPoint ;
436-
437- if ( getOptionValue ( '--experimental-require-module' ) ) {
438- Module . _extensions [ '.mjs' ] = loadESMFromCJS ;
439- }
440439}
441440
442441// Given a module name, and a list of paths to test, returns the first
@@ -646,14 +645,7 @@ function resolveExports(nmPath, request) {
646645// We don't cache this in case user extends the extensions.
647646function getDefaultExtensions ( ) {
648647 const extensions = ObjectKeys ( Module . _extensions ) ;
649- if ( ! getOptionValue ( '--experimental-require-module' ) ) {
650- return extensions ;
651- }
652- // If the .mjs extension is added by --experimental-require-module,
653- // remove it from the supported default extensions to maintain
654- // compatibility.
655- // TODO(joyeecheung): allow both .mjs and .cjs?
656- return ArrayPrototypeFilter ( extensions , ( ext ) => ext !== '.mjs' || Module . _extensions [ '.mjs' ] !== loadESMFromCJS ) ;
648+ return extensions ;
657649}
658650
659651/**
@@ -1280,10 +1272,6 @@ Module.prototype.load = function(filename) {
12801272 this . paths = Module . _nodeModulePaths ( path . dirname ( filename ) ) ;
12811273
12821274 const extension = findLongestRegisteredExtension ( filename ) ;
1283- // allow .mjs to be overridden
1284- if ( StringPrototypeEndsWith ( filename , '.mjs' ) && ! Module . _extensions [ '.mjs' ] ) {
1285- throw new ERR_REQUIRE_ESM ( filename , true ) ;
1286- }
12871275
12881276 Module . _extensions [ extension ] ( this , filename ) ;
12891277 this . loaded = true ;
@@ -1327,9 +1315,10 @@ let hasPausedEntry = false;
13271315 * Resolve and evaluate it synchronously as ESM if it's ESM.
13281316 * @param {Module } mod CJS module instance
13291317 * @param {string } filename Absolute path of the file.
1318+ * @param {string } format Format of the module. If it had types, this would be what it is after type-stripping.
1319+ * @param {string } source Source the module. If it had types, this would have the type stripped.
13301320 */
1331- function loadESMFromCJS ( mod , filename ) {
1332- const source = getMaybeCachedSource ( mod , filename ) ;
1321+ function loadESMFromCJS ( mod , filename , format , source ) {
13331322 const cascadedLoader = require ( 'internal/modules/esm/loader' ) . getOrInitializeCascadedLoader ( ) ;
13341323 const isMain = mod [ kIsMainSymbol ] ;
13351324 if ( isMain ) {
@@ -1483,7 +1472,9 @@ function wrapSafe(filename, content, cjsModuleInstance, format) {
14831472 * `exports`) to the file. Returns exception, if any.
14841473 * @param {string } content The source code of the module
14851474 * @param {string } filename The file path of the module
1486- * @param {'module'|'commonjs'|undefined } format Intended format of the module.
1475+ * @param {
1476+ * 'module'|'commonjs'|'commonjs-typescript'|'module-typescript'
1477+ * } format Intended format of the module.
14871478 */
14881479Module . prototype . _compile = function ( content , filename , format ) {
14891480 let moduleURL ;
@@ -1505,9 +1496,7 @@ Module.prototype._compile = function(content, filename, format) {
15051496 }
15061497
15071498 if ( format === 'module' ) {
1508- // Pass the source into the .mjs extension handler indirectly through the cache.
1509- this [ kModuleSource ] = content ;
1510- loadESMFromCJS ( this , filename ) ;
1499+ loadESMFromCJS ( this , filename , format , content ) ;
15111500 return ;
15121501 }
15131502
@@ -1560,22 +1549,72 @@ Module.prototype._compile = function(content, filename, format) {
15601549
15611550/**
15621551 * Get the source code of a module, using cached ones if it's cached.
1552+ * After this returns, mod[kFormat], mod[kModuleSource] and mod[kURL] will be set.
15631553 * @param {Module } mod Module instance whose source is potentially already cached.
15641554 * @param {string } filename Absolute path to the file of the module.
1565- * @returns {string }
1555+ * @returns {{source: string, format?: string} }
15661556 */
1567- function getMaybeCachedSource ( mod , filename ) {
1568- // If already analyzed the source, then it will be cached.
1569- let content ;
1570- if ( mod [ kModuleSource ] !== undefined ) {
1571- content = mod [ kModuleSource ] ;
1557+ function loadSource ( mod , filename , formatFromNode ) {
1558+ if ( formatFromNode !== undefined ) {
1559+ mod [ kFormat ] = formatFromNode ;
1560+ }
1561+ const format = mod [ kFormat ] ;
1562+
1563+ let source = mod [ kModuleSource ] ;
1564+ if ( source !== undefined ) {
15721565 mod [ kModuleSource ] = undefined ;
15731566 } else {
15741567 // TODO(joyeecheung): we can read a buffer instead to speed up
15751568 // compilation.
1576- content = fs . readFileSync ( filename , 'utf8' ) ;
1569+ source = fs . readFileSync ( filename , 'utf8' ) ;
1570+ }
1571+ return { source, format } ;
1572+ }
1573+
1574+ function reconstructErrorStack ( err , parentPath , parentSource ) {
1575+ const errLine = StringPrototypeSplit (
1576+ StringPrototypeSlice ( err . stack , StringPrototypeIndexOf (
1577+ err . stack , ' at ' ) ) , '\n' , 1 ) [ 0 ] ;
1578+ const { 1 : line , 2 : col } =
1579+ RegExpPrototypeExec ( / ( \d + ) : ( \d + ) \) / , errLine ) || [ ] ;
1580+ if ( line && col ) {
1581+ const srcLine = StringPrototypeSplit ( parentSource , '\n' ) [ line - 1 ] ;
1582+ const frame = `${ parentPath } :${ line } \n${ srcLine } \n${ StringPrototypeRepeat ( ' ' , col - 1 ) } ^\n` ;
1583+ setArrowMessage ( err , frame ) ;
1584+ }
1585+ }
1586+
1587+ /**
1588+ * Generate the legacy ERR_REQUIRE_ESM for the cases where require(esm) is disabled.
1589+ * @param {Module } mod The module being required.
1590+ * @param {undefined|object } pkg Data of the nearest package.json of the module.
1591+ * @param {string } content Source code of the module.
1592+ * @param {string } filename Filename of the module
1593+ * @returns {Error }
1594+ */
1595+ function getRequireESMError ( mod , pkg , content , filename ) {
1596+ // This is an error path because `require` of a `.js` file in a `"type": "module"` scope is not allowed.
1597+ const parent = mod [ kModuleParent ] ;
1598+ const parentPath = parent ?. filename ;
1599+ const packageJsonPath = pkg ?. path ? path . resolve ( pkg . path , 'package.json' ) : null ;
1600+ const usesEsm = containsModuleSyntax ( content , filename ) ;
1601+ const err = new ERR_REQUIRE_ESM ( filename , usesEsm , parentPath ,
1602+ packageJsonPath ) ;
1603+ // Attempt to reconstruct the parent require frame.
1604+ const parentModule = Module . _cache [ parentPath ] ;
1605+ if ( parentModule ) {
1606+ let parentSource ;
1607+ try {
1608+ ( { source : parentSource } = loadSource ( parentModule , parentPath ) ) ;
1609+ } catch {
1610+ // Continue regardless of error.
1611+ }
1612+ if ( parentSource ) {
1613+ // TODO(joyeecheung): trim off internal frames from the stack.
1614+ reconstructErrorStack ( err , parentPath , parentSource ) ;
1615+ }
15771616 }
1578- return content ;
1617+ return err ;
15791618}
15801619
15811620/**
@@ -1584,57 +1623,25 @@ function getMaybeCachedSource(mod, filename) {
15841623 * @param {string } filename The file path of the module
15851624 */
15861625Module . _extensions [ '.js' ] = function ( module , filename ) {
1587- // If already analyzed the source, then it will be cached.
1588- const content = getMaybeCachedSource ( module , filename ) ;
1589-
1590- let format ;
1591- if ( StringPrototypeEndsWith ( filename , '.js' ) ) {
1592- const pkg = packageJsonReader . readPackageScope ( filename ) || { __proto__ : null } ;
1593- // Function require shouldn't be used in ES modules.
1594- if ( pkg . data ?. type === 'module' ) {
1595- if ( getOptionValue ( '--experimental-require-module' ) ) {
1596- module . _compile ( content , filename , 'module' ) ;
1597- return ;
1598- }
1599-
1600- // This is an error path because `require` of a `.js` file in a `"type": "module"` scope is not allowed.
1601- const parent = module [ kModuleParent ] ;
1602- const parentPath = parent ?. filename ;
1603- const packageJsonPath = path . resolve ( pkg . path , 'package.json' ) ;
1604- const usesEsm = containsModuleSyntax ( content , filename ) ;
1605- const err = new ERR_REQUIRE_ESM ( filename , usesEsm , parentPath ,
1606- packageJsonPath ) ;
1607- // Attempt to reconstruct the parent require frame.
1608- if ( Module . _cache [ parentPath ] ) {
1609- let parentSource ;
1610- try {
1611- parentSource = fs . readFileSync ( parentPath , 'utf8' ) ;
1612- } catch {
1613- // Continue regardless of error.
1614- }
1615- if ( parentSource ) {
1616- const errLine = StringPrototypeSplit (
1617- StringPrototypeSlice ( err . stack , StringPrototypeIndexOf (
1618- err . stack , ' at ' ) ) , '\n' , 1 ) [ 0 ] ;
1619- const { 1 : line , 2 : col } =
1620- RegExpPrototypeExec ( / ( \d + ) : ( \d + ) \) / , errLine ) || [ ] ;
1621- if ( line && col ) {
1622- const srcLine = StringPrototypeSplit ( parentSource , '\n' ) [ line - 1 ] ;
1623- const frame = `${ parentPath } :${ line } \n${ srcLine } \n${
1624- StringPrototypeRepeat ( ' ' , col - 1 ) } ^\n`;
1625- setArrowMessage ( err , frame ) ;
1626- }
1627- }
1628- }
1629- throw err ;
1630- } else if ( pkg . data ?. type === 'commonjs' ) {
1631- format = 'commonjs' ;
1632- }
1633- } else if ( StringPrototypeEndsWith ( filename , '.cjs' ) ) {
1626+ let format , pkg ;
1627+ if ( StringPrototypeEndsWith ( filename , '.cjs' ) ) {
16341628 format = 'commonjs' ;
1629+ } else if ( StringPrototypeEndsWith ( filename , '.mjs' ) ) {
1630+ format = 'module' ;
1631+ } else if ( StringPrototypeEndsWith ( filename , '.js' ) ) {
1632+ pkg = packageJsonReader . readPackageScope ( filename ) || { __proto__ : null } ;
1633+ const typeFromPjson = pkg . data ?. type ;
1634+ if ( typeFromPjson === 'module' || typeFromPjson === 'commonjs' || ! typeFromPjson ) {
1635+ format = typeFromPjson ;
1636+ }
16351637 }
1636-
1637- module . _compile ( content , filename , format ) ;
1638+ const { source, format : loadedFormat } = loadSource ( module , filename , format ) ;
1639+ // Function require shouldn't be used in ES modules when require(esm) is disabled.
1640+ if ( loadedFormat === 'module' && ! getOptionValue ( '--experimental-require-module' ) ) {
1641+ const err = getRequireESMError ( module , pkg , source , filename ) ;
1642+ throw err ;
1643+ }
1644+ module . _compile ( source , filename , loadedFormat ) ;
16381645} ;
16391646
16401647/**
0 commit comments