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,428 @@
1
+ /**
2
+ * Performance optimizations for converted React components.
3
+ *
4
+ * Provides helpers that wrap components with React.memo, add lazy-loading,
5
+ * optimise images, configure code-splitting, insert useMemo / useCallback,
6
+ * and generate reusable performance utilities.
7
+ */
8
+ /**
9
+ * Wraps a React component export with React.memo().
10
+ *
11
+ * Handles both `export default function Foo` and `export default Foo` patterns
12
+ * and ensures `memo` is imported from React.
13
+ */
14
+ export function wrapWithMemo(componentCode, componentName) {
15
+ let code = componentCode;
16
+ // Ensure memo import is present
17
+ if (!code.includes('memo')) {
18
+ // Extend existing React import
19
+ const reactImportRe = /import\s+\{([^}]+)\}\s+from\s+['"]react['"]/;
20
+ const match = code.match(reactImportRe);
21
+ if (match) {
22
+ const imports = match[1];
23
+ code = code.replace(reactImportRe, `import {${imports}, memo } from 'react'`);
24
+ }
25
+ else if (/import\s+React\s+from\s+['"]react['"]/.test(code)) {
26
+ code = code.replace(/import\s+React\s+from\s+['"]react['"]/, "import React, { memo } from 'react'");
27
+ }
28
+ else {
29
+ code = `import { memo } from 'react';\n${code}`;
30
+ }
31
+ }
32
+ // Pattern 1: export default function ComponentName(…) { … }
33
+ const fnDeclRe = new RegExp(`export\\s+default\\s+function\\s+${componentName}\\s*\\(`);
34
+ if (fnDeclRe.test(code)) {
35
+ // Convert to named function + memo-wrapped default export
36
+ code = code.replace(new RegExp(`export\\s+default\\s+function\\s+${componentName}`), `function ${componentName}`);
37
+ code = `${code.trimEnd()}\n\nexport default memo(${componentName});\n`;
38
+ return code;
39
+ }
40
+ // Pattern 2: export default ComponentName;
41
+ const defaultExportRe = new RegExp(`export\\s+default\\s+${componentName}\\s*;`);
42
+ if (defaultExportRe.test(code)) {
43
+ code = code.replace(defaultExportRe, `export default memo(${componentName});`);
44
+ return code;
45
+ }
46
+ return code;
47
+ }
48
+ /**
49
+ * Converts static component imports to React.lazy() and wraps the
50
+ * rendered tree in a <Suspense> boundary with a <Loading /> fallback.
51
+ */
52
+ export function addLazyLoading(appCode, componentNames) {
53
+ let code = appCode;
54
+ // Add lazy and Suspense to the React import
55
+ const reactImportRe = /import\s+\{([^}]+)\}\s+from\s+['"]react['"]/;
56
+ const reactMatch = code.match(reactImportRe);
57
+ if (reactMatch) {
58
+ let imports = reactMatch[1];
59
+ if (!imports.includes('lazy'))
60
+ imports += ', lazy';
61
+ if (!imports.includes('Suspense'))
62
+ imports += ', Suspense';
63
+ code = code.replace(reactImportRe, `import {${imports} } from 'react'`);
64
+ }
65
+ else {
66
+ code = `import { lazy, Suspense } from 'react';\n${code}`;
67
+ }
68
+ // Add Loading component import if not present
69
+ if (!code.includes('Loading')) {
70
+ code = `import Loading from './components/Loading';\n${code}`;
71
+ }
72
+ // Convert static imports to lazy() for each component
73
+ for (const name of componentNames) {
74
+ const staticImportRe = new RegExp(`import\\s+${name}\\s+from\\s+['"]([^'"]+)['"]\\s*;?`);
75
+ const importMatch = code.match(staticImportRe);
76
+ if (importMatch) {
77
+ const importPath = importMatch[1];
78
+ code = code.replace(staticImportRe, `const ${name} = lazy(() => import('${importPath}'));`);
79
+ }
80
+ }
81
+ // Wrap Routes (or top-level JSX) in Suspense if not already present
82
+ if (!code.includes('<Suspense')) {
83
+ code = code.replace(/(<Routes[\s>])/, '<Suspense fallback={<Loading />}>\n $1');
84
+ code = code.replace(/(<\/Routes>)/, '$1\n </Suspense>');
85
+ }
86
+ return code;
87
+ }
88
+ /**
89
+ * Converts `<img>` tags to include loading="lazy", adds missing
90
+ * width / height attributes, and adds a comment suggesting a
91
+ * `<picture>` + WebP srcset alternative.
92
+ */
93
+ export function generateImageOptimization(html) {
94
+ let result = html;
95
+ // Process each <img> tag
96
+ result = result.replace(/<img\b([^>]*)>/gi, (_match, attrs) => {
97
+ let attributes = attrs;
98
+ // Add loading="lazy" if missing
99
+ if (!/loading\s*=/i.test(attributes)) {
100
+ attributes += ' loading="lazy"';
101
+ }
102
+ // Add decoding="async" if missing
103
+ if (!/decoding\s*=/i.test(attributes)) {
104
+ attributes += ' decoding="async"';
105
+ }
106
+ // Add width/height defaults for common patterns when missing
107
+ if (!/width\s*=/i.test(attributes)) {
108
+ // Try to infer from common class or filename patterns
109
+ if (/logo/i.test(attributes)) {
110
+ attributes += ' width="150" height="50"';
111
+ }
112
+ else if (/icon/i.test(attributes)) {
113
+ attributes += ' width="24" height="24"';
114
+ }
115
+ else if (/avatar/i.test(attributes)) {
116
+ attributes += ' width="48" height="48"';
117
+ }
118
+ else if (/hero|banner/i.test(attributes)) {
119
+ attributes += ' width="1200" height="600"';
120
+ }
121
+ else if (/thumb/i.test(attributes)) {
122
+ attributes += ' width="200" height="200"';
123
+ }
124
+ }
125
+ // Extract src for picture element comment
126
+ const srcMatch = attributes.match(/src\s*=\s*["']([^"']+)["']/i);
127
+ const src = srcMatch ? srcMatch[1] : '';
128
+ const webpSrc = src.replace(/\.(jpe?g|png|gif)$/i, '.webp');
129
+ const imgTag = `<img${attributes}>`;
130
+ // Add picture element suggestion as a comment when there is a raster source
131
+ if (src && /\.(jpe?g|png|gif)$/i.test(src)) {
132
+ return [
133
+ `{/* Consider using a <picture> element for modern formats:`,
134
+ ` <picture>`,
135
+ ` <source srcSet="${webpSrc}" type="image/webp" />`,
136
+ ` ${imgTag}`,
137
+ ` </picture>`,
138
+ `*/}`,
139
+ imgTag,
140
+ ].join('\n');
141
+ }
142
+ return imgTag;
143
+ });
144
+ return result;
145
+ }
146
+ /**
147
+ * Returns Vite config additions for manual chunk splitting
148
+ * (vendor, router, and UI framework chunks).
149
+ */
150
+ export function generateCodeSplitConfig() {
151
+ return `// Performance: manual chunk splitting for Vite
152
+ // Merge this into your vite.config.ts \`build.rollupOptions\`
153
+
154
+ import { defineConfig } from 'vite';
155
+ import react from '@vitejs/plugin-react';
156
+
157
+ export default defineConfig({
158
+ plugins: [react()],
159
+ build: {
160
+ rollupOptions: {
161
+ output: {
162
+ manualChunks: {
163
+ // React core
164
+ vendor: ['react', 'react-dom'],
165
+ // Router chunk (if used)
166
+ router: ['react-router-dom'],
167
+ // UI framework chunks (uncomment as needed)
168
+ // 'ui-mui': ['@mui/material', '@emotion/react', '@emotion/styled'],
169
+ // 'ui-antd': ['antd'],
170
+ // 'ui-chakra': ['@chakra-ui/react'],
171
+ },
172
+ },
173
+ },
174
+ // Enable source maps for production debugging (optional)
175
+ sourcemap: false,
176
+ // Increase chunk size warning limit (kB)
177
+ chunkSizeWarningLimit: 600,
178
+ },
179
+ });
180
+ `;
181
+ }
182
+ /**
183
+ * Scans JSX for inline object / array literals and arrow functions
184
+ * passed as props and wraps them with useMemo / useCallback.
185
+ *
186
+ * This is a best-effort transformation. Complex patterns may need
187
+ * manual review.
188
+ */
189
+ export function addUseMemoCallbacks(componentCode) {
190
+ let code = componentCode;
191
+ const memoVars = [];
192
+ const callbackVars = [];
193
+ let memoCounter = 0;
194
+ let cbCounter = 0;
195
+ // Find inline objects/arrays in JSX props: prop={{ ... }} or prop={[ ... ]}
196
+ // Replace with a memoised variable
197
+ code = code.replace(/(\w+)=\{(\{[^}]*\}|\[[^\]]*\])\}/g, (_match, propName, value) => {
198
+ // Skip spread and simple boolean/string/number patterns
199
+ if (propName === 'key' || propName === 'ref')
200
+ return _match;
201
+ const varName = `memoized${capitalize(propName)}${memoCounter++}`;
202
+ memoVars.push(` const ${varName} = useMemo(() => (${value}), []);`);
203
+ return `${propName}={${varName}}`;
204
+ });
205
+ // Find inline arrow functions in JSX props: prop={() => ...} or prop={(e) => ...}
206
+ code = code.replace(/(\w+)=\{(\([^)]*\)\s*=>(?:\s*\{[^}]*\}|[^}]*))\}/g, (_match, propName, fn) => {
207
+ if (propName === 'key' || propName === 'ref')
208
+ return _match;
209
+ const varName = `handle${capitalize(propName)}${cbCounter++}`;
210
+ callbackVars.push(` const ${varName} = useCallback(${fn}, []);`);
211
+ return `${propName}={${varName}}`;
212
+ });
213
+ // Insert memoised variables before the return statement
214
+ if (memoVars.length > 0 || callbackVars.length > 0) {
215
+ const declarations = [...memoVars, ...callbackVars].join('\n');
216
+ code = code.replace(/(\n\s*return\s*\()/, `\n${declarations}\n$1`);
217
+ // Ensure useMemo and useCallback are imported
218
+ const reactImportRe = /import\s+\{([^}]+)\}\s+from\s+['"]react['"]/;
219
+ const match = code.match(reactImportRe);
220
+ if (match) {
221
+ let imports = match[1];
222
+ if (memoVars.length > 0 && !imports.includes('useMemo')) {
223
+ imports += ', useMemo';
224
+ }
225
+ if (callbackVars.length > 0 && !imports.includes('useCallback')) {
226
+ imports += ', useCallback';
227
+ }
228
+ code = code.replace(reactImportRe, `import {${imports} } from 'react'`);
229
+ }
230
+ else {
231
+ const needed = [];
232
+ if (memoVars.length > 0)
233
+ needed.push('useMemo');
234
+ if (callbackVars.length > 0)
235
+ needed.push('useCallback');
236
+ code = `import { ${needed.join(', ')} } from 'react';\n${code}`;
237
+ }
238
+ }
239
+ return code;
240
+ }
241
+ /**
242
+ * Returns a utility module with common performance helpers:
243
+ * - useIntersectionObserver hook
244
+ * - useDeferredValue wrapper for search inputs
245
+ * - debounce / throttle utility functions
246
+ */
247
+ export function generatePerformanceUtils() {
248
+ return `import { useState, useEffect, useRef, useDeferredValue, useCallback } from 'react';
249
+
250
+ // ---------- useIntersectionObserver ----------
251
+
252
+ interface IntersectionOptions {
253
+ root?: Element | null;
254
+ rootMargin?: string;
255
+ threshold?: number | number[];
256
+ once?: boolean;
257
+ }
258
+
259
+ /**
260
+ * Observes an element's intersection with its scroll container.
261
+ * Useful for lazy-rendering content that is off-screen.
262
+ */
263
+ export function useIntersectionObserver(
264
+ options: IntersectionOptions = {},
265
+ ): [React.RefObject<HTMLElement | null>, boolean] {
266
+ const { root = null, rootMargin = '0px', threshold = 0, once = true } = options;
267
+ const ref = useRef<HTMLElement | null>(null);
268
+ const [isIntersecting, setIsIntersecting] = useState(false);
269
+
270
+ useEffect(() => {
271
+ const element = ref.current;
272
+ if (!element) return;
273
+
274
+ const observer = new IntersectionObserver(
275
+ ([entry]) => {
276
+ setIsIntersecting(entry.isIntersecting);
277
+ if (entry.isIntersecting && once) {
278
+ observer.unobserve(element);
279
+ }
280
+ },
281
+ { root, rootMargin, threshold },
282
+ );
283
+
284
+ observer.observe(element);
285
+ return () => observer.disconnect();
286
+ }, [root, rootMargin, threshold, once]);
287
+
288
+ return [ref, isIntersecting];
289
+ }
290
+
291
+ // ---------- useDeferredSearch ----------
292
+
293
+ /**
294
+ * Wraps a search input value with React's useDeferredValue so that
295
+ * expensive filtering / rendering is deferred during rapid typing.
296
+ */
297
+ export function useDeferredSearch(searchTerm: string): string {
298
+ return useDeferredValue(searchTerm);
299
+ }
300
+
301
+ // ---------- debounce ----------
302
+
303
+ /**
304
+ * Returns a debounced version of \`fn\` that delays invocation until
305
+ * \`delay\` ms have elapsed since the last call.
306
+ */
307
+ export function debounce<T extends (...args: unknown[]) => void>(
308
+ fn: T,
309
+ delay: number,
310
+ ): (...args: Parameters<T>) => void {
311
+ let timerId: ReturnType<typeof setTimeout> | undefined;
312
+
313
+ return (...args: Parameters<T>) => {
314
+ if (timerId !== undefined) clearTimeout(timerId);
315
+ timerId = setTimeout(() => {
316
+ fn(...args);
317
+ timerId = undefined;
318
+ }, delay);
319
+ };
320
+ }
321
+
322
+ // ---------- throttle ----------
323
+
324
+ /**
325
+ * Returns a throttled version of \`fn\` that invokes at most once
326
+ * every \`limit\` ms.
327
+ */
328
+ export function throttle<T extends (...args: unknown[]) => void>(
329
+ fn: T,
330
+ limit: number,
331
+ ): (...args: Parameters<T>) => void {
332
+ let lastCall = 0;
333
+ let timerId: ReturnType<typeof setTimeout> | undefined;
334
+
335
+ return (...args: Parameters<T>) => {
336
+ const now = Date.now();
337
+ const remaining = limit - (now - lastCall);
338
+
339
+ if (remaining <= 0) {
340
+ if (timerId !== undefined) {
341
+ clearTimeout(timerId);
342
+ timerId = undefined;
343
+ }
344
+ lastCall = now;
345
+ fn(...args);
346
+ } else if (timerId === undefined) {
347
+ timerId = setTimeout(() => {
348
+ lastCall = Date.now();
349
+ timerId = undefined;
350
+ fn(...args);
351
+ }, remaining);
352
+ }
353
+ };
354
+ }
355
+
356
+ // ---------- useDebounce hook ----------
357
+
358
+ /**
359
+ * React hook that returns a debounced value.
360
+ */
361
+ export function useDebounce<T>(value: T, delay: number): T {
362
+ const [debouncedValue, setDebouncedValue] = useState(value);
363
+
364
+ useEffect(() => {
365
+ const timer = setTimeout(() => setDebouncedValue(value), delay);
366
+ return () => clearTimeout(timer);
367
+ }, [value, delay]);
368
+
369
+ return debouncedValue;
370
+ }
371
+ `;
372
+ }
373
+ /**
374
+ * Returns a simple Loading / Skeleton component with a CSS animation
375
+ * suitable as a Suspense fallback.
376
+ */
377
+ export function generateLoadingFallback() {
378
+ return `import React from 'react';
379
+
380
+ const shimmerStyle: React.CSSProperties = {
381
+ display: 'flex',
382
+ flexDirection: 'column',
383
+ alignItems: 'center',
384
+ justifyContent: 'center',
385
+ minHeight: '200px',
386
+ gap: '16px',
387
+ padding: '24px',
388
+ };
389
+
390
+ const skeletonLineStyle: React.CSSProperties = {
391
+ height: '16px',
392
+ borderRadius: '4px',
393
+ background: 'linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%)',
394
+ backgroundSize: '200% 100%',
395
+ animation: 'shimmer 1.5s ease-in-out infinite',
396
+ };
397
+
398
+ /**
399
+ * A lightweight loading / skeleton component with a shimmer animation.
400
+ * Use as a Suspense fallback or standalone placeholder.
401
+ */
402
+ export default function Loading() {
403
+ return (
404
+ <>
405
+ <style>{\`
406
+ @keyframes shimmer {
407
+ 0% { background-position: 200% 0; }
408
+ 100% { background-position: -200% 0; }
409
+ }
410
+ \`}</style>
411
+ <div style={shimmerStyle} role="status" aria-label="Loading">
412
+ <div style={{ ...skeletonLineStyle, width: '60%' }} />
413
+ <div style={{ ...skeletonLineStyle, width: '80%' }} />
414
+ <div style={{ ...skeletonLineStyle, width: '40%' }} />
415
+ <span style={{ marginTop: '12px', color: '#888', fontSize: '14px' }}>
416
+ Loading\u2026
417
+ </span>
418
+ </div>
419
+ </>
420
+ );
421
+ }
422
+ `;
423
+ }
424
+ // ---------- internal helpers ----------
425
+ function capitalize(s) {
426
+ return s.charAt(0).toUpperCase() + s.slice(1);
427
+ }
428
+ //# sourceMappingURL=performance-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"performance-generator.js","sourceRoot":"","sources":["../../src/generators/performance-generator.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,aAAqB,EAAE,aAAqB;IACvE,IAAI,IAAI,GAAG,aAAa,CAAC;IAEzB,gCAAgC;IAChC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3B,+BAA+B;QAC/B,MAAM,aAAa,GAAG,6CAA6C,CAAC;QACpE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QACxC,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACzB,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,WAAW,OAAO,uBAAuB,CAAC,CAAC;QAChF,CAAC;aAAM,IAAI,uCAAuC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9D,IAAI,GAAG,IAAI,CAAC,OAAO,CACjB,uCAAuC,EACvC,qCAAqC,CACtC,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,IAAI,GAAG,kCAAkC,IAAI,EAAE,CAAC;QAClD,CAAC;IACH,CAAC;IAED,4DAA4D;IAC5D,MAAM,QAAQ,GAAG,IAAI,MAAM,CACzB,oCAAoC,aAAa,SAAS,CAC3D,CAAC;IACF,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,0DAA0D;QAC1D,IAAI,GAAG,IAAI,CAAC,OAAO,CACjB,IAAI,MAAM,CAAC,oCAAoC,aAAa,EAAE,CAAC,EAC/D,YAAY,aAAa,EAAE,CAC5B,CAAC;QACF,IAAI,GAAG,GAAG,IAAI,CAAC,OAAO,EAAE,2BAA2B,aAAa,MAAM,CAAC;QACvE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,2CAA2C;IAC3C,MAAM,eAAe,GAAG,IAAI,MAAM,CAChC,wBAAwB,aAAa,OAAO,CAC7C,CAAC;IACF,IAAI,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/B,IAAI,GAAG,IAAI,CAAC,OAAO,CACjB,eAAe,EACf,uBAAuB,aAAa,IAAI,CACzC,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,OAAe,EAAE,cAAwB;IACtE,IAAI,IAAI,GAAG,OAAO,CAAC;IAEnB,4CAA4C;IAC5C,MAAM,aAAa,GAAG,6CAA6C,CAAC;IACpE,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IAC7C,IAAI,UAAU,EAAE,CAAC;QACf,IAAI,OAAO,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;QAC5B,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;YAAE,OAAO,IAAI,QAAQ,CAAC;QACnD,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC;YAAE,OAAO,IAAI,YAAY,CAAC;QAC3D,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,WAAW,OAAO,iBAAiB,CAAC,CAAC;IAC1E,CAAC;SAAM,CAAC;QACN,IAAI,GAAG,4CAA4C,IAAI,EAAE,CAAC;IAC5D,CAAC;IAED,8CAA8C;IAC9C,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QAC9B,IAAI,GAAG,gDAAgD,IAAI,EAAE,CAAC;IAChE,CAAC;IAED,sDAAsD;IACtD,KAAK,MAAM,IAAI,IAAI,cAAc,EAAE,CAAC;QAClC,MAAM,cAAc,GAAG,IAAI,MAAM,CAC/B,aAAa,IAAI,oCAAoC,CACtD,CAAC;QACF,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;QAC/C,IAAI,WAAW,EAAE,CAAC;YAChB,MAAM,UAAU,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;YAClC,IAAI,GAAG,IAAI,CAAC,OAAO,CACjB,cAAc,EACd,SAAS,IAAI,yBAAyB,UAAU,MAAM,CACvD,CAAC;QACJ,CAAC;IACH,CAAC;IAED,oEAAoE;IACpE,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;QAChC,IAAI,GAAG,IAAI,CAAC,OAAO,CACjB,gBAAgB,EAChB,+CAA+C,CAChD,CAAC;QACF,IAAI,GAAG,IAAI,CAAC,OAAO,CACjB,cAAc,EACd,yBAAyB,CAC1B,CAAC;IACJ,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CAAC,IAAY;IACpD,IAAI,MAAM,GAAG,IAAI,CAAC;IAElB,yBAAyB;IACzB,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,kBAAkB,EAAE,CAAC,MAAM,EAAE,KAAa,EAAE,EAAE;QACpE,IAAI,UAAU,GAAG,KAAK,CAAC;QAEvB,gCAAgC;QAChC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YACrC,UAAU,IAAI,iBAAiB,CAAC;QAClC,CAAC;QAED,kCAAkC;QAClC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YACtC,UAAU,IAAI,mBAAmB,CAAC;QACpC,CAAC;QAED,6DAA6D;QAC7D,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YACnC,sDAAsD;YACtD,IAAI,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC7B,UAAU,IAAI,0BAA0B,CAAC;YAC3C,CAAC;iBAAM,IAAI,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;gBACpC,UAAU,IAAI,yBAAyB,CAAC;YAC1C,CAAC;iBAAM,IAAI,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;gBACtC,UAAU,IAAI,yBAAyB,CAAC;YAC1C,CAAC;iBAAM,IAAI,cAAc,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC3C,UAAU,IAAI,4BAA4B,CAAC;YAC7C,CAAC;iBAAM,IAAI,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;gBACrC,UAAU,IAAI,2BAA2B,CAAC;YAC5C,CAAC;QACH,CAAC;QAED,0CAA0C;QAC1C,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC;QACjE,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACxC,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,qBAAqB,EAAE,OAAO,CAAC,CAAC;QAE5D,MAAM,MAAM,GAAG,OAAO,UAAU,GAAG,CAAC;QAEpC,4EAA4E;QAC5E,IAAI,GAAG,IAAI,qBAAqB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YAC3C,OAAO;gBACL,4DAA4D;gBAC5D,eAAe;gBACf,yBAAyB,OAAO,wBAAwB;gBACxD,SAAS,MAAM,EAAE;gBACjB,gBAAgB;gBAChB,KAAK;gBACL,MAAM;aACP,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACf,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB;IACrC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6BR,CAAC;AACF,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CAAC,aAAqB;IACvD,IAAI,IAAI,GAAG,aAAa,CAAC;IACzB,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,MAAM,YAAY,GAAa,EAAE,CAAC;IAClC,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,IAAI,SAAS,GAAG,CAAC,CAAC;IAElB,4EAA4E;IAC5E,mCAAmC;IACnC,IAAI,GAAG,IAAI,CAAC,OAAO,CACjB,mCAAmC,EACnC,CAAC,MAAM,EAAE,QAAgB,EAAE,KAAa,EAAE,EAAE;QAC1C,wDAAwD;QACxD,IAAI,QAAQ,KAAK,KAAK,IAAI,QAAQ,KAAK,KAAK;YAAE,OAAO,MAAM,CAAC;QAC5D,MAAM,OAAO,GAAG,WAAW,UAAU,CAAC,QAAQ,CAAC,GAAG,WAAW,EAAE,EAAE,CAAC;QAClE,QAAQ,CAAC,IAAI,CAAC,WAAW,OAAO,qBAAqB,KAAK,SAAS,CAAC,CAAC;QACrE,OAAO,GAAG,QAAQ,KAAK,OAAO,GAAG,CAAC;IACpC,CAAC,CACF,CAAC;IAEF,kFAAkF;IAClF,IAAI,GAAG,IAAI,CAAC,OAAO,CACjB,mDAAmD,EACnD,CAAC,MAAM,EAAE,QAAgB,EAAE,EAAU,EAAE,EAAE;QACvC,IAAI,QAAQ,KAAK,KAAK,IAAI,QAAQ,KAAK,KAAK;YAAE,OAAO,MAAM,CAAC;QAC5D,MAAM,OAAO,GAAG,SAAS,UAAU,CAAC,QAAQ,CAAC,GAAG,SAAS,EAAE,EAAE,CAAC;QAC9D,YAAY,CAAC,IAAI,CAAC,WAAW,OAAO,kBAAkB,EAAE,QAAQ,CAAC,CAAC;QAClE,OAAO,GAAG,QAAQ,KAAK,OAAO,GAAG,CAAC;IACpC,CAAC,CACF,CAAC;IAEF,wDAAwD;IACxD,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACnD,MAAM,YAAY,GAAG,CAAC,GAAG,QAAQ,EAAE,GAAG,YAAY,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/D,IAAI,GAAG,IAAI,CAAC,OAAO,CACjB,oBAAoB,EACpB,KAAK,YAAY,MAAM,CACxB,CAAC;QAEF,8CAA8C;QAC9C,MAAM,aAAa,GAAG,6CAA6C,CAAC;QACpE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QACxC,IAAI,KAAK,EAAE,CAAC;YACV,IAAI,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACvB,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;gBACxD,OAAO,IAAI,WAAW,CAAC;YACzB,CAAC;YACD,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;gBAChE,OAAO,IAAI,eAAe,CAAC;YAC7B,CAAC;YACD,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,WAAW,OAAO,iBAAiB,CAAC,CAAC;QAC1E,CAAC;aAAM,CAAC;YACN,MAAM,MAAM,GAAa,EAAE,CAAC;YAC5B,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;gBAAE,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAChD,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC;gBAAE,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YACxD,IAAI,GAAG,YAAY,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,qBAAqB,IAAI,EAAE,CAAC;QAClE,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,wBAAwB;IACtC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2HR,CAAC;AACF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB;IACrC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4CR,CAAC;AACF,CAAC;AAED,yCAAyC;AAEzC,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAChD,CAAC"}
@@ -0,0 +1,21 @@
1
+ export interface RouteConfig {
2
+ path: string;
3
+ componentName: string;
4
+ fileName: string;
5
+ isIndex: boolean;
6
+ }
7
+ /**
8
+ * Generate route configurations from component names derived from HTML filenames.
9
+ * Preserves directory structure for nested routes.
10
+ */
11
+ export declare function generateRoutes(componentNames: string[], fileNames: string[], inputDir?: string, filePaths?: string[]): RouteConfig[];
12
+ /**
13
+ * Generate App.tsx with React Router for multi-page sites.
14
+ * Includes lazy loading, 404 route, error boundary, and Tailwind support.
15
+ */
16
+ export declare function generateRouterApp(routes: RouteConfig[], hasGlobalCSS: boolean, errorBoundary?: boolean, tailwind?: boolean, useHashRouter?: boolean): string;
17
+ /**
18
+ * Generate a simple App.tsx (no router) for single-page sites.
19
+ */
20
+ export declare function generateSimpleApp(componentNames: string[], hasGlobalCSS: boolean, errorBoundary?: boolean, tailwind?: boolean): string;
21
+ //# sourceMappingURL=router-generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router-generator.d.ts","sourceRoot":"","sources":["../../src/generators/router-generator.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,cAAc,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,GAAG,WAAW,EAAE,CAwBpI;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,WAAW,EAAE,EACrB,YAAY,EAAE,OAAO,EACrB,aAAa,GAAE,OAAc,EAC7B,QAAQ,GAAE,OAAe,EACzB,aAAa,GAAE,OAAe,GAC7B,MAAM,CAyGR;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,cAAc,EAAE,MAAM,EAAE,EACxB,YAAY,EAAE,OAAO,EACrB,aAAa,GAAE,OAAe,EAC9B,QAAQ,GAAE,OAAe,GACxB,MAAM,CA4CR"}
@@ -0,0 +1,178 @@
1
+ import { toComponentName, toKebabCase } from '../utils/naming.js';
2
+ import { dirname, relative } from 'path';
3
+ /**
4
+ * Generate route configurations from component names derived from HTML filenames.
5
+ * Preserves directory structure for nested routes.
6
+ */
7
+ export function generateRoutes(componentNames, fileNames, inputDir, filePaths) {
8
+ return componentNames.map((name, i) => {
9
+ const fileName = fileNames[i];
10
+ const isIndex = /^index\.html?$/i.test(fileName);
11
+ // Preserve directory structure if file paths are available
12
+ let path;
13
+ if (filePaths && inputDir) {
14
+ const relDir = relative(inputDir, dirname(filePaths[i]));
15
+ const basePath = isIndex ? '' : toKebabCase(name).toLowerCase();
16
+ path = relDir ? `/${relDir}/${basePath}`.replace(/\/+/g, '/') : `/${basePath}`;
17
+ if (path === '/')
18
+ path = '/';
19
+ else
20
+ path = path.replace(/\/$/, '');
21
+ }
22
+ else {
23
+ path = isIndex ? '/' : `/${toKebabCase(name).toLowerCase()}`;
24
+ }
25
+ return {
26
+ path: path || '/',
27
+ componentName: name,
28
+ fileName: `${toKebabCase(name)}.tsx`,
29
+ isIndex,
30
+ };
31
+ });
32
+ }
33
+ /**
34
+ * Generate App.tsx with React Router for multi-page sites.
35
+ * Includes lazy loading, 404 route, error boundary, and Tailwind support.
36
+ */
37
+ export function generateRouterApp(routes, hasGlobalCSS, errorBoundary = true, tailwind = false, useHashRouter = false) {
38
+ const lines = [];
39
+ const routerComponent = useHashRouter ? 'HashRouter' : 'BrowserRouter';
40
+ // Imports
41
+ lines.push(`import { lazy, Suspense } from 'react';`);
42
+ lines.push(`import { ${routerComponent}, Routes, Route, NavLink } from 'react-router-dom';`);
43
+ if (errorBoundary) {
44
+ lines.push(`import ErrorBoundary from './components/ErrorBoundary';`);
45
+ }
46
+ // Lazy imports for route components (deduplicated)
47
+ const seenComponents = new Set();
48
+ for (const route of routes) {
49
+ if (seenComponents.has(route.componentName))
50
+ continue;
51
+ seenComponents.add(route.componentName);
52
+ // Ensure component name is a valid JS identifier
53
+ const safeName = /^[A-Z_$][a-zA-Z0-9_$]*$/.test(route.componentName)
54
+ ? route.componentName
55
+ : toComponentName(route.componentName);
56
+ route.componentName = safeName;
57
+ const kebab = toKebabCase(safeName);
58
+ lines.push(`const ${safeName} = lazy(() => import('./components/${kebab}.tsx'));`);
59
+ }
60
+ if (hasGlobalCSS) {
61
+ lines.push(`import './styles/global.css';`);
62
+ }
63
+ if (tailwind) {
64
+ lines.push(`import './styles/tailwind.css';`);
65
+ }
66
+ lines.push('');
67
+ // 404 component
68
+ lines.push('function NotFound() {');
69
+ lines.push(' return (');
70
+ lines.push(' <div style={{ textAlign: "center", padding: "4rem" }}>');
71
+ lines.push(' <h1>404</h1>');
72
+ lines.push(' <p>Page not found</p>');
73
+ lines.push(' <NavLink to="/">Go Home</NavLink>');
74
+ lines.push(' </div>');
75
+ lines.push(' );');
76
+ lines.push('}');
77
+ lines.push('');
78
+ // Loading fallback
79
+ lines.push('function Loading() {');
80
+ lines.push(' return (');
81
+ lines.push(' <div style={{ display: "flex", justifyContent: "center", alignItems: "center", minHeight: "200px" }}>');
82
+ lines.push(' <p>Loading...</p>');
83
+ lines.push(' </div>');
84
+ lines.push(' );');
85
+ lines.push('}');
86
+ lines.push('');
87
+ // Navigation component with active link styling (deduplicated by path)
88
+ // Skip for hash-routed sites — they already have their own nav in the components
89
+ if (!useHashRouter) {
90
+ lines.push('function Navigation() {');
91
+ lines.push(' return (');
92
+ lines.push(' <nav>');
93
+ lines.push(' <ul style={{ display: "flex", listStyle: "none", gap: "1rem", padding: "1rem" }}>');
94
+ const seenPaths = new Set();
95
+ for (const route of routes) {
96
+ if (seenPaths.has(route.path))
97
+ continue;
98
+ seenPaths.add(route.path);
99
+ const label = route.isIndex ? 'Home' : route.componentName;
100
+ lines.push(` <li><NavLink to="${route.path}" style={({ isActive }) => ({ fontWeight: isActive ? "bold" : "normal" })}>${label}</NavLink></li>`);
101
+ }
102
+ lines.push(' </ul>');
103
+ lines.push(' </nav>');
104
+ lines.push(' );');
105
+ lines.push('}');
106
+ lines.push('');
107
+ }
108
+ // App component with router
109
+ const pad = errorBoundary ? ' ' : ' ';
110
+ lines.push('function App() {');
111
+ lines.push(' return (');
112
+ if (errorBoundary)
113
+ lines.push(' <ErrorBoundary>');
114
+ lines.push(`${pad}<${routerComponent}>`);
115
+ if (!useHashRouter) {
116
+ lines.push(`${pad} <Navigation />`);
117
+ }
118
+ lines.push(`${pad} <Suspense fallback={<Loading />}>`);
119
+ lines.push(`${pad} <Routes>`);
120
+ for (const route of routes) {
121
+ lines.push(`${pad} <Route path="${route.path}" element={<${route.componentName} />} />`);
122
+ }
123
+ lines.push(`${pad} <Route path="*" element={<NotFound />} />`);
124
+ lines.push(`${pad} </Routes>`);
125
+ lines.push(`${pad} </Suspense>`);
126
+ lines.push(`${pad}</${routerComponent}>`);
127
+ if (errorBoundary)
128
+ lines.push(' </ErrorBoundary>');
129
+ lines.push(' );');
130
+ lines.push('}');
131
+ lines.push('');
132
+ lines.push('export default App;');
133
+ lines.push('');
134
+ return lines.join('\n');
135
+ }
136
+ /**
137
+ * Generate a simple App.tsx (no router) for single-page sites.
138
+ */
139
+ export function generateSimpleApp(componentNames, hasGlobalCSS, errorBoundary = false, tailwind = false) {
140
+ const lines = [];
141
+ for (const name of componentNames) {
142
+ const fileName = toKebabCase(name);
143
+ lines.push(`import ${name} from './components/${fileName}.tsx'`);
144
+ }
145
+ if (errorBoundary) {
146
+ lines.push(`import ErrorBoundary from './components/ErrorBoundary'`);
147
+ }
148
+ if (hasGlobalCSS) {
149
+ lines.push(`import './styles/global.css'`);
150
+ }
151
+ if (tailwind) {
152
+ lines.push(`import './styles/tailwind.css'`);
153
+ }
154
+ lines.push('');
155
+ lines.push('function App() {');
156
+ lines.push(' return (');
157
+ if (errorBoundary) {
158
+ lines.push(' <ErrorBoundary>');
159
+ for (const name of componentNames) {
160
+ lines.push(` <${name} />`);
161
+ }
162
+ lines.push(' </ErrorBoundary>');
163
+ }
164
+ else {
165
+ lines.push(' <>');
166
+ for (const name of componentNames) {
167
+ lines.push(` <${name} />`);
168
+ }
169
+ lines.push(' </>');
170
+ }
171
+ lines.push(' )');
172
+ lines.push('}');
173
+ lines.push('');
174
+ lines.push('export default App');
175
+ lines.push('');
176
+ return lines.join('\n');
177
+ }
178
+ //# sourceMappingURL=router-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router-generator.js","sourceRoot":"","sources":["../../src/generators/router-generator.ts"],"names":[],"mappings":"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"}