UNPKG

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
"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