projects-init-cli 2.0.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.
@@ -0,0 +1,141 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.generateHTML = generateHTML;
7
+ const fs_extra_1 = __importDefault(require("fs-extra"));
8
+ const path_1 = __importDefault(require("path"));
9
+ async function generateHTML(frontendPath, config) {
10
+ const packageJson = {
11
+ name: `${config.projectName}-frontend`,
12
+ version: '0.1.0',
13
+ private: true,
14
+ scripts: {
15
+ dev: 'vite',
16
+ build: 'vite build',
17
+ preview: 'vite preview'
18
+ },
19
+ devDependencies: {
20
+ vite: '^6.0.1',
21
+ 'tailwindcss': '^3.4.14',
22
+ 'postcss': '^8.4.47',
23
+ 'autoprefixer': '^10.4.20'
24
+ }
25
+ };
26
+ await fs_extra_1.default.writeJSON(path_1.default.join(frontendPath, 'package.json'), packageJson, { spaces: 2 });
27
+ // Create Vite config
28
+ const viteConfig = `import { defineConfig } from 'vite'
29
+
30
+ export default defineConfig({
31
+ // Vite config for HTML project
32
+ })
33
+ `;
34
+ await fs_extra_1.default.writeFile(path_1.default.join(frontendPath, 'vite.config.js'), viteConfig);
35
+ // Create Tailwind config
36
+ const tailwindConfig = `/** @type {import('tailwindcss').Config} */
37
+ module.exports = {
38
+ content: [
39
+ "./index.html",
40
+ "./src/**/*.{html,js}",
41
+ ],
42
+ theme: {
43
+ extend: {},
44
+ },
45
+ plugins: [],
46
+ }
47
+ `;
48
+ await fs_extra_1.default.writeFile(path_1.default.join(frontendPath, 'tailwind.config.js'), tailwindConfig);
49
+ // Create PostCSS config
50
+ const postcssConfig = `module.exports = {
51
+ plugins: {
52
+ tailwindcss: {},
53
+ autoprefixer: {},
54
+ },
55
+ }
56
+ `;
57
+ await fs_extra_1.default.writeFile(path_1.default.join(frontendPath, 'postcss.config.js'), postcssConfig);
58
+ // Create index.html
59
+ const indexHtml = `<!DOCTYPE html>
60
+ <html lang="en">
61
+ <head>
62
+ <meta charset="UTF-8">
63
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
64
+ <title>${config.projectName}</title>
65
+ <link rel="stylesheet" href="/src/styles.css">
66
+ </head>
67
+ <body>
68
+ <main class="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 flex items-center justify-center">
69
+ <div class="bg-white rounded-lg shadow-xl p-8 max-w-md w-full">
70
+ <h1 class="text-4xl font-bold text-gray-800 mb-4">
71
+ Welcome to ${config.projectName}
72
+ </h1>
73
+ <p class="text-gray-600 mb-6">
74
+ Your HTML application with Tailwind CSS is ready!
75
+ </p>
76
+ <div class="space-y-2">
77
+ <div class="flex items-center text-sm text-gray-500">
78
+ <span class="w-2 h-2 bg-green-500 rounded-full mr-2"></span>
79
+ HTML5
80
+ </div>
81
+ <div class="flex items-center text-sm text-gray-500">
82
+ <span class="w-2 h-2 bg-green-500 rounded-full mr-2"></span>
83
+ Tailwind CSS
84
+ </div>
85
+ <div class="flex items-center text-sm text-gray-500">
86
+ <span class="w-2 h-2 bg-green-500 rounded-full mr-2"></span>
87
+ Vite
88
+ </div>
89
+ </div>
90
+ </div>
91
+ </main>
92
+ <script type="module" src="/src/main.js"></script>
93
+ </body>
94
+ </html>
95
+ `;
96
+ await fs_extra_1.default.writeFile(path_1.default.join(frontendPath, 'index.html'), indexHtml);
97
+ // Create src directory
98
+ const srcPath = path_1.default.join(frontendPath, 'src');
99
+ await fs_extra_1.default.ensureDir(srcPath);
100
+ // Create styles.css
101
+ const stylesCss = `@tailwind base;
102
+ @tailwind components;
103
+ @tailwind utilities;
104
+ `;
105
+ await fs_extra_1.default.writeFile(path_1.default.join(srcPath, 'styles.css'), stylesCss);
106
+ // Create main.js
107
+ const mainJs = `// Your JavaScript code here
108
+ console.log('${config.projectName} is ready!');
109
+ `;
110
+ await fs_extra_1.default.writeFile(path_1.default.join(srcPath, 'main.js'), mainJs);
111
+ // Create deployment files
112
+ await generateFrontendDeployment(frontendPath, config);
113
+ }
114
+ async function generateFrontendDeployment(frontendPath, config) {
115
+ // Netlify configuration
116
+ const netlifyToml = `[build]
117
+ command = "npm run build"
118
+ publish = "dist"
119
+
120
+ [[redirects]]
121
+ from = "/*"
122
+ to = "/index.html"
123
+ status = 200
124
+
125
+ [build.environment]
126
+ NODE_VERSION = "20"
127
+ `;
128
+ await fs_extra_1.default.writeFile(path_1.default.join(frontendPath, 'netlify.toml'), netlifyToml);
129
+ // Render configuration
130
+ const renderYaml = `services:
131
+ - type: web
132
+ name: ${config.projectName}-frontend
133
+ env: node
134
+ buildCommand: npm install && npm run build
135
+ staticPublishPath: ./dist
136
+ envVars:
137
+ - key: NODE_ENV
138
+ value: production
139
+ `;
140
+ await fs_extra_1.default.writeFile(path_1.default.join(frontendPath, 'render.yaml'), renderYaml);
141
+ }
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateHTML = exports.generateReact = exports.generateNextJS = void 0;
4
+ var nextjs_1 = require("./nextjs");
5
+ Object.defineProperty(exports, "generateNextJS", { enumerable: true, get: function () { return nextjs_1.generateNextJS; } });
6
+ var react_1 = require("./react");
7
+ Object.defineProperty(exports, "generateReact", { enumerable: true, get: function () { return react_1.generateReact; } });
8
+ var html_1 = require("./html");
9
+ Object.defineProperty(exports, "generateHTML", { enumerable: true, get: function () { return html_1.generateHTML; } });
@@ -0,0 +1,240 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.generateNextJS = generateNextJS;
7
+ const fs_extra_1 = __importDefault(require("fs-extra"));
8
+ const path_1 = __importDefault(require("path"));
9
+ async function generateNextJS(frontendPath, config) {
10
+ const packageJson = {
11
+ name: `${config.projectName}-frontend`,
12
+ version: '0.1.0',
13
+ private: true,
14
+ scripts: {
15
+ dev: 'next dev',
16
+ build: 'next build',
17
+ start: 'next start',
18
+ lint: 'next lint',
19
+ test: 'vitest',
20
+ 'test:ui': 'vitest --ui',
21
+ 'test:coverage': 'vitest --coverage'
22
+ },
23
+ dependencies: {
24
+ react: '^18.3.1',
25
+ 'react-dom': '^18.3.1',
26
+ next: '^15.0.3'
27
+ },
28
+ devDependencies: {
29
+ '@types/node': '^22.7.5',
30
+ '@types/react': '^18.3.12',
31
+ '@types/react-dom': '^18.3.1',
32
+ typescript: '^5.6.3',
33
+ 'tailwindcss': '^3.4.14',
34
+ 'postcss': '^8.4.47',
35
+ 'autoprefixer': '^10.4.20',
36
+ 'eslint': '^9.15.0',
37
+ 'eslint-config-next': '^15.0.3',
38
+ 'vitest': '^2.1.3',
39
+ '@vitest/ui': '^2.1.3',
40
+ '@testing-library/react': '^16.0.1',
41
+ '@testing-library/jest-dom': '^6.6.3',
42
+ '@vitejs/plugin-react': '^4.3.2',
43
+ 'jsdom': '^25.0.1',
44
+ '@vitest/coverage-v8': '^2.1.3'
45
+ }
46
+ };
47
+ await fs_extra_1.default.writeJSON(path_1.default.join(frontendPath, 'package.json'), packageJson, { spaces: 2 });
48
+ // Create Next.js config
49
+ const nextConfig = `/** @type {import('next').NextConfig} */
50
+ const nextConfig = {
51
+ reactStrictMode: true,
52
+ }
53
+
54
+ module.exports = nextConfig
55
+ `;
56
+ await fs_extra_1.default.writeFile(path_1.default.join(frontendPath, 'next.config.js'), nextConfig);
57
+ // Create Tailwind config
58
+ const tailwindConfig = `/** @type {import('tailwindcss').Config} */
59
+ module.exports = {
60
+ content: [
61
+ './pages/**/*.{js,ts,jsx,tsx,mdx}',
62
+ './components/**/*.{js,ts,jsx,tsx,mdx}',
63
+ './app/**/*.{js,ts,jsx,tsx,mdx}',
64
+ ],
65
+ theme: {
66
+ extend: {},
67
+ },
68
+ plugins: [],
69
+ }
70
+ `;
71
+ await fs_extra_1.default.writeFile(path_1.default.join(frontendPath, 'tailwind.config.js'), tailwindConfig);
72
+ // Create PostCSS config
73
+ const postcssConfig = `module.exports = {
74
+ plugins: {
75
+ tailwindcss: {},
76
+ autoprefixer: {},
77
+ },
78
+ }
79
+ `;
80
+ await fs_extra_1.default.writeFile(path_1.default.join(frontendPath, 'postcss.config.js'), postcssConfig);
81
+ // Create Vitest config
82
+ const vitestConfig = `import { defineConfig } from 'vitest/config'
83
+ import react from '@vitejs/plugin-react'
84
+ import path from 'path'
85
+
86
+ export default defineConfig({
87
+ plugins: [react()],
88
+ test: {
89
+ globals: true,
90
+ environment: 'jsdom',
91
+ setupFiles: './src/test/setup.ts',
92
+ coverage: {
93
+ provider: 'v8',
94
+ reporter: ['text', 'json', 'html'],
95
+ },
96
+ },
97
+ resolve: {
98
+ alias: {
99
+ '@': path.resolve(__dirname, './'),
100
+ },
101
+ },
102
+ })
103
+ `;
104
+ await fs_extra_1.default.writeFile(path_1.default.join(frontendPath, 'vitest.config.ts'), vitestConfig);
105
+ // Create TypeScript config
106
+ const tsconfig = {
107
+ compilerOptions: {
108
+ target: 'ES2020',
109
+ lib: ['dom', 'dom.iterable', 'esnext'],
110
+ allowJs: true,
111
+ skipLibCheck: true,
112
+ strict: true,
113
+ forceConsistentCasingInFileNames: true,
114
+ noEmit: true,
115
+ esModuleInterop: true,
116
+ module: 'esnext',
117
+ moduleResolution: 'bundler',
118
+ resolveJsonModule: true,
119
+ isolatedModules: true,
120
+ jsx: 'preserve',
121
+ incremental: true,
122
+ plugins: [{ name: 'next' }],
123
+ paths: {
124
+ '@/*': ['./*']
125
+ }
126
+ },
127
+ include: ['next-env.d.ts', '**/*.ts', '**/*.tsx', '.next/types/**/*.ts'],
128
+ exclude: ['node_modules']
129
+ };
130
+ await fs_extra_1.default.writeJSON(path_1.default.join(frontendPath, 'tsconfig.json'), tsconfig, { spaces: 2 });
131
+ // Create app directory structure
132
+ const appPath = path_1.default.join(frontendPath, 'app');
133
+ await fs_extra_1.default.ensureDir(appPath);
134
+ // Create layout
135
+ const layout = `import './globals.css'
136
+ import type { Metadata } from 'next'
137
+
138
+ export const metadata: Metadata = {
139
+ title: '${config.projectName}',
140
+ description: 'Generated with Projects Init CLI',
141
+ }
142
+
143
+ export default function RootLayout({
144
+ children,
145
+ }: {
146
+ children: React.ReactNode
147
+ }) {
148
+ return (
149
+ <html lang="en">
150
+ <body>{children}</body>
151
+ </html>
152
+ )
153
+ }
154
+ `;
155
+ await fs_extra_1.default.writeFile(path_1.default.join(appPath, 'layout.tsx'), layout);
156
+ // Create page
157
+ const page = `export default function Home() {
158
+ return (
159
+ <main className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 flex items-center justify-center">
160
+ <div className="bg-white rounded-lg shadow-xl p-8 max-w-md w-full">
161
+ <h1 className="text-4xl font-bold text-gray-800 mb-4">
162
+ Welcome to ${config.projectName}
163
+ </h1>
164
+ <p className="text-gray-600 mb-6">
165
+ Your Next.js application with Tailwind CSS is ready!
166
+ </p>
167
+ <div className="space-y-2">
168
+ <div className="flex items-center text-sm text-gray-500">
169
+ <span className="w-2 h-2 bg-green-500 rounded-full mr-2"></span>
170
+ Next.js 14
171
+ </div>
172
+ <div className="flex items-center text-sm text-gray-500">
173
+ <span className="w-2 h-2 bg-green-500 rounded-full mr-2"></span>
174
+ Tailwind CSS
175
+ </div>
176
+ <div className="flex items-center text-sm text-gray-500">
177
+ <span className="w-2 h-2 bg-green-500 rounded-full mr-2"></span>
178
+ TypeScript
179
+ </div>
180
+ </div>
181
+ </div>
182
+ </main>
183
+ )
184
+ }
185
+ `;
186
+ await fs_extra_1.default.writeFile(path_1.default.join(appPath, 'page.tsx'), page);
187
+ // Create globals.css
188
+ const globalsCss = `@tailwind base;
189
+ @tailwind components;
190
+ @tailwind utilities;
191
+ `;
192
+ await fs_extra_1.default.writeFile(path_1.default.join(appPath, 'globals.css'), globalsCss);
193
+ // Create test directory and setup
194
+ const testPath = path_1.default.join(frontendPath, 'src', 'test');
195
+ await fs_extra_1.default.ensureDir(testPath);
196
+ const testSetup = `import '@testing-library/jest-dom'
197
+ `;
198
+ await fs_extra_1.default.writeFile(path_1.default.join(testPath, 'setup.ts'), testSetup);
199
+ // Create example test
200
+ const testExample = `import { describe, it, expect } from 'vitest'
201
+ import { render, screen } from '@testing-library/react'
202
+ import Home from '../app/page'
203
+
204
+ describe('Home Page', () => {
205
+ it('renders welcome message', () => {
206
+ render(<Home />)
207
+ expect(screen.getByText(/Welcome to ${config.projectName}/i)).toBeInTheDocument()
208
+ })
209
+ })
210
+ `;
211
+ await fs_extra_1.default.writeFile(path_1.default.join(testPath, 'page.test.tsx'), testExample);
212
+ // Create deployment files
213
+ await generateFrontendDeployment(frontendPath, config);
214
+ }
215
+ async function generateFrontendDeployment(frontendPath, config) {
216
+ // Netlify configuration
217
+ const netlifyToml = `[build]
218
+ command = "npm run build"
219
+ publish = ".next"
220
+
221
+ [[plugins]]
222
+ package = "@netlify/plugin-nextjs"
223
+
224
+ [build.environment]
225
+ NODE_VERSION = "20"
226
+ `;
227
+ await fs_extra_1.default.writeFile(path_1.default.join(frontendPath, 'netlify.toml'), netlifyToml);
228
+ // Render configuration
229
+ const renderYaml = `services:
230
+ - type: web
231
+ name: ${config.projectName}-frontend
232
+ env: node
233
+ buildCommand: npm install && npm run build
234
+ startCommand: npm start
235
+ envVars:
236
+ - key: NODE_ENV
237
+ value: production
238
+ `;
239
+ await fs_extra_1.default.writeFile(path_1.default.join(frontendPath, 'render.yaml'), renderYaml);
240
+ }
@@ -0,0 +1,249 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.generateReact = generateReact;
7
+ const fs_extra_1 = __importDefault(require("fs-extra"));
8
+ const path_1 = __importDefault(require("path"));
9
+ async function generateReact(frontendPath, config) {
10
+ const packageJson = {
11
+ name: `${config.projectName}-frontend`,
12
+ version: '0.1.0',
13
+ private: true,
14
+ scripts: {
15
+ dev: 'vite',
16
+ build: 'tsc && vite build',
17
+ preview: 'vite preview',
18
+ lint: 'eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0',
19
+ test: 'vitest',
20
+ 'test:ui': 'vitest --ui',
21
+ 'test:coverage': 'vitest --coverage'
22
+ },
23
+ dependencies: {
24
+ react: '^18.3.1',
25
+ 'react-dom': '^18.3.1'
26
+ },
27
+ devDependencies: {
28
+ '@types/react': '^18.3.12',
29
+ '@types/react-dom': '^18.3.1',
30
+ '@typescript-eslint/eslint-plugin': '^8.15.0',
31
+ '@typescript-eslint/parser': '^8.15.0',
32
+ '@vitejs/plugin-react': '^4.3.2',
33
+ eslint: '^9.15.0',
34
+ 'eslint-plugin-react-hooks': '^5.1.0',
35
+ 'eslint-plugin-react-refresh': '^0.4.14',
36
+ typescript: '^5.6.3',
37
+ vite: '^6.0.1',
38
+ 'tailwindcss': '^3.4.14',
39
+ 'postcss': '^8.4.47',
40
+ 'autoprefixer': '^10.4.20',
41
+ 'vitest': '^2.1.3',
42
+ '@vitest/ui': '^2.1.3',
43
+ '@testing-library/react': '^16.0.1',
44
+ '@testing-library/jest-dom': '^6.6.3',
45
+ 'jsdom': '^25.0.1',
46
+ '@vitest/coverage-v8': '^2.1.3'
47
+ }
48
+ };
49
+ await fs_extra_1.default.writeJSON(path_1.default.join(frontendPath, 'package.json'), packageJson, { spaces: 2 });
50
+ // Create Vite config
51
+ const viteConfig = `import { defineConfig } from 'vite'
52
+ import react from '@vitejs/plugin-react'
53
+
54
+ export default defineConfig({
55
+ plugins: [react()],
56
+ test: {
57
+ globals: true,
58
+ environment: 'jsdom',
59
+ setupFiles: './src/test/setup.ts',
60
+ coverage: {
61
+ provider: 'v8',
62
+ reporter: ['text', 'json', 'html'],
63
+ },
64
+ },
65
+ })
66
+ `;
67
+ await fs_extra_1.default.writeFile(path_1.default.join(frontendPath, 'vite.config.ts'), viteConfig);
68
+ // Create Tailwind config
69
+ const tailwindConfig = `/** @type {import('tailwindcss').Config} */
70
+ export default {
71
+ content: [
72
+ "./index.html",
73
+ "./src/**/*.{js,ts,jsx,tsx}",
74
+ ],
75
+ theme: {
76
+ extend: {},
77
+ },
78
+ plugins: [],
79
+ }
80
+ `;
81
+ await fs_extra_1.default.writeFile(path_1.default.join(frontendPath, 'tailwind.config.js'), tailwindConfig);
82
+ // Create PostCSS config
83
+ const postcssConfig = `export default {
84
+ plugins: {
85
+ tailwindcss: {},
86
+ autoprefixer: {},
87
+ },
88
+ }
89
+ `;
90
+ await fs_extra_1.default.writeFile(path_1.default.join(frontendPath, 'postcss.config.js'), postcssConfig);
91
+ // Create TypeScript config
92
+ const tsconfig = {
93
+ compilerOptions: {
94
+ target: 'ES2020',
95
+ useDefineForClassFields: true,
96
+ lib: ['ES2020', 'DOM', 'DOM.Iterable'],
97
+ module: 'ESNext',
98
+ skipLibCheck: true,
99
+ moduleResolution: 'bundler',
100
+ allowImportingTsExtensions: true,
101
+ resolveJsonModule: true,
102
+ isolatedModules: true,
103
+ noEmit: true,
104
+ jsx: 'react-jsx',
105
+ strict: true,
106
+ noUnusedLocals: true,
107
+ noUnusedParameters: true,
108
+ noFallthroughCasesInSwitch: true
109
+ },
110
+ include: ['src'],
111
+ references: [{ path: './tsconfig.node.json' }]
112
+ };
113
+ await fs_extra_1.default.writeJSON(path_1.default.join(frontendPath, 'tsconfig.json'), tsconfig, { spaces: 2 });
114
+ const tsconfigNode = {
115
+ compilerOptions: {
116
+ composite: true,
117
+ skipLibCheck: true,
118
+ module: 'ESNext',
119
+ moduleResolution: 'bundler',
120
+ allowSyntheticDefaultImports: true
121
+ },
122
+ include: ['vite.config.ts']
123
+ };
124
+ await fs_extra_1.default.writeJSON(path_1.default.join(frontendPath, 'tsconfig.node.json'), tsconfigNode, { spaces: 2 });
125
+ // Create index.html
126
+ const indexHtml = `<!doctype html>
127
+ <html lang="en">
128
+ <head>
129
+ <meta charset="UTF-8" />
130
+ <link rel="icon" type="image/svg+xml" href="/vite.svg" />
131
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
132
+ <title>${config.projectName}</title>
133
+ </head>
134
+ <body>
135
+ <div id="root"></div>
136
+ <script type="module" src="/src/main.tsx"></script>
137
+ </body>
138
+ </html>
139
+ `;
140
+ await fs_extra_1.default.writeFile(path_1.default.join(frontendPath, 'index.html'), indexHtml);
141
+ // Create src directory
142
+ const srcPath = path_1.default.join(frontendPath, 'src');
143
+ await fs_extra_1.default.ensureDir(srcPath);
144
+ // Create main.tsx
145
+ const mainTsx = `import React from 'react'
146
+ import ReactDOM from 'react-dom/client'
147
+ import App from './App.tsx'
148
+ import './index.css'
149
+
150
+ ReactDOM.createRoot(document.getElementById('root')!).render(
151
+ <React.StrictMode>
152
+ <App />
153
+ </React.StrictMode>,
154
+ )
155
+ `;
156
+ await fs_extra_1.default.writeFile(path_1.default.join(srcPath, 'main.tsx'), mainTsx);
157
+ // Create App.tsx
158
+ const appTsx = `function App() {
159
+ return (
160
+ <main className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 flex items-center justify-center">
161
+ <div className="bg-white rounded-lg shadow-xl p-8 max-w-md w-full">
162
+ <h1 className="text-4xl font-bold text-gray-800 mb-4">
163
+ Welcome to ${config.projectName}
164
+ </h1>
165
+ <p className="text-gray-600 mb-6">
166
+ Your React application with Tailwind CSS is ready!
167
+ </p>
168
+ <div className="space-y-2">
169
+ <div className="flex items-center text-sm text-gray-500">
170
+ <span className="w-2 h-2 bg-green-500 rounded-full mr-2"></span>
171
+ React 18
172
+ </div>
173
+ <div className="flex items-center text-sm text-gray-500">
174
+ <span className="w-2 h-2 bg-green-500 rounded-full mr-2"></span>
175
+ Vite
176
+ </div>
177
+ <div className="flex items-center text-sm text-gray-500">
178
+ <span className="w-2 h-2 bg-green-500 rounded-full mr-2"></span>
179
+ Tailwind CSS
180
+ </div>
181
+ <div className="flex items-center text-sm text-gray-500">
182
+ <span className="w-2 h-2 bg-green-500 rounded-full mr-2"></span>
183
+ TypeScript
184
+ </div>
185
+ </div>
186
+ </div>
187
+ </main>
188
+ )
189
+ }
190
+
191
+ export default App
192
+ `;
193
+ await fs_extra_1.default.writeFile(path_1.default.join(srcPath, 'App.tsx'), appTsx);
194
+ // Create index.css
195
+ const indexCss = `@tailwind base;
196
+ @tailwind components;
197
+ @tailwind utilities;
198
+ `;
199
+ await fs_extra_1.default.writeFile(path_1.default.join(srcPath, 'index.css'), indexCss);
200
+ // Create test directory and setup
201
+ const testPath = path_1.default.join(srcPath, 'test');
202
+ await fs_extra_1.default.ensureDir(testPath);
203
+ const testSetup = `import '@testing-library/jest-dom'
204
+ `;
205
+ await fs_extra_1.default.writeFile(path_1.default.join(testPath, 'setup.ts'), testSetup);
206
+ // Create example test
207
+ const testExample = `import { describe, it, expect } from 'vitest'
208
+ import { render, screen } from '@testing-library/react'
209
+ import App from '../App'
210
+
211
+ describe('App', () => {
212
+ it('renders welcome message', () => {
213
+ render(<App />)
214
+ expect(screen.getByText(/Welcome to ${config.projectName}/i)).toBeInTheDocument()
215
+ })
216
+ })
217
+ `;
218
+ await fs_extra_1.default.writeFile(path_1.default.join(testPath, 'App.test.tsx'), testExample);
219
+ // Create deployment files
220
+ await generateFrontendDeployment(frontendPath, config);
221
+ }
222
+ async function generateFrontendDeployment(frontendPath, config) {
223
+ // Netlify configuration
224
+ const netlifyToml = `[build]
225
+ command = "npm run build"
226
+ publish = "dist"
227
+
228
+ [[redirects]]
229
+ from = "/*"
230
+ to = "/index.html"
231
+ status = 200
232
+
233
+ [build.environment]
234
+ NODE_VERSION = "20"
235
+ `;
236
+ await fs_extra_1.default.writeFile(path_1.default.join(frontendPath, 'netlify.toml'), netlifyToml);
237
+ // Render configuration
238
+ const renderYaml = `services:
239
+ - type: web
240
+ name: ${config.projectName}-frontend
241
+ env: node
242
+ buildCommand: npm install && npm run build
243
+ staticPublishPath: ./dist
244
+ envVars:
245
+ - key: NODE_ENV
246
+ value: production
247
+ `;
248
+ await fs_extra_1.default.writeFile(path_1.default.join(frontendPath, 'render.yaml'), renderYaml);
249
+ }