UNPKG

agentsqripts

Version:

Comprehensive static code analysis toolkit for identifying technical debt, security vulnerabilities, performance issues, and code quality problems

152 lines (125 loc) 5.29 kB
/** * @file Project complexity analyzer * @description Handles project-level complexity analysis and metrics calculation */ const fs = require('fs'); const path = require('path'); const { analyzeCodeComplexity } = require('../../lib/code-complexity/analyzeCodeComplexity'); const qerrors = require('qerrors'); async function analyzeProject(targetPath, options) { const projectAnalysis = { projectPath: targetPath, totalFiles: 0, analyzedFiles: 0, fileAnalyses: [], overallComplexity: 'LOW', averageComplexityScore: 0, averageMaintainabilityIndex: 0, complexityBreakdown: { low: 0, medium: 0, high: 0, critical: 0 }, topComplexFiles: [], recommendations: [] }; await analyzeDirectory(targetPath, projectAnalysis, options); calculateProjectMetrics(projectAnalysis); return projectAnalysis; } async function analyzeFileAsync(filePath, projectAnalysis, options) { try { const fileAnalysis = await analyzeCodeComplexity(filePath, options); projectAnalysis.analyzedFiles++; projectAnalysis.fileAnalyses.push(fileAnalysis); // Update breakdown counters projectAnalysis.complexityBreakdown[fileAnalysis.complexity.toLowerCase()]++; } catch (error) { // Log error but continue analysis qerrors.default(error, 'Failed to analyze file complexity', { file: filePath }); } } async function analyzeDirectory(dirPath, projectAnalysis, options) { const items = await fs.promises.readdir(dirPath, { withFileTypes: true }); const promises = []; items.forEach(item => { const fullPath = path.join(dirPath, item.name); if (item.isDirectory() && !shouldSkipDirectory(item.name)) { promises.push(analyzeDirectory(fullPath, projectAnalysis, options)); } else if (item.isFile() && shouldAnalyzeFile(item.name)) { projectAnalysis.totalFiles++; promises.push(analyzeFileAsync(fullPath, projectAnalysis, options)); } }); await Promise.all(promises); } function calculateProjectMetrics(projectAnalysis) { if (projectAnalysis.fileAnalyses.length === 0) return; // Calculate averages in single pass to avoid O(n²) complexity let totalComplexity = 0; let totalMaintainability = 0; projectAnalysis.fileAnalyses.forEach(file => { totalComplexity += file.metrics.cyclomaticComplexity; totalMaintainability += file.maintainabilityIndex; }); projectAnalysis.averageComplexityScore = totalComplexity / projectAnalysis.fileAnalyses.length; projectAnalysis.averageMaintainabilityIndex = totalMaintainability / projectAnalysis.fileAnalyses.length; // Determine overall complexity const { complexityBreakdown } = projectAnalysis; if (complexityBreakdown.critical > 0) { projectAnalysis.overallComplexity = 'CRITICAL'; } else if (complexityBreakdown.high > 0) { projectAnalysis.overallComplexity = 'HIGH'; } else if (complexityBreakdown.medium > complexityBreakdown.low) { projectAnalysis.overallComplexity = 'MEDIUM'; } else { projectAnalysis.overallComplexity = 'LOW'; } // Get top complex files projectAnalysis.topComplexFiles = projectAnalysis.fileAnalyses .sort((a, b) => b.metrics.cyclomaticComplexity - a.metrics.cyclomaticComplexity) .slice(0, 10); // Generate project-level recommendations projectAnalysis.recommendations = generateProjectComplexityRecommendations(projectAnalysis); } function generateProjectComplexityRecommendations(projectAnalysis) { const recommendations = []; if (projectAnalysis.overallComplexity === 'CRITICAL' || projectAnalysis.overallComplexity === 'HIGH') { recommendations.push({ priority: 'HIGH', type: 'OVERALL_COMPLEXITY', title: 'High Project Complexity', description: 'Project has high overall complexity that may impact maintainability', action: 'Focus on refactoring the most complex files and functions' }); } if (projectAnalysis.averageMaintainabilityIndex < 50) { recommendations.push({ priority: 'MEDIUM', type: 'MAINTAINABILITY', title: 'Low Maintainability Index', description: `Average maintainability index is ${Math.round(projectAnalysis.averageMaintainabilityIndex)}`, action: 'Break down large functions and reduce complexity' }); } const highComplexityCount = projectAnalysis.complexityBreakdown.high + projectAnalysis.complexityBreakdown.critical; if (highComplexityCount > projectAnalysis.analyzedFiles * 0.2) { recommendations.push({ priority: 'HIGH', type: 'WIDESPREAD_COMPLEXITY', title: 'Widespread Complexity Issues', description: `${highComplexityCount} files have high or critical complexity`, action: 'Implement coding standards and complexity limits' }); } return recommendations; } function shouldSkipDirectory(dirName) { const skipPatterns = ['node_modules', '.git', 'dist', 'build', '.next', 'coverage']; return skipPatterns.includes(dirName) || dirName.startsWith('.'); } function shouldAnalyzeFile(fileName) { const extensions = ['.js', '.ts', '.jsx', '.tsx', '.vue', '.py', '.java', '.cpp', '.cs']; return extensions.some(ext => fileName.endsWith(ext)); } module.exports = { analyzeProject, shouldSkipDirectory, shouldAnalyzeFile };