@@ -88,8 +88,14 @@ const loadMjsFromPath = endHiddenCallStack(async function loadMjsFromPath(
8888 }
8989} ) ;
9090
91- const SUPPORTED_EXTENSIONS = new Set ( [ ".js" , ".mjs" , ".cjs" , ".cts" ] as const ) ;
92- type SetValue < T extends Set < unknown > > = T extends Set < infer U > ? U : never ;
91+ const SUPPORTED_EXTENSIONS = {
92+ ".js" : "unknown" ,
93+ ".mjs" : "esm" ,
94+ ".cjs" : "cjs" ,
95+ ".ts" : "unknown" ,
96+ ".mts" : "esm" ,
97+ ".cts" : "cjs" ,
98+ } as const ;
9399
94100const asyncModules = new Set ( ) ;
95101
@@ -101,15 +107,23 @@ export default function* loadCodeDefault(
101107) : Handler < unknown > {
102108 let async ;
103109
104- let ext = path . extname ( filepath ) ;
105- if ( ! SUPPORTED_EXTENSIONS . has ( ext as any ) ) ext = ".js " ;
110+ const ext = path . extname ( filepath ) ;
111+ const isTS = ext === ".ts" || ext === ".cts" || ext === ".mts " ;
106112
107- const pattern =
108- `${ loader } ${ ext } ` as `${typeof loader } ${SetValue < typeof SUPPORTED_EXTENSIONS > } `;
113+ const type =
114+ SUPPORTED_EXTENSIONS [
115+ Object . hasOwn ( SUPPORTED_EXTENSIONS , ext )
116+ ? ( ext as keyof typeof SUPPORTED_EXTENSIONS )
117+ : ( ".js" as const )
118+ ] ;
119+
120+ const pattern = `${ loader } ${ type } ` as const ;
109121 switch ( pattern ) {
110- case "require .cjs" :
111- case "auto .cjs" :
112- if ( process . env . BABEL_8_BREAKING ) {
122+ case "require cjs" :
123+ case "auto cjs" :
124+ if ( isTS ) {
125+ return ensureTsSupport ( filepath , ext , ( ) => loadCjsDefault ( filepath ) ) ;
126+ } else if ( process . env . BABEL_8_BREAKING ) {
113127 return loadCjsDefault ( filepath ) ;
114128 } else {
115129 return loadCjsDefault (
@@ -118,14 +132,13 @@ export default function* loadCodeDefault(
118132 /* fallbackToTranspiledModule */ arguments [ 2 ] ,
119133 ) ;
120134 }
121- case "require .cts" :
122- case "auto .cts" :
123- return loadCtsDefault ( filepath ) ;
124- case "auto .js" :
125- case "require .js" :
126- case "require .mjs" : // Some versions of Node.js support require(esm):
135+ case "auto unknown" :
136+ case "require unknown" :
137+ case "require esm" :
127138 try {
128- if ( process . env . BABEL_8_BREAKING ) {
139+ if ( isTS ) {
140+ return ensureTsSupport ( filepath , ext , ( ) => loadCjsDefault ( filepath ) ) ;
141+ } else if ( process . env . BABEL_8_BREAKING ) {
129142 return loadCjsDefault ( filepath ) ;
130143 } else {
131144 return loadCjsDefault (
@@ -151,92 +164,104 @@ export default function* loadCodeDefault(
151164 // fall through: require() failed due to TLA
152165 } else if (
153166 e . code === "ERR_REQUIRE_ESM" ||
154- ( ! process . env . BABEL_8_BREAKING && ext === ".mjs " )
167+ ( ! process . env . BABEL_8_BREAKING && type === "esm " )
155168 ) {
156169 // fall through: require() failed due to ESM
157170 } else {
158171 throw e ;
159172 }
160173 }
161174 // fall through: require() failed due to ESM or TLA, try import()
162- case "auto .mjs " :
175+ case "auto esm " :
163176 if ( ( async ??= yield * isAsync ( ) ) ) {
164- return ( yield * waitFor ( loadMjsFromPath ( filepath ) ) ) . default ;
177+ const promise = isTS
178+ ? ensureTsSupport ( filepath , ext , ( ) => loadMjsFromPath ( filepath ) )
179+ : loadMjsFromPath ( filepath ) ;
180+
181+ return ( yield * waitFor ( promise ) ) . default ;
165182 }
166183 throw new ConfigError ( esmError , filepath ) ;
167184 default :
168185 throw new Error ( "Internal Babel error: unreachable code." ) ;
169186 }
170187}
171188
172- function loadCtsDefault ( filepath : string ) {
173- const ext = ".cts" ;
174- const hasTsSupport = ! ! (
189+ function ensureTsSupport < T > (
190+ filepath : string ,
191+ ext : string ,
192+ callback : ( ) => T ,
193+ ) : T {
194+ if (
175195 require . extensions [ ".ts" ] ||
176196 require . extensions [ ".cts" ] ||
177197 require . extensions [ ".mts" ]
178- ) ;
198+ ) {
199+ return callback ( ) ;
200+ }
179201
180- let handler : NodeJS . RequireExtensions [ "" ] ;
202+ if ( ext !== ".cts" ) {
203+ throw new ConfigError (
204+ `\
205+ You are using a ${ ext } config file, but Babel only supports transpiling .cts configs. Either:
206+ - Use a .cts config file
207+ - Update to Node.js 23.6.0, which has native TypeScript support
208+ - Install ts-node to transpile ${ ext } files on the fly\
209+ ` ,
210+ filepath ,
211+ ) ;
212+ }
181213
182- if ( ! hasTsSupport ) {
183- const opts : InputOptions = {
184- babelrc : false ,
185- configFile : false ,
186- sourceType : "unambiguous" ,
187- sourceMaps : "inline" ,
188- sourceFileName : path . basename ( filepath ) ,
189- presets : [
190- [
191- getTSPreset ( filepath ) ,
192- {
193- onlyRemoveTypeImports : true ,
194- optimizeConstEnums : true ,
195- ...( process . env . BABEL_8_BREAKING
196- ? { }
197- : { allowDeclareFields : true } ) ,
198- } ,
199- ] ,
214+ const opts : InputOptions = {
215+ babelrc : false ,
216+ configFile : false ,
217+ sourceType : "unambiguous" ,
218+ sourceMaps : "inline" ,
219+ sourceFileName : path . basename ( filepath ) ,
220+ presets : [
221+ [
222+ getTSPreset ( filepath ) ,
223+ {
224+ onlyRemoveTypeImports : true ,
225+ optimizeConstEnums : true ,
226+ ...( process . env . BABEL_8_BREAKING ? { } : { allowDeclareFields : true } ) ,
227+ } ,
200228 ] ,
201- } ;
229+ ] ,
230+ } ;
202231
203- handler = function ( m , filename ) {
204- // If we want to support `.ts`, `.d.ts` must be handled specially.
205- if ( handler && filename . endsWith ( ext ) ) {
206- try {
207- // @ts -expect-error Undocumented API
208- return m . _compile (
209- transformFileSync ( filename , {
210- ...opts ,
211- filename,
212- } ) . code ,
232+ let handler : NodeJS . RequireExtensions [ "" ] = function ( m , filename ) {
233+ // If we want to support `.ts`, `.d.ts` must be handled specially.
234+ if ( handler && filename . endsWith ( ".cts" ) ) {
235+ try {
236+ // @ts -expect-error Undocumented API
237+ return m . _compile (
238+ transformFileSync ( filename , {
239+ ...opts ,
213240 filename,
241+ } ) . code ,
242+ filename ,
243+ ) ;
244+ } catch ( error ) {
245+ // TODO(Babel 8): Add this as an optional peer dependency
246+ // eslint-disable-next-line import/no-extraneous-dependencies
247+ const packageJson = require ( "@babel/preset-typescript/package.json" ) ;
248+ if ( semver . lt ( packageJson . version , "7.21.4" ) ) {
249+ console . error (
250+ "`.cts` configuration file failed to load, please try to update `@babel/preset-typescript`." ,
214251 ) ;
215- } catch ( error ) {
216- if ( ! hasTsSupport ) {
217- // TODO(Babel 8): Add this as an optional peer dependency
218- // eslint-disable-next-line import/no-extraneous-dependencies
219- const packageJson = require ( "@babel/preset-typescript/package.json" ) ;
220- if ( semver . lt ( packageJson . version , "7.21.4" ) ) {
221- console . error (
222- "`.cts` configuration file failed to load, please try to update `@babel/preset-typescript`." ,
223- ) ;
224- }
225- }
226- throw error ;
227252 }
253+ throw error ;
228254 }
229- return require . extensions [ ".js" ] ( m , filename ) ;
230- } ;
231- require . extensions [ ext ] = handler ;
232- }
255+ }
256+ return require . extensions [ ".js" ] ( m , filename ) ;
257+ } ;
258+ require . extensions [ ext ] = handler ;
259+
233260 try {
234- return loadCjsDefault ( filepath ) ;
261+ return callback ( ) ;
235262 } finally {
236- if ( ! hasTsSupport ) {
237- if ( require . extensions [ ext ] === handler ) delete require . extensions [ ext ] ;
238- handler = undefined ;
239- }
263+ if ( require . extensions [ ext ] === handler ) delete require . extensions [ ext ] ;
264+ handler = undefined ;
240265 }
241266}
242267
0 commit comments