build-app-with 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/LICENSE +21 -0
- package/README.md +240 -0
- package/bin/cli.js +18 -0
- package/index.js +3 -0
- package/package.json +80 -0
- package/src/__tests__/core/error-handler.test.js +99 -0
- package/src/__tests__/core/logger.test.js +93 -0
- package/src/__tests__/e2e/cli-integration.test.js +220 -0
- package/src/__tests__/e2e/framework-generation.test.js +249 -0
- package/src/__tests__/setup.js +70 -0
- package/src/config/package-mappings.js +110 -0
- package/src/constants/index.js +42 -0
- package/src/core/error-handler.js +89 -0
- package/src/core/logger.js +89 -0
- package/src/core/package-manager.js +114 -0
- package/src/create-app.js +90 -0
- package/src/generators/express/index.js +52 -0
- package/src/generators/express/project-generator.js +367 -0
- package/src/generators/express/prompts.js +74 -0
- package/src/generators/express/simple-generator.js +275 -0
- package/src/generators/express/templates/app.js +73 -0
- package/src/generators/express/templates/config/database.js +122 -0
- package/src/generators/express/templates/config.js +37 -0
- package/src/generators/express/templates/controllers.js +49 -0
- package/src/generators/express/templates/docker.js +72 -0
- package/src/generators/express/templates/middleware/errorHandler.js +49 -0
- package/src/generators/express/templates/middleware.js +59 -0
- package/src/generators/express/templates/models.js +77 -0
- package/src/generators/express/templates/package-json.js +55 -0
- package/src/generators/express/templates/readme.js +310 -0
- package/src/generators/express/templates/routes.js +36 -0
- package/src/generators/express/templates/server.js +59 -0
- package/src/generators/express/templates/services.js +55 -0
- package/src/generators/express/templates/tests.js +46 -0
- package/src/generators/express/templates/utils/logger.js +54 -0
- package/src/generators/fastify/index.js +46 -0
- package/src/generators/fastify/project-generator.js +373 -0
- package/src/generators/fastify/prompts.js +76 -0
- package/src/generators/fastify/templates/app.js +179 -0
- package/src/generators/fastify/templates/config.js +33 -0
- package/src/generators/fastify/templates/docker.js +73 -0
- package/src/generators/fastify/templates/models.js +77 -0
- package/src/generators/fastify/templates/package-json.js +57 -0
- package/src/generators/fastify/templates/plugins.js +38 -0
- package/src/generators/fastify/templates/readme.js +328 -0
- package/src/generators/fastify/templates/routes.js +32 -0
- package/src/generators/fastify/templates/server.js +71 -0
- package/src/generators/fastify/templates/services.js +50 -0
- package/src/generators/fastify/templates/tests.js +60 -0
- package/src/generators/nextjs/dependency-manager.js +99 -0
- package/src/generators/nextjs/file-generator.js +256 -0
- package/src/generators/nextjs/nextjs-generator.js +177 -0
- package/src/generators/nextjs/nextjs-project-generator.js +896 -0
- package/src/generators/nextjs/package-mappings.js +51 -0
- package/src/generators/nextjs/templates.js +272 -0
- package/src/generators/package-json-generator.js +117 -0
- package/src/generators/vite/components/CreditComponent.jsx +41 -0
- package/src/generators/vite/components/app-component.js +359 -0
- package/src/generators/vite/components/main-file.js +88 -0
- package/src/generators/vite/eslint-config-generator.js +20 -0
- package/src/generators/vite/file-generator.js +796 -0
- package/src/generators/vite/prettier-config-generator.js +10 -0
- package/src/generators/vite/structures/domain-driven-structure.js +465 -0
- package/src/generators/vite/structures/feature-based-structure.js +342 -0
- package/src/generators/vite/structures/simple-structure.js +62 -0
- package/src/generators/vite/styles/index-css.js +130 -0
- package/src/generators/vite/tailwind-config-generator.js +14 -0
- package/src/generators/vite/vite-config-generator.js +22 -0
- package/src/generators/vite/vite-project-generator.js +263 -0
- package/src/generators/vite-project-generator.js +136 -0
- package/src/prompts/index.js +262 -0
- package/src/types/index.js +113 -0
- package/src/utils/answer-helpers.js +24 -0
- package/src/utils/credits.js +192 -0
- package/src/utils/dependencies.js +25 -0
- package/src/utils/messages.js +27 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate middleware template
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export function generateMiddleware(projectPath, answers) {
|
|
6
|
+
const middlewareContent = `import { logger } from '../utils/logger.js';
|
|
7
|
+
|
|
8
|
+
export const requestLogger = (req, res, next) => {
|
|
9
|
+
const start = Date.now();
|
|
10
|
+
|
|
11
|
+
res.on('finish', () => {
|
|
12
|
+
const duration = Date.now() - start;
|
|
13
|
+
logger.info(\`\${req.method} \${req.url} - \${res.statusCode} - \${duration}ms\`);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
next();
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const notFound = (req, res, next) => {
|
|
20
|
+
const error = new Error(\`Not found - \${req.originalUrl}\`);
|
|
21
|
+
res.status(404);
|
|
22
|
+
next(error);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const errorHandler = (err, req, res, next) => {
|
|
26
|
+
let error = { ...err };
|
|
27
|
+
error.message = err.message;
|
|
28
|
+
|
|
29
|
+
// Log error
|
|
30
|
+
logger.error(err);
|
|
31
|
+
|
|
32
|
+
// Mongoose bad ObjectId
|
|
33
|
+
if (err.name === 'CastError') {
|
|
34
|
+
const message = 'Resource not found';
|
|
35
|
+
error = { message, statusCode: 404 };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Mongoose duplicate key
|
|
39
|
+
if (err.code === 11000) {
|
|
40
|
+
const message = 'Duplicate field value entered';
|
|
41
|
+
error = { message, statusCode: 400 };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Mongoose validation error
|
|
45
|
+
if (err.name === 'ValidationError') {
|
|
46
|
+
const message = Object.values(err.errors).map(val => val.message).join(', ');
|
|
47
|
+
error = { message, statusCode: 400 };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
res.status(error.statusCode || 500).json({
|
|
51
|
+
success: false,
|
|
52
|
+
error: error.message || 'Server Error',
|
|
53
|
+
...(process.env.NODE_ENV === 'development' && { stack: err.stack })
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
`;
|
|
57
|
+
|
|
58
|
+
return middlewareContent;
|
|
59
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate models template
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { DATABASES } from '../../../types/index.js';
|
|
6
|
+
|
|
7
|
+
export function generateModels(projectPath, answers) {
|
|
8
|
+
if (answers.database === DATABASES.MONGODB) {
|
|
9
|
+
return generateMongooseModels(answers);
|
|
10
|
+
} else if (answers.database === DATABASES.POSTGRESQL || answers.database === DATABASES.SQLITE) {
|
|
11
|
+
return generatePrismaModels(answers);
|
|
12
|
+
} else if (answers.database === DATABASES.MYSQL) {
|
|
13
|
+
return generateSequelizeModels(answers);
|
|
14
|
+
}
|
|
15
|
+
return '';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function generateMongooseModels(answers) {
|
|
19
|
+
return `import mongoose from 'mongoose';
|
|
20
|
+
|
|
21
|
+
const userSchema = new mongoose.Schema({
|
|
22
|
+
name: {
|
|
23
|
+
type: String,
|
|
24
|
+
required: true,
|
|
25
|
+
trim: true
|
|
26
|
+
},
|
|
27
|
+
email: {
|
|
28
|
+
type: String,
|
|
29
|
+
required: true,
|
|
30
|
+
unique: true,
|
|
31
|
+
lowercase: true,
|
|
32
|
+
trim: true
|
|
33
|
+
},
|
|
34
|
+
password: {
|
|
35
|
+
type: String,
|
|
36
|
+
required: true,
|
|
37
|
+
minlength: 6
|
|
38
|
+
}
|
|
39
|
+
}, {
|
|
40
|
+
timestamps: true
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
export const User = mongoose.model('User', userSchema);
|
|
44
|
+
`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function generatePrismaModels(answers) {
|
|
48
|
+
return `// Prisma schema will be generated in schema.prisma
|
|
49
|
+
// This is a placeholder for Prisma models
|
|
50
|
+
|
|
51
|
+
export const prisma = new PrismaClient();
|
|
52
|
+
`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function generateSequelizeModels(answers) {
|
|
56
|
+
return `import { DataTypes } from 'sequelize';
|
|
57
|
+
import { sequelize } from '../config/database.js';
|
|
58
|
+
|
|
59
|
+
export const User = sequelize.define('User', {
|
|
60
|
+
name: {
|
|
61
|
+
type: DataTypes.STRING,
|
|
62
|
+
allowNull: false
|
|
63
|
+
},
|
|
64
|
+
email: {
|
|
65
|
+
type: DataTypes.STRING,
|
|
66
|
+
allowNull: false,
|
|
67
|
+
unique: true
|
|
68
|
+
},
|
|
69
|
+
password: {
|
|
70
|
+
type: DataTypes.STRING,
|
|
71
|
+
allowNull: false
|
|
72
|
+
}
|
|
73
|
+
}, {
|
|
74
|
+
timestamps: true
|
|
75
|
+
});
|
|
76
|
+
`;
|
|
77
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate package.json for Express.js project
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { FRAMEWORKS, DATABASES, AUTH_STRATEGIES } from '../../../types/index.js';
|
|
6
|
+
|
|
7
|
+
export function generatePackageJson(answers) {
|
|
8
|
+
const basePackage = {
|
|
9
|
+
name: answers.projectName,
|
|
10
|
+
version: '1.0.0',
|
|
11
|
+
description: `A production-ready Express.js application - ${answers.projectName}`,
|
|
12
|
+
main: 'src/server.js',
|
|
13
|
+
type: 'module',
|
|
14
|
+
scripts: {
|
|
15
|
+
start: 'node src/server.js',
|
|
16
|
+
dev: 'nodemon src/server.js',
|
|
17
|
+
test: answers.includeTests ? 'jest' : undefined,
|
|
18
|
+
'test:watch': answers.includeTests ? 'jest --watch' : undefined,
|
|
19
|
+
'test:coverage': answers.includeTests ? 'jest --coverage' : undefined
|
|
20
|
+
},
|
|
21
|
+
keywords: [
|
|
22
|
+
'express',
|
|
23
|
+
'nodejs',
|
|
24
|
+
'api',
|
|
25
|
+
'backend',
|
|
26
|
+
'server',
|
|
27
|
+
'rest',
|
|
28
|
+
'production-ready'
|
|
29
|
+
],
|
|
30
|
+
author: 'Generated by build-app-with',
|
|
31
|
+
license: 'MIT',
|
|
32
|
+
engines: {
|
|
33
|
+
node: '>=18.0.0'
|
|
34
|
+
},
|
|
35
|
+
dependencies: {},
|
|
36
|
+
devDependencies: {}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Add database-specific scripts
|
|
40
|
+
if (answers.database === DATABASES.POSTGRESQL || answers.database === DATABASES.SQLITE) {
|
|
41
|
+
basePackage.scripts['db:generate'] = 'prisma generate';
|
|
42
|
+
basePackage.scripts['db:push'] = 'prisma db push';
|
|
43
|
+
basePackage.scripts['db:migrate'] = 'prisma migrate dev';
|
|
44
|
+
basePackage.scripts['db:studio'] = 'prisma studio';
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Remove undefined scripts
|
|
48
|
+
Object.keys(basePackage.scripts).forEach(key => {
|
|
49
|
+
if (basePackage.scripts[key] === undefined) {
|
|
50
|
+
delete basePackage.scripts[key];
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
return basePackage;
|
|
55
|
+
}
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate README for Express.js project
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { DATABASES, AUTH_STRATEGIES } from '../../../types/index.js';
|
|
6
|
+
import { generateCreditsSection } from '../../../utils/credits.js';
|
|
7
|
+
|
|
8
|
+
export function generateReadme(projectPath, answers) {
|
|
9
|
+
const selectedFeatures = Array.isArray(answers.features) ? answers.features : [];
|
|
10
|
+
const allFeatures = [...selectedFeatures];
|
|
11
|
+
if (answers.database && answers.database !== 'none') {
|
|
12
|
+
allFeatures.push(answers.database);
|
|
13
|
+
}
|
|
14
|
+
if (answers.authStrategy && answers.authStrategy !== 'none') {
|
|
15
|
+
allFeatures.push(answers.authStrategy);
|
|
16
|
+
}
|
|
17
|
+
if (answers.includeTests) {
|
|
18
|
+
allFeatures.push('jest');
|
|
19
|
+
}
|
|
20
|
+
if (answers.includeDocker) {
|
|
21
|
+
allFeatures.push('docker');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const credits = generateCreditsSection('express', allFeatures);
|
|
25
|
+
|
|
26
|
+
const readme = `# ${answers.projectName}
|
|
27
|
+
|
|
28
|
+
A production-ready Express.js application built with modern best practices.
|
|
29
|
+
|
|
30
|
+
## 🚀 Features
|
|
31
|
+
|
|
32
|
+
- ⚡ **Express.js** - Fast, unopinionated web framework
|
|
33
|
+
- 🔒 **Security** - Helmet, CORS, rate limiting
|
|
34
|
+
- 🗄️ **Database** - ${answers.database === 'none' ? 'No database' : answers.database} integration
|
|
35
|
+
- 🔐 **Authentication** - ${answers.authStrategy === 'none' ? 'No authentication' : answers.authStrategy} strategy
|
|
36
|
+
- 📝 **Logging** - Winston logger with structured logging
|
|
37
|
+
- ✅ **Validation** - Request validation and sanitization
|
|
38
|
+
- 🧪 **Testing** - Jest and Supertest setup
|
|
39
|
+
- 📚 **Documentation** - API documentation with Swagger
|
|
40
|
+
- 🐳 **Docker** - Production-ready containerization
|
|
41
|
+
|
|
42
|
+
## 🛠️ Tech Stack
|
|
43
|
+
|
|
44
|
+
${generateTechStack(answers)}
|
|
45
|
+
|
|
46
|
+
## 📁 Project Structure
|
|
47
|
+
|
|
48
|
+
\`\`\`
|
|
49
|
+
${answers.projectName}/
|
|
50
|
+
├── src/
|
|
51
|
+
│ ├── controllers/ # Route controllers
|
|
52
|
+
│ ├── services/ # Business logic
|
|
53
|
+
│ ├── models/ # Database models
|
|
54
|
+
│ ├── routes/ # API routes
|
|
55
|
+
│ ├── middleware/ # Custom middleware
|
|
56
|
+
│ ├── config/ # Configuration files
|
|
57
|
+
│ ├── utils/ # Utility functions
|
|
58
|
+
│ ├── app.js # Express app setup
|
|
59
|
+
│ └── server.js # Server entry point
|
|
60
|
+
├── tests/ # Test files
|
|
61
|
+
├── logs/ # Log files
|
|
62
|
+
├── .env.example # Environment variables example
|
|
63
|
+
├── .gitignore # Git ignore rules
|
|
64
|
+
├── package.json # Dependencies and scripts
|
|
65
|
+
└── README.md # This file
|
|
66
|
+
\`\`\`
|
|
67
|
+
|
|
68
|
+
## 🚀 Getting Started
|
|
69
|
+
|
|
70
|
+
### Prerequisites
|
|
71
|
+
|
|
72
|
+
- Node.js (v18 or higher)
|
|
73
|
+
- npm, yarn, or pnpm
|
|
74
|
+
${answers.database !== 'none' ? `- ${answers.database} database` : ''}
|
|
75
|
+
|
|
76
|
+
### Installation
|
|
77
|
+
|
|
78
|
+
1. **Clone the repository**
|
|
79
|
+
\`\`\`bash
|
|
80
|
+
git clone <your-repo-url>
|
|
81
|
+
cd ${answers.projectName}
|
|
82
|
+
\`\`\`
|
|
83
|
+
|
|
84
|
+
2. **Install dependencies**
|
|
85
|
+
\`\`\`bash
|
|
86
|
+
npm install
|
|
87
|
+
\`\`\`
|
|
88
|
+
|
|
89
|
+
3. **Set up environment variables**
|
|
90
|
+
\`\`\`bash
|
|
91
|
+
cp .env.example .env
|
|
92
|
+
# Edit .env with your configuration
|
|
93
|
+
\`\`\`
|
|
94
|
+
|
|
95
|
+
4. **Set up the database**
|
|
96
|
+
${generateDatabaseSetup(answers)}
|
|
97
|
+
|
|
98
|
+
5. **Start the development server**
|
|
99
|
+
\`\`\`bash
|
|
100
|
+
npm run dev
|
|
101
|
+
\`\`\`
|
|
102
|
+
|
|
103
|
+
The server will start on \`http://localhost:3000\`
|
|
104
|
+
|
|
105
|
+
## 📚 API Documentation
|
|
106
|
+
|
|
107
|
+
Once the server is running, you can access:
|
|
108
|
+
|
|
109
|
+
- **API Base URL**: \`http://localhost:3000/api\`
|
|
110
|
+
- **Health Check**: \`http://localhost:3000/health\`
|
|
111
|
+
${answers.features.includes('swagger') ? '- **API Documentation**: `http://localhost:3000/api-docs`' : ''}
|
|
112
|
+
|
|
113
|
+
## 🧪 Testing
|
|
114
|
+
|
|
115
|
+
\`\`\`bash
|
|
116
|
+
# Run all tests
|
|
117
|
+
npm test
|
|
118
|
+
|
|
119
|
+
# Run tests in watch mode
|
|
120
|
+
npm run test:watch
|
|
121
|
+
|
|
122
|
+
# Run tests with coverage
|
|
123
|
+
npm run test:coverage
|
|
124
|
+
\`\`\`
|
|
125
|
+
|
|
126
|
+
## 🐳 Docker
|
|
127
|
+
|
|
128
|
+
\`\`\`bash
|
|
129
|
+
# Build Docker image
|
|
130
|
+
docker build -t ${answers.projectName} .
|
|
131
|
+
|
|
132
|
+
# Run with Docker Compose
|
|
133
|
+
docker-compose up -d
|
|
134
|
+
\`\`\`
|
|
135
|
+
|
|
136
|
+
## 📝 Available Scripts
|
|
137
|
+
|
|
138
|
+
- \`npm start\` - Start production server
|
|
139
|
+
- \`npm run dev\` - Start development server with nodemon
|
|
140
|
+
- \`npm test\` - Run tests
|
|
141
|
+
- \`npm run test:watch\` - Run tests in watch mode
|
|
142
|
+
- \`npm run test:coverage\` - Run tests with coverage report
|
|
143
|
+
${answers.database === 'postgresql' || answers.database === 'sqlite' ? `
|
|
144
|
+
- \`npm run db:generate\` - Generate Prisma client
|
|
145
|
+
- \`npm run db:push\` - Push schema to database
|
|
146
|
+
- \`npm run db:migrate\` - Run database migrations
|
|
147
|
+
- \`npm run db:studio\` - Open Prisma Studio
|
|
148
|
+
` : ''}
|
|
149
|
+
|
|
150
|
+
## 🔧 Configuration
|
|
151
|
+
|
|
152
|
+
### Environment Variables
|
|
153
|
+
|
|
154
|
+
| Variable | Description | Default |
|
|
155
|
+
|----------|-------------|---------|
|
|
156
|
+
| \`PORT\` | Server port | \`3000\` |
|
|
157
|
+
| \`NODE_ENV\` | Environment | \`development\` |
|
|
158
|
+
${generateEnvVars(answers)}
|
|
159
|
+
|
|
160
|
+
## 🚀 Deployment
|
|
161
|
+
|
|
162
|
+
### Production Checklist
|
|
163
|
+
|
|
164
|
+
- [ ] Set \`NODE_ENV=production\`
|
|
165
|
+
- [ ] Configure proper database connection
|
|
166
|
+
- [ ] Set up logging and monitoring
|
|
167
|
+
- [ ] Configure reverse proxy (nginx)
|
|
168
|
+
- [ ] Set up SSL certificates
|
|
169
|
+
- [ ] Configure environment variables
|
|
170
|
+
- [ ] Set up process manager (PM2)
|
|
171
|
+
- [ ] Configure backup strategy
|
|
172
|
+
|
|
173
|
+
### PM2 Configuration
|
|
174
|
+
|
|
175
|
+
\`\`\`bash
|
|
176
|
+
# Install PM2 globally
|
|
177
|
+
npm install -g pm2
|
|
178
|
+
|
|
179
|
+
# Start application with PM2
|
|
180
|
+
pm2 start src/server.js --name "${answers.projectName}"
|
|
181
|
+
|
|
182
|
+
# Save PM2 configuration
|
|
183
|
+
pm2 save
|
|
184
|
+
pm2 startup
|
|
185
|
+
\`\`\`
|
|
186
|
+
|
|
187
|
+
## 🤝 Contributing
|
|
188
|
+
|
|
189
|
+
1. Fork the repository
|
|
190
|
+
2. Create your feature branch (\`git checkout -b feature/amazing-feature\`)
|
|
191
|
+
3. Commit your changes (\`git commit -m 'Add some amazing feature'\`)
|
|
192
|
+
4. Push to the branch (\`git push origin feature/amazing-feature\`)
|
|
193
|
+
5. Open a Pull Request
|
|
194
|
+
|
|
195
|
+
## 📄 License
|
|
196
|
+
|
|
197
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
198
|
+
|
|
199
|
+
## 🙏 Acknowledgments
|
|
200
|
+
|
|
201
|
+
- **Express.js Team** - For the amazing web framework
|
|
202
|
+
- **Node.js Community** - For the incredible ecosystem
|
|
203
|
+
- **MongoDB/PostgreSQL/MySQL Teams** - For the robust databases
|
|
204
|
+
- **Jest Team** - For the testing framework
|
|
205
|
+
- **Winston Team** - For the logging library
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
${credits}
|
|
210
|
+
`;
|
|
211
|
+
|
|
212
|
+
return readme;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function generateTechStack(answers) {
|
|
216
|
+
const stack = ['- **Node.js** - JavaScript runtime'];
|
|
217
|
+
|
|
218
|
+
if (answers.database !== 'none') {
|
|
219
|
+
stack.push(`- **Database** - ${answers.database}`);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (answers.authStrategy !== 'none') {
|
|
223
|
+
stack.push(`- **Authentication** - ${answers.authStrategy}`);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (answers.features.includes('cors')) {
|
|
227
|
+
stack.push('- **CORS** - Cross-origin resource sharing');
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (answers.features.includes('helmet')) {
|
|
231
|
+
stack.push('- **Helmet** - Security headers');
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (answers.features.includes('morgan')) {
|
|
235
|
+
stack.push('- **Morgan** - HTTP request logger');
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (answers.features.includes('winston')) {
|
|
239
|
+
stack.push('- **Winston** - Logging library');
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (answers.features.includes('swagger')) {
|
|
243
|
+
stack.push('- **Swagger** - API documentation');
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (answers.includeTests) {
|
|
247
|
+
stack.push('- **Jest** - Testing framework');
|
|
248
|
+
stack.push('- **Supertest** - HTTP assertion library');
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return stack.join('\n');
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function generateDatabaseSetup(answers) {
|
|
255
|
+
if (answers.database === 'mongodb') {
|
|
256
|
+
return ` \`\`\`bash
|
|
257
|
+
# Start MongoDB (if using local instance)
|
|
258
|
+
mongod
|
|
259
|
+
\`\`\``;
|
|
260
|
+
} else if (answers.database === 'postgresql') {
|
|
261
|
+
return ` \`\`\`bash
|
|
262
|
+
# Generate Prisma client
|
|
263
|
+
npm run db:generate
|
|
264
|
+
|
|
265
|
+
# Push schema to database
|
|
266
|
+
npm run db:push
|
|
267
|
+
\`\`\``;
|
|
268
|
+
} else if (answers.database === 'mysql') {
|
|
269
|
+
return ` \`\`\`bash
|
|
270
|
+
# Create MySQL database
|
|
271
|
+
mysql -u root -p -e "CREATE DATABASE ${answers.projectName};"
|
|
272
|
+
\`\`\``;
|
|
273
|
+
} else if (answers.database === 'sqlite') {
|
|
274
|
+
return ` \`\`\`bash
|
|
275
|
+
# Generate Prisma client
|
|
276
|
+
npm run db:generate
|
|
277
|
+
|
|
278
|
+
# Push schema to database
|
|
279
|
+
npm run db:push
|
|
280
|
+
\`\`\``;
|
|
281
|
+
}
|
|
282
|
+
return '';
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function generateEnvVars(answers) {
|
|
286
|
+
const vars = [];
|
|
287
|
+
|
|
288
|
+
if (answers.database === 'mongodb') {
|
|
289
|
+
vars.push(`| \`MONGODB_URI\` | MongoDB connection string | \`mongodb://localhost:27017/${answers.projectName}\` |`);
|
|
290
|
+
} else if (answers.database === 'postgresql' || answers.database === 'sqlite') {
|
|
291
|
+
vars.push('| `DATABASE_URL` | Database connection string | `postgresql://...` |');
|
|
292
|
+
} else if (answers.database === 'mysql') {
|
|
293
|
+
vars.push('| `DB_HOST` | MySQL host | `localhost` |');
|
|
294
|
+
vars.push('| `DB_PORT` | MySQL port | `3306` |');
|
|
295
|
+
vars.push(`| \`DB_NAME\` | MySQL database name | \`${answers.projectName}\` |`);
|
|
296
|
+
vars.push('| `DB_USER` | MySQL username | `root` |');
|
|
297
|
+
vars.push('| `DB_PASSWORD` | MySQL password | `password` |');
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (answers.authStrategy !== 'none') {
|
|
301
|
+
vars.push('| `JWT_SECRET` | JWT secret key | `your-secret-key` |');
|
|
302
|
+
vars.push('| `JWT_EXPIRES_IN` | JWT expiration time | `7d` |');
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (answers.features.includes('cors')) {
|
|
306
|
+
vars.push('| `CORS_ORIGIN` | CORS allowed origin | `http://localhost:3000` |');
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return vars.join('\n');
|
|
310
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate routes template
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export function generateRoutes(projectPath, answers) {
|
|
6
|
+
const routesContent = `import express from 'express';
|
|
7
|
+
import { logger } from '../utils/logger.js';
|
|
8
|
+
|
|
9
|
+
const router = express.Router();
|
|
10
|
+
|
|
11
|
+
// Health check route
|
|
12
|
+
router.get('/health', (req, res) => {
|
|
13
|
+
res.json({
|
|
14
|
+
status: 'OK',
|
|
15
|
+
timestamp: new Date().toISOString(),
|
|
16
|
+
service: '${answers.projectName}'
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// API routes
|
|
21
|
+
router.get('/', (req, res) => {
|
|
22
|
+
res.json({
|
|
23
|
+
message: 'Welcome to ${answers.projectName} API',
|
|
24
|
+
version: '1.0.0',
|
|
25
|
+
endpoints: {
|
|
26
|
+
health: '/health',
|
|
27
|
+
api: '/api'
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export default router;
|
|
33
|
+
`;
|
|
34
|
+
|
|
35
|
+
return routesContent;
|
|
36
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate server.js file
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { DATABASES } from '../../../types/index.js';
|
|
6
|
+
|
|
7
|
+
export function generateServerJs(projectPath, answers) {
|
|
8
|
+
let content = `import app from './app.js';
|
|
9
|
+
import { logger } from './utils/logger.js';
|
|
10
|
+
import { connectDatabase } from './config/database.js';
|
|
11
|
+
|
|
12
|
+
const PORT = process.env.PORT || 3000;
|
|
13
|
+
const NODE_ENV = process.env.NODE_ENV || 'development';
|
|
14
|
+
|
|
15
|
+
async function startServer() {
|
|
16
|
+
try {
|
|
17
|
+
// Connect to database
|
|
18
|
+
await connectDatabase();
|
|
19
|
+
|
|
20
|
+
// Start server
|
|
21
|
+
app.listen(PORT, () => {
|
|
22
|
+
logger.info(\`🚀 Server running on port \${PORT} in \${NODE_ENV} mode\`);
|
|
23
|
+
logger.info(\`📊 Health check: http://localhost:\${PORT}/health\`);
|
|
24
|
+
logger.info(\`🔗 API endpoint: http://localhost:\${PORT}/api\`);
|
|
25
|
+
});
|
|
26
|
+
} catch (error) {
|
|
27
|
+
logger.error('Failed to start server:', error);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Handle uncaught exceptions
|
|
33
|
+
process.on('uncaughtException', (error) => {
|
|
34
|
+
logger.error('Uncaught Exception:', error);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Handle unhandled promise rejections
|
|
39
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
40
|
+
logger.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Graceful shutdown
|
|
45
|
+
process.on('SIGTERM', () => {
|
|
46
|
+
logger.info('SIGTERM received, shutting down gracefully');
|
|
47
|
+
process.exit(0);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
process.on('SIGINT', () => {
|
|
51
|
+
logger.info('SIGINT received, shutting down gracefully');
|
|
52
|
+
process.exit(0);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
startServer();
|
|
56
|
+
`;
|
|
57
|
+
|
|
58
|
+
return content;
|
|
59
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate services template
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export function generateServices(projectPath, answers) {
|
|
6
|
+
const servicesContent = `import { logger } from '../utils/logger.js';
|
|
7
|
+
|
|
8
|
+
export class BaseService {
|
|
9
|
+
static async handleServiceCall(serviceName, operation, data = null) {
|
|
10
|
+
try {
|
|
11
|
+
logger.info(\`Service: \${serviceName} - Operation: \${operation}\`);
|
|
12
|
+
|
|
13
|
+
// Add your business logic here
|
|
14
|
+
const result = await this.performOperation(operation, data);
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
success: true,
|
|
18
|
+
data: result
|
|
19
|
+
};
|
|
20
|
+
} catch (error) {
|
|
21
|
+
logger.error(\`Service error in \${serviceName}:\`, error);
|
|
22
|
+
throw error;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
static async performOperation(operation, data) {
|
|
27
|
+
// Implement your business logic here
|
|
28
|
+
return { operation, data };
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export class HealthService extends BaseService {
|
|
33
|
+
static async getHealthStatus() {
|
|
34
|
+
return this.handleServiceCall('HealthService', 'getStatus', {
|
|
35
|
+
timestamp: new Date().toISOString(),
|
|
36
|
+
uptime: process.uptime(),
|
|
37
|
+
memory: process.memoryUsage(),
|
|
38
|
+
service: '${answers.projectName}'
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export class ApiService extends BaseService {
|
|
44
|
+
static async getApiInfo() {
|
|
45
|
+
return this.handleServiceCall('ApiService', 'getInfo', {
|
|
46
|
+
message: 'Welcome to ${answers.projectName} API',
|
|
47
|
+
version: '1.0.0',
|
|
48
|
+
features: ['REST API', 'Health Check', 'Error Handling']
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
`;
|
|
53
|
+
|
|
54
|
+
return servicesContent;
|
|
55
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate tests template
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export function generateTests(projectPath, answers) {
|
|
6
|
+
const testsContent = `import request from 'supertest';
|
|
7
|
+
import app from '../app.js';
|
|
8
|
+
|
|
9
|
+
describe('${answers.projectName} API', () => {
|
|
10
|
+
describe('GET /health', () => {
|
|
11
|
+
it('should return health status', async () => {
|
|
12
|
+
const response = await request(app)
|
|
13
|
+
.get('/health')
|
|
14
|
+
.expect(200);
|
|
15
|
+
|
|
16
|
+
expect(response.body).toHaveProperty('status', 'OK');
|
|
17
|
+
expect(response.body).toHaveProperty('timestamp');
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe('GET /api', () => {
|
|
22
|
+
it('should return API information', async () => {
|
|
23
|
+
const response = await request(app)
|
|
24
|
+
.get('/api')
|
|
25
|
+
.expect(200);
|
|
26
|
+
|
|
27
|
+
expect(response.body).toHaveProperty('message');
|
|
28
|
+
expect(response.body).toHaveProperty('version');
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe('404 handling', () => {
|
|
33
|
+
it('should return 404 for unknown routes', async () => {
|
|
34
|
+
const response = await request(app)
|
|
35
|
+
.get('/unknown-route')
|
|
36
|
+
.expect(404);
|
|
37
|
+
|
|
38
|
+
expect(response.body).toHaveProperty('success', false);
|
|
39
|
+
expect(response.body).toHaveProperty('error');
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
`;
|
|
44
|
+
|
|
45
|
+
return testsContent;
|
|
46
|
+
}
|