UNPKG

vibe-code-build

Version:

Real-time code monitoring with teaching explanations, CLAUDE.md compliance checking, and interactive chat

402 lines (325 loc) â€ĸ 12.4 kB
import chalk from 'chalk'; import boxen from 'boxen'; export class CheckFormatter { constructor() { this.severityColors = { critical: chalk.bgRed.white.bold, high: chalk.red.bold, medium: chalk.yellow, low: chalk.gray, info: chalk.blue, passed: chalk.green, failed: chalk.red, warning: chalk.yellow, skipped: chalk.gray }; this.severityIcons = { critical: '🚨', high: '❌', medium: 'âš ī¸', low: '💡', info: 'â„šī¸', passed: '✅', failed: '❌', warning: 'âš ī¸', skipped: 'â­ī¸' }; } formatCheckResults(allResults) { const sections = []; sections.push(this.formatHeader()); sections.push(this.formatSummary(allResults)); for (const [category, results] of Object.entries(allResults)) { if (results) { sections.push(this.formatCategory(category, results)); } } sections.push(this.formatFooter(allResults)); return sections.filter(Boolean).join('\n\n'); } formatHeader() { const title = chalk.bold.cyan('🔍 Vibe Code Quality Report'); return boxen(title, { padding: 1, margin: 1, borderStyle: 'round', borderColor: 'cyan' }); } formatSummary(allResults) { const stats = this.calculateStats(allResults); const summaryLines = [ chalk.bold('📊 Summary'), '', `Total Checks: ${stats.totalChecks}`, `${this.severityIcons.passed} Passed: ${stats.passed}`, `${this.severityIcons.failed} Failed: ${stats.failed}`, `${this.severityIcons.warning} Warnings: ${stats.warnings}`, `${this.severityIcons.skipped} Skipped: ${stats.skipped}`, '', this.formatHealthScore(stats) ]; return boxen(summaryLines.join('\n'), { padding: 1, borderStyle: 'single', borderColor: this.getHealthColor(stats.healthScore) }); } formatCategory(category, results) { const categoryTitle = this.formatCategoryTitle(category); const sections = [chalk.bold(categoryTitle)]; if (results.formatResults && typeof results.formatResults === 'function') { sections.push(results.formatResults()); } else { for (const [check, result] of Object.entries(results)) { if (result && typeof result === 'object') { sections.push(this.formatCheckResult(check, result)); } } } return sections.join('\n'); } formatCheckResult(checkName, result) { const status = result.status || 'unknown'; const icon = this.severityIcons[status] || '❓'; const color = this.severityColors[status] || chalk.white; const lines = [ color(`${icon} ${this.formatCheckName(checkName)}: ${result.message || 'No message'}`) ]; // Special handling for SEO results with score and grade if (checkName === 'seo' && result.score !== undefined) { lines.push(this.formatSEOScore(result)); // Show category breakdown if (result.categories) { lines.push(chalk.bold(' 📊 Category Breakdown:')); Object.entries(result.categories).forEach(([category, data]) => { if (data && data.score !== undefined) { const catColor = this.getScoreColor(data.score); lines.push(catColor(` â€ĸ ${this.capitalize(category)}: ${data.score}/100 - ${data.message || ''}`)); } }); } } if (result.findings && result.findings.length > 0) { lines.push(this.formatFindings(result.findings.slice(0, 5))); if (result.findings.length > 5) { lines.push(chalk.gray(` ... and ${result.findings.length - 5} more`)); } } // Enhanced handling for SEO issues with explanations if (result.issues && result.issues.length > 0) { lines.push(this.formatSEOIssues(result.issues.slice(0, 5))); if (result.issues.length > 5) { lines.push(chalk.gray(` ... and ${result.issues.length - 5} more issues`)); } } if (result.recommendations && result.recommendations.length > 0) { if (checkName === 'seo' && result.recommendations[0].priority) { lines.push(this.formatSEORecommendations(result.recommendations.slice(0, 3))); } else { lines.push(chalk.gray(' 💡 Recommendations:')); result.recommendations.slice(0, 3).forEach(rec => { lines.push(chalk.gray(` â€ĸ ${rec}`)); }); } } if (result.summary) { lines.push(this.formatResultSummary(result.summary)); } return lines.join('\n'); } formatFindings(findings) { return findings.map(finding => { const severity = finding.severity || 'info'; const color = this.severityColors[severity] || chalk.white; const icon = this.severityIcons[severity] || 'â€ĸ'; if (finding.file && finding.line) { return color(` ${icon} ${finding.file}:${finding.line} - ${finding.description || finding.message || finding.type}`); } else if (finding.file) { return color(` ${icon} ${finding.file} - ${finding.description || finding.message || finding.type}`); } else { return color(` ${icon} ${finding.description || finding.message || finding.type || 'Issue found'}`); } }).join('\n'); } formatResultSummary(summary) { const lines = [' Summary:']; for (const [key, value] of Object.entries(summary)) { if (typeof value === 'number' && value > 0) { const severity = key.toLowerCase(); const color = this.severityColors[severity] || chalk.white; const icon = this.severityIcons[severity] || 'â€ĸ'; lines.push(color(` ${icon} ${key}: ${value}`)); } } return lines.join('\n'); } formatFooter(allResults) { const stats = this.calculateStats(allResults); const recommendations = this.getOverallRecommendations(allResults); const footerLines = []; if (recommendations.length > 0) { footerLines.push(chalk.bold('đŸŽ¯ Top Recommendations:')); recommendations.slice(0, 5).forEach((rec, i) => { footerLines.push(`${i + 1}. ${rec}`); }); } footerLines.push(''); footerLines.push(chalk.gray(`Generated at ${new Date().toLocaleString()}`)); return boxen(footerLines.join('\n'), { padding: 1, borderStyle: 'single', borderColor: 'gray' }); } calculateStats(allResults) { let totalChecks = 0; let passed = 0; let failed = 0; let warnings = 0; let skipped = 0; for (const category of Object.values(allResults)) { if (!category) continue; for (const result of Object.values(category)) { if (!result || typeof result !== 'object' || !result.status) continue; totalChecks++; switch (result.status) { case 'passed': passed++; break; case 'failed': failed++; break; case 'warning': warnings++; break; case 'skipped': skipped++; break; } } } const healthScore = totalChecks > 0 ? Math.round(((passed + skipped) / totalChecks) * 100) : 0; return { totalChecks, passed, failed, warnings, skipped, healthScore }; } formatHealthScore(stats) { const score = stats.healthScore; const color = this.getHealthColor(score); const emoji = score >= 90 ? '🌟' : score >= 70 ? '👍' : score >= 50 ? '😐' : 'âš ī¸'; return color(`${emoji} Health Score: ${score}%`); } getHealthColor(score) { if (score >= 90) return chalk.green; if (score >= 70) return chalk.yellow; if (score >= 50) return chalk.red; return chalk.red; } formatCategoryTitle(category) { const titles = { build: '🔨 Build & Compilation', dependencies: 'đŸ“Ļ Dependencies', claude: '🤖 CLAUDE.md Compliance', security: '🔒 Security', performance: '⚡ Performance & SEO' }; return titles[category] || category.charAt(0).toUpperCase() + category.slice(1); } formatCheckName(checkName) { return checkName .replace(/([A-Z])/g, ' $1') .replace(/^./, str => str.toUpperCase()) .trim(); } getOverallRecommendations(allResults) { const recommendations = []; if (allResults.security?.godMode?.status === 'failed') { recommendations.push('🚨 Critical: Remove god mode patterns from your code immediately'); } if (allResults.security?.secrets?.status === 'failed') { recommendations.push('🔐 Critical: Remove exposed secrets and add them to .gitignore'); } if (allResults.dependencies?.vulnerabilities?.status === 'failed') { recommendations.push('đŸ“Ļ Run "npm audit fix" to resolve dependency vulnerabilities'); } if (allResults.build?.build?.status === 'failed') { recommendations.push('🔨 Fix build errors before deploying'); } if (allResults.performance?.bundleSize?.totalSize > 10 * 1024 * 1024) { recommendations.push('📉 Reduce bundle size with code splitting and tree shaking'); } if (allResults.claude?.claudeMdExists?.status === 'failed') { recommendations.push('📝 Create a CLAUDE.md file to document AI guidelines'); } if (allResults.performance?.seo?.totalIssues > 5) { recommendations.push('🔍 Improve SEO by adding meta tags and alt text'); } return recommendations; } formatCompactResults(allResults) { const stats = this.calculateStats(allResults); const status = stats.failed > 0 ? '❌' : stats.warnings > 0 ? 'âš ī¸' : '✅'; return `${status} Checks: ${stats.passed}/${stats.totalChecks} passed | ${stats.failed} failed | ${stats.warnings} warnings`; } formatJsonResults(allResults) { const stats = this.calculateStats(allResults); return JSON.stringify({ timestamp: new Date().toISOString(), stats, results: allResults, recommendations: this.getOverallRecommendations(allResults) }, null, 2); } formatSEOScore(result) { const score = result.score || 0; const grade = result.grade || 'N/A'; const color = this.getScoreColor(score); return color.bold(` đŸŽ¯ SEO Score: ${score}/100 (${grade})`); } formatSEOIssues(issues) { const lines = [' 📋 Issues Found:']; issues.forEach(issue => { const severity = issue.severity || 'info'; const color = this.severityColors[severity] || chalk.white; const icon = this.severityIcons[severity] || 'â€ĸ'; lines.push(color(` ${icon} ${issue.message || issue.type}`)); if (issue.file) { lines.push(chalk.gray(` 📄 ${issue.file}`)); } if (issue.explanation) { lines.push(chalk.gray(` â„šī¸ ${issue.explanation}`)); } if (issue.recommendation) { lines.push(chalk.cyan(` 💡 ${issue.recommendation}`)); } if (issue.example) { lines.push(chalk.gray(` 📝 Example: ${issue.example.split('\n')[0]}...`)); } }); return lines.join('\n'); } formatSEORecommendations(recommendations) { const lines = [' đŸŽ¯ Priority Actions:']; recommendations.forEach((rec, index) => { const priorityColors = { critical: chalk.bgRed.white.bold, high: chalk.red.bold, medium: chalk.yellow, low: chalk.gray }; const color = priorityColors[rec.priority] || chalk.white; lines.push(color(` ${index + 1}. ${rec.title}`)); lines.push(chalk.gray(` ${rec.description}`)); if (rec.timeframe) { lines.push(chalk.green(` âąī¸ ${rec.timeframe}`)); } if (rec.impact) { lines.push(chalk.cyan(` 💡 Impact: ${rec.impact}`)); } }); return lines.join('\n'); } getScoreColor(score) { if (score >= 90) return chalk.green.bold; if (score >= 80) return chalk.green; if (score >= 70) return chalk.yellow; if (score >= 60) return chalk.yellow.dim; if (score >= 50) return chalk.red; return chalk.red.bold; } capitalize(str) { return str.charAt(0).toUpperCase() + str.slice(1); } }