@@ -158,28 +158,134 @@ async function loadConfigFile(configFile) {
158158 try {
159159 // Try to load ts-node and compile the file
160160 const { transpile } = require ( 'typescript' )
161- const tsContent = fs . readFileSync ( configFile , 'utf8' )
161+
162+ // Recursively transpile the file and its dependencies
163+ const transpileTS = ( filePath ) => {
164+ const tsContent = fs . readFileSync ( filePath , 'utf8' )
165+
166+ // Transpile TypeScript to JavaScript with ES module output
167+ let jsContent = transpile ( tsContent , {
168+ module : 99 , // ModuleKind.ESNext
169+ target : 99 , // ScriptTarget.ESNext
170+ esModuleInterop : true ,
171+ allowSyntheticDefaultImports : true ,
172+ } )
173+
174+ // Check if the code uses __dirname or __filename (CommonJS globals)
175+ const usesCommonJSGlobals = / _ _ d i r n a m e | _ _ f i l e n a m e / . test ( jsContent )
176+
177+ if ( usesCommonJSGlobals ) {
178+ // Inject ESM equivalents at the top of the file
179+ const esmGlobals = `import { fileURLToPath as __fileURLToPath } from 'url';
180+ import { dirname as __dirname_fn } from 'path';
181+ const __filename = __fileURLToPath(import.meta.url);
182+ const __dirname = __dirname_fn(__filename);
162183
163- // Transpile TypeScript to JavaScript with ES module output
164- const jsContent = transpile ( tsContent , {
165- module : 99 , // ModuleKind.ESNext
166- target : 99 , // ScriptTarget.ESNext
167- esModuleInterop : true ,
168- allowSyntheticDefaultImports : true ,
169- } )
184+ `
185+ jsContent = esmGlobals + jsContent
186+ }
187+
188+ return jsContent
189+ }
190+
191+ // Create a map to track transpiled files
192+ const transpiledFiles = new Map ( )
193+ const baseDir = path . dirname ( configFile )
194+
195+ // Transpile main file
196+ let jsContent = transpileTS ( configFile )
197+
198+ // Find and transpile all relative TypeScript imports
199+ // Match: import ... from './file' or '../file' or './file.ts'
200+ const importRegex = / f r o m \s + [ ' " ] ( \. .+ ?) (?: \. t s ) ? [ ' " ] / g
201+ let match
202+ const imports = [ ]
203+
204+ while ( ( match = importRegex . exec ( jsContent ) ) !== null ) {
205+ imports . push ( match [ 1 ] )
206+ }
207+
208+ // Transpile each imported TypeScript file
209+ for ( const relativeImport of imports ) {
210+ let importedPath = path . resolve ( baseDir , relativeImport )
211+
212+ // Handle .js extensions that might actually be .ts files
213+ if ( importedPath . endsWith ( '.js' ) ) {
214+ const tsVersion = importedPath . replace ( / \. j s $ / , '.ts' )
215+ if ( fs . existsSync ( tsVersion ) ) {
216+ importedPath = tsVersion
217+ }
218+ }
219+
220+ // Try adding .ts extension if file doesn't exist and no extension provided
221+ if ( ! path . extname ( importedPath ) ) {
222+ if ( fs . existsSync ( importedPath + '.ts' ) ) {
223+ importedPath = importedPath + '.ts'
224+ }
225+ }
226+
227+ // If it's a TypeScript file, transpile it
228+ if ( importedPath . endsWith ( '.ts' ) && fs . existsSync ( importedPath ) ) {
229+ const transpiledImportContent = transpileTS ( importedPath )
230+ const tempImportFile = importedPath . replace ( / \. t s $ / , '.temp.mjs' )
231+ fs . writeFileSync ( tempImportFile , transpiledImportContent )
232+ transpiledFiles . set ( importedPath , tempImportFile )
233+ }
234+ }
235+
236+ // Replace imports in the main file to point to temp .mjs files
237+ jsContent = jsContent . replace (
238+ / f r o m \s + [ ' " ] ( \. .+ ?) (?: \. t s ) ? [ ' " ] / g,
239+ ( match , importPath ) => {
240+ let resolvedPath = path . resolve ( baseDir , importPath )
241+
242+ // Handle .js extension that might be .ts
243+ if ( resolvedPath . endsWith ( '.js' ) ) {
244+ const tsVersion = resolvedPath . replace ( / \. j s $ / , '.ts' )
245+ if ( transpiledFiles . has ( tsVersion ) ) {
246+ const tempFile = transpiledFiles . get ( tsVersion )
247+ const relPath = path . relative ( baseDir , tempFile ) . replace ( / \\ / g, '/' )
248+ return `from './${ relPath } '`
249+ }
250+ }
251+
252+ // Try with .ts extension
253+ const tsPath = resolvedPath . endsWith ( '.ts' ) ? resolvedPath : resolvedPath + '.ts'
254+
255+ // If we transpiled this file, use the temp file
256+ if ( transpiledFiles . has ( tsPath ) ) {
257+ const tempFile = transpiledFiles . get ( tsPath )
258+ // Get relative path from main temp file to this temp file
259+ const relPath = path . relative ( baseDir , tempFile ) . replace ( / \\ / g, '/' )
260+ return `from './${ relPath } '`
261+ }
262+
263+ // Otherwise, keep the import as-is
264+ return match
265+ }
266+ )
170267
171268 // Create a temporary JS file with .mjs extension to force ES module treatment
172269 const tempJsFile = configFile . replace ( '.ts' , '.temp.mjs' )
173270 fs . writeFileSync ( tempJsFile , jsContent )
271+
272+ // Store all temp files for cleanup
273+ const allTempFiles = [ tempJsFile , ...Array . from ( transpiledFiles . values ( ) ) ]
174274
175275 try {
176276 configModule = await import ( tempJsFile )
177- // Clean up temp file
178- fs . unlinkSync ( tempJsFile )
277+ // Clean up all temp files
278+ for ( const file of allTempFiles ) {
279+ if ( fs . existsSync ( file ) ) {
280+ fs . unlinkSync ( file )
281+ }
282+ }
179283 } catch ( err ) {
180- // Clean up temp file even on error
181- if ( fs . existsSync ( tempJsFile ) ) {
182- fs . unlinkSync ( tempJsFile )
284+ // Clean up all temp files even on error
285+ for ( const file of allTempFiles ) {
286+ if ( fs . existsSync ( file ) ) {
287+ fs . unlinkSync ( file )
288+ }
183289 }
184290 throw err
185291 }
0 commit comments