codemeld 2.1.0

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 (243) hide show
  1. package/README.md +514 -0
  2. package/bin/cli.js +2 -0
  3. package/dist/ai/agent.d.ts +124 -0
  4. package/dist/ai/agent.d.ts.map +1 -0
  5. package/dist/ai/agent.js +289 -0
  6. package/dist/ai/agent.js.map +1 -0
  7. package/dist/ai/index.d.ts +10 -0
  8. package/dist/ai/index.d.ts.map +1 -0
  9. package/dist/ai/index.js +10 -0
  10. package/dist/ai/index.js.map +1 -0
  11. package/dist/ai/prompts.d.ts +35 -0
  12. package/dist/ai/prompts.d.ts.map +1 -0
  13. package/dist/ai/prompts.js +166 -0
  14. package/dist/ai/prompts.js.map +1 -0
  15. package/dist/ai/refinement-loop.d.ts +29 -0
  16. package/dist/ai/refinement-loop.d.ts.map +1 -0
  17. package/dist/ai/refinement-loop.js +180 -0
  18. package/dist/ai/refinement-loop.js.map +1 -0
  19. package/dist/ai/tools.d.ts +17 -0
  20. package/dist/ai/tools.d.ts.map +1 -0
  21. package/dist/ai/tools.js +353 -0
  22. package/dist/ai/tools.js.map +1 -0
  23. package/dist/ai/visual-compare.d.ts +43 -0
  24. package/dist/ai/visual-compare.d.ts.map +1 -0
  25. package/dist/ai/visual-compare.js +176 -0
  26. package/dist/ai/visual-compare.js.map +1 -0
  27. package/dist/cli.d.ts +3 -0
  28. package/dist/cli.d.ts.map +1 -0
  29. package/dist/cli.js +179 -0
  30. package/dist/cli.js.map +1 -0
  31. package/dist/converter.d.ts +10 -0
  32. package/dist/converter.d.ts.map +1 -0
  33. package/dist/converter.js +836 -0
  34. package/dist/converter.js.map +1 -0
  35. package/dist/deconverter.d.ts +19 -0
  36. package/dist/deconverter.d.ts.map +1 -0
  37. package/dist/deconverter.js +188 -0
  38. package/dist/deconverter.js.map +1 -0
  39. package/dist/frameworks/angular-adapter.d.ts +27 -0
  40. package/dist/frameworks/angular-adapter.d.ts.map +1 -0
  41. package/dist/frameworks/angular-adapter.js +617 -0
  42. package/dist/frameworks/angular-adapter.js.map +1 -0
  43. package/dist/frameworks/index.d.ts +10 -0
  44. package/dist/frameworks/index.d.ts.map +1 -0
  45. package/dist/frameworks/index.js +21 -0
  46. package/dist/frameworks/index.js.map +1 -0
  47. package/dist/frameworks/nextjs-adapter.d.ts +22 -0
  48. package/dist/frameworks/nextjs-adapter.d.ts.map +1 -0
  49. package/dist/frameworks/nextjs-adapter.js +392 -0
  50. package/dist/frameworks/nextjs-adapter.js.map +1 -0
  51. package/dist/frameworks/react-adapter.d.ts +21 -0
  52. package/dist/frameworks/react-adapter.d.ts.map +1 -0
  53. package/dist/frameworks/react-adapter.js +71 -0
  54. package/dist/frameworks/react-adapter.js.map +1 -0
  55. package/dist/frameworks/svelte-adapter.d.ts +27 -0
  56. package/dist/frameworks/svelte-adapter.d.ts.map +1 -0
  57. package/dist/frameworks/svelte-adapter.js +519 -0
  58. package/dist/frameworks/svelte-adapter.js.map +1 -0
  59. package/dist/frameworks/types.d.ts +78 -0
  60. package/dist/frameworks/types.d.ts.map +1 -0
  61. package/dist/frameworks/types.js +2 -0
  62. package/dist/frameworks/types.js.map +1 -0
  63. package/dist/frameworks/vue-adapter.d.ts +34 -0
  64. package/dist/frameworks/vue-adapter.d.ts.map +1 -0
  65. package/dist/frameworks/vue-adapter.js +632 -0
  66. package/dist/frameworks/vue-adapter.js.map +1 -0
  67. package/dist/generators/accessibility-generator.d.ts +43 -0
  68. package/dist/generators/accessibility-generator.d.ts.map +1 -0
  69. package/dist/generators/accessibility-generator.js +507 -0
  70. package/dist/generators/accessibility-generator.js.map +1 -0
  71. package/dist/generators/asset-handler.d.ts +14 -0
  72. package/dist/generators/asset-handler.d.ts.map +1 -0
  73. package/dist/generators/asset-handler.js +79 -0
  74. package/dist/generators/asset-handler.js.map +1 -0
  75. package/dist/generators/build-verifier.d.ts +8 -0
  76. package/dist/generators/build-verifier.d.ts.map +1 -0
  77. package/dist/generators/build-verifier.js +64 -0
  78. package/dist/generators/build-verifier.js.map +1 -0
  79. package/dist/generators/component-extractor.d.ts +25 -0
  80. package/dist/generators/component-extractor.d.ts.map +1 -0
  81. package/dist/generators/component-extractor.js +146 -0
  82. package/dist/generators/component-extractor.js.map +1 -0
  83. package/dist/generators/component-generator.d.ts +12 -0
  84. package/dist/generators/component-generator.d.ts.map +1 -0
  85. package/dist/generators/component-generator.js +724 -0
  86. package/dist/generators/component-generator.js.map +1 -0
  87. package/dist/generators/deploy-generator.d.ts +9 -0
  88. package/dist/generators/deploy-generator.d.ts.map +1 -0
  89. package/dist/generators/deploy-generator.js +409 -0
  90. package/dist/generators/deploy-generator.js.map +1 -0
  91. package/dist/generators/error-boundary.d.ts +5 -0
  92. package/dist/generators/error-boundary.d.ts.map +1 -0
  93. package/dist/generators/error-boundary.js +59 -0
  94. package/dist/generators/error-boundary.js.map +1 -0
  95. package/dist/generators/form-generator.d.ts +42 -0
  96. package/dist/generators/form-generator.d.ts.map +1 -0
  97. package/dist/generators/form-generator.js +662 -0
  98. package/dist/generators/form-generator.js.map +1 -0
  99. package/dist/generators/hooks-generator.d.ts +40 -0
  100. package/dist/generators/hooks-generator.d.ts.map +1 -0
  101. package/dist/generators/hooks-generator.js +297 -0
  102. package/dist/generators/hooks-generator.js.map +1 -0
  103. package/dist/generators/html-generator.d.ts +27 -0
  104. package/dist/generators/html-generator.d.ts.map +1 -0
  105. package/dist/generators/html-generator.js +772 -0
  106. package/dist/generators/html-generator.js.map +1 -0
  107. package/dist/generators/jquery-converter.d.ts +41 -0
  108. package/dist/generators/jquery-converter.d.ts.map +1 -0
  109. package/dist/generators/jquery-converter.js +594 -0
  110. package/dist/generators/jquery-converter.js.map +1 -0
  111. package/dist/generators/pattern-implementer.d.ts +26 -0
  112. package/dist/generators/pattern-implementer.d.ts.map +1 -0
  113. package/dist/generators/pattern-implementer.js +336 -0
  114. package/dist/generators/pattern-implementer.js.map +1 -0
  115. package/dist/generators/performance-generator.d.ts +51 -0
  116. package/dist/generators/performance-generator.d.ts.map +1 -0
  117. package/dist/generators/performance-generator.js +428 -0
  118. package/dist/generators/performance-generator.js.map +1 -0
  119. package/dist/generators/router-generator.d.ts +21 -0
  120. package/dist/generators/router-generator.d.ts.map +1 -0
  121. package/dist/generators/router-generator.js +178 -0
  122. package/dist/generators/router-generator.js.map +1 -0
  123. package/dist/generators/scaffolder.d.ts +28 -0
  124. package/dist/generators/scaffolder.d.ts.map +1 -0
  125. package/dist/generators/scaffolder.js +266 -0
  126. package/dist/generators/scaffolder.js.map +1 -0
  127. package/dist/generators/seo-generator.d.ts +29 -0
  128. package/dist/generators/seo-generator.d.ts.map +1 -0
  129. package/dist/generators/seo-generator.js +223 -0
  130. package/dist/generators/seo-generator.js.map +1 -0
  131. package/dist/generators/test-generator.d.ts +19 -0
  132. package/dist/generators/test-generator.d.ts.map +1 -0
  133. package/dist/generators/test-generator.js +398 -0
  134. package/dist/generators/test-generator.js.map +1 -0
  135. package/dist/generators/type-generator.d.ts +33 -0
  136. package/dist/generators/type-generator.d.ts.map +1 -0
  137. package/dist/generators/type-generator.js +663 -0
  138. package/dist/generators/type-generator.js.map +1 -0
  139. package/dist/index.d.ts +23 -0
  140. package/dist/index.d.ts.map +1 -0
  141. package/dist/index.js +12 -0
  142. package/dist/index.js.map +1 -0
  143. package/dist/parsers/css-processor.d.ts +23 -0
  144. package/dist/parsers/css-processor.d.ts.map +1 -0
  145. package/dist/parsers/css-processor.js +129 -0
  146. package/dist/parsers/css-processor.js.map +1 -0
  147. package/dist/parsers/framework-parser.d.ts +48 -0
  148. package/dist/parsers/framework-parser.d.ts.map +1 -0
  149. package/dist/parsers/framework-parser.js +770 -0
  150. package/dist/parsers/framework-parser.js.map +1 -0
  151. package/dist/parsers/html-parser.d.ts +12 -0
  152. package/dist/parsers/html-parser.d.ts.map +1 -0
  153. package/dist/parsers/html-parser.js +444 -0
  154. package/dist/parsers/html-parser.js.map +1 -0
  155. package/dist/parsers/js-analyzer.d.ts +199 -0
  156. package/dist/parsers/js-analyzer.d.ts.map +1 -0
  157. package/dist/parsers/js-analyzer.js +680 -0
  158. package/dist/parsers/js-analyzer.js.map +1 -0
  159. package/dist/parsers/js-resolver.d.ts +8 -0
  160. package/dist/parsers/js-resolver.d.ts.map +1 -0
  161. package/dist/parsers/js-resolver.js +45 -0
  162. package/dist/parsers/js-resolver.js.map +1 -0
  163. package/dist/parsers/tailwind-detector.d.ts +23 -0
  164. package/dist/parsers/tailwind-detector.d.ts.map +1 -0
  165. package/dist/parsers/tailwind-detector.js +104 -0
  166. package/dist/parsers/tailwind-detector.js.map +1 -0
  167. package/dist/tests/advanced-features.test.d.ts +2 -0
  168. package/dist/tests/advanced-features.test.d.ts.map +1 -0
  169. package/dist/tests/advanced-features.test.js +235 -0
  170. package/dist/tests/advanced-features.test.js.map +1 -0
  171. package/dist/tests/css-modules.test.d.ts +2 -0
  172. package/dist/tests/css-modules.test.d.ts.map +1 -0
  173. package/dist/tests/css-modules.test.js +61 -0
  174. package/dist/tests/css-modules.test.js.map +1 -0
  175. package/dist/tests/css-processor.test.d.ts +2 -0
  176. package/dist/tests/css-processor.test.d.ts.map +1 -0
  177. package/dist/tests/css-processor.test.js +48 -0
  178. package/dist/tests/css-processor.test.js.map +1 -0
  179. package/dist/tests/html-parser.test.d.ts +2 -0
  180. package/dist/tests/html-parser.test.d.ts.map +1 -0
  181. package/dist/tests/html-parser.test.js +78 -0
  182. package/dist/tests/html-parser.test.js.map +1 -0
  183. package/dist/tests/integration.test.d.ts +2 -0
  184. package/dist/tests/integration.test.d.ts.map +1 -0
  185. package/dist/tests/integration.test.js +65 -0
  186. package/dist/tests/integration.test.js.map +1 -0
  187. package/dist/tests/js-analyzer.test.d.ts +2 -0
  188. package/dist/tests/js-analyzer.test.d.ts.map +1 -0
  189. package/dist/tests/js-analyzer.test.js +58 -0
  190. package/dist/tests/js-analyzer.test.js.map +1 -0
  191. package/dist/tests/naming.test.d.ts +2 -0
  192. package/dist/tests/naming.test.d.ts.map +1 -0
  193. package/dist/tests/naming.test.js +43 -0
  194. package/dist/tests/naming.test.js.map +1 -0
  195. package/dist/tests/router-generator.test.d.ts +2 -0
  196. package/dist/tests/router-generator.test.d.ts.map +1 -0
  197. package/dist/tests/router-generator.test.js +60 -0
  198. package/dist/tests/router-generator.test.js.map +1 -0
  199. package/dist/tui/chat.d.ts +13 -0
  200. package/dist/tui/chat.d.ts.map +1 -0
  201. package/dist/tui/chat.js +499 -0
  202. package/dist/tui/chat.js.map +1 -0
  203. package/dist/tui/design-guide.d.ts +41 -0
  204. package/dist/tui/design-guide.d.ts.map +1 -0
  205. package/dist/tui/design-guide.js +184 -0
  206. package/dist/tui/design-guide.js.map +1 -0
  207. package/dist/tui/input.d.ts +30 -0
  208. package/dist/tui/input.d.ts.map +1 -0
  209. package/dist/tui/input.js +239 -0
  210. package/dist/tui/input.js.map +1 -0
  211. package/dist/tui/renderer.d.ts +48 -0
  212. package/dist/tui/renderer.d.ts.map +1 -0
  213. package/dist/tui/renderer.js +212 -0
  214. package/dist/tui/renderer.js.map +1 -0
  215. package/dist/tui/tools.d.ts +14 -0
  216. package/dist/tui/tools.d.ts.map +1 -0
  217. package/dist/tui/tools.js +1370 -0
  218. package/dist/tui/tools.js.map +1 -0
  219. package/dist/types.d.ts +93 -0
  220. package/dist/types.d.ts.map +1 -0
  221. package/dist/types.js +2 -0
  222. package/dist/types.js.map +1 -0
  223. package/dist/utils/config.d.ts +20 -0
  224. package/dist/utils/config.d.ts.map +1 -0
  225. package/dist/utils/config.js +33 -0
  226. package/dist/utils/config.js.map +1 -0
  227. package/dist/utils/formatter.d.ts +5 -0
  228. package/dist/utils/formatter.d.ts.map +1 -0
  229. package/dist/utils/formatter.js +68 -0
  230. package/dist/utils/formatter.js.map +1 -0
  231. package/dist/utils/logger.d.ts +8 -0
  232. package/dist/utils/logger.d.ts.map +1 -0
  233. package/dist/utils/logger.js +19 -0
  234. package/dist/utils/logger.js.map +1 -0
  235. package/dist/utils/naming.d.ts +17 -0
  236. package/dist/utils/naming.d.ts.map +1 -0
  237. package/dist/utils/naming.js +48 -0
  238. package/dist/utils/naming.js.map +1 -0
  239. package/dist/utils/report.d.ts +56 -0
  240. package/dist/utils/report.d.ts.map +1 -0
  241. package/dist/utils/report.js +339 -0
  242. package/dist/utils/report.js.map +1 -0
  243. package/package.json +61 -0
@@ -0,0 +1,398 @@
1
+ /**
2
+ * Generate a vitest.config.ts file for the converted project.
3
+ */
4
+ export function generateVitestConfig() {
5
+ return `/// <reference types="vitest" />
6
+ import { defineConfig } from 'vite';
7
+ import react from '@vitejs/plugin-react';
8
+
9
+ export default defineConfig({
10
+ plugins: [react()],
11
+ test: {
12
+ globals: true,
13
+ environment: 'jsdom',
14
+ setupFiles: ['./src/test/setup.ts'],
15
+ css: true,
16
+ include: ['src/**/*.{test,spec}.{ts,tsx}'],
17
+ coverage: {
18
+ provider: 'v8',
19
+ reporter: ['text', 'json', 'html'],
20
+ include: ['src/components/**/*.tsx'],
21
+ },
22
+ },
23
+ });
24
+ `;
25
+ }
26
+ /**
27
+ * Generate the test setup file (src/test/setup.ts) with testing-library matchers.
28
+ */
29
+ export function generateTestSetup() {
30
+ return `import '@testing-library/jest-dom/vitest';
31
+ import { cleanup } from '@testing-library/react';
32
+ import { afterEach } from 'vitest';
33
+
34
+ // Automatic cleanup after each test
35
+ afterEach(() => {
36
+ cleanup();
37
+ });
38
+
39
+ // Mock window.matchMedia for components that use media queries
40
+ Object.defineProperty(window, 'matchMedia', {
41
+ writable: true,
42
+ value: (query: string) => ({
43
+ matches: false,
44
+ media: query,
45
+ onchange: null,
46
+ addListener: () => {},
47
+ removeListener: () => {},
48
+ addEventListener: () => {},
49
+ removeEventListener: () => {},
50
+ dispatchEvent: () => false,
51
+ }),
52
+ });
53
+
54
+ // Mock IntersectionObserver for scroll-based components
55
+ class MockIntersectionObserver {
56
+ observe() {}
57
+ unobserve() {}
58
+ disconnect() {}
59
+ }
60
+ Object.defineProperty(window, 'IntersectionObserver', {
61
+ writable: true,
62
+ value: MockIntersectionObserver,
63
+ });
64
+ `;
65
+ }
66
+ /**
67
+ * Generate a test file for a specific component.
68
+ */
69
+ export function generateComponentTest(component, analysis) {
70
+ const imports = buildImports(component, analysis);
71
+ const tests = buildTests(component, analysis);
72
+ return `${imports}
73
+
74
+ describe('${component.name}', () => {
75
+ ${tests}
76
+ });
77
+ `;
78
+ }
79
+ /**
80
+ * Return the devDependency packages needed for the test infrastructure.
81
+ */
82
+ export function generateTestPackageDeps() {
83
+ return {
84
+ 'vitest': '^1.6.0',
85
+ '@testing-library/react': '^15.0.0',
86
+ '@testing-library/jest-dom': '^6.4.0',
87
+ '@testing-library/user-event': '^14.5.0',
88
+ '@vitest/coverage-v8': '^1.6.0',
89
+ 'jsdom': '^24.0.0',
90
+ };
91
+ }
92
+ // ---------------------------------------------------------------------------
93
+ // Internal helpers
94
+ // ---------------------------------------------------------------------------
95
+ function needsRouter(component) {
96
+ return (component.jsx.includes('<Link') ||
97
+ component.jsx.includes('<NavLink') ||
98
+ component.jsx.includes('useNavigate') ||
99
+ component.jsx.includes('useLocation') ||
100
+ component.hooks.has('useNavigate') ||
101
+ component.hooks.has('useLocation'));
102
+ }
103
+ function hasForm(component, analysis) {
104
+ if (component.jsx.includes('<form') || component.jsx.includes('<Form'))
105
+ return true;
106
+ if (analysis?.interactivePatterns?.formHandlers?.length)
107
+ return true;
108
+ if (analysis?.interactivePatterns?.formValidations?.length)
109
+ return true;
110
+ return false;
111
+ }
112
+ function buildImports(component, analysis) {
113
+ const lines = [];
114
+ // Core testing imports
115
+ lines.push(`import { describe, it, expect, vi } from 'vitest';`);
116
+ // Determine which testing-library utilities we need
117
+ const tlUtils = new Set(['render', 'screen']);
118
+ if (component.eventHandlers.length || hasForm(component, analysis)) {
119
+ tlUtils.add('fireEvent');
120
+ }
121
+ if (analysis?.fetchCalls?.length) {
122
+ tlUtils.add('waitFor');
123
+ }
124
+ lines.push(`import { ${[...tlUtils].join(', ')} } from '@testing-library/react';`);
125
+ if (component.eventHandlers.length || hasForm(component, analysis)) {
126
+ lines.push(`import userEvent from '@testing-library/user-event';`);
127
+ }
128
+ // Router wrapper
129
+ if (needsRouter(component)) {
130
+ lines.push(`import { MemoryRouter } from 'react-router-dom';`);
131
+ }
132
+ // Component under test
133
+ lines.push(`import ${component.name} from './${component.name}';`);
134
+ return lines.join('\n');
135
+ }
136
+ function indent(text, level) {
137
+ const pad = ' '.repeat(level);
138
+ return text
139
+ .split('\n')
140
+ .map((l) => (l.trim() ? pad + l : ''))
141
+ .join('\n');
142
+ }
143
+ function renderCall(component) {
144
+ if (needsRouter(component)) {
145
+ return `render(
146
+ <MemoryRouter>
147
+ <${component.name} />
148
+ </MemoryRouter>
149
+ )`;
150
+ }
151
+ return `render(<${component.name} />)`;
152
+ }
153
+ function buildTests(component, analysis) {
154
+ const blocks = [];
155
+ // 1. Basic render test
156
+ blocks.push(buildRenderTest(component));
157
+ // 2. Snapshot test
158
+ blocks.push(buildSnapshotTest(component));
159
+ // 3. State interaction tests
160
+ if (component.stateVars.length > 0) {
161
+ blocks.push(buildStateTests(component));
162
+ }
163
+ // 4. Event handler tests
164
+ if (component.eventHandlers.length > 0) {
165
+ blocks.push(buildEventHandlerTests(component));
166
+ }
167
+ // 5. Form submission tests
168
+ if (hasForm(component, analysis)) {
169
+ blocks.push(buildFormTests(component, analysis));
170
+ }
171
+ // 6. Async / fetch tests
172
+ if (analysis?.fetchCalls?.length) {
173
+ blocks.push(buildAsyncTests(component, analysis));
174
+ }
175
+ return blocks.join('\n\n');
176
+ }
177
+ // -- Individual test builders -----------------------------------------------
178
+ function buildRenderTest(component) {
179
+ return indent(`it('renders without crashing', () => {
180
+ ${renderCall(component)};
181
+ expect(document.querySelector('.${camelToKebab(component.name)}') || document.body.firstChild).toBeTruthy();
182
+ });`, 1);
183
+ }
184
+ function buildSnapshotTest(component) {
185
+ return indent(`it('matches snapshot', () => {
186
+ const { container } = ${renderCall(component)};
187
+ expect(container).toMatchSnapshot();
188
+ });`, 1);
189
+ }
190
+ function buildStateTests(component) {
191
+ const tests = component.stateVars.map((sv) => {
192
+ const label = camelToWords(sv.name);
193
+ // If the initial value is a boolean, we can test toggle behaviour
194
+ if (sv.initialValue === 'false' || sv.initialValue === 'true') {
195
+ return indent(`it('toggles ${label} state', async () => {
196
+ const user = userEvent.setup();
197
+ ${renderCall(component)};
198
+
199
+ // Find the interactive element that likely controls "${sv.name}"
200
+ const toggleEl =
201
+ screen.queryByRole('button', { name: new RegExp('${label}', 'i') }) ??
202
+ screen.queryByText(new RegExp('${label}', 'i'));
203
+
204
+ if (toggleEl) {
205
+ await user.click(toggleEl);
206
+ // After click the DOM should have changed
207
+ expect(toggleEl).toBeInTheDocument();
208
+ }
209
+ });`, 1);
210
+ }
211
+ // Numeric counters
212
+ if (/^\d+$/.test(sv.initialValue)) {
213
+ return indent(`it('updates ${label} state', async () => {
214
+ const user = userEvent.setup();
215
+ ${renderCall(component)};
216
+
217
+ // Look for an element displaying the initial value
218
+ const display = screen.queryByText('${sv.initialValue}');
219
+ if (display) {
220
+ expect(display).toBeInTheDocument();
221
+ }
222
+
223
+ // Attempt to find a button that changes the value
224
+ const trigger =
225
+ screen.queryByRole('button', { name: new RegExp('${label}|increment|add', 'i') }) ??
226
+ screen.queryByRole('button');
227
+ if (trigger) {
228
+ await user.click(trigger);
229
+ }
230
+ });`, 1);
231
+ }
232
+ // String / generic state
233
+ return indent(`it('manages ${label} state', () => {
234
+ const { container } = ${renderCall(component)};
235
+ // "${sv.name}" initialised to ${sv.initialValue}
236
+ expect(container).toBeTruthy();
237
+ });`, 1);
238
+ });
239
+ return tests.join('\n\n');
240
+ }
241
+ function buildEventHandlerTests(component) {
242
+ const tests = component.eventHandlers.map((eh) => {
243
+ const eventName = eh.event.replace(/^on/, '').toLowerCase();
244
+ const label = camelToWords(eh.handler);
245
+ // Map common React event names to fireEvent methods
246
+ const fireEventMethod = mapEventToFireEvent(eventName);
247
+ return indent(`it('handles ${eventName} event (${label})', async () => {
248
+ const user = userEvent.setup();
249
+ ${renderCall(component)};
250
+
251
+ // Find the element that has the ${eventName} handler
252
+ const target =
253
+ screen.queryByRole('button') ??
254
+ screen.queryByRole('link') ??
255
+ document.querySelector('${eh.elementSelector || '*'}');
256
+
257
+ if (target) {
258
+ ${fireEventMethod === 'click'
259
+ ? 'await user.click(target);'
260
+ : `fireEvent.${fireEventMethod}(target);`}
261
+ // Verify the component is still mounted after event
262
+ expect(target).toBeInTheDocument();
263
+ }
264
+ });`, 1);
265
+ });
266
+ return tests.join('\n\n');
267
+ }
268
+ function buildFormTests(component, analysis) {
269
+ const formHandlers = analysis?.interactivePatterns?.formHandlers ?? [];
270
+ const hasEmail = formHandlers.some((fh) => fh.isEmail);
271
+ const emailBlock = hasEmail
272
+ ? `
273
+ // Fill in email field
274
+ const emailInput = screen.queryByRole('textbox', { name: /email/i }) ??
275
+ screen.queryByPlaceholderText(/email/i) ??
276
+ container.querySelector('input[type="email"]');
277
+ if (emailInput) {
278
+ await user.clear(emailInput);
279
+ await user.type(emailInput, 'test@example.com');
280
+ expect(emailInput).toHaveValue('test@example.com');
281
+ }`
282
+ : '';
283
+ return indent(`it('handles form submission', async () => {
284
+ const user = userEvent.setup();
285
+ const { container } = ${renderCall(component)};
286
+
287
+ const form = container.querySelector('form');
288
+ expect(form).toBeTruthy();
289
+
290
+ // Fill in text inputs
291
+ const textInputs = container.querySelectorAll('input[type="text"], input:not([type])');
292
+ for (const input of textInputs) {
293
+ await user.clear(input as HTMLElement);
294
+ await user.type(input as HTMLElement, 'test value');
295
+ }
296
+ ${emailBlock}
297
+
298
+ // Submit
299
+ const submitBtn =
300
+ screen.queryByRole('button', { name: /submit|send|save/i }) ??
301
+ container.querySelector('button[type="submit"]') ??
302
+ container.querySelector('input[type="submit"]');
303
+ if (submitBtn) {
304
+ await user.click(submitBtn);
305
+ }
306
+ });
307
+
308
+ it('validates required fields', async () => {
309
+ const user = userEvent.setup();
310
+ const { container } = ${renderCall(component)};
311
+
312
+ // Try submitting without filling required fields
313
+ const submitBtn =
314
+ screen.queryByRole('button', { name: /submit|send|save/i }) ??
315
+ container.querySelector('button[type="submit"]');
316
+ if (submitBtn) {
317
+ await user.click(submitBtn);
318
+ // Check if validation messages appear
319
+ const errorMessages = container.querySelectorAll('[class*="error"], [class*="invalid"], [role="alert"]');
320
+ // Component should still be mounted
321
+ expect(container).toBeTruthy();
322
+ }
323
+ });`, 1);
324
+ }
325
+ function buildAsyncTests(component, analysis) {
326
+ const fetchCalls = analysis.fetchCalls ?? [];
327
+ const firstFetch = fetchCalls[0];
328
+ const url = firstFetch?.url ?? '/api/data';
329
+ return indent(`it('handles async data fetching', async () => {
330
+ // Mock fetch
331
+ const mockData = { data: 'test' };
332
+ const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue(
333
+ new Response(JSON.stringify(mockData), {
334
+ status: 200,
335
+ headers: { 'Content-Type': 'application/json' },
336
+ }),
337
+ );
338
+
339
+ ${renderCall(component)};
340
+
341
+ await waitFor(() => {
342
+ // Verify fetch was called
343
+ expect(fetchSpy).toHaveBeenCalled();
344
+ });
345
+
346
+ fetchSpy.mockRestore();
347
+ });
348
+
349
+ it('handles fetch error gracefully', async () => {
350
+ const fetchSpy = vi.spyOn(globalThis, 'fetch').mockRejectedValue(
351
+ new Error('Network error'),
352
+ );
353
+
354
+ ${renderCall(component)};
355
+
356
+ await waitFor(() => {
357
+ expect(fetchSpy).toHaveBeenCalled();
358
+ });
359
+
360
+ // Component should not crash on error
361
+ expect(document.body.firstChild).toBeTruthy();
362
+
363
+ fetchSpy.mockRestore();
364
+ });`, 1);
365
+ }
366
+ // -- Utility helpers --------------------------------------------------------
367
+ function camelToKebab(str) {
368
+ return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
369
+ }
370
+ function camelToWords(str) {
371
+ return str
372
+ .replace(/([a-z])([A-Z])/g, '$1 $2')
373
+ .replace(/[_-]/g, ' ')
374
+ .toLowerCase();
375
+ }
376
+ function mapEventToFireEvent(event) {
377
+ const map = {
378
+ click: 'click',
379
+ submit: 'submit',
380
+ change: 'change',
381
+ input: 'input',
382
+ focus: 'focus',
383
+ blur: 'blur',
384
+ keydown: 'keyDown',
385
+ keyup: 'keyUp',
386
+ keypress: 'keyPress',
387
+ mouseover: 'mouseOver',
388
+ mouseout: 'mouseOut',
389
+ mouseenter: 'mouseEnter',
390
+ mouseleave: 'mouseLeave',
391
+ scroll: 'scroll',
392
+ resize: 'resize',
393
+ touchstart: 'touchStart',
394
+ touchend: 'touchEnd',
395
+ };
396
+ return map[event] ?? 'click';
397
+ }
398
+ //# sourceMappingURL=test-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-generator.js","sourceRoot":"","sources":["../../src/generators/test-generator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import type { ExtractedComponent, StateVariable } from '../types.js';
2
+ import type { AnalyzedJS, FetchCall } from '../parsers/js-analyzer.js';
3
+ export interface PropType {
4
+ name: string;
5
+ type: string;
6
+ required: boolean;
7
+ defaultValue?: string;
8
+ description?: string;
9
+ }
10
+ /**
11
+ * Analyzes component JSX for props usage and infers TypeScript types
12
+ * from usage context. Scans for `{props.x}` and destructured props patterns.
13
+ */
14
+ export declare function inferPropTypes(component: ExtractedComponent): PropType[];
15
+ /**
16
+ * Generates complete TypeScript interface definitions for a component's props and state.
17
+ */
18
+ export declare function generateTypeDefinitions(component: ExtractedComponent, analysis?: AnalyzedJS): string;
19
+ /**
20
+ * Analyzes fetch call URLs and patterns to generate response type interfaces.
21
+ * Groups endpoints by path and generates typed response interfaces.
22
+ */
23
+ export declare function inferApiResponseType(fetchCalls: FetchCall[]): string;
24
+ /**
25
+ * Generates global type definitions file content suitable for a `global.d.ts` file.
26
+ */
27
+ export declare function generateGlobalTypes(): string;
28
+ /**
29
+ * Maps state variable names and initial values to TypeScript types.
30
+ * Analyzes literal values and setter usage patterns to infer accurate types.
31
+ */
32
+ export declare function inferStateTypes(stateVars: StateVariable[]): Map<string, string>;
33
+ //# sourceMappingURL=type-generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"type-generator.d.ts","sourceRoot":"","sources":["../../src/generators/type-generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAEvE,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,kBAAkB,GAAG,QAAQ,EAAE,CA8ExE;AA8LD;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,kBAAkB,EAAE,QAAQ,CAAC,EAAE,UAAU,GAAG,MAAM,CAmEpG;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,MAAM,CAgDpE;AA8CD;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CA8G5C;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,aAAa,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAS/E"}