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.
- package/README.md +514 -0
- package/bin/cli.js +2 -0
- package/dist/ai/agent.d.ts +124 -0
- package/dist/ai/agent.d.ts.map +1 -0
- package/dist/ai/agent.js +289 -0
- package/dist/ai/agent.js.map +1 -0
- package/dist/ai/index.d.ts +10 -0
- package/dist/ai/index.d.ts.map +1 -0
- package/dist/ai/index.js +10 -0
- package/dist/ai/index.js.map +1 -0
- package/dist/ai/prompts.d.ts +35 -0
- package/dist/ai/prompts.d.ts.map +1 -0
- package/dist/ai/prompts.js +166 -0
- package/dist/ai/prompts.js.map +1 -0
- package/dist/ai/refinement-loop.d.ts +29 -0
- package/dist/ai/refinement-loop.d.ts.map +1 -0
- package/dist/ai/refinement-loop.js +180 -0
- package/dist/ai/refinement-loop.js.map +1 -0
- package/dist/ai/tools.d.ts +17 -0
- package/dist/ai/tools.d.ts.map +1 -0
- package/dist/ai/tools.js +353 -0
- package/dist/ai/tools.js.map +1 -0
- package/dist/ai/visual-compare.d.ts +43 -0
- package/dist/ai/visual-compare.d.ts.map +1 -0
- package/dist/ai/visual-compare.js +176 -0
- package/dist/ai/visual-compare.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +179 -0
- package/dist/cli.js.map +1 -0
- package/dist/converter.d.ts +10 -0
- package/dist/converter.d.ts.map +1 -0
- package/dist/converter.js +836 -0
- package/dist/converter.js.map +1 -0
- package/dist/deconverter.d.ts +19 -0
- package/dist/deconverter.d.ts.map +1 -0
- package/dist/deconverter.js +188 -0
- package/dist/deconverter.js.map +1 -0
- package/dist/frameworks/angular-adapter.d.ts +27 -0
- package/dist/frameworks/angular-adapter.d.ts.map +1 -0
- package/dist/frameworks/angular-adapter.js +617 -0
- package/dist/frameworks/angular-adapter.js.map +1 -0
- package/dist/frameworks/index.d.ts +10 -0
- package/dist/frameworks/index.d.ts.map +1 -0
- package/dist/frameworks/index.js +21 -0
- package/dist/frameworks/index.js.map +1 -0
- package/dist/frameworks/nextjs-adapter.d.ts +22 -0
- package/dist/frameworks/nextjs-adapter.d.ts.map +1 -0
- package/dist/frameworks/nextjs-adapter.js +392 -0
- package/dist/frameworks/nextjs-adapter.js.map +1 -0
- package/dist/frameworks/react-adapter.d.ts +21 -0
- package/dist/frameworks/react-adapter.d.ts.map +1 -0
- package/dist/frameworks/react-adapter.js +71 -0
- package/dist/frameworks/react-adapter.js.map +1 -0
- package/dist/frameworks/svelte-adapter.d.ts +27 -0
- package/dist/frameworks/svelte-adapter.d.ts.map +1 -0
- package/dist/frameworks/svelte-adapter.js +519 -0
- package/dist/frameworks/svelte-adapter.js.map +1 -0
- package/dist/frameworks/types.d.ts +78 -0
- package/dist/frameworks/types.d.ts.map +1 -0
- package/dist/frameworks/types.js +2 -0
- package/dist/frameworks/types.js.map +1 -0
- package/dist/frameworks/vue-adapter.d.ts +34 -0
- package/dist/frameworks/vue-adapter.d.ts.map +1 -0
- package/dist/frameworks/vue-adapter.js +632 -0
- package/dist/frameworks/vue-adapter.js.map +1 -0
- package/dist/generators/accessibility-generator.d.ts +43 -0
- package/dist/generators/accessibility-generator.d.ts.map +1 -0
- package/dist/generators/accessibility-generator.js +507 -0
- package/dist/generators/accessibility-generator.js.map +1 -0
- package/dist/generators/asset-handler.d.ts +14 -0
- package/dist/generators/asset-handler.d.ts.map +1 -0
- package/dist/generators/asset-handler.js +79 -0
- package/dist/generators/asset-handler.js.map +1 -0
- package/dist/generators/build-verifier.d.ts +8 -0
- package/dist/generators/build-verifier.d.ts.map +1 -0
- package/dist/generators/build-verifier.js +64 -0
- package/dist/generators/build-verifier.js.map +1 -0
- package/dist/generators/component-extractor.d.ts +25 -0
- package/dist/generators/component-extractor.d.ts.map +1 -0
- package/dist/generators/component-extractor.js +146 -0
- package/dist/generators/component-extractor.js.map +1 -0
- package/dist/generators/component-generator.d.ts +12 -0
- package/dist/generators/component-generator.d.ts.map +1 -0
- package/dist/generators/component-generator.js +724 -0
- package/dist/generators/component-generator.js.map +1 -0
- package/dist/generators/deploy-generator.d.ts +9 -0
- package/dist/generators/deploy-generator.d.ts.map +1 -0
- package/dist/generators/deploy-generator.js +409 -0
- package/dist/generators/deploy-generator.js.map +1 -0
- package/dist/generators/error-boundary.d.ts +5 -0
- package/dist/generators/error-boundary.d.ts.map +1 -0
- package/dist/generators/error-boundary.js +59 -0
- package/dist/generators/error-boundary.js.map +1 -0
- package/dist/generators/form-generator.d.ts +42 -0
- package/dist/generators/form-generator.d.ts.map +1 -0
- package/dist/generators/form-generator.js +662 -0
- package/dist/generators/form-generator.js.map +1 -0
- package/dist/generators/hooks-generator.d.ts +40 -0
- package/dist/generators/hooks-generator.d.ts.map +1 -0
- package/dist/generators/hooks-generator.js +297 -0
- package/dist/generators/hooks-generator.js.map +1 -0
- package/dist/generators/html-generator.d.ts +27 -0
- package/dist/generators/html-generator.d.ts.map +1 -0
- package/dist/generators/html-generator.js +772 -0
- package/dist/generators/html-generator.js.map +1 -0
- package/dist/generators/jquery-converter.d.ts +41 -0
- package/dist/generators/jquery-converter.d.ts.map +1 -0
- package/dist/generators/jquery-converter.js +594 -0
- package/dist/generators/jquery-converter.js.map +1 -0
- package/dist/generators/pattern-implementer.d.ts +26 -0
- package/dist/generators/pattern-implementer.d.ts.map +1 -0
- package/dist/generators/pattern-implementer.js +336 -0
- package/dist/generators/pattern-implementer.js.map +1 -0
- package/dist/generators/performance-generator.d.ts +51 -0
- package/dist/generators/performance-generator.d.ts.map +1 -0
- package/dist/generators/performance-generator.js +428 -0
- package/dist/generators/performance-generator.js.map +1 -0
- package/dist/generators/router-generator.d.ts +21 -0
- package/dist/generators/router-generator.d.ts.map +1 -0
- package/dist/generators/router-generator.js +178 -0
- package/dist/generators/router-generator.js.map +1 -0
- package/dist/generators/scaffolder.d.ts +28 -0
- package/dist/generators/scaffolder.d.ts.map +1 -0
- package/dist/generators/scaffolder.js +266 -0
- package/dist/generators/scaffolder.js.map +1 -0
- package/dist/generators/seo-generator.d.ts +29 -0
- package/dist/generators/seo-generator.d.ts.map +1 -0
- package/dist/generators/seo-generator.js +223 -0
- package/dist/generators/seo-generator.js.map +1 -0
- package/dist/generators/test-generator.d.ts +19 -0
- package/dist/generators/test-generator.d.ts.map +1 -0
- package/dist/generators/test-generator.js +398 -0
- package/dist/generators/test-generator.js.map +1 -0
- package/dist/generators/type-generator.d.ts +33 -0
- package/dist/generators/type-generator.d.ts.map +1 -0
- package/dist/generators/type-generator.js +663 -0
- package/dist/generators/type-generator.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/parsers/css-processor.d.ts +23 -0
- package/dist/parsers/css-processor.d.ts.map +1 -0
- package/dist/parsers/css-processor.js +129 -0
- package/dist/parsers/css-processor.js.map +1 -0
- package/dist/parsers/framework-parser.d.ts +48 -0
- package/dist/parsers/framework-parser.d.ts.map +1 -0
- package/dist/parsers/framework-parser.js +770 -0
- package/dist/parsers/framework-parser.js.map +1 -0
- package/dist/parsers/html-parser.d.ts +12 -0
- package/dist/parsers/html-parser.d.ts.map +1 -0
- package/dist/parsers/html-parser.js +444 -0
- package/dist/parsers/html-parser.js.map +1 -0
- package/dist/parsers/js-analyzer.d.ts +199 -0
- package/dist/parsers/js-analyzer.d.ts.map +1 -0
- package/dist/parsers/js-analyzer.js +680 -0
- package/dist/parsers/js-analyzer.js.map +1 -0
- package/dist/parsers/js-resolver.d.ts +8 -0
- package/dist/parsers/js-resolver.d.ts.map +1 -0
- package/dist/parsers/js-resolver.js +45 -0
- package/dist/parsers/js-resolver.js.map +1 -0
- package/dist/parsers/tailwind-detector.d.ts +23 -0
- package/dist/parsers/tailwind-detector.d.ts.map +1 -0
- package/dist/parsers/tailwind-detector.js +104 -0
- package/dist/parsers/tailwind-detector.js.map +1 -0
- package/dist/tests/advanced-features.test.d.ts +2 -0
- package/dist/tests/advanced-features.test.d.ts.map +1 -0
- package/dist/tests/advanced-features.test.js +235 -0
- package/dist/tests/advanced-features.test.js.map +1 -0
- package/dist/tests/css-modules.test.d.ts +2 -0
- package/dist/tests/css-modules.test.d.ts.map +1 -0
- package/dist/tests/css-modules.test.js +61 -0
- package/dist/tests/css-modules.test.js.map +1 -0
- package/dist/tests/css-processor.test.d.ts +2 -0
- package/dist/tests/css-processor.test.d.ts.map +1 -0
- package/dist/tests/css-processor.test.js +48 -0
- package/dist/tests/css-processor.test.js.map +1 -0
- package/dist/tests/html-parser.test.d.ts +2 -0
- package/dist/tests/html-parser.test.d.ts.map +1 -0
- package/dist/tests/html-parser.test.js +78 -0
- package/dist/tests/html-parser.test.js.map +1 -0
- package/dist/tests/integration.test.d.ts +2 -0
- package/dist/tests/integration.test.d.ts.map +1 -0
- package/dist/tests/integration.test.js +65 -0
- package/dist/tests/integration.test.js.map +1 -0
- package/dist/tests/js-analyzer.test.d.ts +2 -0
- package/dist/tests/js-analyzer.test.d.ts.map +1 -0
- package/dist/tests/js-analyzer.test.js +58 -0
- package/dist/tests/js-analyzer.test.js.map +1 -0
- package/dist/tests/naming.test.d.ts +2 -0
- package/dist/tests/naming.test.d.ts.map +1 -0
- package/dist/tests/naming.test.js +43 -0
- package/dist/tests/naming.test.js.map +1 -0
- package/dist/tests/router-generator.test.d.ts +2 -0
- package/dist/tests/router-generator.test.d.ts.map +1 -0
- package/dist/tests/router-generator.test.js +60 -0
- package/dist/tests/router-generator.test.js.map +1 -0
- package/dist/tui/chat.d.ts +13 -0
- package/dist/tui/chat.d.ts.map +1 -0
- package/dist/tui/chat.js +499 -0
- package/dist/tui/chat.js.map +1 -0
- package/dist/tui/design-guide.d.ts +41 -0
- package/dist/tui/design-guide.d.ts.map +1 -0
- package/dist/tui/design-guide.js +184 -0
- package/dist/tui/design-guide.js.map +1 -0
- package/dist/tui/input.d.ts +30 -0
- package/dist/tui/input.d.ts.map +1 -0
- package/dist/tui/input.js +239 -0
- package/dist/tui/input.js.map +1 -0
- package/dist/tui/renderer.d.ts +48 -0
- package/dist/tui/renderer.d.ts.map +1 -0
- package/dist/tui/renderer.js +212 -0
- package/dist/tui/renderer.js.map +1 -0
- package/dist/tui/tools.d.ts +14 -0
- package/dist/tui/tools.d.ts.map +1 -0
- package/dist/tui/tools.js +1370 -0
- package/dist/tui/tools.js.map +1 -0
- package/dist/types.d.ts +93 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/config.d.ts +20 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +33 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/formatter.d.ts +5 -0
- package/dist/utils/formatter.d.ts.map +1 -0
- package/dist/utils/formatter.js +68 -0
- package/dist/utils/formatter.js.map +1 -0
- package/dist/utils/logger.d.ts +8 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +19 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/naming.d.ts +17 -0
- package/dist/utils/naming.d.ts.map +1 -0
- package/dist/utils/naming.js +48 -0
- package/dist/utils/naming.js.map +1 -0
- package/dist/utils/report.d.ts +56 -0
- package/dist/utils/report.d.ts.map +1 -0
- package/dist/utils/report.js +339 -0
- package/dist/utils/report.js.map +1 -0
- 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":"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"}
|
|
@@ -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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"}
|