#!/usr/bin/env node const { execSync } = require('child_process'); const path = require('path'); const fs = require('fs'); /** * Quality Gate - 质量门禁工具 * 执行 TypeScript 编译检查和 ESLint 检查 */ class QualityGate { constructor(nestjsBasePath) { this.nestjsBasePath = nestjsBasePath || '/Users/wanwu/Documents/wwjcloud/wwjcloud-nsetjs/wwjcloud-nest'; this.stats = { tsErrors: 0, eslintErrors: 0, eslintWarnings: 0, filesChecked: 0 }; } /** * 运行所有质量检查 */ async run() { console.log('🚦 启动 Quality Gate 检查...\n'); let passed = true; // TypeScript 编译检查 console.log('📝 第1阶段:TypeScript 编译检查...'); const tsResult = await this.checkTypeScript(); if (!tsResult) { passed = false; console.log(' ❌ TypeScript 编译检查失败\n'); } else { console.log(' ✅ TypeScript 编译检查通过\n'); } // ESLint 检查 console.log('📝 第2阶段:ESLint 代码规范检查...'); const eslintResult = await this.checkESLint(); if (!eslintResult) { passed = false; console.log(' ❌ ESLint 检查失败\n'); } else { console.log(' ✅ ESLint 检查通过\n'); } // 输出统计报告 this.printStats(); return passed; } /** * TypeScript 编译检查 */ async checkTypeScript() { try { console.log(' 🔍 检查 TypeScript 类型...'); // 运行 tsc --noEmit 进行类型检查 const result = execSync('npm run type-check', { cwd: this.nestjsBasePath, encoding: 'utf8', stdio: 'pipe' }); console.log(' ✅ TypeScript 类型检查通过'); return true; } catch (error) { this.stats.tsErrors++; if (error.stdout) { console.error(' ❌ TypeScript 错误:'); console.error(error.stdout); } if (error.stderr) { console.error(error.stderr); } return false; } } /** * ESLint 检查 */ async checkESLint() { try { console.log(' 🔍 检查代码规范...'); // 运行 ESLint const result = execSync('npm run lint', { cwd: this.nestjsBasePath, encoding: 'utf8', stdio: 'pipe' }); console.log(' ✅ ESLint 检查通过'); return true; } catch (error) { // ESLint 返回非零退出码表示有错误或警告 if (error.stdout) { const output = error.stdout; // 解析错误和警告数量 const errorMatch = output.match(/(\d+)\s+errors?/); const warningMatch = output.match(/(\d+)\s+warnings?/); if (errorMatch) { this.stats.eslintErrors = parseInt(errorMatch[1]); } if (warningMatch) { this.stats.eslintWarnings = parseInt(warningMatch[1]); } console.error(' ❌ ESLint 发现问题:'); console.error(output); // 如果只有警告,不算失败 return this.stats.eslintErrors === 0; } return false; } } /** * 检查单个文件 */ async checkFile(filePath) { console.log(` 🔍 检查文件: ${filePath}`); try { // 使用 tsc 检查单个文件 execSync(`npx tsc --noEmit ${filePath}`, { cwd: this.nestjsBasePath, encoding: 'utf8', stdio: 'pipe' }); // 使用 ESLint 检查单个文件 execSync(`npx eslint ${filePath}`, { cwd: this.nestjsBasePath, encoding: 'utf8', stdio: 'pipe' }); this.stats.filesChecked++; return true; } catch (error) { console.error(` ❌ 文件检查失败: ${filePath}`); if (error.stdout) { console.error(error.stdout); } return false; } } /** * 快速检查(只检查核心层) */ async quickCheck() { console.log('🚀 快速质量检查(仅核心层)...\n'); const coreFiles = this.getGeneratedFiles(); console.log(` 📁 发现 ${coreFiles.length} 个生成的文件\n`); let passed = 0; let failed = 0; for (const file of coreFiles) { const result = await this.checkFile(file); if (result) { passed++; } else { failed++; } } console.log(`\n📊 快速检查结果:`); console.log(` ✅ 通过: ${passed}`); console.log(` ❌ 失败: ${failed}`); return failed === 0; } /** * 获取所有生成的文件 */ getGeneratedFiles() { const coreDir = path.join(this.nestjsBasePath, 'src', 'core'); const files = []; const scanDir = (dir) => { if (!fs.existsSync(dir)) return; const entries = fs.readdirSync(dir, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(dir, entry.name); if (entry.isDirectory()) { scanDir(fullPath); } else if (entry.name.endsWith('.ts') && !entry.name.endsWith('.d.ts')) { files.push(fullPath); } } }; scanDir(coreDir); return files; } /** * 输出统计报告 */ printStats() { console.log('📊 Quality Gate 统计报告'); console.log('=================================================='); console.log(` 📝 TypeScript 错误: ${this.stats.tsErrors}`); console.log(` 📝 ESLint 错误: ${this.stats.eslintErrors}`); console.log(` ⚠️ ESLint 警告: ${this.stats.eslintWarnings}`); console.log(` 📁 检查文件数: ${this.stats.filesChecked}`); console.log('=================================================='); const passed = this.stats.tsErrors === 0 && this.stats.eslintErrors === 0; if (passed) { console.log('\n✅ 🎉 所有质量检查通过!'); } else { console.log('\n❌ 质量检查失败,请修复上述问题'); console.log('提示: 运行 "npm run lint:fix" 自动修复部分问题'); } return passed; } } // 如果直接运行此文件 if (require.main === module) { const args = process.argv.slice(2); const mode = args[0] || 'full'; const gate = new QualityGate(); if (mode === 'quick') { gate.quickCheck().then(passed => { process.exit(passed ? 0 : 1); }); } else { gate.run().then(passed => { process.exit(passed ? 0 : 1); }); } } module.exports = QualityGate;