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,663 @@
1
+ /**
2
+ * Analyzes component JSX for props usage and infers TypeScript types
3
+ * from usage context. Scans for `{props.x}` and destructured props patterns.
4
+ */
5
+ export function inferPropTypes(component) {
6
+ const props = new Map();
7
+ const jsx = component.jsx;
8
+ // Match props.xxx patterns
9
+ const propsAccessRegex = /\bprops\.(\w+)/g;
10
+ let match;
11
+ while ((match = propsAccessRegex.exec(jsx)) !== null) {
12
+ const name = match[1];
13
+ if (!props.has(name)) {
14
+ props.set(name, {
15
+ name,
16
+ type: inferTypeFromName(name),
17
+ required: false,
18
+ });
19
+ }
20
+ }
21
+ // Match destructured props: { foo, bar } = props or ({ foo, bar })
22
+ const destructuredRegex = /\{\s*([\w\s,]+)\s*\}\s*=\s*props/g;
23
+ while ((match = destructuredRegex.exec(jsx)) !== null) {
24
+ const names = match[1].split(',').map((s) => s.trim()).filter(Boolean);
25
+ for (const name of names) {
26
+ if (!props.has(name)) {
27
+ props.set(name, {
28
+ name,
29
+ type: inferTypeFromName(name),
30
+ required: false,
31
+ });
32
+ }
33
+ }
34
+ }
35
+ // Match function component parameter destructuring: function Comp({ foo, bar })
36
+ const paramDestructureRegex = /(?:function\s+\w+|const\s+\w+\s*=\s*)\s*\(\s*\{\s*([\w\s,='":\-.*]+)\s*\}\s*\)/g;
37
+ while ((match = paramDestructureRegex.exec(jsx)) !== null) {
38
+ const params = match[1].split(',');
39
+ for (const param of params) {
40
+ const trimmed = param.trim();
41
+ // Handle default values: name = 'default'
42
+ const defaultMatch = trimmed.match(/^(\w+)\s*=\s*(.+)$/);
43
+ const name = defaultMatch ? defaultMatch[1] : trimmed.split(':')[0].trim();
44
+ if (!name || name.startsWith('...'))
45
+ continue;
46
+ const cleanName = name.replace(/\.\.\./g, '');
47
+ if (!cleanName)
48
+ continue;
49
+ const propType = {
50
+ name: cleanName,
51
+ type: inferTypeFromName(cleanName),
52
+ required: !defaultMatch,
53
+ };
54
+ if (defaultMatch) {
55
+ propType.defaultValue = defaultMatch[2].trim();
56
+ // Refine type based on default value
57
+ const refined = inferTypeFromValue(propType.defaultValue);
58
+ if (refined !== 'unknown') {
59
+ propType.type = refined;
60
+ }
61
+ }
62
+ if (!props.has(cleanName)) {
63
+ props.set(cleanName, propType);
64
+ }
65
+ }
66
+ }
67
+ // Refine types by analyzing usage context in JSX
68
+ for (const [name, prop] of props) {
69
+ const contextType = inferTypeFromUsageContext(name, jsx);
70
+ if (contextType) {
71
+ prop.type = contextType;
72
+ }
73
+ prop.description = generatePropDescription(name, prop.type);
74
+ }
75
+ return Array.from(props.values());
76
+ }
77
+ /**
78
+ * Infers a TypeScript type from a prop name using common naming conventions.
79
+ */
80
+ function inferTypeFromName(name) {
81
+ // Event handlers
82
+ if (/^on[A-Z]/.test(name)) {
83
+ return inferEventHandlerType(name);
84
+ }
85
+ // Boolean patterns
86
+ if (/^(is|has|can|should|will|did|show|hide|enable|disable|visible|active|checked|disabled|loading|open|closed|selected|readonly|required)/.test(name)) {
87
+ return 'boolean';
88
+ }
89
+ // String patterns
90
+ if (/^(className|class|style|id|name|label|title|description|placeholder|alt|src|href|url|text|content|message|error|value|type|variant|size|color|icon|path|key|token|email|password|username)$/i.test(name)) {
91
+ return 'string';
92
+ }
93
+ // Number patterns
94
+ if (/^(count|index|length|size|width|height|min|max|step|limit|offset|page|total|amount|quantity|duration|delay|timeout|interval|priority|order|level|depth|columns|rows|tabIndex|zIndex|opacity|flex|span)$/i.test(name)) {
95
+ return 'number';
96
+ }
97
+ // Children
98
+ if (name === 'children') {
99
+ return 'React.ReactNode';
100
+ }
101
+ // Ref
102
+ if (/ref$/i.test(name)) {
103
+ return 'React.Ref<HTMLElement>';
104
+ }
105
+ // Arrays
106
+ if (/^(items|list|options|data|rows|columns|entries|results|tags|values|elements|records|children)$/i.test(name) && name !== 'children') {
107
+ return 'unknown[]';
108
+ }
109
+ return 'unknown';
110
+ }
111
+ /**
112
+ * Infers event handler type from the handler name.
113
+ */
114
+ function inferEventHandlerType(name) {
115
+ const eventMap = {
116
+ onClick: '(event: React.MouseEvent) => void',
117
+ onChange: '(event: React.ChangeEvent<HTMLInputElement>) => void',
118
+ onSubmit: '(event: React.FormEvent) => void',
119
+ onFocus: '(event: React.FocusEvent) => void',
120
+ onBlur: '(event: React.FocusEvent) => void',
121
+ onKeyDown: '(event: React.KeyboardEvent) => void',
122
+ onKeyUp: '(event: React.KeyboardEvent) => void',
123
+ onKeyPress: '(event: React.KeyboardEvent) => void',
124
+ onMouseEnter: '(event: React.MouseEvent) => void',
125
+ onMouseLeave: '(event: React.MouseEvent) => void',
126
+ onScroll: '(event: React.UIEvent) => void',
127
+ onDrag: '(event: React.DragEvent) => void',
128
+ onDrop: '(event: React.DragEvent) => void',
129
+ onInput: '(event: React.FormEvent<HTMLInputElement>) => void',
130
+ onLoad: '() => void',
131
+ onError: '(error: Error) => void',
132
+ };
133
+ if (eventMap[name]) {
134
+ return eventMap[name];
135
+ }
136
+ // Generic handler pattern: onItemClick → (id: string) => void
137
+ if (/^on\w+Click$/.test(name)) {
138
+ return '(id: string) => void';
139
+ }
140
+ if (/^on\w+Change$/.test(name)) {
141
+ return '(value: unknown) => void';
142
+ }
143
+ return '() => void';
144
+ }
145
+ /**
146
+ * Infers type from a literal value string.
147
+ */
148
+ function inferTypeFromValue(value) {
149
+ const trimmed = value.trim();
150
+ if (trimmed === 'true' || trimmed === 'false')
151
+ return 'boolean';
152
+ if (/^-?\d+(\.\d+)?$/.test(trimmed))
153
+ return 'number';
154
+ if (/^['"`]/.test(trimmed))
155
+ return 'string';
156
+ if (trimmed === 'null')
157
+ return 'null';
158
+ if (trimmed === 'undefined')
159
+ return 'undefined';
160
+ if (trimmed.startsWith('['))
161
+ return 'unknown[]';
162
+ if (trimmed.startsWith('{'))
163
+ return 'Record<string, unknown>';
164
+ return 'unknown';
165
+ }
166
+ /**
167
+ * Analyzes how a prop is used in JSX to infer a more accurate type.
168
+ */
169
+ function inferTypeFromUsageContext(name, jsx) {
170
+ // Used as event handler: onClick={props.x} or onSubmit={x}
171
+ const eventHandlerRegex = new RegExp(`on\\w+\\s*=\\s*\\{\\s*(?:props\\.)?${escapeRegex(name)}\\s*\\}`);
172
+ if (eventHandlerRegex.test(jsx)) {
173
+ return '(...args: unknown[]) => void';
174
+ }
175
+ // Used as src attribute
176
+ const srcRegex = new RegExp(`src\\s*=\\s*\\{\\s*(?:props\\.)?${escapeRegex(name)}\\s*\\}`);
177
+ if (srcRegex.test(jsx)) {
178
+ return 'string';
179
+ }
180
+ // Used as className
181
+ const classRegex = new RegExp(`className\\s*=\\s*\\{\\s*(?:props\\.)?${escapeRegex(name)}\\s*\\}`);
182
+ if (classRegex.test(jsx)) {
183
+ return 'string';
184
+ }
185
+ // Used in template literal or string concatenation
186
+ const backtick = '`';
187
+ const templateRegex = new RegExp(backtick + '[^' + backtick + ']*\\$\\{\\s*(?:props\\.)?' + escapeRegex(name) + '\\s*\\}[^' + backtick + ']*' + backtick);
188
+ if (templateRegex.test(jsx)) {
189
+ return 'string | number';
190
+ }
191
+ // Used in arithmetic
192
+ const arithmeticRegex = new RegExp(`(?:props\\.)?${escapeRegex(name)}\\s*[+\\-*/%]\\s*\\d|\\d\\s*[+\\-*/%]\\s*(?:props\\.)?${escapeRegex(name)}`);
193
+ if (arithmeticRegex.test(jsx)) {
194
+ return 'number';
195
+ }
196
+ // Used in .map() → array
197
+ const mapRegex = new RegExp(`(?:props\\.)?${escapeRegex(name)}\\.map\\s*\\(`);
198
+ if (mapRegex.test(jsx)) {
199
+ return 'unknown[]';
200
+ }
201
+ // Used in .length → string or array
202
+ const lengthRegex = new RegExp(`(?:props\\.)?${escapeRegex(name)}\\.length`);
203
+ if (lengthRegex.test(jsx)) {
204
+ return 'string | unknown[]';
205
+ }
206
+ // Used in conditional → boolean
207
+ const conditionalRegex = new RegExp(`\\{\\s*(?:props\\.)?${escapeRegex(name)}\\s*&&`);
208
+ if (conditionalRegex.test(jsx)) {
209
+ return 'boolean';
210
+ }
211
+ return null;
212
+ }
213
+ function escapeRegex(str) {
214
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
215
+ }
216
+ /**
217
+ * Generates a JSDoc description for a prop based on its name and type.
218
+ */
219
+ function generatePropDescription(name, type) {
220
+ if (name === 'children')
221
+ return 'Child elements';
222
+ if (name === 'className')
223
+ return 'CSS class name';
224
+ if (name === 'style')
225
+ return 'Inline styles';
226
+ // Event handlers
227
+ if (/^on[A-Z]/.test(name)) {
228
+ const event = name.replace(/^on/, '');
229
+ return `${event} handler`;
230
+ }
231
+ // Convert camelCase to readable text
232
+ const readable = name
233
+ .replace(/([A-Z])/g, ' $1')
234
+ .replace(/^./, (s) => s.toUpperCase())
235
+ .trim();
236
+ if (type === 'boolean') {
237
+ return `Whether ${readable.toLowerCase()}`;
238
+ }
239
+ if (type === 'number') {
240
+ return `Number of ${readable.toLowerCase()}`;
241
+ }
242
+ return readable;
243
+ }
244
+ /**
245
+ * Generates complete TypeScript interface definitions for a component's props and state.
246
+ */
247
+ export function generateTypeDefinitions(component, analysis) {
248
+ const lines = [];
249
+ const propsName = `${component.name}Props`;
250
+ const propTypes = inferPropTypes(component);
251
+ // Check for children usage in JSX
252
+ const hasChildren = component.jsx.includes('{children}') ||
253
+ component.jsx.includes('{props.children}') ||
254
+ component.jsx.includes('children');
255
+ if (hasChildren && !propTypes.find((p) => p.name === 'children')) {
256
+ propTypes.push({
257
+ name: 'children',
258
+ type: 'React.ReactNode',
259
+ required: false,
260
+ description: 'Child elements',
261
+ });
262
+ }
263
+ // Generate Props interface
264
+ if (propTypes.length > 0) {
265
+ lines.push(`interface ${propsName} {`);
266
+ for (const prop of propTypes) {
267
+ if (prop.description) {
268
+ lines.push(` /** ${prop.description} */`);
269
+ }
270
+ const optional = prop.required ? '' : '?';
271
+ lines.push(` ${prop.name}${optional}: ${prop.type};`);
272
+ }
273
+ lines.push('}');
274
+ lines.push('');
275
+ }
276
+ else {
277
+ lines.push(`interface ${propsName} {`);
278
+ lines.push(' children?: React.ReactNode;');
279
+ lines.push('}');
280
+ lines.push('');
281
+ }
282
+ // Generate State type from component stateVars
283
+ const stateVars = component.stateVars;
284
+ if (stateVars.length > 0) {
285
+ const stateTypes = inferStateTypes(stateVars);
286
+ const stateName = `${component.name}State`;
287
+ lines.push(`interface ${stateName} {`);
288
+ for (const stateVar of stateVars) {
289
+ const tsType = stateTypes.get(stateVar.name) ?? 'unknown';
290
+ const description = stateVar.name
291
+ .replace(/([A-Z])/g, ' $1')
292
+ .replace(/^./, (s) => s.toUpperCase())
293
+ .trim();
294
+ lines.push(` /** ${description} */`);
295
+ lines.push(` ${stateVar.name}: ${tsType};`);
296
+ }
297
+ lines.push('}');
298
+ lines.push('');
299
+ }
300
+ // Generate API response types if fetch calls exist
301
+ if (analysis?.fetchCalls && analysis.fetchCalls.length > 0) {
302
+ const apiTypes = inferApiResponseType(analysis.fetchCalls);
303
+ if (apiTypes) {
304
+ lines.push(apiTypes);
305
+ lines.push('');
306
+ }
307
+ }
308
+ return lines.join('\n');
309
+ }
310
+ /**
311
+ * Analyzes fetch call URLs and patterns to generate response type interfaces.
312
+ * Groups endpoints by path and generates typed response interfaces.
313
+ */
314
+ export function inferApiResponseType(fetchCalls) {
315
+ if (fetchCalls.length === 0)
316
+ return '';
317
+ const lines = [];
318
+ // Generic API response wrapper
319
+ lines.push('interface ApiResponse<T> {');
320
+ lines.push(' data: T;');
321
+ lines.push(' status: number;');
322
+ lines.push(' message?: string;');
323
+ lines.push('}');
324
+ lines.push('');
325
+ // Group fetch calls by endpoint to generate specific types
326
+ const endpointTypes = new Map();
327
+ for (const call of fetchCalls) {
328
+ const resourceName = extractResourceName(call.url);
329
+ if (!endpointTypes.has(resourceName)) {
330
+ endpointTypes.set(resourceName, { methods: new Set(), hasBody: false });
331
+ }
332
+ const info = endpointTypes.get(resourceName);
333
+ info.methods.add(call.method);
334
+ if (call.hasBody)
335
+ info.hasBody = true;
336
+ }
337
+ for (const [resource, info] of endpointTypes) {
338
+ const typeName = toPascalCase(resource);
339
+ // Generate response type
340
+ lines.push(`interface ${typeName}Response {`);
341
+ lines.push(` id: string;`);
342
+ lines.push(` [key: string]: unknown;`);
343
+ lines.push('}');
344
+ lines.push('');
345
+ // Generate request type if there are POST/PUT/PATCH calls
346
+ const mutationMethods = ['POST', 'PUT', 'PATCH'];
347
+ const hasMutation = [...info.methods].some((m) => mutationMethods.includes(m.toUpperCase()));
348
+ if (hasMutation && info.hasBody) {
349
+ lines.push(`interface ${typeName}Request {`);
350
+ lines.push(` [key: string]: unknown;`);
351
+ lines.push('}');
352
+ lines.push('');
353
+ }
354
+ }
355
+ return lines.join('\n').trimEnd();
356
+ }
357
+ /**
358
+ * Extracts a resource name from a URL path for type generation.
359
+ * E.g., `/api/users/123` → `User`, `/products` → `Product`
360
+ */
361
+ function extractResourceName(url) {
362
+ // Remove protocol and domain
363
+ let path = url.replace(/^https?:\/\/[^/]+/, '');
364
+ // Remove query string and hash
365
+ path = path.replace(/[?#].*$/, '');
366
+ // Remove trailing slash
367
+ path = path.replace(/\/$/, '');
368
+ // Extract meaningful path segments
369
+ const segments = path.split('/').filter((s) => s && !s.startsWith(':') && !/^\d+$/.test(s) && s !== 'api' && s !== 'v1' && s !== 'v2' && s !== 'v3');
370
+ if (segments.length === 0) {
371
+ return 'Api';
372
+ }
373
+ // Use the last meaningful segment, singularize if needed
374
+ let resource = segments[segments.length - 1];
375
+ resource = singularize(resource);
376
+ return resource;
377
+ }
378
+ /**
379
+ * Naive singularization of common English plurals.
380
+ */
381
+ function singularize(word) {
382
+ if (word.endsWith('ies'))
383
+ return word.slice(0, -3) + 'y';
384
+ if (word.endsWith('ses') || word.endsWith('xes') || word.endsWith('zes'))
385
+ return word.slice(0, -2);
386
+ if (word.endsWith('s') && !word.endsWith('ss') && !word.endsWith('us'))
387
+ return word.slice(0, -1);
388
+ return word;
389
+ }
390
+ /**
391
+ * Converts a kebab-case or snake_case string to PascalCase.
392
+ */
393
+ function toPascalCase(str) {
394
+ return str
395
+ .replace(/[-_]+(.)/g, (_, c) => c.toUpperCase())
396
+ .replace(/^./, (s) => s.toUpperCase());
397
+ }
398
+ /**
399
+ * Generates global type definitions file content suitable for a `global.d.ts` file.
400
+ */
401
+ export function generateGlobalTypes() {
402
+ return `// Global type definitions for the converted React application
403
+
404
+ // Utility type for components that accept children
405
+ type PropsWithChildren<P = {}> = P & { children?: React.ReactNode };
406
+
407
+ // Common event handler types
408
+ type ClickHandler = (event: React.MouseEvent<HTMLElement>) => void;
409
+ type ChangeHandler = (event: React.ChangeEvent<HTMLInputElement>) => void;
410
+ type SubmitHandler = (event: React.FormEvent<HTMLFormElement>) => void;
411
+ type KeyHandler = (event: React.KeyboardEvent<HTMLElement>) => void;
412
+ type FocusHandler = (event: React.FocusEvent<HTMLElement>) => void;
413
+ type DragHandler = (event: React.DragEvent<HTMLElement>) => void;
414
+ type ScrollHandler = (event: React.UIEvent<HTMLElement>) => void;
415
+ type InputHandler = (event: React.FormEvent<HTMLInputElement>) => void;
416
+
417
+ // CSS Modules
418
+ declare module '*.module.css' {
419
+ const classes: { readonly [key: string]: string };
420
+ export default classes;
421
+ }
422
+
423
+ declare module '*.module.scss' {
424
+ const classes: { readonly [key: string]: string };
425
+ export default classes;
426
+ }
427
+
428
+ declare module '*.module.less' {
429
+ const classes: { readonly [key: string]: string };
430
+ export default classes;
431
+ }
432
+
433
+ // SVG module declaration
434
+ declare module '*.svg' {
435
+ import * as React from 'react';
436
+ const ReactComponent: React.FunctionComponent<React.SVGProps<SVGSVGElement> & { title?: string }>;
437
+ export { ReactComponent };
438
+ const src: string;
439
+ export default src;
440
+ }
441
+
442
+ // Image module declarations
443
+ declare module '*.png' {
444
+ const src: string;
445
+ export default src;
446
+ }
447
+
448
+ declare module '*.jpg' {
449
+ const src: string;
450
+ export default src;
451
+ }
452
+
453
+ declare module '*.jpeg' {
454
+ const src: string;
455
+ export default src;
456
+ }
457
+
458
+ declare module '*.gif' {
459
+ const src: string;
460
+ export default src;
461
+ }
462
+
463
+ declare module '*.webp' {
464
+ const src: string;
465
+ export default src;
466
+ }
467
+
468
+ declare module '*.avif' {
469
+ const src: string;
470
+ export default src;
471
+ }
472
+
473
+ declare module '*.ico' {
474
+ const src: string;
475
+ export default src;
476
+ }
477
+
478
+ declare module '*.bmp' {
479
+ const src: string;
480
+ export default src;
481
+ }
482
+
483
+ // Font module declarations
484
+ declare module '*.woff' {
485
+ const src: string;
486
+ export default src;
487
+ }
488
+
489
+ declare module '*.woff2' {
490
+ const src: string;
491
+ export default src;
492
+ }
493
+
494
+ // Environment variables type
495
+ interface ImportMetaEnv {
496
+ readonly VITE_API_URL: string;
497
+ readonly VITE_APP_TITLE: string;
498
+ readonly VITE_PUBLIC_URL: string;
499
+ readonly MODE: string;
500
+ readonly BASE_URL: string;
501
+ readonly PROD: boolean;
502
+ readonly DEV: boolean;
503
+ readonly SSR: boolean;
504
+ readonly [key: string]: string | boolean | undefined;
505
+ }
506
+
507
+ interface ImportMeta {
508
+ readonly env: ImportMetaEnv;
509
+ }
510
+ `;
511
+ }
512
+ /**
513
+ * Maps state variable names and initial values to TypeScript types.
514
+ * Analyzes literal values and setter usage patterns to infer accurate types.
515
+ */
516
+ export function inferStateTypes(stateVars) {
517
+ const typeMap = new Map();
518
+ for (const stateVar of stateVars) {
519
+ const type = inferStateType(stateVar);
520
+ typeMap.set(stateVar.name, type);
521
+ }
522
+ return typeMap;
523
+ }
524
+ /**
525
+ * Infers the TypeScript type for a single state variable.
526
+ */
527
+ function inferStateType(stateVar) {
528
+ const { name, initialValue, setter } = stateVar;
529
+ const trimmed = initialValue.trim();
530
+ // Direct literal types
531
+ if (trimmed === 'true' || trimmed === 'false')
532
+ return 'boolean';
533
+ if (/^-?\d+$/.test(trimmed))
534
+ return 'number';
535
+ if (/^-?\d+\.\d+$/.test(trimmed))
536
+ return 'number';
537
+ if (trimmed === '0' || trimmed === '1')
538
+ return 'number';
539
+ // String values
540
+ if (/^['"`]/.test(trimmed) && /['"`]$/.test(trimmed))
541
+ return 'string';
542
+ if (trimmed === "''" || trimmed === '""' || trimmed === '``')
543
+ return 'string';
544
+ // Array values
545
+ if (trimmed.startsWith('[')) {
546
+ return inferArrayType(trimmed);
547
+ }
548
+ // Object values
549
+ if (trimmed.startsWith('{')) {
550
+ return 'Record<string, unknown>';
551
+ }
552
+ // null - try to infer from name or setter usage
553
+ if (trimmed === 'null' || trimmed === 'undefined') {
554
+ return inferNullableType(name, setter);
555
+ }
556
+ // Named patterns
557
+ if (/^(is|has|show|can|should|enable|visible|active|loading|open|checked|disabled|selected)/.test(name)) {
558
+ return 'boolean';
559
+ }
560
+ if (/^(count|index|total|page|size|limit|offset|num|amount|quantity)/.test(name)) {
561
+ return 'number';
562
+ }
563
+ if (/^(name|title|text|message|error|label|description|email|password|query|search|input|value|content|url|path)/.test(name)) {
564
+ return 'string';
565
+ }
566
+ if (/^(items|list|options|data|results|entries|rows|tags|records|elements)/.test(name)) {
567
+ return 'unknown[]';
568
+ }
569
+ return 'unknown';
570
+ }
571
+ /**
572
+ * Infers array element type from an initial array value.
573
+ */
574
+ function inferArrayType(value) {
575
+ const trimmed = value.trim();
576
+ // Empty array
577
+ if (trimmed === '[]')
578
+ return 'unknown[]';
579
+ // Try to parse array contents
580
+ const inner = trimmed.slice(1, -1).trim();
581
+ if (!inner)
582
+ return 'unknown[]';
583
+ // Check first element
584
+ const elements = splitTopLevel(inner);
585
+ if (elements.length === 0)
586
+ return 'unknown[]';
587
+ const firstEl = elements[0].trim();
588
+ if (/^['"`]/.test(firstEl))
589
+ return 'string[]';
590
+ if (/^-?\d+(\.\d+)?$/.test(firstEl))
591
+ return 'number[]';
592
+ if (firstEl === 'true' || firstEl === 'false')
593
+ return 'boolean[]';
594
+ if (firstEl.startsWith('{'))
595
+ return 'Record<string, unknown>[]';
596
+ return 'unknown[]';
597
+ }
598
+ /**
599
+ * Splits a string by top-level commas (ignoring commas inside brackets/braces/parens).
600
+ */
601
+ function splitTopLevel(str) {
602
+ const result = [];
603
+ let depth = 0;
604
+ let current = '';
605
+ for (const char of str) {
606
+ if (char === '[' || char === '{' || char === '(') {
607
+ depth++;
608
+ current += char;
609
+ }
610
+ else if (char === ']' || char === '}' || char === ')') {
611
+ depth--;
612
+ current += char;
613
+ }
614
+ else if (char === ',' && depth === 0) {
615
+ result.push(current);
616
+ current = '';
617
+ }
618
+ else {
619
+ current += char;
620
+ }
621
+ }
622
+ if (current.trim()) {
623
+ result.push(current);
624
+ }
625
+ return result;
626
+ }
627
+ /**
628
+ * Infers a nullable type from the state variable name and setter usage patterns.
629
+ */
630
+ function inferNullableType(name, setter) {
631
+ // Infer from naming conventions
632
+ if (/^(selected|current|active)/.test(name)) {
633
+ if (/item|element|row|record/i.test(name))
634
+ return 'Record<string, unknown> | null';
635
+ if (/index|id/i.test(name))
636
+ return 'number | null';
637
+ return 'string | null';
638
+ }
639
+ if (/^(error|message|notification)/.test(name))
640
+ return 'string | null';
641
+ if (/^(user|profile|account)$/i.test(name))
642
+ return 'Record<string, unknown> | null';
643
+ if (/^(data|response|result)$/i.test(name))
644
+ return 'unknown | null';
645
+ if (/^(timer|timeout|interval)/.test(name))
646
+ return 'ReturnType<typeof setTimeout> | null';
647
+ if (/^(ref|element|node)/.test(name))
648
+ return 'HTMLElement | null';
649
+ // Infer from setter name pattern
650
+ if (setter) {
651
+ const setterTarget = setter.replace(/^set/, '');
652
+ if (/^Is|^Has|^Show|^Can/.test(setterTarget))
653
+ return 'boolean | null';
654
+ if (/Count|Index|Total|Page/.test(setterTarget))
655
+ return 'number | null';
656
+ if (/Name|Title|Text|Message|Error/.test(setterTarget))
657
+ return 'string | null';
658
+ if (/Items|List|Options|Data/.test(setterTarget))
659
+ return 'unknown[] | null';
660
+ }
661
+ return 'unknown | null';
662
+ }
663
+ //# sourceMappingURL=type-generator.js.map