projects-init-cli 1.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.
@@ -0,0 +1,258 @@
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.generateSAM = generateSAM;
7
+ const fs_extra_1 = __importDefault(require("fs-extra"));
8
+ const path_1 = __importDefault(require("path"));
9
+ async function generateSAM(backendPath, config) {
10
+ // Create root package.json with SAM scripts
11
+ const rootPackageJson = {
12
+ name: `${config.projectName}-backend`,
13
+ version: '1.0.0',
14
+ private: true,
15
+ scripts: {
16
+ 'build': 'sam build',
17
+ 'deploy': 'sam deploy',
18
+ 'deploy:guided': 'sam deploy --guided',
19
+ 'local': 'sam local start-api',
20
+ 'validate': 'sam validate',
21
+ 'synth': 'sam build && sam deploy --no-execute'
22
+ },
23
+ devDependencies: {}
24
+ };
25
+ await fs_extra_1.default.writeJSON(path_1.default.join(backendPath, 'package.json'), rootPackageJson, { spaces: 2 });
26
+ // Create template.yaml
27
+ let templateContent = `AWSTemplateFormatVersion: '2010-09-09'
28
+ Transform: AWS::Serverless-2016-10-31
29
+ Description: >
30
+ ${config.projectName}
31
+ SAM Template for ${config.projectName}
32
+
33
+ Globals:
34
+ Function:
35
+ Timeout: 3
36
+ MemorySize: 128
37
+
38
+ Resources:
39
+ HelloWorldFunction:
40
+ Type: AWS::Serverless::Function
41
+ Properties:
42
+ CodeUri: hello-world/
43
+ Handler: app.lambdaHandler
44
+ Runtime: nodejs18.x
45
+ Architectures:
46
+ - x86_64
47
+ Events:
48
+ HelloWorld:
49
+ Type: Api
50
+ Properties:
51
+ Path: /hello
52
+ Method: get
53
+ HelloWorldPost:
54
+ Type: Api
55
+ Properties:
56
+ Path: /hello
57
+ Method: post
58
+
59
+ `;
60
+ if (config.databaseType === 'nosql') {
61
+ templateContent += ` DynamoDBTable:
62
+ Type: AWS::DynamoDB::Table
63
+ Properties:
64
+ TableName: ${config.projectName}Table
65
+ BillingMode: PAY_PER_REQUEST
66
+ AttributeDefinitions:
67
+ - AttributeName: id
68
+ AttributeType: S
69
+ KeySchema:
70
+ - AttributeName: id
71
+ KeyType: HASH
72
+
73
+ `;
74
+ }
75
+ else if (config.databaseType === 'sql') {
76
+ templateContent += ` # Note: RDS requires VPC configuration
77
+ # You'll need to add VPC, Subnets, and Security Groups manually
78
+ # or use AWS RDS Proxy for serverless access
79
+
80
+ `;
81
+ }
82
+ templateContent += `Outputs:
83
+ HelloWorldApi:
84
+ Description: "API Gateway endpoint URL for Prod stage for Hello World function"
85
+ Value: !Sub "https://\${ServerlessRestApi}.execute-api.\${AWS::Region}.amazonaws.com/Prod/hello/"
86
+ HelloWorldFunction:
87
+ Description: "Hello World Lambda Function ARN"
88
+ Value: !GetAtt HelloWorldFunction.Arn
89
+ HelloWorldFunctionIamRole:
90
+ Description: "Implicit IAM Role created for Hello World function"
91
+ Value: !GetAtt HelloWorldFunctionRole.Arn
92
+ `;
93
+ await fs_extra_1.default.writeFile(path_1.default.join(backendPath, 'template.yaml'), templateContent);
94
+ // Create hello-world function
95
+ const helloWorldPath = path_1.default.join(backendPath, 'hello-world');
96
+ await fs_extra_1.default.ensureDir(helloWorldPath);
97
+ const packageJson = {
98
+ name: 'hello-world',
99
+ version: '1.0.0',
100
+ description: 'Hello World SAM Lambda function',
101
+ main: 'app.js',
102
+ scripts: {
103
+ test: 'node tests/unit/test-handler.js'
104
+ },
105
+ dependencies: {}
106
+ };
107
+ if (config.databaseType === 'nosql') {
108
+ packageJson.dependencies = {
109
+ '@aws-sdk/client-dynamodb': '^3.699.0',
110
+ '@aws-sdk/lib-dynamodb': '^3.699.0'
111
+ };
112
+ }
113
+ await fs_extra_1.default.writeJSON(path_1.default.join(helloWorldPath, 'package.json'), packageJson, { spaces: 2 });
114
+ let appJs = `exports.lambdaHandler = async (event) => {
115
+ try {
116
+ const response = {
117
+ statusCode: 200,
118
+ body: JSON.stringify({
119
+ message: 'Hello from ${config.projectName}!',
120
+ input: event,
121
+ }),
122
+ };
123
+ return response;
124
+ } catch (err) {
125
+ console.log(err);
126
+ return err;
127
+ }
128
+ };
129
+ `;
130
+ if (config.databaseType === 'nosql') {
131
+ appJs = `const { DynamoDBClient } = require('@aws-sdk/client-dynamodb');
132
+ const { DynamoDBDocumentClient, PutCommand, GetCommand } = require('@aws-sdk/lib-dynamodb');
133
+
134
+ const client = new DynamoDBClient({});
135
+ const docClient = DynamoDBDocumentClient.from(client);
136
+
137
+ const TABLE_NAME = process.env.TABLE_NAME || '${config.projectName}Table';
138
+
139
+ exports.lambdaHandler = async (event) => {
140
+ try {
141
+ const response = {
142
+ statusCode: 200,
143
+ body: JSON.stringify({
144
+ message: 'Hello from ${config.projectName}!',
145
+ input: event,
146
+ }),
147
+ };
148
+ return response;
149
+ } catch (err) {
150
+ console.log(err);
151
+ return {
152
+ statusCode: 500,
153
+ body: JSON.stringify({ error: err.message }),
154
+ };
155
+ }
156
+ };
157
+ `;
158
+ }
159
+ await fs_extra_1.default.writeFile(path_1.default.join(helloWorldPath, 'app.js'), appJs);
160
+ // Create test directory and test file
161
+ const testsPath = path_1.default.join(helloWorldPath, 'tests', 'unit');
162
+ await fs_extra_1.default.ensureDir(testsPath);
163
+ const testFile = `import { describe, it, expect } from 'vitest';
164
+ const app = require('../../app');
165
+
166
+ describe('Tests index', () => {
167
+ it('verifies successful response', async () => {
168
+ const event = {
169
+ httpMethod: 'GET',
170
+ path: '/hello'
171
+ };
172
+ const context = {};
173
+
174
+ const result = await app.lambdaHandler(event, context);
175
+
176
+ expect(result).toBeDefined();
177
+ expect(result.statusCode).toBe(200);
178
+
179
+ const response = JSON.parse(result.body);
180
+ expect(response.message).toBeDefined();
181
+ });
182
+ });
183
+ `;
184
+ await fs_extra_1.default.writeFile(path_1.default.join(testsPath, 'test-handler.js'), testFile);
185
+ // Create package.json for tests
186
+ const testPackageJson = {
187
+ name: 'hello-world-tests',
188
+ version: '1.0.0',
189
+ scripts: {
190
+ test: 'vitest',
191
+ 'test:ui': 'vitest --ui',
192
+ 'test:coverage': 'vitest --coverage'
193
+ },
194
+ dependencies: {
195
+ 'aws-sdk': '^2.814.0'
196
+ },
197
+ devDependencies: {
198
+ 'vitest': '^2.1.3',
199
+ '@vitest/ui': '^2.1.3',
200
+ '@vitest/coverage-v8': '^2.1.3'
201
+ }
202
+ };
203
+ await fs_extra_1.default.writeJSON(path_1.default.join(testsPath, 'package.json'), testPackageJson, { spaces: 2 });
204
+ // Create samconfig.toml
205
+ const samConfig = `version = 0.1
206
+
207
+ [default]
208
+ [default.global.parameters]
209
+ stack_name = "${config.projectName}"
210
+
211
+ [default.build.parameters]
212
+ cached = true
213
+ parallel = true
214
+
215
+ [default.validate.parameters]
216
+ lint = true
217
+
218
+ [default.deploy.parameters]
219
+ capabilities = "CAPABILITY_IAM"
220
+ confirm_changeset = true
221
+ resolve_s3 = true
222
+ region = "us-east-1"
223
+ `;
224
+ await fs_extra_1.default.writeFile(path_1.default.join(backendPath, 'samconfig.toml'), samConfig);
225
+ // Create README
226
+ const readme = `# AWS SAM Backend
227
+
228
+ This is an AWS SAM project for serverless deployment.
229
+
230
+ ## Prerequisites
231
+
232
+ - AWS CLI configured
233
+ - SAM CLI installed (\`pip install aws-sam-cli\`)
234
+
235
+ ## Build and Deploy
236
+
237
+ \`\`\`bash
238
+ npm run build
239
+ npm run deploy:guided
240
+ \`\`\`
241
+
242
+ ## Local Development
243
+
244
+ \`\`\`bash
245
+ npm run local
246
+ \`\`\`
247
+
248
+ ## Useful Commands
249
+
250
+ - \`npm run build\` - Build your application
251
+ - \`npm run local\` - Start local API Gateway
252
+ - \`npm run deploy:guided\` - Deploy your application with guided prompts
253
+ - \`npm run deploy\` - Deploy your application
254
+ - \`npm run validate\` - Validate SAM template
255
+ - \`sam logs -n HelloWorldFunction --stack-name ${config.projectName} --tail\` - View logs
256
+ `;
257
+ await fs_extra_1.default.writeFile(path_1.default.join(backendPath, 'README.md'), readme);
258
+ }
@@ -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
+ }