codeceptjs 4.0.0-beta.2 → 4.0.0-beta.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/README.md +133 -120
  2. package/bin/codecept.js +107 -96
  3. package/bin/test-server.js +64 -0
  4. package/docs/webapi/clearCookie.mustache +1 -1
  5. package/docs/webapi/click.mustache +5 -1
  6. package/lib/actor.js +71 -103
  7. package/lib/ai.js +159 -188
  8. package/lib/assert/empty.js +22 -24
  9. package/lib/assert/equal.js +30 -37
  10. package/lib/assert/error.js +14 -14
  11. package/lib/assert/include.js +43 -48
  12. package/lib/assert/throws.js +11 -11
  13. package/lib/assert/truth.js +22 -22
  14. package/lib/assert.js +20 -18
  15. package/lib/codecept.js +262 -162
  16. package/lib/colorUtils.js +50 -52
  17. package/lib/command/check.js +206 -0
  18. package/lib/command/configMigrate.js +56 -51
  19. package/lib/command/definitions.js +96 -109
  20. package/lib/command/dryRun.js +77 -79
  21. package/lib/command/generate.js +234 -194
  22. package/lib/command/gherkin/init.js +42 -33
  23. package/lib/command/gherkin/snippets.js +76 -74
  24. package/lib/command/gherkin/steps.js +20 -17
  25. package/lib/command/info.js +74 -38
  26. package/lib/command/init.js +301 -290
  27. package/lib/command/interactive.js +41 -32
  28. package/lib/command/list.js +28 -27
  29. package/lib/command/run-multiple/chunk.js +51 -48
  30. package/lib/command/run-multiple/collection.js +5 -5
  31. package/lib/command/run-multiple/run.js +5 -1
  32. package/lib/command/run-multiple.js +97 -97
  33. package/lib/command/run-rerun.js +19 -25
  34. package/lib/command/run-workers.js +68 -92
  35. package/lib/command/run.js +39 -27
  36. package/lib/command/utils.js +80 -64
  37. package/lib/command/workers/runTests.js +388 -226
  38. package/lib/config.js +109 -50
  39. package/lib/container.js +641 -261
  40. package/lib/data/context.js +60 -61
  41. package/lib/data/dataScenarioConfig.js +47 -47
  42. package/lib/data/dataTableArgument.js +32 -32
  43. package/lib/data/table.js +22 -22
  44. package/lib/effects.js +307 -0
  45. package/lib/element/WebElement.js +327 -0
  46. package/lib/els.js +160 -0
  47. package/lib/event.js +173 -163
  48. package/lib/globals.js +141 -0
  49. package/lib/heal.js +89 -85
  50. package/lib/helper/AI.js +131 -41
  51. package/lib/helper/ApiDataFactory.js +107 -75
  52. package/lib/helper/Appium.js +542 -404
  53. package/lib/helper/FileSystem.js +100 -79
  54. package/lib/helper/GraphQL.js +44 -43
  55. package/lib/helper/GraphQLDataFactory.js +52 -52
  56. package/lib/helper/JSONResponse.js +126 -88
  57. package/lib/helper/Mochawesome.js +54 -29
  58. package/lib/helper/Playwright.js +2547 -1316
  59. package/lib/helper/Puppeteer.js +1578 -1181
  60. package/lib/helper/REST.js +209 -68
  61. package/lib/helper/WebDriver.js +1482 -1342
  62. package/lib/helper/errors/ConnectionRefused.js +6 -6
  63. package/lib/helper/errors/ElementAssertion.js +11 -16
  64. package/lib/helper/errors/ElementNotFound.js +5 -9
  65. package/lib/helper/errors/RemoteBrowserConnectionRefused.js +5 -5
  66. package/lib/helper/extras/Console.js +11 -11
  67. package/lib/helper/extras/PlaywrightLocator.js +110 -0
  68. package/lib/helper/extras/PlaywrightPropEngine.js +18 -18
  69. package/lib/helper/extras/PlaywrightReactVueLocator.js +17 -8
  70. package/lib/helper/extras/PlaywrightRestartOpts.js +25 -11
  71. package/lib/helper/extras/Popup.js +22 -22
  72. package/lib/helper/extras/React.js +27 -28
  73. package/lib/helper/network/actions.js +36 -42
  74. package/lib/helper/network/utils.js +78 -84
  75. package/lib/helper/scripts/blurElement.js +5 -5
  76. package/lib/helper/scripts/focusElement.js +5 -5
  77. package/lib/helper/scripts/highlightElement.js +8 -8
  78. package/lib/helper/scripts/isElementClickable.js +34 -34
  79. package/lib/helper.js +2 -3
  80. package/lib/history.js +23 -19
  81. package/lib/hooks.js +8 -8
  82. package/lib/html.js +94 -104
  83. package/lib/index.js +38 -27
  84. package/lib/listener/config.js +30 -23
  85. package/lib/listener/emptyRun.js +54 -0
  86. package/lib/listener/enhancedGlobalRetry.js +110 -0
  87. package/lib/listener/exit.js +16 -18
  88. package/lib/listener/globalRetry.js +70 -0
  89. package/lib/listener/globalTimeout.js +181 -0
  90. package/lib/listener/helpers.js +76 -51
  91. package/lib/listener/mocha.js +10 -11
  92. package/lib/listener/result.js +11 -0
  93. package/lib/listener/retryEnhancer.js +85 -0
  94. package/lib/listener/steps.js +71 -59
  95. package/lib/listener/store.js +20 -0
  96. package/lib/locator.js +214 -197
  97. package/lib/mocha/asyncWrapper.js +274 -0
  98. package/lib/mocha/bdd.js +167 -0
  99. package/lib/mocha/cli.js +341 -0
  100. package/lib/mocha/factory.js +163 -0
  101. package/lib/mocha/featureConfig.js +89 -0
  102. package/lib/mocha/gherkin.js +231 -0
  103. package/lib/mocha/hooks.js +121 -0
  104. package/lib/mocha/index.js +21 -0
  105. package/lib/mocha/inject.js +46 -0
  106. package/lib/{interfaces → mocha}/scenarioConfig.js +58 -34
  107. package/lib/mocha/suite.js +89 -0
  108. package/lib/mocha/test.js +184 -0
  109. package/lib/mocha/types.d.ts +42 -0
  110. package/lib/mocha/ui.js +242 -0
  111. package/lib/output.js +141 -71
  112. package/lib/parser.js +47 -44
  113. package/lib/pause.js +173 -145
  114. package/lib/plugin/analyze.js +403 -0
  115. package/lib/plugin/{autoLogin.js → auth.js} +178 -79
  116. package/lib/plugin/autoDelay.js +36 -40
  117. package/lib/plugin/coverage.js +131 -78
  118. package/lib/plugin/customLocator.js +22 -21
  119. package/lib/plugin/customReporter.js +53 -0
  120. package/lib/plugin/enhancedRetryFailedStep.js +99 -0
  121. package/lib/plugin/heal.js +101 -110
  122. package/lib/plugin/htmlReporter.js +3648 -0
  123. package/lib/plugin/pageInfo.js +140 -0
  124. package/lib/plugin/pauseOnFail.js +12 -11
  125. package/lib/plugin/retryFailedStep.js +82 -47
  126. package/lib/plugin/screenshotOnFail.js +111 -92
  127. package/lib/plugin/stepByStepReport.js +159 -101
  128. package/lib/plugin/stepTimeout.js +20 -25
  129. package/lib/plugin/subtitles.js +38 -38
  130. package/lib/recorder.js +193 -130
  131. package/lib/rerun.js +94 -49
  132. package/lib/result.js +238 -0
  133. package/lib/retryCoordinator.js +207 -0
  134. package/lib/secret.js +20 -18
  135. package/lib/session.js +95 -89
  136. package/lib/step/base.js +239 -0
  137. package/lib/step/comment.js +10 -0
  138. package/lib/step/config.js +50 -0
  139. package/lib/step/func.js +46 -0
  140. package/lib/step/helper.js +50 -0
  141. package/lib/step/meta.js +99 -0
  142. package/lib/step/record.js +74 -0
  143. package/lib/step/retry.js +11 -0
  144. package/lib/step/section.js +55 -0
  145. package/lib/step.js +18 -329
  146. package/lib/steps.js +54 -0
  147. package/lib/store.js +38 -7
  148. package/lib/template/heal.js +3 -12
  149. package/lib/template/prompts/generatePageObject.js +31 -0
  150. package/lib/template/prompts/healStep.js +13 -0
  151. package/lib/template/prompts/writeStep.js +9 -0
  152. package/lib/test-server.js +334 -0
  153. package/lib/timeout.js +60 -0
  154. package/lib/transform.js +8 -8
  155. package/lib/translation.js +34 -21
  156. package/lib/utils/loaderCheck.js +124 -0
  157. package/lib/utils/mask_data.js +47 -0
  158. package/lib/utils/typescript.js +237 -0
  159. package/lib/utils.js +411 -228
  160. package/lib/workerStorage.js +37 -34
  161. package/lib/workers.js +532 -296
  162. package/package.json +124 -95
  163. package/translations/de-DE.js +5 -3
  164. package/translations/fr-FR.js +5 -4
  165. package/translations/index.js +22 -12
  166. package/translations/it-IT.js +4 -3
  167. package/translations/ja-JP.js +4 -3
  168. package/translations/nl-NL.js +76 -0
  169. package/translations/pl-PL.js +4 -3
  170. package/translations/pt-BR.js +4 -3
  171. package/translations/ru-RU.js +4 -3
  172. package/translations/utils.js +10 -0
  173. package/translations/zh-CN.js +4 -3
  174. package/translations/zh-TW.js +4 -3
  175. package/typings/index.d.ts +546 -185
  176. package/typings/promiseBasedTypes.d.ts +150 -875
  177. package/typings/types.d.ts +547 -992
  178. package/lib/cli.js +0 -249
  179. package/lib/dirname.js +0 -5
  180. package/lib/helper/Expect.js +0 -425
  181. package/lib/helper/ExpectHelper.js +0 -399
  182. package/lib/helper/MockServer.js +0 -223
  183. package/lib/helper/Nightmare.js +0 -1411
  184. package/lib/helper/Protractor.js +0 -1835
  185. package/lib/helper/SoftExpectHelper.js +0 -381
  186. package/lib/helper/TestCafe.js +0 -1410
  187. package/lib/helper/clientscripts/nightmare.js +0 -213
  188. package/lib/helper/testcafe/testControllerHolder.js +0 -42
  189. package/lib/helper/testcafe/testcafe-utils.js +0 -63
  190. package/lib/interfaces/bdd.js +0 -98
  191. package/lib/interfaces/featureConfig.js +0 -69
  192. package/lib/interfaces/gherkin.js +0 -195
  193. package/lib/listener/artifacts.js +0 -19
  194. package/lib/listener/retry.js +0 -68
  195. package/lib/listener/timeout.js +0 -109
  196. package/lib/mochaFactory.js +0 -110
  197. package/lib/plugin/allure.js +0 -15
  198. package/lib/plugin/commentStep.js +0 -136
  199. package/lib/plugin/debugErrors.js +0 -67
  200. package/lib/plugin/eachElement.js +0 -127
  201. package/lib/plugin/fakerTransform.js +0 -49
  202. package/lib/plugin/retryTo.js +0 -121
  203. package/lib/plugin/selenoid.js +0 -371
  204. package/lib/plugin/standardActingHelpers.js +0 -9
  205. package/lib/plugin/tryTo.js +0 -105
  206. package/lib/plugin/wdio.js +0 -246
  207. package/lib/scenario.js +0 -222
  208. package/lib/ui.js +0 -238
  209. package/lib/within.js +0 -70
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Utilities for checking TypeScript loader availability
3
+ */
4
+
5
+ /**
6
+ * Check if a TypeScript loader is available for test files
7
+ * Note: This checks if loaders are in the require array, not if packages are installed
8
+ * Package installation is checked when actually requiring modules
9
+ * @param {string[]} requiredModules - Array of required modules from config
10
+ * @returns {boolean}
11
+ */
12
+ export function checkTypeScriptLoader(requiredModules = []) {
13
+ // Check if a loader is configured in the require array
14
+ return (
15
+ requiredModules.includes('tsx/esm') ||
16
+ requiredModules.includes('tsx/cjs') ||
17
+ requiredModules.includes('tsx') ||
18
+ requiredModules.includes('ts-node/esm') ||
19
+ requiredModules.includes('ts-node/register') ||
20
+ requiredModules.includes('ts-node')
21
+ )
22
+ }
23
+
24
+ /**
25
+ * Generate helpful error message if .ts tests found but no loader configured
26
+ * @param {string[]} testFiles - Array of test file paths
27
+ * @returns {string|null} Error message or null if no TypeScript files
28
+ */
29
+ export function getTypeScriptLoaderError(testFiles) {
30
+ const tsFiles = testFiles.filter(f => f.endsWith('.ts'))
31
+
32
+ if (tsFiles.length === 0) return null
33
+
34
+ return `
35
+ ╔═════════════════════════════════════════════════════════════════════════════╗
36
+ ║ ║
37
+ ║ ⚠️ TypeScript Test Files Detected but No Loader Configured ║
38
+ ║ ║
39
+ ╚═════════════════════════════════════════════════════════════════════════════╝
40
+
41
+ Found ${tsFiles.length} TypeScript test file(s) but no TypeScript loader is configured.
42
+
43
+ CodeceptJS 4.x uses ES Modules (ESM) and requires a loader to run TypeScript tests.
44
+
45
+ ┌─────────────────────────────────────────────────────────────────────────────┐
46
+ │ Option 1: tsx (Recommended - Fast, Zero Config) │
47
+ └─────────────────────────────────────────────────────────────────────────────┘
48
+
49
+ Installation:
50
+ npm install --save-dev tsx
51
+
52
+ Configuration:
53
+ Add to your codecept.conf.ts or codecept.conf.js:
54
+
55
+ export const config = {
56
+ tests: './**/*_test.ts',
57
+ require: ['tsx/cjs'], // ← Add this line
58
+ helpers: { /* ... */ }
59
+ }
60
+
61
+ Why tsx?
62
+ ⚡ Fast: Built on esbuild
63
+ 🎯 Zero config: No tsconfig.json required
64
+ ✅ Works with Mocha: Uses CommonJS hooks
65
+ ✅ Complete: Handles all TypeScript features
66
+
67
+ ┌─────────────────────────────────────────────────────────────────────────────┐
68
+ │ Option 2: ts-node/esm (Alternative - Established, Requires Config) │
69
+ └─────────────────────────────────────────────────────────────────────────────┘
70
+
71
+ Installation:
72
+ npm install --save-dev ts-node
73
+
74
+ Configuration:
75
+ 1. Add to your codecept.conf.ts:
76
+ require: ['ts-node/esm']
77
+
78
+ 2. Create tsconfig.json:
79
+ {
80
+ "compilerOptions": {
81
+ "module": "ESNext",
82
+ "target": "ES2022",
83
+ "moduleResolution": "node",
84
+ "esModuleInterop": true
85
+ },
86
+ "ts-node": {
87
+ "esm": true,
88
+ "experimentalSpecifierResolution": "node"
89
+ }
90
+ }
91
+
92
+ 📚 Documentation: https://codecept.io/typescript
93
+
94
+ Note: TypeScript config files (codecept.conf.ts) and helpers are automatically
95
+ transpiled. Only test files require a loader to be configured.
96
+ `
97
+ }
98
+
99
+ /**
100
+ * Check if user is trying to run TypeScript tests without proper loader
101
+ * @param {string[]} testFiles - Array of test file paths
102
+ * @param {string[]} requiredModules - Array of required modules from config
103
+ * @returns {{hasError: boolean, message: string|null}}
104
+ */
105
+ export function validateTypeScriptSetup(testFiles, requiredModules = []) {
106
+ const tsFiles = testFiles.filter(f => f.endsWith('.ts'))
107
+
108
+ if (tsFiles.length === 0) {
109
+ // No TypeScript test files, all good
110
+ return { hasError: false, message: null }
111
+ }
112
+
113
+ // Check if a loader is configured in the require array
114
+ const hasLoader = checkTypeScriptLoader(requiredModules)
115
+
116
+ if (hasLoader) {
117
+ // Loader configured, all good (package will be checked when requireModules runs)
118
+ return { hasError: false, message: null }
119
+ }
120
+
121
+ // No loader configured and TypeScript tests exist
122
+ const message = getTypeScriptLoaderError(testFiles)
123
+ return { hasError: true, message }
124
+ }
@@ -0,0 +1,47 @@
1
+ import { maskSensitiveData } from 'invisi-data'
2
+
3
+ /**
4
+ * Mask sensitive data utility for CodeceptJS
5
+ * Supports both boolean and object configuration formats
6
+ *
7
+ * @param {string} input - The string to mask
8
+ * @param {boolean|object} config - Masking configuration
9
+ * @returns {string} - Masked string
10
+ */
11
+ export function maskData(input, config) {
12
+ if (!config) {
13
+ return input
14
+ }
15
+
16
+ // Handle boolean config (backward compatibility)
17
+ if (typeof config === 'boolean' && config === true) {
18
+ return maskSensitiveData(input)
19
+ }
20
+
21
+ // Handle object config with custom patterns
22
+ if (typeof config === 'object' && config.enabled === true) {
23
+ const customPatterns = config.patterns || []
24
+ return maskSensitiveData(input, customPatterns)
25
+ }
26
+
27
+ return input
28
+ }
29
+
30
+ /**
31
+ * Check if masking is enabled based on global configuration
32
+ *
33
+ * @returns {boolean|object} - Current masking configuration
34
+ */
35
+ export function getMaskConfig() {
36
+ return global.maskSensitiveData || false
37
+ }
38
+
39
+ /**
40
+ * Check if data should be masked
41
+ *
42
+ * @returns {boolean} - True if masking is enabled
43
+ */
44
+ export function shouldMaskData() {
45
+ const config = getMaskConfig()
46
+ return config === true || (typeof config === 'object' && config.enabled === true)
47
+ }
@@ -0,0 +1,237 @@
1
+ import fs from 'fs'
2
+ import path from 'path'
3
+
4
+ /**
5
+ * Transpile TypeScript files to ES modules with CommonJS shim support
6
+ * Handles recursive transpilation of imported TypeScript files
7
+ *
8
+ * @param {string} mainFilePath - Path to the main TypeScript file to transpile
9
+ * @param {object} typescript - TypeScript compiler instance
10
+ * @returns {Promise<{tempFile: string, allTempFiles: string[]}>} - Main temp file and all temp files created
11
+ */
12
+ export async function transpileTypeScript(mainFilePath, typescript) {
13
+ const { transpile } = typescript
14
+
15
+ /**
16
+ * Transpile a single TypeScript file to JavaScript
17
+ * Injects CommonJS shims (require, module, exports, __dirname, __filename) as needed
18
+ */
19
+ const transpileTS = (filePath) => {
20
+ const tsContent = fs.readFileSync(filePath, 'utf8')
21
+
22
+ // Transpile TypeScript to JavaScript with ES module output
23
+ let jsContent = transpile(tsContent, {
24
+ module: 99, // ModuleKind.ESNext
25
+ target: 99, // ScriptTarget.ESNext
26
+ esModuleInterop: true,
27
+ allowSyntheticDefaultImports: true,
28
+ lib: ['lib.esnext.d.ts'], // Enable latest features including top-level await
29
+ suppressOutputPathCheck: true,
30
+ skipLibCheck: true,
31
+ })
32
+
33
+ // Check if the code uses CommonJS globals
34
+ const usesCommonJSGlobals = /__dirname|__filename/.test(jsContent)
35
+ const usesRequire = /\brequire\s*\(/.test(jsContent)
36
+ const usesModuleExports = /\b(module\.exports|exports\.)/.test(jsContent)
37
+
38
+ if (usesCommonJSGlobals || usesRequire || usesModuleExports) {
39
+ // Inject ESM equivalents at the top of the file
40
+ let esmGlobals = ''
41
+
42
+ if (usesRequire || usesModuleExports) {
43
+ // IMPORTANT: Use the original .ts file path as the base for require()
44
+ // This ensures dynamic require() calls work with relative paths from the original file location
45
+ const originalFileUrl = `file://${filePath.replace(/\\/g, '/')}`
46
+ esmGlobals += `import { createRequire } from 'module';
47
+ import { extname as __extname } from 'path';
48
+ const __baseRequire = createRequire('${originalFileUrl}');
49
+
50
+ // Wrap require to auto-resolve extensions (mimics CommonJS behavior)
51
+ const require = (id) => {
52
+ try {
53
+ return __baseRequire(id);
54
+ } catch (err) {
55
+ // If module not found and it's a relative/absolute path without extension, try common extensions
56
+ if (err.code === 'MODULE_NOT_FOUND' && (id.startsWith('./') || id.startsWith('../') || id.startsWith('/'))) {
57
+ const ext = __extname(id);
58
+ // Only treat known file extensions as real extensions (so names like .TEST don't block probing)
59
+ const __knownExts = ['.js', '.cjs', '.mjs', '.json', '.node'];
60
+ const hasKnownExt = ext && __knownExts.includes(ext.toLowerCase());
61
+ if (!hasKnownExt) {
62
+ // Try common extensions in order: .js, .cjs, .json, .node
63
+ // Note: .ts files cannot be required - they need transpilation first
64
+ const extensions = ['.js', '.cjs', '.json', '.node'];
65
+ for (const testExt of extensions) {
66
+ try {
67
+ return __baseRequire(id + testExt);
68
+ } catch (e) {
69
+ // Continue to next extension
70
+ }
71
+ }
72
+ }
73
+ }
74
+ // Re-throw original error if all attempts failed
75
+ throw err;
76
+ }
77
+ };
78
+
79
+ const module = { exports: {} };
80
+ const exports = module.exports;
81
+
82
+ `
83
+ }
84
+
85
+ if (usesCommonJSGlobals) {
86
+ // For __dirname and __filename, also use the original file path
87
+ const originalFileUrl = `file://${filePath.replace(/\\/g, '/')}`
88
+ esmGlobals += `import { fileURLToPath as __fileURLToPath } from 'url';
89
+ import { dirname as __dirname_fn } from 'path';
90
+ const __filename = '${filePath.replace(/\\/g, '/')}';
91
+ const __dirname = __dirname_fn(__filename);
92
+
93
+ `
94
+ }
95
+
96
+ jsContent = esmGlobals + jsContent
97
+
98
+ // If module.exports is used, we need to export it as default
99
+ if (usesModuleExports) {
100
+ jsContent += `\nexport default module.exports;\n`
101
+ }
102
+ }
103
+
104
+ return jsContent
105
+ }
106
+
107
+ // Create a map to track transpiled files
108
+ const transpiledFiles = new Map()
109
+ const baseDir = path.dirname(mainFilePath)
110
+
111
+ // Recursive function to transpile a file and all its TypeScript dependencies
112
+ const transpileFileAndDeps = (filePath) => {
113
+ // Already transpiled, skip
114
+ if (transpiledFiles.has(filePath)) {
115
+ return
116
+ }
117
+
118
+ // Transpile this file
119
+ let jsContent = transpileTS(filePath)
120
+
121
+ // Find all relative TypeScript imports in this file
122
+ const importRegex = /from\s+['"](\..+?)(?:\.ts)?['"]/g
123
+ let match
124
+ const imports = []
125
+
126
+ while ((match = importRegex.exec(jsContent)) !== null) {
127
+ imports.push(match[1])
128
+ }
129
+
130
+ // Get the base directory for this file
131
+ const fileBaseDir = path.dirname(filePath)
132
+
133
+ // 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(/\.js$/, '.ts')
140
+ if (fs.existsSync(tsVersion)) {
141
+ importedPath = tsVersion
142
+ }
143
+ }
144
+
145
+ // Try adding .ts extension if file doesn't exist and no extension provided
146
+ if (!path.extname(importedPath)) {
147
+ const tsPath = importedPath + '.ts'
148
+ if (fs.existsSync(tsPath)) {
149
+ importedPath = tsPath
150
+ } else {
151
+ // Try .js extension as well
152
+ const jsPath = importedPath + '.js'
153
+ if (fs.existsSync(jsPath)) {
154
+ // Skip .js files, they don't need transpilation
155
+ continue
156
+ }
157
+ }
158
+ }
159
+
160
+ // If it's a TypeScript file, recursively transpile it and its dependencies
161
+ if (importedPath.endsWith('.ts') && fs.existsSync(importedPath)) {
162
+ transpileFileAndDeps(importedPath)
163
+ }
164
+ }
165
+
166
+ // After all dependencies are transpiled, rewrite imports in this file
167
+ jsContent = jsContent.replace(
168
+ /from\s+['"](\..+?)(?:\.ts)?['"]/g,
169
+ (match, importPath) => {
170
+ let resolvedPath = path.resolve(fileBaseDir, importPath)
171
+
172
+ // Handle .js extension that might be .ts
173
+ if (resolvedPath.endsWith('.js')) {
174
+ const tsVersion = resolvedPath.replace(/\.js$/, '.ts')
175
+ if (transpiledFiles.has(tsVersion)) {
176
+ const tempFile = transpiledFiles.get(tsVersion)
177
+ const relPath = path.relative(fileBaseDir, tempFile).replace(/\\/g, '/')
178
+ // Ensure the path starts with ./
179
+ if (!relPath.startsWith('.')) {
180
+ return `from './${relPath}'`
181
+ }
182
+ return `from '${relPath}'`
183
+ }
184
+ }
185
+
186
+ // Try with .ts extension
187
+ const tsPath = resolvedPath.endsWith('.ts') ? resolvedPath : resolvedPath + '.ts'
188
+
189
+ // If we transpiled this file, use the temp file
190
+ if (transpiledFiles.has(tsPath)) {
191
+ const tempFile = transpiledFiles.get(tsPath)
192
+ const relPath = path.relative(fileBaseDir, tempFile).replace(/\\/g, '/')
193
+ // Ensure the path starts with ./
194
+ if (!relPath.startsWith('.')) {
195
+ return `from './${relPath}'`
196
+ }
197
+ return `from '${relPath}'`
198
+ }
199
+
200
+ // Otherwise, keep the import as-is
201
+ return match
202
+ }
203
+ )
204
+
205
+ // Write the transpiled file with updated imports
206
+ const tempFile = filePath.replace(/\.ts$/, '.temp.mjs')
207
+ fs.writeFileSync(tempFile, jsContent)
208
+ transpiledFiles.set(filePath, tempFile)
209
+ }
210
+
211
+ // Start recursive transpilation from the main file
212
+ transpileFileAndDeps(mainFilePath)
213
+
214
+ // Get the main transpiled file
215
+ const tempJsFile = transpiledFiles.get(mainFilePath)
216
+
217
+ // Store all temp files for cleanup
218
+ const allTempFiles = Array.from(transpiledFiles.values())
219
+
220
+ return { tempFile: tempJsFile, allTempFiles }
221
+ }
222
+
223
+ /**
224
+ * Clean up temporary transpiled files
225
+ * @param {string[]} tempFiles - Array of temp file paths to delete
226
+ */
227
+ export function cleanupTempFiles(tempFiles) {
228
+ for (const file of tempFiles) {
229
+ if (fs.existsSync(file)) {
230
+ try {
231
+ fs.unlinkSync(file)
232
+ } catch (err) {
233
+ // Ignore cleanup errors
234
+ }
235
+ }
236
+ }
237
+ }