initkit 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,915 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import ora from 'ora';
4
+ import chalk from 'chalk';
5
+ import { getLatestVersion } from '../utils/versionFetcher.js';
6
+
7
+ async function fetchVersion(packageName, fallback = 'latest') {
8
+ try {
9
+ const version = await getLatestVersion(packageName);
10
+ return `^${version}`;
11
+ } catch {
12
+ return fallback;
13
+ }
14
+ }
15
+
16
+ /**
17
+ * Generate Express.js backend API project with organized architecture
18
+ *
19
+ * Creates a production-ready Express.js server with:
20
+ * - RESTful API structure
21
+ * - Multiple architecture patterns (MVC, Clean Architecture, Domain-Driven Design)
22
+ * - Database integration (MongoDB, PostgreSQL, MySQL, SQLite)
23
+ * - TypeScript or JavaScript support
24
+ * - Authentication middleware setup
25
+ * - Error handling and validation
26
+ * - Environment configuration
27
+ *
28
+ * Generated project includes:
29
+ * - Organized folder structure based on pattern
30
+ * - Server entry point with middleware configuration
31
+ * - Route handlers and controllers
32
+ * - Database models and schemas
33
+ * - Package.json with Express and dependencies
34
+ * - .env.example for environment variables
35
+ * - README with API documentation
36
+ *
37
+ * @param {string} projectPath - Absolute path to the project directory
38
+ * @param {Object} config - User configuration object
39
+ * @param {string} config.projectName - Name of the project
40
+ * @param {string} config.language - Programming language ('typescript'|'javascript')
41
+ * @param {string} [config.folderStructure='mvc'] - Architecture pattern
42
+ * - 'mvc': Model-View-Controller (recommended for REST APIs)
43
+ * - 'clean-architecture': Clean Architecture with domain/infrastructure layers
44
+ * - 'ddd': Domain-Driven Design with bounded contexts
45
+ * - 'feature-based': Organize by features/modules
46
+ * @param {string} [config.database='none'] - Database choice ('mongodb'|'postgresql'|'mysql'|'sqlite'|'none')
47
+ * @param {string} config.packageManager - Package manager to use
48
+ *
49
+ * @returns {Promise<void>}
50
+ *
51
+ * @example
52
+ * // Create Express API with PostgreSQL and MVC pattern
53
+ * await generateExpressTemplate('/path/to/project', {
54
+ * projectName: 'my-express-api',
55
+ * language: 'typescript',
56
+ * folderStructure: 'mvc',
57
+ * database: 'postgresql',
58
+ * packageManager: 'npm'
59
+ * });
60
+ */
61
+ export async function generateExpressTemplate(projectPath, config) {
62
+ // Create folder structure
63
+ await createExpressFolderStructure(projectPath, config);
64
+
65
+ // Generate configuration files
66
+ await generateExpressConfigFiles(projectPath, config);
67
+
68
+ // Generate package.json
69
+ await generateExpressPackageJson(projectPath, config);
70
+
71
+ // Generate README
72
+ await generateExpressReadme(projectPath, config);
73
+
74
+ // Generate environment file example
75
+ await generateEnvExample(projectPath, config);
76
+ }
77
+
78
+ async function createExpressFolderStructure(projectPath, config) {
79
+ const srcPath = path.join(projectPath, 'src');
80
+ const folderStructure = config.folderStructure || 'mvc';
81
+ const database = config.database || 'none';
82
+
83
+ if (folderStructure === 'mvc') {
84
+ // MVC Pattern
85
+ await fs.ensureDir(path.join(srcPath, 'models'));
86
+ await fs.ensureDir(path.join(srcPath, 'views'));
87
+ await fs.ensureDir(path.join(srcPath, 'controllers'));
88
+ await fs.ensureDir(path.join(srcPath, 'routes'));
89
+ await fs.ensureDir(path.join(srcPath, 'middleware'));
90
+ await fs.ensureDir(path.join(srcPath, 'utils'));
91
+ await fs.ensureDir(path.join(srcPath, 'config'));
92
+
93
+ // Routes index
94
+ await fs.writeFile(path.join(srcPath, 'routes', 'index.ts'), generateRoutesIndex());
95
+ } else if (folderStructure === 'clean-architecture') {
96
+ // Clean Architecture
97
+ await fs.ensureDir(path.join(srcPath, 'domain', 'entities'));
98
+ await fs.ensureDir(path.join(srcPath, 'domain', 'repositories'));
99
+ await fs.ensureDir(path.join(srcPath, 'domain', 'use-cases'));
100
+
101
+ await fs.ensureDir(path.join(srcPath, 'application', 'dto'));
102
+ await fs.ensureDir(path.join(srcPath, 'application', 'services'));
103
+ await fs.ensureDir(path.join(srcPath, 'application', 'interfaces'));
104
+
105
+ await fs.ensureDir(path.join(srcPath, 'infrastructure', 'database'));
106
+ await fs.ensureDir(path.join(srcPath, 'infrastructure', 'repositories'));
107
+ await fs.ensureDir(path.join(srcPath, 'infrastructure', 'external-services'));
108
+
109
+ await fs.ensureDir(path.join(srcPath, 'presentation', 'controllers'));
110
+ await fs.ensureDir(path.join(srcPath, 'presentation', 'middleware'));
111
+ await fs.ensureDir(path.join(srcPath, 'presentation', 'routes'));
112
+ } else if (folderStructure === 'feature-based') {
113
+ // Feature-based structure
114
+ const features = ['auth', 'users', 'posts'];
115
+
116
+ for (const feature of features) {
117
+ await fs.ensureDir(path.join(srcPath, 'features', feature, 'controllers'));
118
+ await fs.ensureDir(path.join(srcPath, 'features', feature, 'services'));
119
+ await fs.ensureDir(path.join(srcPath, 'features', feature, 'routes'));
120
+ await fs.ensureDir(path.join(srcPath, 'features', feature, 'models'));
121
+ await fs.ensureDir(path.join(srcPath, 'features', feature, 'middleware'));
122
+ await fs.ensureDir(path.join(srcPath, 'features', feature, 'validators'));
123
+ }
124
+
125
+ // Shared directory
126
+ await fs.ensureDir(path.join(srcPath, 'shared', 'middleware'));
127
+ await fs.ensureDir(path.join(srcPath, 'shared', 'utils'));
128
+ await fs.ensureDir(path.join(srcPath, 'shared', 'types'));
129
+ await fs.ensureDir(path.join(srcPath, 'shared', 'config'));
130
+ } else if (folderStructure === 'layered') {
131
+ // Layered Architecture
132
+ await fs.ensureDir(path.join(srcPath, 'controllers'));
133
+ await fs.ensureDir(path.join(srcPath, 'services'));
134
+ await fs.ensureDir(path.join(srcPath, 'repositories'));
135
+ await fs.ensureDir(path.join(srcPath, 'models'));
136
+ await fs.ensureDir(path.join(srcPath, 'routes'));
137
+ await fs.ensureDir(path.join(srcPath, 'middleware'));
138
+ await fs.ensureDir(path.join(srcPath, 'validators'));
139
+ await fs.ensureDir(path.join(srcPath, 'utils'));
140
+ await fs.ensureDir(path.join(srcPath, 'config'));
141
+ await fs.ensureDir(path.join(srcPath, 'types'));
142
+ }
143
+
144
+ // Common directories for all structures
145
+ await fs.ensureDir(path.join(srcPath, 'tests'));
146
+ await fs.ensureDir(path.join(projectPath, 'logs'));
147
+
148
+ // Database specific setup
149
+ if (database === 'prisma') {
150
+ await fs.ensureDir(path.join(projectPath, 'prisma'));
151
+ await fs.ensureDir(path.join(projectPath, 'prisma', 'migrations'));
152
+ await fs.writeFile(path.join(projectPath, 'prisma', 'schema.prisma'), generatePrismaSchema());
153
+ }
154
+ }
155
+
156
+ async function generateExpressConfigFiles(projectPath, config) {
157
+ const { language, database } = config;
158
+ const srcPath = path.join(projectPath, 'src');
159
+ const isTypeScript = language === 'typescript';
160
+ const folderStructure = config.folderStructure || 'mvc';
161
+
162
+ // Determine the correct paths based on folder structure
163
+ const configPath =
164
+ folderStructure === 'feature-based'
165
+ ? path.join(srcPath, 'shared', 'config')
166
+ : path.join(srcPath, 'config');
167
+
168
+ const middlewarePath =
169
+ folderStructure === 'feature-based'
170
+ ? path.join(srcPath, 'shared', 'middleware')
171
+ : folderStructure === 'clean-architecture'
172
+ ? path.join(srcPath, 'presentation', 'middleware')
173
+ : path.join(srcPath, 'middleware');
174
+
175
+ // Ensure directories exist
176
+ await fs.ensureDir(configPath);
177
+ await fs.ensureDir(middlewarePath);
178
+
179
+ // TypeScript configuration
180
+ if (isTypeScript) {
181
+ await fs.writeFile(path.join(projectPath, 'tsconfig.json'), generateTsConfig());
182
+ }
183
+
184
+ // ESLint configuration
185
+ await fs.writeFile(
186
+ path.join(projectPath, 'eslint.config.js'),
187
+ generateEslintConfig(isTypeScript)
188
+ );
189
+
190
+ // Nodemon configuration
191
+ await fs.writeFile(path.join(projectPath, 'nodemon.json'), generateNodemonConfig(isTypeScript));
192
+
193
+ // Main app file
194
+ await fs.writeFile(
195
+ path.join(srcPath, isTypeScript ? 'app.ts' : 'app.js'),
196
+ generateAppFile(config)
197
+ );
198
+
199
+ // Server entry point
200
+ await fs.writeFile(
201
+ path.join(srcPath, isTypeScript ? 'server.ts' : 'server.js'),
202
+ generateServerFile(isTypeScript)
203
+ );
204
+
205
+ // Database configuration
206
+ if (database !== 'none') {
207
+ await fs.writeFile(
208
+ path.join(configPath, isTypeScript ? 'database.ts' : 'database.js'),
209
+ generateDatabaseConfig(database, isTypeScript)
210
+ );
211
+ }
212
+
213
+ // Error handler middleware
214
+ await fs.writeFile(
215
+ path.join(middlewarePath, isTypeScript ? 'errorHandler.ts' : 'errorHandler.js'),
216
+ generateErrorHandler(isTypeScript)
217
+ );
218
+
219
+ // CORS configuration
220
+ await fs.writeFile(
221
+ path.join(configPath, isTypeScript ? 'cors.ts' : 'cors.js'),
222
+ generateCorsConfig(isTypeScript)
223
+ );
224
+ }
225
+
226
+ async function generateExpressPackageJson(projectPath, config) {
227
+ const { language, database, projectName } = config;
228
+ const isTypeScript = language === 'typescript';
229
+
230
+ const spinner = ora('Fetching latest package versions...').start();
231
+
232
+ try {
233
+ const scripts = {
234
+ dev: isTypeScript ? 'nodemon' : 'nodemon src/server.js',
235
+ build: isTypeScript ? 'tsc' : 'echo "No build step for JavaScript"',
236
+ start: isTypeScript ? 'node dist/server.js' : 'node src/server.js',
237
+ lint: 'eslint .',
238
+ 'lint:fix': 'eslint . --fix',
239
+ };
240
+
241
+ if (database === 'prisma') {
242
+ scripts['db:generate'] = 'prisma generate';
243
+ scripts['db:push'] = 'prisma db push';
244
+ scripts['db:migrate'] = 'prisma migrate dev';
245
+ scripts['db:studio'] = 'prisma studio';
246
+ }
247
+
248
+ // Fetch core dependencies
249
+ const [expressVer, dotenvVer, corsVer, helmetVer, nodemonVer] = await Promise.all([
250
+ fetchVersion('express'),
251
+ fetchVersion('dotenv'),
252
+ fetchVersion('cors'),
253
+ fetchVersion('helmet'),
254
+ fetchVersion('nodemon'),
255
+ ]);
256
+
257
+ const dependencies = {
258
+ express: expressVer,
259
+ dotenv: dotenvVer,
260
+ cors: corsVer,
261
+ helmet: helmetVer,
262
+ };
263
+
264
+ const devDependencies = {
265
+ nodemon: nodemonVer,
266
+ };
267
+
268
+ // TypeScript dependencies
269
+ if (isTypeScript) {
270
+ const [tsVer, typesExpressVer, typesNodeVer, typesCorsVer, tsNodeVer] = await Promise.all([
271
+ fetchVersion('typescript'),
272
+ fetchVersion('@types/express'),
273
+ fetchVersion('@types/node'),
274
+ fetchVersion('@types/cors'),
275
+ fetchVersion('ts-node'),
276
+ ]);
277
+ devDependencies['typescript'] = tsVer;
278
+ devDependencies['@types/express'] = typesExpressVer;
279
+ devDependencies['@types/node'] = typesNodeVer;
280
+ devDependencies['@types/cors'] = typesCorsVer;
281
+ devDependencies['ts-node'] = tsNodeVer;
282
+ }
283
+
284
+ // Database specific dependencies
285
+ if (database === 'prisma') {
286
+ const [prismaClientVer, prismaVer] = await Promise.all([
287
+ fetchVersion('@prisma/client'),
288
+ fetchVersion('prisma'),
289
+ ]);
290
+ dependencies['@prisma/client'] = prismaClientVer;
291
+ devDependencies['prisma'] = prismaVer;
292
+ } else if (database === 'mongodb') {
293
+ dependencies['mongoose'] = await fetchVersion('mongoose');
294
+ } else if (database === 'postgresql' || database === 'mysql') {
295
+ dependencies['pg'] = await fetchVersion('pg');
296
+ if (isTypeScript) {
297
+ devDependencies['@types/pg'] = await fetchVersion('@types/pg');
298
+ }
299
+ }
300
+
301
+ spinner.succeed(chalk.green('Fetched latest versions'));
302
+
303
+ const packageJson = {
304
+ name: projectName || 'express-api',
305
+ version: '1.0.0',
306
+ description: 'Express.js API server',
307
+ main: isTypeScript ? 'dist/server.js' : 'src/server.js',
308
+ type: 'module',
309
+ scripts,
310
+ keywords: ['express', 'api', 'backend'],
311
+ author: '',
312
+ license: 'MIT',
313
+ dependencies,
314
+ devDependencies,
315
+ };
316
+
317
+ await fs.writeFile(
318
+ path.join(projectPath, 'package.json'),
319
+ JSON.stringify(packageJson, null, 2)
320
+ );
321
+ } catch (error) {
322
+ spinner.fail(chalk.yellow('Could not fetch versions, using fallbacks'));
323
+
324
+ // Fallback with latest tag
325
+ const scripts = {
326
+ dev: isTypeScript ? 'nodemon' : 'nodemon src/server.js',
327
+ build: isTypeScript ? 'tsc' : 'echo "No build step for JavaScript"',
328
+ start: isTypeScript ? 'node dist/server.js' : 'node src/server.js',
329
+ lint: 'eslint .',
330
+ 'lint:fix': 'eslint . --fix',
331
+ };
332
+
333
+ if (database === 'prisma') {
334
+ scripts['db:generate'] = 'prisma generate';
335
+ scripts['db:push'] = 'prisma db push';
336
+ scripts['db:migrate'] = 'prisma migrate dev';
337
+ scripts['db:studio'] = 'prisma studio';
338
+ }
339
+
340
+ const dependencies = { express: 'latest', dotenv: 'latest', cors: 'latest', helmet: 'latest' };
341
+ const devDependencies = {
342
+ nodemon: 'latest',
343
+ ...(isTypeScript && {
344
+ typescript: 'latest',
345
+ '@types/express': 'latest',
346
+ '@types/node': 'latest',
347
+ '@types/cors': 'latest',
348
+ 'ts-node': 'latest',
349
+ }),
350
+ };
351
+
352
+ if (database === 'prisma') {
353
+ dependencies['@prisma/client'] = 'latest';
354
+ devDependencies['prisma'] = 'latest';
355
+ } else if (database === 'mongodb') {
356
+ dependencies['mongoose'] = 'latest';
357
+ } else if (database === 'postgresql' || database === 'mysql') {
358
+ dependencies['pg'] = 'latest';
359
+ if (isTypeScript) devDependencies['@types/pg'] = 'latest';
360
+ }
361
+
362
+ const packageJson = {
363
+ name: projectName || 'express-api',
364
+ version: '1.0.0',
365
+ description: 'Express.js API server',
366
+ main: isTypeScript ? 'dist/server.js' : 'src/server.js',
367
+ type: 'module',
368
+ scripts,
369
+ keywords: ['express', 'api', 'backend'],
370
+ author: '',
371
+ license: 'MIT',
372
+ dependencies,
373
+ devDependencies,
374
+ };
375
+
376
+ await fs.writeFile(
377
+ path.join(projectPath, 'package.json'),
378
+ JSON.stringify(packageJson, null, 2)
379
+ );
380
+ }
381
+ }
382
+
383
+ async function generateExpressReadme(projectPath, config) {
384
+ const { language, database, folderStructure } = config;
385
+ const isTypeScript = language === 'typescript';
386
+
387
+ const readme = `# ${config.projectName || 'Express API'}
388
+
389
+ Express.js backend API with ${language === 'typescript' ? 'TypeScript' : 'JavaScript'}
390
+
391
+ ## 📁 Folder Structure
392
+
393
+ This project uses **${folderStructure}** architecture.
394
+
395
+ ${getArchitectureDescription(folderStructure)}
396
+
397
+ ## 🚀 Getting Started
398
+
399
+ ### Prerequisites
400
+
401
+ - Node.js 18+ installed
402
+ ${database !== 'none' ? `- ${database} database` : ''}
403
+
404
+ ### Installation
405
+
406
+ \`\`\`bash
407
+ # Install dependencies
408
+ npm install
409
+
410
+ ${
411
+ database === 'prisma'
412
+ ? `# Generate Prisma Client
413
+ npm run db:generate
414
+
415
+ # Run database migrations
416
+ npm run db:migrate
417
+ `
418
+ : ''
419
+ }
420
+ \`\`\`
421
+
422
+ ### Environment Variables
423
+
424
+ Copy \`.env.example\` to \`.env\` and fill in your values:
425
+
426
+ \`\`\`bash
427
+ cp .env.example .env
428
+ \`\`\`
429
+
430
+ ### Development
431
+
432
+ \`\`\`bash
433
+ # Start development server with hot reload
434
+ npm run dev
435
+ \`\`\`
436
+
437
+ ${
438
+ isTypeScript
439
+ ? `### Build
440
+
441
+ \`\`\`bash
442
+ # Build TypeScript to JavaScript
443
+ npm run build
444
+
445
+ # Start production server
446
+ npm start
447
+ \`\`\`
448
+ `
449
+ : ''
450
+ }
451
+
452
+ ## 🔧 Available Scripts
453
+
454
+ - \`npm run dev\` - Start development server
455
+ - \`npm run build\` - Build for production
456
+ - \`npm start\` - Start production server
457
+ - \`npm run lint\` - Lint code
458
+ - \`npm run lint:fix\` - Fix linting issues
459
+
460
+ ${
461
+ database === 'prisma'
462
+ ? `
463
+ ## 💾 Database Commands
464
+
465
+ - \`npm run db:generate\` - Generate Prisma Client
466
+ - \`npm run db:push\` - Push schema changes
467
+ - \`npm run db:migrate\` - Run migrations
468
+ - \`npm run db:studio\` - Open Prisma Studio
469
+ `
470
+ : ''
471
+ }
472
+
473
+ ## 📝 License
474
+
475
+ MIT
476
+ `;
477
+
478
+ await fs.writeFile(path.join(projectPath, 'README.md'), readme);
479
+ }
480
+
481
+ async function generateEnvExample(projectPath, config) {
482
+ const { database } = config;
483
+
484
+ let envContent = `# Server Configuration
485
+ NODE_ENV=development
486
+ PORT=3000
487
+
488
+ # CORS Configuration
489
+ CORS_ORIGIN=http://localhost:5173
490
+
491
+ `;
492
+
493
+ if (database === 'prisma') {
494
+ envContent += `# Database Configuration (Prisma)
495
+ DATABASE_URL="postgresql://user:password@localhost:5432/mydb"
496
+ `;
497
+ } else if (database === 'mongodb') {
498
+ envContent += `# MongoDB Configuration
499
+ MONGODB_URI=mongodb://localhost:27017/mydb
500
+ `;
501
+ } else if (database === 'postgresql') {
502
+ envContent += `# PostgreSQL Configuration
503
+ DB_HOST=localhost
504
+ DB_PORT=5432
505
+ DB_NAME=mydb
506
+ DB_USER=postgres
507
+ DB_PASSWORD=password
508
+ `;
509
+ } else if (database === 'mysql') {
510
+ envContent += `# MySQL Configuration
511
+ DB_HOST=localhost
512
+ DB_PORT=3306
513
+ DB_NAME=mydb
514
+ DB_USER=root
515
+ DB_PASSWORD=password
516
+ `;
517
+ }
518
+
519
+ await fs.writeFile(path.join(projectPath, '.env.example'), envContent);
520
+ }
521
+
522
+ // Configuration file generators
523
+ function generatePrismaSchema() {
524
+ return `// This is your Prisma schema file,
525
+ // learn more about it in the docs: https://pris.ly/d/prisma-schema
526
+
527
+ generator client {
528
+ provider = "prisma-client-js"
529
+ }
530
+
531
+ datasource db {
532
+ provider = "postgresql"
533
+ url = env("DATABASE_URL")
534
+ }
535
+
536
+ // Add your models here
537
+ model User {
538
+ id String @id @default(cuid())
539
+ email String @unique
540
+ name String
541
+ createdAt DateTime @default(now())
542
+ updatedAt DateTime @updatedAt
543
+
544
+ @@map("users")
545
+ }
546
+ `;
547
+ }
548
+
549
+ function generateTsConfig() {
550
+ return JSON.stringify(
551
+ {
552
+ compilerOptions: {
553
+ target: 'ES2022',
554
+ module: 'ESNext',
555
+ lib: ['ES2022'],
556
+ moduleResolution: 'node',
557
+ rootDir: './src',
558
+ outDir: './dist',
559
+ strict: true,
560
+ esModuleInterop: true,
561
+ skipLibCheck: true,
562
+ forceConsistentCasingInFileNames: true,
563
+ resolveJsonModule: true,
564
+ declaration: true,
565
+ sourceMap: true,
566
+ types: ['node'],
567
+ },
568
+ include: ['src/**/*'],
569
+ exclude: ['node_modules', 'dist'],
570
+ },
571
+ null,
572
+ 2
573
+ );
574
+ }
575
+
576
+ function generateEslintConfig(isTypeScript) {
577
+ if (isTypeScript) {
578
+ return `import js from '@eslint/js';
579
+ import tsPlugin from '@typescript-eslint/eslint-plugin';
580
+ import tsParser from '@typescript-eslint/parser';
581
+
582
+ export default [
583
+ js.configs.recommended,
584
+ {
585
+ files: ['**/*.ts'],
586
+ languageOptions: {
587
+ parser: tsParser,
588
+ parserOptions: {
589
+ ecmaVersion: 'latest',
590
+ sourceType: 'module',
591
+ },
592
+ },
593
+ plugins: {
594
+ '@typescript-eslint': tsPlugin,
595
+ },
596
+ rules: {
597
+ ...tsPlugin.configs.recommended.rules,
598
+ '@typescript-eslint/no-explicit-any': 'warn',
599
+ '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
600
+ },
601
+ },
602
+ {
603
+ ignores: ['dist/', 'node_modules/', '*.config.js'],
604
+ },
605
+ ];
606
+ `;
607
+ }
608
+
609
+ return `import js from '@eslint/js';
610
+
611
+ export default [
612
+ js.configs.recommended,
613
+ {
614
+ rules: {
615
+ 'no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
616
+ },
617
+ },
618
+ {
619
+ ignores: ['node_modules/', '*.config.js'],
620
+ },
621
+ ];
622
+ `;
623
+ }
624
+
625
+ function generateNodemonConfig(isTypeScript) {
626
+ if (isTypeScript) {
627
+ return JSON.stringify(
628
+ {
629
+ watch: ['src'],
630
+ ext: 'ts',
631
+ exec: 'ts-node src/server.ts',
632
+ env: {
633
+ NODE_ENV: 'development',
634
+ },
635
+ },
636
+ null,
637
+ 2
638
+ );
639
+ }
640
+
641
+ return JSON.stringify(
642
+ {
643
+ watch: ['src'],
644
+ ext: 'js',
645
+ exec: 'node src/server.js',
646
+ env: {
647
+ NODE_ENV: 'development',
648
+ },
649
+ },
650
+ null,
651
+ 2
652
+ );
653
+ }
654
+
655
+ function generateAppFile(_config) {
656
+ return `import express from 'express';
657
+ import cors from 'cors';
658
+ import helmet from 'helmet';
659
+ import { corsOptions } from './config/cors';
660
+ import { errorHandler } from './middleware/errorHandler';
661
+ import routes from './routes';
662
+
663
+ const app = express();
664
+
665
+ // Security Middleware
666
+ app.use(helmet());
667
+ app.use(cors(corsOptions));
668
+
669
+ // Body Parser
670
+ app.use(express.json());
671
+ app.use(express.urlencoded({ extended: true }));
672
+
673
+ // API Routes
674
+ app.use('/api', routes);
675
+
676
+ // Root Route
677
+ app.get('/', (req, res) => {
678
+ res.json({
679
+ message: 'Welcome to the API',
680
+ version: '1.0.0',
681
+ endpoints: {
682
+ health: '/api/health',
683
+ },
684
+ });
685
+ });
686
+
687
+ // 404 Handler
688
+ app.use((req, res) => {
689
+ res.status(404).json({
690
+ success: false,
691
+ message: 'Route not found',
692
+ });
693
+ });
694
+
695
+ // Error Handler (must be last)
696
+ app.use(errorHandler);
697
+
698
+ export default app;
699
+ `;
700
+ }
701
+
702
+ function generateServerFile(isTypeScript) {
703
+ return `${isTypeScript ? "import 'dotenv/config';" : "import dotenv from 'dotenv';\ndotenv.config();"}
704
+ import app from './app';
705
+
706
+ const PORT = process.env.PORT || 3000;
707
+
708
+ const server = app.listen(PORT, () => {
709
+ console.log(\`🚀 Server running on http://localhost:\${PORT}\`);
710
+ console.log(\`📝 Environment: \${process.env.NODE_ENV || 'development'}\`);
711
+ });
712
+
713
+ // Graceful shutdown
714
+ process.on('SIGTERM', () => {
715
+ console.log('SIGTERM signal received: closing HTTP server');
716
+ server.close(() => {
717
+ console.log('HTTP server closed');
718
+ });
719
+ });
720
+ `;
721
+ }
722
+
723
+ function generateDatabaseConfig(database, isTypeScript) {
724
+ if (database === 'mongodb') {
725
+ return `import mongoose from 'mongoose';
726
+
727
+ export const connectDB = async () => {
728
+ try {
729
+ const conn = await mongoose.connect(process.env.MONGODB_URI${isTypeScript ? ' as string' : ''});
730
+ console.log(\`✅ MongoDB Connected: \${conn.connection.host}\`);
731
+ } catch (error) {
732
+ console.error(\`❌ MongoDB Error: \${error${isTypeScript ? '.message' : ''}}\`);
733
+ process.exit(1);
734
+ }
735
+ };
736
+
737
+ export default connectDB;
738
+ `;
739
+ }
740
+
741
+ if (database === 'prisma') {
742
+ return `import { PrismaClient } from '@prisma/client';
743
+
744
+ const prisma = new PrismaClient();
745
+
746
+ export const connectDB = async () => {
747
+ try {
748
+ await prisma.$connect();
749
+ console.log('✅ Database connected successfully');
750
+ } catch (error) {
751
+ console.error('❌ Database connection failed:', error);
752
+ process.exit(1);
753
+ }
754
+ };
755
+
756
+ export const disconnectDB = async () => {
757
+ await prisma.$disconnect();
758
+ };
759
+
760
+ export default prisma;
761
+ `;
762
+ }
763
+
764
+ return `// Database configuration
765
+ // TODO: Implement database connection
766
+
767
+ export const connectDB = async () => {
768
+ console.log('✅ Database connected');
769
+ };
770
+ `;
771
+ }
772
+
773
+ function generateErrorHandler(isTypeScript) {
774
+ return `import { Request, Response, NextFunction } from 'express';
775
+
776
+ export const errorHandler = (
777
+ err${isTypeScript ? ': any' : ''},
778
+ req${isTypeScript ? ': Request' : ''},
779
+ res${isTypeScript ? ': Response' : ''},
780
+ next${isTypeScript ? ': NextFunction' : ''}
781
+ ) => {
782
+ const statusCode = err.statusCode || 500;
783
+ const message = err.message || 'Internal Server Error';
784
+
785
+ res.status(statusCode).json({
786
+ success: false,
787
+ message,
788
+ ...(process.env.NODE_ENV === 'development' && {
789
+ stack: err.stack,
790
+ }),
791
+ });
792
+ };
793
+ `;
794
+ }
795
+
796
+ function generateCorsConfig(isTypeScript) {
797
+ return `import { CorsOptions } from 'cors';
798
+
799
+ export const corsOptions${isTypeScript ? ': CorsOptions' : ''} = {
800
+ origin: (origin${isTypeScript ? ': string | undefined' : ''}, callback${isTypeScript ? ': (err: Error | null, allow?: boolean) => void' : ''}) => {
801
+ const allowedOrigins = [
802
+ 'http://localhost:3000',
803
+ 'http://localhost:5173',
804
+ process.env.CORS_ORIGIN,
805
+ ].filter(Boolean);
806
+
807
+ if (!origin || allowedOrigins.includes(origin)) {
808
+ callback(null, true);
809
+ } else {
810
+ callback(new Error('Not allowed by CORS'));
811
+ }
812
+ },
813
+ credentials: true,
814
+ optionsSuccessStatus: 200,
815
+ methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
816
+ allowedHeaders: ['Content-Type', 'Authorization'],
817
+ };
818
+ `;
819
+ }
820
+
821
+ function generateRoutesIndex() {
822
+ return `import { Router } from 'express';
823
+
824
+ const router = Router();
825
+
826
+ // Health check
827
+ router.get('/health', (req, res) => {
828
+ res.json({
829
+ status: 'OK',
830
+ timestamp: new Date().toISOString()
831
+ });
832
+ });
833
+
834
+ // Add your routes here
835
+ // Example: router.use('/users', userRoutes);
836
+
837
+ export default router;
838
+ `;
839
+ }
840
+
841
+ function getArchitectureDescription(structure) {
842
+ const descriptions = {
843
+ mvc: `
844
+ **Model-View-Controller (MVC)**
845
+
846
+ - \`models/\` - Data models and database schemas
847
+ - \`views/\` - Template rendering (if needed)
848
+ - \`controllers/\` - Request handlers and business logic
849
+ - \`routes/\` - API route definitions
850
+ - \`middleware/\` - Custom middleware functions
851
+ - \`utils/\` - Utility functions and helpers
852
+ - \`config/\` - Configuration files
853
+
854
+ **Flow:** Route → Controller → Model → Controller → Response
855
+ `,
856
+ 'clean-architecture': `
857
+ **Clean Architecture (Onion Architecture)**
858
+
859
+ - \`domain/\` - Business entities and rules (core layer)
860
+ - \`entities/\` - Business objects
861
+ - \`repositories/\` - Repository interfaces
862
+ - \`use-cases/\` - Application business rules
863
+
864
+ - \`application/\` - Application business rules
865
+ - \`dto/\` - Data transfer objects
866
+ - \`services/\` - Application services
867
+ - \`interfaces/\` - Application interfaces
868
+
869
+ - \`infrastructure/\` - External concerns
870
+ - \`database/\` - Database implementations
871
+ - \`repositories/\` - Repository implementations
872
+ - \`external-services/\` - Third-party integrations
873
+
874
+ - \`presentation/\` - UI layer (HTTP)
875
+ - \`controllers/\` - HTTP request handlers
876
+ - \`middleware/\` - HTTP middleware
877
+ - \`routes/\` - Route definitions
878
+
879
+ **Dependencies flow inward:** Presentation → Application → Domain
880
+ `,
881
+ 'feature-based': `
882
+ **Feature-based Architecture**
883
+
884
+ Each feature is self-contained with its own:
885
+ - \`controllers/\` - Feature-specific controllers
886
+ - \`services/\` - Feature business logic
887
+ - \`routes/\` - Feature routes
888
+ - \`models/\` - Feature data models
889
+ - \`middleware/\` - Feature-specific middleware
890
+ - \`validators/\` - Input validation
891
+
892
+ **Benefits:** Easy to understand, scale, and maintain. Each feature can be developed independently.
893
+ `,
894
+ layered: `
895
+ **Layered Architecture**
896
+
897
+ - \`controllers/\` - Presentation layer (HTTP handlers)
898
+ - \`services/\` - Business logic layer
899
+ - \`repositories/\` - Data access layer
900
+ - \`models/\` - Data models
901
+ - \`routes/\` - Route definitions
902
+ - \`middleware/\` - Middleware functions
903
+ - \`validators/\` - Input validation
904
+ - \`utils/\` - Utility functions
905
+ - \`config/\` - Configuration files
906
+
907
+ **Flow:** Controller → Service → Repository → Database
908
+ Each layer only communicates with adjacent layers.
909
+ `,
910
+ };
911
+
912
+ return descriptions[structure] || 'Custom architecture';
913
+ }
914
+
915
+ export default generateExpressTemplate;