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
JavaScript
/**
* @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
};