UNPKG

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