@@ -4,10 +4,10 @@ import path from 'path'
44/**
55 * Transpile TypeScript files to ES modules with CommonJS shim support
66 * Handles recursive transpilation of imported TypeScript files
7- *
7+ *
88 * @param {string } mainFilePath - Path to the main TypeScript file to transpile
99 * @param {object } typescript - TypeScript compiler instance
10- * @returns {Promise<{tempFile: string, allTempFiles: string[]}> } - Main temp file and all temp files created
10+ * @returns {Promise<{tempFile: string, allTempFiles: string[], fileMapping: any }> } - Main temp file and all temp files created
1111 */
1212export async function transpileTypeScript ( mainFilePath , typescript ) {
1313 const { transpile } = typescript
@@ -18,7 +18,7 @@ export async function transpileTypeScript(mainFilePath, typescript) {
1818 */
1919 const transpileTS = ( filePath ) => {
2020 const tsContent = fs . readFileSync ( filePath , 'utf8' )
21-
21+
2222 // Transpile TypeScript to JavaScript with ES module output
2323 let jsContent = transpile ( tsContent , {
2424 module : 99 , // ModuleKind.ESNext
@@ -29,16 +29,16 @@ export async function transpileTypeScript(mainFilePath, typescript) {
2929 suppressOutputPathCheck : true ,
3030 skipLibCheck : true ,
3131 } )
32-
32+
3333 // Check if the code uses CommonJS globals
3434 const usesCommonJSGlobals = / _ _ d i r n a m e | _ _ f i l e n a m e / . test ( jsContent )
3535 const usesRequire = / \b r e q u i r e \s * \( / . test ( jsContent )
3636 const usesModuleExports = / \b ( m o d u l e \. e x p o r t s | e x p o r t s \. ) / . test ( jsContent )
37-
37+
3838 if ( usesCommonJSGlobals || usesRequire || usesModuleExports ) {
3939 // Inject ESM equivalents at the top of the file
4040 let esmGlobals = ''
41-
41+
4242 if ( usesRequire || usesModuleExports ) {
4343 // IMPORTANT: Use the original .ts file path as the base for require()
4444 // This ensures dynamic require() calls work with relative paths from the original file location
@@ -81,7 +81,7 @@ const exports = module.exports;
8181
8282`
8383 }
84-
84+
8585 if ( usesCommonJSGlobals ) {
8686 // For __dirname and __filename, also use the original file path
8787 const originalFileUrl = `file://${ filePath . replace ( / \\ / g, '/' ) } `
@@ -92,61 +92,61 @@ const __dirname = __dirname_fn(__filename);
9292
9393`
9494 }
95-
95+
9696 jsContent = esmGlobals + jsContent
97-
97+
9898 // If module.exports is used, we need to export it as default
9999 if ( usesModuleExports ) {
100100 jsContent += `\nexport default module.exports;\n`
101101 }
102102 }
103-
103+
104104 return jsContent
105105 }
106-
106+
107107 // Create a map to track transpiled files
108108 const transpiledFiles = new Map ( )
109109 const baseDir = path . dirname ( mainFilePath )
110-
110+
111111 // Recursive function to transpile a file and all its TypeScript dependencies
112112 const transpileFileAndDeps = ( filePath ) => {
113113 // Already transpiled, skip
114114 if ( transpiledFiles . has ( filePath ) ) {
115115 return
116116 }
117-
117+
118118 // Transpile this file
119119 let jsContent = transpileTS ( filePath )
120-
120+
121121 // Find all relative TypeScript imports in this file
122122 const importRegex = / f r o m \s + [ ' " ] ( \. .+ ?) (?: \. t s ) ? [ ' " ] / g
123123 let match
124124 const imports = [ ]
125-
125+
126126 while ( ( match = importRegex . exec ( jsContent ) ) !== null ) {
127127 imports . push ( match [ 1 ] )
128128 }
129-
129+
130130 // Get the base directory for this file
131131 const fileBaseDir = path . dirname ( filePath )
132-
132+
133133 // Recursively transpile each imported TypeScript file
134134 for ( const relativeImport of imports ) {
135135 let importedPath = path . resolve ( fileBaseDir , relativeImport )
136-
136+
137137 // Handle .js extensions that might actually be .ts files
138138 if ( importedPath . endsWith ( '.js' ) ) {
139139 const tsVersion = importedPath . replace ( / \. j s $ / , '.ts' )
140140 if ( fs . existsSync ( tsVersion ) ) {
141141 importedPath = tsVersion
142142 }
143143 }
144-
144+
145145 // Check for standard module extensions to determine if we should try adding .ts
146146 const ext = path . extname ( importedPath )
147147 const standardExtensions = [ '.js' , '.mjs' , '.cjs' , '.json' , '.node' ]
148148 const hasStandardExtension = standardExtensions . includes ( ext . toLowerCase ( ) )
149-
149+
150150 // If it doesn't end with .ts and doesn't have a standard extension, try adding .ts
151151 if ( ! importedPath . endsWith ( '.ts' ) && ! hasStandardExtension ) {
152152 const tsPath = importedPath + '.ts'
@@ -161,20 +161,20 @@ const __dirname = __dirname_fn(__filename);
161161 }
162162 }
163163 }
164-
164+
165165 // If it's a TypeScript file, recursively transpile it and its dependencies
166166 if ( importedPath . endsWith ( '.ts' ) && fs . existsSync ( importedPath ) ) {
167167 transpileFileAndDeps ( importedPath )
168168 }
169169 }
170-
170+
171171 // After all dependencies are transpiled, rewrite imports in this file
172172 jsContent = jsContent . replace (
173173 / f r o m \s + [ ' " ] ( \. .+ ?) (?: \. t s ) ? [ ' " ] / g,
174174 ( match , importPath ) => {
175175 let resolvedPath = path . resolve ( fileBaseDir , importPath )
176176 const originalExt = path . extname ( importPath )
177-
177+
178178 // Handle .js extension that might be .ts
179179 if ( resolvedPath . endsWith ( '.js' ) ) {
180180 const tsVersion = resolvedPath . replace ( / \. j s $ / , '.ts' )
@@ -190,10 +190,10 @@ const __dirname = __dirname_fn(__filename);
190190 // Keep .js extension as-is (might be a real .js file)
191191 return match
192192 }
193-
193+
194194 // Try with .ts extension
195195 const tsPath = resolvedPath . endsWith ( '.ts' ) ? resolvedPath : resolvedPath + '.ts'
196-
196+
197197 // If we transpiled this file, use the temp file
198198 if ( transpiledFiles . has ( tsPath ) ) {
199199 const tempFile = transpiledFiles . get ( tsPath )
@@ -204,40 +204,67 @@ const __dirname = __dirname_fn(__filename);
204204 }
205205 return `from '${ relPath } '`
206206 }
207-
207+
208208 // If the import doesn't have a standard module extension (.js, .mjs, .cjs, .json)
209209 // add .js for ESM compatibility
210210 // This handles cases where:
211211 // 1. Import has no real extension (e.g., "./utils" or "./helper")
212212 // 2. Import has a non-standard extension that's part of the name (e.g., "./abstract.helper")
213213 const standardExtensions = [ '.js' , '.mjs' , '.cjs' , '.json' , '.node' ]
214214 const hasStandardExtension = standardExtensions . includes ( originalExt . toLowerCase ( ) )
215-
215+
216216 if ( ! hasStandardExtension ) {
217217 return match . replace ( importPath , importPath + '.js' )
218218 }
219-
219+
220220 // Otherwise, keep the import as-is
221221 return match
222222 }
223223 )
224-
224+
225225 // Write the transpiled file with updated imports
226226 const tempFile = filePath . replace ( / \. t s $ / , '.temp.mjs' )
227227 fs . writeFileSync ( tempFile , jsContent )
228228 transpiledFiles . set ( filePath , tempFile )
229229 }
230-
230+
231231 // Start recursive transpilation from the main file
232232 transpileFileAndDeps ( mainFilePath )
233-
233+
234234 // Get the main transpiled file
235235 const tempJsFile = transpiledFiles . get ( mainFilePath )
236-
236+
237237 // Store all temp files for cleanup
238238 const allTempFiles = Array . from ( transpiledFiles . values ( ) )
239-
240- return { tempFile : tempJsFile , allTempFiles }
239+
240+ return { tempFile : tempJsFile , allTempFiles, fileMapping : transpiledFiles }
241+ }
242+
243+ /**
244+ * Map error stack traces from temp .mjs files back to original .ts files
245+ * @param {Error } error - The error object to fix
246+ * @param {Map<string, string> } fileMapping - Map of original .ts files to temp .mjs files
247+ * @returns {Error } - Error with fixed stack trace
248+ */
249+ export function fixErrorStack ( error , fileMapping ) {
250+ if ( ! error . stack || ! fileMapping ) return error
251+
252+ let stack = error . stack
253+
254+ // Create reverse mapping (temp.mjs -> original.ts)
255+ const reverseMap = new Map ( )
256+ for ( const [ tsFile , mjsFile ] of fileMapping . entries ( ) ) {
257+ reverseMap . set ( mjsFile , tsFile )
258+ }
259+
260+ // Replace all temp.mjs references with original .ts files
261+ for ( const [ mjsFile , tsFile ] of reverseMap . entries ( ) ) {
262+ const mjsPattern = mjsFile . replace ( / [ . * + ? ^ $ { } ( ) | [ \] \\ ] / g, '\\$&' )
263+ stack = stack . replace ( new RegExp ( mjsPattern , 'g' ) , tsFile )
264+ }
265+
266+ error . stack = stack
267+ return error
241268}
242269
243270/**
0 commit comments