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.
- package/README.md +127 -0
- package/dist/generator.js +59 -0
- package/dist/index.js +32 -0
- package/dist/prompts.js +155 -0
- package/dist/templates/backend/cdk.js +211 -0
- package/dist/templates/backend/express.js +339 -0
- package/dist/templates/backend/fastapi.js +173 -0
- package/dist/templates/backend/nestjs.js +388 -0
- package/dist/templates/backend/sam.js +258 -0
- package/dist/templates/frontend/html.js +141 -0
- package/dist/templates/frontend/index.js +9 -0
- package/dist/templates/frontend/nextjs.js +240 -0
- package/dist/templates/frontend/react.js +249 -0
- package/dist/templates/index.js +337 -0
- package/dist/types.js +2 -0
- package/package.json +52 -0
|
@@ -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
|
+
}
|