agentsqripts
Version:
Comprehensive static code analysis toolkit for identifying technical debt, security vulnerabilities, performance issues, and code quality problems
208 lines (173 loc) • 6.82 kB
JavaScript
/**
* @file Project performance analyzer with cross-file analysis
* @description Enhanced project analyzer that includes cross-file pattern detection
*/
const { analyzeFilePerformance } = require('./performanceFileAnalyzerAST');
const { analyzeCrossFilePatterns } = require('./crossFileAnalyzer');
const fs = require('fs');
const path = require('path');
/**
* Analyzes performance patterns across an entire project
* @param {string} projectPath - Root path to analyze
* @param {Object} options - Analysis options
* @returns {Promise<Object>} Project performance analysis results
*/
async function analyzeProjectPerformance(projectPath, options = {}) {
const excludePatterns = options.excludePatterns || ['node_modules', '.git', 'dist', 'build'];
console.log(`🔍 Analyzing project performance: ${projectPath}`);
// Get all files to analyze
const files = await getAllFiles(projectPath, excludePatterns);
const jsFiles = files.filter(file => /\.(js|jsx|ts|tsx)$/.test(file));
console.log(`📁 Files to analyze: ${files.length} (${jsFiles.length} JS files)`);
// Analyze individual files
const fileResults = [];
let totalIssues = 0;
let totalEffort = 0;
const categoryBreakdown = {};
const severityBreakdown = { CRITICAL: 0, HIGH: 0, MEDIUM: 0, LOW: 0 };
// Use Promise.all for parallel analysis (but limit concurrency)
const batchSize = 10;
for (let i = 0; i < files.length; i += batchSize) {
const batch = files.slice(i, i + batchSize);
const batchResults = await Promise.all(
batch.map(async (file) => {
try {
const result = await analyzeFilePerformance(file);
return result;
} catch (error) {
console.warn(`Warning: Could not analyze ${file}: ${error.message}`);
return { file, issues: [], performanceScore: 100, error: error.message };
}
})
);
fileResults.push(...batchResults);
}
// Analyze cross-file patterns
console.log(`🔗 Analyzing cross-file patterns...`);
let crossFileIssues = [];
try {
crossFileIssues = analyzeCrossFilePatterns(projectPath, { excludePatterns });
} catch (error) {
console.warn(`Warning: Cross-file analysis failed: ${error.message}`);
}
// Aggregate results
const allIssues = [];
const fileMetrics = [];
fileResults.forEach(result => {
if (result.issues && result.issues.length > 0) {
allIssues.push(...result.issues);
totalIssues += result.issues.length;
totalEffort += result.metrics?.totalEffort || 0;
// Update category breakdown
Object.entries(result.metrics?.categoryBreakdown || {}).forEach(([category, count]) => {
categoryBreakdown[category] = (categoryBreakdown[category] || 0) + count;
});
// Update severity breakdown
Object.entries(result.metrics?.severityBreakdown || {}).forEach(([severity, count]) => {
severityBreakdown[severity] = (severityBreakdown[severity] || 0) + count;
});
}
fileMetrics.push({
file: result.file,
score: result.performanceScore,
grade: result.metrics?.grade,
issues: result.issues?.length || 0,
effort: result.metrics?.totalEffort || 0
});
});
// Add cross-file issues
if (crossFileIssues.length > 0) {
allIssues.push(...crossFileIssues);
totalIssues += crossFileIssues.length;
crossFileIssues.forEach(issue => {
categoryBreakdown[issue.category] = (categoryBreakdown[issue.category] || 0) + 1;
severityBreakdown[issue.severity] = (severityBreakdown[issue.severity] || 0) + 1;
totalEffort += issue.effort || 1;
});
}
// Calculate overall project score
const filesWithIssues = fileResults.filter(r => r.issues && r.issues.length > 0).length;
const avgScore = fileResults.reduce((sum, r) => sum + (r.performanceScore || 100), 0) / fileResults.length;
// Penalty for cross-file issues
const crossFilePenalty = Math.min(crossFileIssues.length * 2, 20);
const projectScore = Math.max(0, Math.round(avgScore - crossFilePenalty));
const grade = getGradeFromScore(projectScore);
// Sort issues by priority
allIssues.sort((a, b) => {
const severityOrder = { CRITICAL: 4, HIGH: 3, MEDIUM: 2, LOW: 1 };
return (severityOrder[b.severity] || 0) - (severityOrder[a.severity] || 0);
});
// Get high priority issues (CRITICAL and HIGH)
const highPriorityIssues = allIssues.filter(issue =>
issue.severity === 'CRITICAL' || issue.severity === 'HIGH'
);
console.log(`✅ Analysis complete: ${totalIssues} issues found across ${files.length} files`);
return {
projectPath,
totalFiles: files.length,
analyzedFiles: fileResults.length,
filesWithIssues,
performanceScore: projectScore,
grade,
totalIssues,
highPriorityIssues: highPriorityIssues.length,
totalEffort,
crossFileIssues: crossFileIssues.length,
categoryBreakdown,
severityBreakdown,
issues: allIssues,
fileMetrics,
crossFileAnalysis: {
enabled: true,
issuesFound: crossFileIssues.length,
patterns: crossFileIssues
}
};
}
/**
* Get all files in directory recursively (async version)
*/
async function getAllFiles(dirPath, excludePatterns = []) {
const files = [];
async function scanDirectory(currentPath) {
try {
const entries = await fs.promises.readdir(currentPath, { withFileTypes: true });
const promises = [];
for (const entry of entries) {
const fullPath = path.join(currentPath, entry.name);
const relativePath = path.relative(dirPath, fullPath);
// Skip excluded patterns
if (excludePatterns.some(pattern => relativePath.includes(pattern))) {
continue;
}
if (entry.isDirectory()) {
promises.push(scanDirectory(fullPath));
} else if (entry.isFile()) {
// Only analyze text files that could contain performance issues
const ext = path.extname(entry.name).toLowerCase();
const supportedExts = ['.js', '.jsx', '.ts', '.tsx', '.vue', '.py', '.java', '.cpp', '.c', '.cs', '.go', '.rs', '.php'];
if (supportedExts.includes(ext)) {
files.push(fullPath);
}
}
}
// Process subdirectories in parallel
await Promise.all(promises);
} catch (error) {
console.warn(`Warning: Could not scan directory ${currentPath}: ${error.message}`);
}
}
await scanDirectory(dirPath);
return files;
}
const { getLetterGrade } = require('../utils/gradeUtils');
/**
* Convert score to grade
*/
function getGradeFromScore(score) {
return getLetterGrade(score);
}
module.exports = {
analyzeProjectPerformance,
getAllFiles
};