agentsqripts
Version:
Comprehensive static code analysis toolkit for identifying technical debt, security vulnerabilities, performance issues, and code quality problems
145 lines (124 loc) • 4.29 kB
JavaScript
/**
* @file Static bug project analyzer
* @description Analyzes entire project for static bugs
*/
/**
* Analyze static bugs across a project
* @param {string} projectPath - Project directory path
* @param {Object} options - Analysis options
* @returns {Object} Project bug analysis results
*/
function analyzeProjectStaticBugs(projectPath, options = {}) {
const fs = require('fs');
const path = require('path');
const { analyzeFileStaticBugs } = require('./staticBugFileAnalyzer');
// Create a simple recommendation generator if none exists
function generateBugRecommendations(issues) {
if (!issues || issues.length === 0) {
return [];
}
const recommendations = [];
const categoryCounts = {};
issues.forEach(issue => {
categoryCounts[issue.category] = (categoryCounts[issue.category] || 0) + 1;
});
Object.entries(categoryCounts).forEach(([category, count]) => {
recommendations.push({
category,
count,
priority: count > 5 ? 'HIGH' : count > 2 ? 'MEDIUM' : 'LOW',
recommendation: `Fix ${count} ${category} issue${count > 1 ? 's' : ''}`
});
});
return recommendations.sort((a, b) => b.count - a.count);
}
try {
const files = getAllJSFiles(projectPath);
console.log(`Found ${files.length} JavaScript files to analyze`);
const fileAnalyses = [];
const allIssues = [];
let totalScore = 0;
files.forEach(file => {
try {
console.log(`Analyzing: ${file}`);
const analysis = analyzeFileStaticBugs(file);
fileAnalyses.push(analysis);
if (analysis.issues) {
allIssues.push(...analysis.issues);
}
totalScore += (analysis.qualityScore || 100);
} catch (error) {
console.log(`Error analyzing ${file}: ${error.message}`);
// Skip files that can't be analyzed but count them
fileAnalyses.push({
file,
qualityScore: 100,
issues: [],
error: error.message
});
totalScore += 100;
}
});
const averageScore = files.length > 0 ? Math.round(totalScore / files.length) : 100;
// Calculate breakdowns
const severityBreakdown = { HIGH: 0, MEDIUM: 0, LOW: 0 };
const categoryBreakdown = {};
let totalEffort = 0;
allIssues.forEach(issue => {
severityBreakdown[issue.severity] = (severityBreakdown[issue.severity] || 0) + 1;
categoryBreakdown[issue.category] = (categoryBreakdown[issue.category] || 0) + 1;
totalEffort += issue.effort || 1;
});
return {
summary: {
filesAnalyzed: fileAnalyses.length,
totalFiles: fileAnalyses.length,
totalIssues: allIssues.length,
totalEffort,
qualityScore: averageScore,
qualityGrade: getQualityGrade(averageScore),
highSeverity: allIssues.filter(i => i.severity === 'HIGH').length,
severityBreakdown,
categoryBreakdown
},
files: fileAnalyses,
fileResults: fileAnalyses,
issues: allIssues,
recommendations: generateBugRecommendations(allIssues)
};
} catch (error) {
console.error(`Project analysis error: ${error.message}`);
console.error(`Stack trace: ${error.stack}`);
throw error;
}
}
function getAllJSFiles(projectPath) {
const fs = require('fs');
const path = require('path');
const files = [];
const extensions = ['.js', '.ts', '.jsx', '.tsx'];
function traverse(dir) {
try {
const entries = fs.readdirSync(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory() && !entry.name.includes('node_modules') && !entry.name.startsWith('.')) {
traverse(fullPath);
} else if (extensions.includes(path.extname(fullPath))) {
files.push(fullPath);
}
}
} catch (error) {
console.error(`Error reading directory ${dir}: ${error.message}`);
}
}
traverse(projectPath);
return files;
}
const { getLetterGrade } = require('../utils/gradeUtils');
function getQualityGrade(score) {
return getLetterGrade(score);
}
module.exports = {
analyzeProjectStaticBugs
};