agentsqripts
Version:
Comprehensive static code analysis toolkit for identifying technical debt, security vulnerabilities, performance issues, and code quality problems
183 lines (163 loc) • 6.68 kB
JavaScript
/**
* @file Cleanup analysis orchestrator for code maintenance optimization
* @description Single responsibility: Coordinate comprehensive cleanup opportunity detection
*
* This main interface orchestrates multiple specialized cleanup detectors to identify
* code maintenance opportunities including dead code, barrel files, and other cleanup
* patterns. It provides both file-level and project-level analysis with prioritized
* recommendations for code organization improvements.
*
* Design rationale:
* - Orchestrator pattern coordinates multiple specialized cleanup detectors
* - Atomic module imports maintain clear separation of concerns
* - Scoring system provides quantitative cleanup quality metrics
* - Unified interface abstracts complexity of individual detectors
*/
const fs = require('fs');
const { promises: fsPromises } = require('fs');
// Import directly from atomic modules
const { getAllFiles } = require('./fileSystemScanner');
const { calculateOpportunityDistribution } = require('./opportunityDistributionCalculator');
const { calculateProjectPriority } = require('./projectPriorityCalculator');
const { generateRecommendations } = require('./recommendationGenerator');
/**
* Analyze cleanup opportunities in a single file with comprehensive detection
*
* Technical function: Coordinates multiple cleanup detectors for file-level analysis
*
* Implementation rationale:
* - Async/await pattern handles file I/O efficiently
* - Dynamic imports of detectors enable lazy loading for performance
* - Scoring system provides quantitative metrics (0-100 scale)
* - Error handling maintains robustness across large codebases
*
* Cleanup detection strategy:
* - Barrel file detection identifies over-abstracted exports
* - Dead code detection finds unused functions and variables
* - Scoring system weights different cleanup opportunities by impact
* - Priority calculation guides refactoring effort allocation
*
* @param {string} filePath - Path to the file to analyze
* @param {Object} options - Analysis options
* @returns {Object} Cleanup analysis results
*/
async function analyzeFileCleanup(filePath, options = {}) {
try {
const content = await fsPromises.readFile(filePath, 'utf8');
// Import atomic functions
const { isBarrelFile } = require('./barrelFileDetector');
const { findDeadCode } = require('./deadCodeDetector');
const opportunities = [];
let cleanupScore = 100;
// Check for barrel file pattern
if (isBarrelFile(content)) {
opportunities.push({
type: 'barrel_file',
severity: 'MEDIUM',
description: 'File appears to be a barrel file - consider individual exports',
line: 1
});
cleanupScore -= 20;
}
// Check for dead code efficiently
const deadCodeIssues = findDeadCode(content);
const issueCount = deadCodeIssues.length;
for (let i = 0; i < issueCount; i++) {
const issue = deadCodeIssues[i];
opportunities.push({
type: 'dead_code',
severity: 'HIGH',
description: issue.description,
line: issue.line
});
}
cleanupScore -= issueCount * 15;
return {
file: filePath,
opportunities,
cleanupScore: Math.max(0, cleanupScore),
priority: cleanupScore < 50 ? 'HIGH' : cleanupScore < 75 ? 'MEDIUM' : 'LOW',
metrics: {
totalIssues: opportunities.length,
cleanupScore: Math.max(0, cleanupScore)
},
issues: opportunities
};
} catch (error) {
console.warn(`Warning: Could not analyze ${filePath}: ${error.message}`);
throw error;
}
}
/**
* Analyze cleanup opportunities across entire project
* @param {string} projectPath - Project directory path
* @param {Object} options - Analysis options
* @returns {Object} Project cleanup analysis
*/
async function analyzeProjectCleanup(projectPath, options = {}) {
const extensions = options.extensions || ['.js', '.ts', '.jsx', '.tsx'];
const excludePatterns = options.exclude || ['node_modules', '.git', 'dist', 'build'];
const files = await getAllFiles(projectPath, extensions, excludePatterns);
const results = [];
let totalOpportunities = 0;
let highPriorityFiles = 0;
for (const file of files) {
try {
const analysis = await analyzeFileCleanup(file, options);
results.push(analysis);
totalOpportunities += (analysis.opportunities ? analysis.opportunities.length : 0);
if (analysis.priority === 'HIGH') highPriorityFiles++;
} catch (error) {
console.warn(`Warning: Could not analyze ${file}: ${error.message}`);
}
}
// Calculate project-level metrics efficiently with single pass
let totalScore = 0;
let filesWithIssues = 0;
const issueBreakdown = { barrel_files: 0, dead_code: 0, orphaned_comments: 0 };
for (const result of results) {
totalScore += (result.cleanupScore || 100);
if (result.opportunities && result.opportunities.length > 0) {
filesWithIssues++;
// Count issue types efficiently without nested iteration
const opportunities = result.opportunities;
for (let i = 0; i < opportunities.length; i++) {
const type = opportunities[i].type;
if (type === 'barrel_file') issueBreakdown.barrel_files++;
else if (type === 'dead_code') issueBreakdown.dead_code++;
else if (type === 'orphaned_comment') issueBreakdown.orphaned_comments++;
}
}
}
const averageScore = results.length > 0 ? Math.round(totalScore / results.length) : 100;
const cleanupRate = Math.round(((files.length - filesWithIssues) / files.length) * 100);
const opportunityDistribution = calculateOpportunityDistribution(results);
const overallPriority = calculateProjectPriority(averageScore, highPriorityFiles / results.length, results.length);
const startTime = Date.now();
return {
summary: {
totalFiles: files.length,
filesAnalyzed: files.length,
totalIssues: totalOpportunities,
totalOpportunities,
filesWithIssues,
cleanupRate,
highPriorityFiles,
averageCleanupScore: averageScore,
overallPriority,
issueBreakdown,
severityBreakdown: opportunityDistribution.bySeverity || { HIGH: 0, MEDIUM: 0, LOW: 0 }
},
distribution: opportunityDistribution,
files: results,
fileResults: results,
recommendations: generateRecommendations(results, averageScore, highPriorityFiles),
analysisTime: Date.now() - startTime,
timestamp: Date.now()
};
}
// Export main functions
module.exports = {
analyzeFileCleanup,
analyzeProjectCleanup
};