express-hale
Version:
🚀 Interactive Express.js scaffold CLI with comprehensive error handling, TypeScript/JavaScript, database integrations, Git Flow, and development tools
403 lines (354 loc) • 12 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TemplateRenderer = void 0;
const app_templates_1 = require("./templates/app-templates");
const config_templates_1 = require("./templates/config-templates");
const database_templates_1 = require("./templates/database-templates");
const middleware_templates_1 = require("./templates/middleware-templates");
class TemplateRenderer {
constructor() {
this.appTemplates = new app_templates_1.AppTemplates();
this.configTemplates = new config_templates_1.ConfigTemplates();
this.databaseTemplates = new database_templates_1.DatabaseTemplates();
this.middlewareTemplates = new middleware_templates_1.MiddlewareTemplates();
}
// App Templates
renderIndexFile(data) {
return this.appTemplates.renderIndexFile(data);
}
renderRouteFile(data) {
return this.appTemplates.renderRouteFile(data);
}
renderControllerFile(data) {
return this.appTemplates.renderControllerFile(data);
}
// Config Templates
renderNodemonConfig() {
return this.configTemplates.renderNodemonConfig();
}
renderEnvFile(config) {
return this.configTemplates.renderEnvFile(config);
}
renderEnvExampleFile(config) {
return this.configTemplates.renderEnvExampleFile(config);
}
renderTsConfig() {
return this.configTemplates.renderTsConfig();
}
renderEslintConfig(config) {
return this.configTemplates.renderEslintConfig(config);
}
renderPrettierConfig() {
return this.configTemplates.renderPrettierConfig();
}
renderJestConfig(config) {
return this.configTemplates.renderJestConfig(config);
}
renderGitignore() {
return this.configTemplates.renderGitignore();
}
renderReadme(projectName, config) {
return this.configTemplates.renderReadme(projectName, config);
}
// Database Templates
renderDatabaseConfig(database, language) {
return this.databaseTemplates.renderDatabaseConfig(database, language);
}
// Middleware Templates
renderErrorHandlerMiddleware(language) {
return this.middlewareTemplates.renderErrorHandlerMiddleware(language);
}
renderGracefulShutdownUtil(language) {
return this.middlewareTemplates.renderGracefulShutdownUtil(language);
}
renderErrorMonitoringConfig(language) {
return this.middlewareTemplates.renderErrorMonitoringConfig(language);
}
// Test Templates (简化版本,可以后续扩展)
renderTestFile(config) {
if (config.language === 'typescript') {
return `import request from 'supertest';
import app from '../src/index';
describe('App', () => {
it('should respond with 200 for health check', async () => {
const response = await request(app)
.get('/health')
.expect(200);
expect(response.body).toHaveProperty('status', 'OK');
expect(response.body).toHaveProperty('timestamp');
expect(response.body).toHaveProperty('uptime');
});
it('should respond with 404 for unknown routes', async () => {
await request(app)
.get('/unknown-route')
.expect(404);
});
});`;
}
else {
return `const request = require('supertest');
const app = require('../src/index');
describe('App', () => {
it('should respond with 200 for health check', async () => {
const response = await request(app)
.get('/health')
.expect(200);
expect(response.body).toHaveProperty('status', 'OK');
expect(response.body).toHaveProperty('timestamp');
expect(response.body).toHaveProperty('uptime');
});
it('should respond with 404 for unknown routes', async () => {
await request(app)
.get('/unknown-route')
.expect(404);
});
});`;
}
}
renderApiTestFile(config) {
if (config.language === 'typescript') {
return `import request from 'supertest';
import app from '../src/index';
describe('API Routes', () => {
describe('GET /api/users', () => {
it('should return users list', async () => {
const response = await request(app)
.get('/api/users')
.expect(200);
expect(response.body).toHaveProperty('status', 'success');
expect(response.body).toHaveProperty('data');
expect(response.body.data).toHaveProperty('users');
expect(Array.isArray(response.body.data.users)).toBe(true);
});
});
describe('POST /api/users', () => {
it('should create a new user', async () => {
const userData = {
name: 'Test User',
email: 'test@example.com'
};
const response = await request(app)
.post('/api/users')
.send(userData)
.expect(201);
expect(response.body).toHaveProperty('status', 'success');
expect(response.body).toHaveProperty('data');
expect(response.body.data.user).toHaveProperty('name', userData.name);
expect(response.body.data.user).toHaveProperty('email', userData.email);
});
it('should return 400 for missing required fields', async () => {
const response = await request(app)
.post('/api/users')
.send({})
.expect(400);
expect(response.body).toHaveProperty('status', 'fail');
expect(response.body).toHaveProperty('message');
});
});
describe('GET /api/error-test', () => {
it('should handle errors gracefully', async () => {
const response = await request(app)
.get('/api/error-test')
.expect(500);
expect(response.body).toHaveProperty('status', 'error');
expect(response.body).toHaveProperty('message');
});
});
});`;
}
else {
return `const request = require('supertest');
const app = require('../src/index');
describe('API Routes', () => {
describe('GET /api/users', () => {
it('should return users list', async () => {
const response = await request(app)
.get('/api/users')
.expect(200);
expect(response.body).toHaveProperty('status', 'success');
expect(response.body).toHaveProperty('data');
expect(response.body.data).toHaveProperty('users');
expect(Array.isArray(response.body.data.users)).toBe(true);
});
});
describe('POST /api/users', () => {
it('should create a new user', async () => {
const userData = {
name: 'Test User',
email: 'test@example.com'
};
const response = await request(app)
.post('/api/users')
.send(userData)
.expect(201);
expect(response.body).toHaveProperty('status', 'success');
expect(response.body).toHaveProperty('data');
expect(response.body.data.user).toHaveProperty('name', userData.name);
expect(response.body.data.user).toHaveProperty('email', userData.email);
});
it('should return 400 for missing required fields', async () => {
const response = await request(app)
.post('/api/users')
.send({})
.expect(400);
expect(response.body).toHaveProperty('status', 'fail');
expect(response.body).toHaveProperty('message');
});
});
describe('GET /api/error-test', () => {
it('should handle errors gracefully', async () => {
const response = await request(app)
.get('/api/error-test')
.expect(500);
expect(response.body).toHaveProperty('status', 'error');
expect(response.body).toHaveProperty('message');
});
});
});`;
}
}
// Docker Templates (简化版本)
renderDockerfile(config) {
return `FROM node:18-alpine
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production
# Copy source code
COPY . .
${config.language === 'typescript' ? '# Build TypeScript\nRUN npm run build\n' : ''}
# Create non-root user
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nodejs -u 1001
# Change ownership of the app directory
RUN chown -R nodejs:nodejs /app
USER nodejs
EXPOSE 3000
${config.language === 'typescript' ? 'CMD ["npm", "start"]' : 'CMD ["node", "src/index.js"]'}`;
}
renderDockerCompose(config) {
let content = `version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
depends_on:`;
const services = [];
if (config.databases.includes('mysql')) {
services.push('mysql');
}
if (config.databases.includes('mongodb')) {
services.push('mongodb');
}
if (config.databases.includes('redis')) {
services.push('redis');
}
if (services.length > 0) {
content += '\n - ' + services.join('\n - ');
}
else {
content = content.replace('depends_on:', '# No database dependencies');
}
if (config.databases.includes('mysql')) {
content += `
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_DATABASE: myapp
MYSQL_USER: user
MYSQL_PASSWORD: password
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql`;
}
if (config.databases.includes('mongodb')) {
content += `
mongodb:
image: mongo:6.0
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: password
MONGO_INITDB_DATABASE: myapp
ports:
- "27017:27017"
volumes:
- mongodb_data:/data/db`;
}
if (config.databases.includes('redis')) {
content += `
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data`;
}
// Add volumes section if needed
const volumes = [];
if (config.databases.includes('mysql'))
volumes.push('mysql_data:');
if (config.databases.includes('mongodb'))
volumes.push('mongodb_data:');
if (config.databases.includes('redis'))
volumes.push('redis_data:');
if (volumes.length > 0) {
content += `
volumes:
${volumes.join('\n ')}`;
}
return content;
}
// Git Flow Templates (简化版本)
renderGitFlowInfo() {
return `
## Git Flow 工作流
本项目使用 Git Flow 工作流来管理代码版本和发布流程。
### 分支说明
- **main**: 主分支,包含生产环境的稳定代码
- **develop**: 开发分支,包含最新的开发功能
- **feature/***: 功能分支,用于开发新功能
- **release/***: 发布分支,用于准备新版本发布
- **hotfix/***: 热修复分支,用于修复生产环境的紧急问题
### 常用命令
\`\`\`bash
# 开始新功能开发
git flow feature start <feature-name>
# 完成功能开发
git flow feature finish <feature-name>
# 开始发布准备
git flow release start <version>
# 完成发布
git flow release finish <version>
# 开始热修复
git flow hotfix start <hotfix-name>
# 完成热修复
git flow hotfix finish <hotfix-name>
\`\`\`
### 工作流程
1. 从 develop 分支创建 feature 分支开发新功能
2. 功能开发完成后合并回 develop 分支
3. 准备发布时从 develop 创建 release 分支
4. 发布完成后合并到 main 和 develop 分支
5. 如需紧急修复,从 main 创建 hotfix 分支
6. 修复完成后合并到 main 和 develop 分支
`;
}
renderGitFlowPackageScripts() {
return {
'git:feature': 'git flow feature start',
'git:feature:finish': 'git flow feature finish',
'git:release': 'git flow release start',
'git:release:finish': 'git flow release finish',
'git:hotfix': 'git flow hotfix start',
'git:hotfix:finish': 'git flow hotfix finish',
};
}
}
exports.TemplateRenderer = TemplateRenderer;
//# sourceMappingURL=template-renderer.js.map