11import fs from 'fs'
22import path from 'path'
3+ import { pathToFileURL } from 'url'
4+
5+ /**
6+ * Load tsconfig.json if it exists
7+ * @param {string } tsConfigPath - Path to tsconfig.json
8+ * @returns {object|null } - Parsed tsconfig or null
9+ */
10+ function loadTsConfig ( tsConfigPath ) {
11+ if ( ! fs . existsSync ( tsConfigPath ) ) {
12+ return null
13+ }
14+
15+ try {
16+ const tsConfigContent = fs . readFileSync ( tsConfigPath , 'utf8' )
17+ return JSON . parse ( tsConfigContent )
18+ } catch ( err ) {
19+ return null
20+ }
21+ }
22+
23+ /**
24+ * Resolve TypeScript path alias to actual file path
25+ * @param {string } importPath - Import path with alias (e.g., '#config/urls')
26+ * @param {object } tsConfig - Parsed tsconfig.json
27+ * @param {string } configDir - Directory containing tsconfig.json
28+ * @returns {string|null } - Resolved file path or null if not an alias
29+ */
30+ function resolveTsPathAlias ( importPath , tsConfig , configDir ) {
31+ if ( ! tsConfig || ! tsConfig . compilerOptions || ! tsConfig . compilerOptions . paths ) {
32+ return null
33+ }
34+
35+ const paths = tsConfig . compilerOptions . paths
36+
37+ for ( const [ pattern , targets ] of Object . entries ( paths ) ) {
38+ if ( ! targets || targets . length === 0 ) {
39+ continue
40+ }
41+
42+ const patternRegex = new RegExp (
43+ '^' + pattern . replace ( / \* / g, '(.*)' ) + '$'
44+ )
45+ const match = importPath . match ( patternRegex )
46+
47+ if ( match ) {
48+ const wildcard = match [ 1 ] || ''
49+ const target = targets [ 0 ]
50+ const resolvedTarget = target . replace ( / \* / g, wildcard )
51+
52+ return path . resolve ( configDir , resolvedTarget )
53+ }
54+ }
55+
56+ return null
57+ }
358
459/**
560 * Transpile TypeScript files to ES modules with CommonJS shim support
@@ -108,6 +163,22 @@ const __dirname = __dirname_fn(__filename);
108163 const transpiledFiles = new Map ( )
109164 const baseDir = path . dirname ( mainFilePath )
110165
166+ // Try to find tsconfig.json by walking up the directory tree
167+ let tsConfigPath = path . join ( baseDir , 'tsconfig.json' )
168+ let configDir = baseDir
169+ let searchDir = baseDir
170+
171+ while ( ! fs . existsSync ( tsConfigPath ) && searchDir !== path . dirname ( searchDir ) ) {
172+ searchDir = path . dirname ( searchDir )
173+ tsConfigPath = path . join ( searchDir , 'tsconfig.json' )
174+ if ( fs . existsSync ( tsConfigPath ) ) {
175+ configDir = searchDir
176+ break
177+ }
178+ }
179+
180+ const tsConfig = loadTsConfig ( tsConfigPath )
181+
111182 // Recursive function to transpile a file and all its TypeScript dependencies
112183 const transpileFileAndDeps = ( filePath ) => {
113184 // Already transpiled, skip
@@ -118,9 +189,9 @@ const __dirname = __dirname_fn(__filename);
118189 // Transpile this file
119190 let jsContent = transpileTS ( filePath )
120191
121- // Find all relative TypeScript imports in this file (both ESM imports and require() calls)
122- const importRegex = / f r o m \s + [ ' " ] ( \. [ ^ ' " ] + ?) (?: \. t s ) ? [ ' " ] / g
123- const requireRegex = / r e q u i r e \s * \( \s * [ ' " ] ( \. [ ^ ' " ] + ?) (?: \. t s ) ? [ ' " ] \s * \) / g
192+ // Find all TypeScript imports in this file (both ESM imports and require() calls)
193+ const importRegex = / f r o m \s + [ ' " ] ( [ ^ ' " ] + ?) [ ' " ] / g
194+ const requireRegex = / r e q u i r e \s * \( \s * [ ' " ] ( [ ^ ' " ] + ?) [ ' " ] \s * \) / g
124195 let match
125196 const imports = [ ]
126197
@@ -136,8 +207,18 @@ const __dirname = __dirname_fn(__filename);
136207 const fileBaseDir = path . dirname ( filePath )
137208
138209 // Recursively transpile each imported TypeScript file
139- for ( const { path : relativeImport } of imports ) {
140- let importedPath = path . resolve ( fileBaseDir , relativeImport )
210+ for ( const { path : importPath } of imports ) {
211+ let importedPath = importPath
212+
213+ // Check if this is a path alias
214+ const resolvedAlias = resolveTsPathAlias ( importPath , tsConfig , configDir )
215+ if ( resolvedAlias ) {
216+ importedPath = resolvedAlias
217+ } else if ( importPath . startsWith ( '.' ) ) {
218+ importedPath = path . resolve ( fileBaseDir , importPath )
219+ } else {
220+ continue
221+ }
141222
142223 // Handle .js extensions that might actually be .ts files
143224 if ( importedPath . endsWith ( '.js' ) ) {
@@ -181,11 +262,34 @@ const __dirname = __dirname_fn(__filename);
181262
182263 // After all dependencies are transpiled, rewrite imports in this file
183264 jsContent = jsContent . replace (
184- / f r o m \s + [ ' " ] ( \. [ ^ ' " ] + ?) (?: \. t s ) ? [ ' " ] / g,
265+ / f r o m \s + [ ' " ] ( [ ^ ' " ] + ?) [ ' " ] / g,
185266 ( match , importPath ) => {
186- let resolvedPath = path . resolve ( fileBaseDir , importPath )
267+ let resolvedPath = importPath
187268 const originalExt = path . extname ( importPath )
188269
270+ // Check if this is a path alias
271+ const resolvedAlias = resolveTsPathAlias ( importPath , tsConfig , configDir )
272+ if ( resolvedAlias ) {
273+ resolvedPath = resolvedAlias
274+ } else if ( importPath . startsWith ( '.' ) ) {
275+ resolvedPath = path . resolve ( fileBaseDir , importPath )
276+ } else {
277+ return match
278+ }
279+
280+ // If resolved path is a directory, try index.ts
281+ if ( fs . existsSync ( resolvedPath ) && fs . statSync ( resolvedPath ) . isDirectory ( ) ) {
282+ const indexPath = path . join ( resolvedPath , 'index.ts' )
283+ if ( fs . existsSync ( indexPath ) && transpiledFiles . has ( indexPath ) ) {
284+ const tempFile = transpiledFiles . get ( indexPath )
285+ const relPath = path . relative ( fileBaseDir , tempFile ) . replace ( / \\ / g, '/' )
286+ if ( ! relPath . startsWith ( '.' ) ) {
287+ return `from './${ relPath } '`
288+ }
289+ return `from '${ relPath } '`
290+ }
291+ }
292+
189293 // Handle .js extension that might be .ts
190294 if ( resolvedPath . endsWith ( '.js' ) ) {
191295 const tsVersion = resolvedPath . replace ( / \. j s $ / , '.ts' )
@@ -238,9 +342,19 @@ const __dirname = __dirname_fn(__filename);
238342
239343 // Also rewrite require() calls to point to transpiled TypeScript files
240344 jsContent = jsContent . replace (
241- / r e q u i r e \s * \( \s * [ ' " ] ( \. [ ^ ' " ] + ?) (?: \. t s ) ? [ ' " ] \s * \) / g,
345+ / r e q u i r e \s * \( \s * [ ' " ] ( [ ^ ' " ] + ?) [ ' " ] \s * \) / g,
242346 ( match , requirePath ) => {
243- let resolvedPath = path . resolve ( fileBaseDir , requirePath )
347+ let resolvedPath = requirePath
348+
349+ // Check if this is a path alias
350+ const resolvedAlias = resolveTsPathAlias ( requirePath , tsConfig , configDir )
351+ if ( resolvedAlias ) {
352+ resolvedPath = resolvedAlias
353+ } else if ( requirePath . startsWith ( '.' ) ) {
354+ resolvedPath = path . resolve ( fileBaseDir , requirePath )
355+ } else {
356+ return match
357+ }
244358
245359 // Handle .js extension that might be .ts
246360 if ( resolvedPath . endsWith ( '.js' ) ) {
@@ -282,10 +396,13 @@ const __dirname = __dirname_fn(__filename);
282396 // Get the main transpiled file
283397 const tempJsFile = transpiledFiles . get ( mainFilePath )
284398
285- // Store all temp files for cleanup
399+ // Convert to file:// URL for dynamic import() (required on Windows)
400+ const tempFileUrl = pathToFileURL ( tempJsFile ) . href
401+
402+ // Store all temp files for cleanup (keep as paths, not URLs)
286403 const allTempFiles = Array . from ( transpiledFiles . values ( ) )
287404
288- return { tempFile : tempJsFile , allTempFiles, fileMapping : transpiledFiles }
405+ return { tempFile : tempFileUrl , allTempFiles, fileMapping : transpiledFiles }
289406}
290407
291408/**
0 commit comments