11import fs from 'fs'
22import path from 'path'
3+ import { loadConfig , createMatchPath } from 'tsconfig-paths'
34
45/**
56 * Transpile TypeScript files to ES modules with CommonJS shim support
@@ -107,6 +108,53 @@ const __dirname = __dirname_fn(__filename);
107108 // Create a map to track transpiled files
108109 const transpiledFiles = new Map ( )
109110 const baseDir = path . dirname ( mainFilePath )
111+ const pathsMatcher = loadTsConfigPaths ( baseDir )
112+
113+ /**
114+ * Load tsconfig.json and create path matcher for resolving aliases
115+ */
116+ function loadTsConfigPaths ( startDir ) {
117+ try {
118+ let currentDir = startDir
119+ let configPath = null
120+ while ( currentDir && currentDir !== path . parse ( currentDir ) . root ) {
121+ const testPath = path . join ( currentDir , 'tsconfig.json' )
122+ if ( fs . existsSync ( testPath ) ) {
123+ configPath = testPath
124+ break
125+ }
126+ currentDir = path . dirname ( currentDir )
127+ }
128+
129+ if ( ! configPath ) return null
130+
131+ const configResult = loadConfig ( configPath )
132+
133+ if ( configResult . resultType !== 'success' ) return null
134+
135+ const { paths, absoluteBaseUrl } = configResult
136+ if ( ! paths ) return null
137+
138+ const matchPath = createMatchPath (
139+ absoluteBaseUrl ,
140+ paths ,
141+ undefined ,
142+ false
143+ )
144+
145+ return {
146+ matchPath : ( importPath ) => {
147+ for ( const ext of [ '.ts' , '.tsx' , '.js' , '.jsx' , '.json' , '' ] ) {
148+ const resolved = matchPath ( importPath + ext )
149+ if ( resolved ) return resolved
150+ }
151+ return null
152+ }
153+ }
154+ } catch ( error ) {
155+ return null
156+ }
157+ }
110158
111159 // Recursive function to transpile a file and all its TypeScript dependencies
112160 const transpileFileAndDeps = ( filePath ) => {
@@ -118,8 +166,7 @@ const __dirname = __dirname_fn(__filename);
118166 // Transpile this file
119167 let jsContent = transpileTS ( filePath )
120168
121- // Find all relative TypeScript imports in this file
122- const importRegex = / f r o m \s + [ ' " ] ( \. .+ ?) (?: \. t s ) ? [ ' " ] / g
169+ const importRegex = / f r o m \s + [ ' " ] ( [ ^ ' " ] + ?) [ ' " ] / g
123170 let match
124171 const imports = [ ]
125172
@@ -131,50 +178,113 @@ const __dirname = __dirname_fn(__filename);
131178 const fileBaseDir = path . dirname ( filePath )
132179
133180 // Recursively transpile each imported TypeScript file
134- for ( const relativeImport of imports ) {
135- let importedPath = path . resolve ( fileBaseDir , relativeImport )
136-
137- // Handle .js extensions that might actually be .ts files
138- if ( importedPath . endsWith ( '.js' ) ) {
139- const tsVersion = importedPath . replace ( / \. j s $ / , '.ts' )
140- if ( fs . existsSync ( tsVersion ) ) {
141- importedPath = tsVersion
142- }
181+ for ( const importPath of imports ) {
182+ if ( importPath . startsWith ( 'node:' ) ) {
183+ continue
143184 }
144185
145- // Check for standard module extensions to determine if we should try adding .ts
146- const ext = path . extname ( importedPath )
147- const standardExtensions = [ '.js' , '.mjs' , '.cjs' , '.json' , '.node' ]
148- const hasStandardExtension = standardExtensions . includes ( ext . toLowerCase ( ) )
149-
150- // If it doesn't end with .ts and doesn't have a standard extension, try adding .ts
151- if ( ! importedPath . endsWith ( '.ts' ) && ! hasStandardExtension ) {
152- const tsPath = importedPath + '.ts'
153- if ( fs . existsSync ( tsPath ) ) {
154- importedPath = tsPath
155- } else {
156- // Try .js extension as well
157- const jsPath = importedPath + '.js'
158- if ( fs . existsSync ( jsPath ) ) {
159- // Skip .js files, they don't need transpilation
160- continue
186+ if ( importPath . startsWith ( '.' ) ) {
187+ let importedPath = path . resolve ( fileBaseDir , importPath )
188+
189+ // Handle .js extensions that might actually be .ts files
190+ if ( importedPath . endsWith ( '.js' ) ) {
191+ const tsVersion = importedPath . replace ( / \. j s $ / , '.ts' )
192+ if ( fs . existsSync ( tsVersion ) ) {
193+ importedPath = tsVersion
194+ }
195+ }
196+
197+ // Check for standard module extensions to determine if we should try adding .ts
198+ const ext = path . extname ( importedPath )
199+ const standardExtensions = [ '.js' , '.mjs' , '.cjs' , '.json' , '.node' ]
200+ const hasStandardExtension = standardExtensions . includes ( ext . toLowerCase ( ) )
201+
202+ // If it doesn't end with .ts and doesn't have a standard extension, try adding .ts
203+ if ( ! importedPath . endsWith ( '.ts' ) && ! hasStandardExtension ) {
204+ const tsPath = importedPath + '.ts'
205+ if ( fs . existsSync ( tsPath ) ) {
206+ importedPath = tsPath
207+ } else {
208+ // Try .js extension as well
209+ const jsPath = importedPath + '.js'
210+ if ( fs . existsSync ( jsPath ) ) {
211+ // Skip .js files, they don't need transpilation
212+ continue
213+ }
161214 }
162215 }
216+
217+ // If it's a TypeScript file, recursively transpile it and its dependencies
218+ if ( importedPath . endsWith ( '.ts' ) && fs . existsSync ( importedPath ) ) {
219+ transpileFileAndDeps ( importedPath )
220+ }
221+
222+ continue
163223 }
164224
165- // If it's a TypeScript file, recursively transpile it and its dependencies
166- if ( importedPath . endsWith ( '.ts' ) && fs . existsSync ( importedPath ) ) {
167- transpileFileAndDeps ( importedPath )
225+ // Try to resolve as a path alias (non-relative import)
226+ const resolvedAlias = pathsMatcher ? pathsMatcher . matchPath ( importPath ) : null
227+ if ( resolvedAlias ) {
228+ let importedPath = resolvedAlias
229+
230+ if ( ! importedPath . endsWith ( '.ts' ) && ! path . extname ( importedPath ) ) {
231+ const tsPath = importedPath + '.ts'
232+ if ( fs . existsSync ( tsPath ) ) {
233+ importedPath = tsPath
234+ }
235+ }
236+
237+ if ( importedPath . endsWith ( '.ts' ) && fs . existsSync ( importedPath ) ) {
238+ transpileFileAndDeps ( importedPath )
239+ }
240+
241+ continue
168242 }
169243 }
170244
171245 // After all dependencies are transpiled, rewrite imports in this file
172246 jsContent = jsContent . replace (
173- / f r o m \s + [ ' " ] ( \. . + ?) (?: \. t s ) ? [ ' " ] / g,
247+ / f r o m \s + [ ' " ] ( [ ^ ' " ] + ?) [ ' " ] / g,
174248 ( match , importPath ) => {
175- let resolvedPath = path . resolve ( fileBaseDir , importPath )
176249 const originalExt = path . extname ( importPath )
177250
251+ // Check if this is a path alias (non-relative import)
252+ if ( ! importPath . startsWith ( '.' ) ) {
253+ const resolvedAlias = pathsMatcher ? pathsMatcher . matchPath ( importPath ) : null
254+ if ( resolvedAlias ) {
255+ let tsPath = resolvedAlias . endsWith ( '.ts' ) ? resolvedAlias : resolvedAlias + '.ts'
256+
257+ if ( transpiledFiles . has ( tsPath ) ) {
258+ const tempFile = transpiledFiles . get ( tsPath )
259+ const relPath = path . relative ( fileBaseDir , tempFile ) . replace ( / \\ / g, '/' )
260+ if ( ! relPath . startsWith ( '.' ) ) {
261+ return `from './${ relPath } '`
262+ }
263+ return `from '${ relPath } '`
264+ }
265+
266+ if ( fs . existsSync ( tsPath ) ) {
267+ const jsPath = tsPath . replace ( / \. t s $ / , '.js' )
268+ const relPath = path . relative ( fileBaseDir , jsPath ) . replace ( / \\ / g, '/' )
269+ if ( ! relPath . startsWith ( '.' ) ) {
270+ return `from './${ relPath } '`
271+ }
272+ return `from '${ relPath } '`
273+ }
274+ }
275+
276+ const standardExtensions = [ '.js' , '.mjs' , '.cjs' , '.json' , '.node' ]
277+ const hasStandardExtension = standardExtensions . includes ( originalExt . toLowerCase ( ) )
278+
279+ if ( ! hasStandardExtension && ! importPath . startsWith ( 'node:' ) ) {
280+ return match . replace ( importPath , importPath + '.js' )
281+ }
282+
283+ return match
284+ }
285+
286+ let resolvedPath = path . resolve ( fileBaseDir , importPath )
287+
178288 // Handle .js extension that might be .ts
179289 if ( resolvedPath . endsWith ( '.js' ) ) {
180290 const tsVersion = resolvedPath . replace ( / \. j s $ / , '.ts' )
0 commit comments