UNPKG

@paulohenriquevn/m2js

Version:

Transform TypeScript/JavaScript code into LLM-friendly Markdown summaries + Smart Dead Code Detection + Graph-Deep Diff Analysis. Extract exported functions, classes, and JSDoc comments for better AI context with 60%+ token reduction. Intelligent dead cod

319 lines (311 loc) 15.8 kB
"use strict"; /** * CLI Integration for Dead Code Analysis * Handles --detect-unused flag and output formatting */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.executeDeadCodeAnalysis = executeDeadCodeAnalysis; exports.getDeadCodeHelpText = getDeadCodeHelpText; const chalk_1 = __importDefault(require("chalk")); const dead_code_analyzer_1 = require("./dead-code-analyzer"); const config_loader_1 = require("./config-loader"); /** * Execute dead code analysis and output results */ async function executeDeadCodeAnalysis(files, options = { format: 'table', includeMetrics: true, includeSuggestions: true, }) { try { console.log(chalk_1.default.cyan.bold('Dead Code Analysis Report')); console.log(chalk_1.default.dim(`Analyzing ${files.length} files...\n`)); // Load configuration from .m2jsrc and environment variables const config = config_loader_1.ConfigLoader.loadConfig(); const performanceOptions = config_loader_1.ConfigLoader.toPerformanceOptions(config); // Override showProgress based on file count if not explicitly set if (files.length <= 10 && !process.env.M2JS_SHOW_PROGRESS) { performanceOptions.showProgress = false; } const report = await (0, dead_code_analyzer_1.analyzeDeadCode)(files, performanceOptions); if (options.format === 'json') { outputJsonFormat(report); } else { outputTableFormat(report, options); } // Exit with code 0 (informational, not error) process.exit(0); } catch (error) { console.error(chalk_1.default.red(`Error: ${error.message}`)); process.exit(1); } } /** * Output report in table format (default) */ function outputTableFormat(report, options) { const { deadExports, unusedImports, suggestions, metrics, projectPath } = report; // Project info console.log(chalk_1.default.dim(`Project: ${projectPath}`)); if (options.includeMetrics) { console.log(chalk_1.default.dim(`Files analyzed: ${metrics.totalFiles}`)); console.log(chalk_1.default.dim(`Analysis time: ${metrics.analysisTimeMs}ms\n`)); } // Dead exports section if (deadExports.length === 0 && unusedImports.length === 0) { console.log(chalk_1.default.green.bold('Great! No dead code found!')); console.log(chalk_1.default.green('All exports and imports are being used.\n')); } else { console.log(chalk_1.default.red.bold(`Dead Exports (${deadExports.length} found):`)); console.log(chalk_1.default.gray('┌─────────────────────────────────────────────────┐')); deadExports.forEach((deadExport, index) => { const isLast = index === deadExports.length - 1; const fileLocation = chalk_1.default.yellow(`${deadExport.file}:${deadExport.line}`); const exportName = chalk_1.default.white.bold(deadExport.name); const exportType = chalk_1.default.cyan(`${deadExport.type}`); const reason = chalk_1.default.dim(deadExport.reason); const confidenceColor = getConfidenceColor(deadExport.confidence); const confidence = confidenceColor(`[${deadExport.confidence.toUpperCase()}]`); console.log(chalk_1.default.gray('│ ') + fileLocation + ' ' + confidence); console.log(chalk_1.default.gray('│ ') + chalk_1.default.gray('└─ ') + exportType + ' ' + exportName); console.log(chalk_1.default.gray('│ ') + reason); // Show risk factors if any if (deadExport.riskFactors.length > 0) { console.log(chalk_1.default.gray('│ ') + chalk_1.default.red('Risk factors:')); deadExport.riskFactors.forEach(risk => { console.log(chalk_1.default.gray('│ • ') + chalk_1.default.red(risk)); }); } if (!isLast) { console.log(chalk_1.default.gray('│')); } }); console.log(chalk_1.default.gray('└─────────────────────────────────────────────────┘\n')); // Unused imports section if (unusedImports.length > 0) { console.log(chalk_1.default.yellow.bold(`Unused Imports (${unusedImports.length} found):`)); console.log(chalk_1.default.gray('┌─────────────────────────────────────────────────┐')); unusedImports.forEach((unusedImport, index) => { const isLast = index === unusedImports.length - 1; const fileLocation = chalk_1.default.yellow(`${unusedImport.file}:${unusedImport.line}`); const importName = chalk_1.default.white.bold(unusedImport.name); const importFrom = chalk_1.default.cyan(`from '${unusedImport.from}'`); const reason = chalk_1.default.dim(unusedImport.reason); const confidenceColor = getConfidenceColor(unusedImport.confidence); const confidence = confidenceColor(`[${unusedImport.confidence.toUpperCase()}]`); console.log(chalk_1.default.gray('│ ') + fileLocation + ' ' + confidence); console.log(chalk_1.default.gray('│ ') + chalk_1.default.gray('└─ ') + importName + ' ' + importFrom); console.log(chalk_1.default.gray('│ ') + reason); // Show risk factors if any if (unusedImport.riskFactors.length > 0) { console.log(chalk_1.default.gray('│ ') + chalk_1.default.red('Risk factors:')); unusedImport.riskFactors.forEach(risk => { console.log(chalk_1.default.gray('│ • ') + chalk_1.default.red(risk)); }); } if (!isLast) { console.log(chalk_1.default.gray('│')); } }); console.log(chalk_1.default.gray('└─────────────────────────────────────────────────┘\n')); } } // Impact summary if (options.includeMetrics && (deadExports.length > 0 || unusedImports.length > 0)) { console.log(chalk_1.default.blue.bold('Impact Summary:')); // Count by type const typeCount = deadExports.reduce((acc, exp) => { acc[exp.type] = (acc[exp.type] || 0) + 1; return acc; }, {}); Object.entries(typeCount).forEach(([type, count]) => { const plural = count === 1 ? '' : 's'; console.log(chalk_1.default.blue(`• ${capitalize(type)}${plural}: ${count} unused`)); }); // Add unused imports to summary if (unusedImports.length > 0) { console.log(chalk_1.default.blue(`• Imports: ${unusedImports.length} unused`)); } console.log(chalk_1.default.blue(`• Estimated size: ~${metrics.estimatedSavingsKB}KB`)); // Show performance metrics if available if (metrics.performanceStats) { const perfStats = metrics.performanceStats; console.log(chalk_1.default.dim(`Cache: ${perfStats.cacheHits} hits, ${perfStats.cacheMisses} misses (${Math.round(perfStats.hitRate * 100)}% hit rate)`)); } console.log(); } // Removal suggestions section if (options.includeSuggestions !== false && suggestions && suggestions.length > 0) { console.log(chalk_1.default.blue.bold('Removal Suggestions:')); console.log(chalk_1.default.gray('┌─────────────────────────────────────────────────┐')); // Group suggestions by safety level const safeSuggestions = suggestions.filter(s => s.safety === 'safe'); const reviewSuggestions = suggestions.filter(s => s.safety === 'review-needed'); const riskySuggestions = suggestions.filter(s => s.safety === 'risky'); // Show safe suggestions first if (safeSuggestions.length > 0) { console.log(chalk_1.default.gray('│ ') + chalk_1.default.green.bold('SAFE TO REMOVE:')); safeSuggestions.slice(0, 5).forEach((suggestion, index) => { const isLast = index === Math.min(4, safeSuggestions.length - 1); const priorityIcon = getPriorityIcon(suggestion.priority); console.log(chalk_1.default.gray('│ ') + priorityIcon + ' ' + chalk_1.default.white(suggestion.action)); console.log(chalk_1.default.gray('│ ') + chalk_1.default.dim(`${suggestion.file}:${suggestion.line}`)); console.log(chalk_1.default.gray('│ ') + chalk_1.default.dim(suggestion.impact)); if (suggestion.command) { console.log(chalk_1.default.gray('│ ') + chalk_1.default.cyan(suggestion.command)); } if (!isLast) { console.log(chalk_1.default.gray('│')); } }); if (safeSuggestions.length > 5) { console.log(chalk_1.default.gray('│ ') + chalk_1.default.dim(`... and ${safeSuggestions.length - 5} more safe suggestions`)); } console.log(chalk_1.default.gray('│')); } // Show review-needed suggestions if (reviewSuggestions.length > 0) { console.log(chalk_1.default.gray('│ ') + chalk_1.default.yellow.bold('REVIEW BEFORE REMOVING:')); reviewSuggestions.slice(0, 3).forEach((suggestion, index) => { const isLast = index === Math.min(2, reviewSuggestions.length - 1); const priorityIcon = getPriorityIcon(suggestion.priority); console.log(chalk_1.default.gray('│ ') + priorityIcon + ' ' + chalk_1.default.white(suggestion.action)); console.log(chalk_1.default.gray('│ ') + chalk_1.default.dim(`${suggestion.file}:${suggestion.line}`)); if (suggestion.warnings && suggestion.warnings.length > 0) { suggestion.warnings.forEach(warning => { console.log(chalk_1.default.gray('│ ') + chalk_1.default.yellow('Warning: ') + chalk_1.default.yellow(warning)); }); } if (!isLast) { console.log(chalk_1.default.gray('│')); } }); if (reviewSuggestions.length > 3) { console.log(chalk_1.default.gray('│ ') + chalk_1.default.dim(`... and ${reviewSuggestions.length - 3} more suggestions to review`)); } console.log(chalk_1.default.gray('│')); } // Show risky suggestions (just count) if (riskySuggestions.length > 0) { console.log(chalk_1.default.gray('│ ') + chalk_1.default.red.bold('HIGH RISK:')); console.log(chalk_1.default.gray('│ ') + chalk_1.default.red(`${riskySuggestions.length} suggestions require careful analysis`)); console.log(chalk_1.default.gray('│ ') + chalk_1.default.red('Manual review strongly recommended')); } console.log(chalk_1.default.gray('└─────────────────────────────────────────────────┘\n')); } // Next steps guidance if (deadExports.length > 0 || unusedImports.length > 0) { console.log(chalk_1.default.blue.bold('Next Steps:')); const safeSuggestions = suggestions?.filter(s => s.safety === 'safe') || []; const reviewSuggestions = suggestions?.filter(s => s.safety === 'review-needed') || []; const riskySuggestions = suggestions?.filter(s => s.safety === 'risky') || []; if (safeSuggestions.length > 0) { console.log(chalk_1.default.blue('1. Start with safe-to-remove items (high confidence)')); console.log(chalk_1.default.blue('2. Use provided commands for quick removal')); } if (reviewSuggestions.length > 0) { console.log(chalk_1.default.blue('3. Review medium-risk items manually')); console.log(chalk_1.default.blue('4. Check external consumers and dynamic imports')); } if (riskySuggestions.length > 0) { console.log(chalk_1.default.blue('5. Analyze high-risk items very carefully')); console.log(chalk_1.default.blue('6. Consider keeping public API exports')); } } } /** * Output report in JSON format */ function outputJsonFormat(report) { console.log(JSON.stringify(report, null, 2)); } /** * Capitalize first letter of a string */ function capitalize(str) { return str.charAt(0).toUpperCase() + str.slice(1); } /** * Get color function for confidence level */ function getConfidenceColor(confidence) { switch (confidence) { case 'high': return chalk_1.default.green; case 'medium': return chalk_1.default.yellow; case 'low': return chalk_1.default.red; default: return chalk_1.default.gray; } } /** * Get priority icon for suggestions */ function getPriorityIcon(priority) { switch (priority) { case 'high': return chalk_1.default.red('HIGH'); case 'medium': return chalk_1.default.yellow('MED'); case 'low': return chalk_1.default.blue('LOW'); default: return '•'; } } /** * Get help text for dead code analysis */ function getDeadCodeHelpText() { return ` Dead Code Analysis: --detect-unused Detect unused exports and imports with confidence levels --format <type> Output format: table, json (default: table) Performance Options: Environment Variables: M2JS_CACHE_ENABLED=true Enable/disable parsing cache (default: true) M2JS_CACHE_SIZE=1000 Max files in cache (default: 1000) M2JS_CHUNK_SIZE=50 Files processed per chunk (default: 50) M2JS_SHOW_PROGRESS=true Show progress bar for large projects (default: true) M2JS_MAX_FILE_SIZE=10 Max file size in MB (default: 10) Configuration File (.m2jsrc): Create a .m2jsrc file in your project root for persistent settings. Run 'npx m2js --init-config' to generate an example configuration. Examples: # Basic dead code analysis m2js src/**/*.ts --detect-unused # Analyze specific file with JSON output m2js src/utils.ts --detect-unused --format json # Analyze entire project m2js . --detect-unused # Analyze with custom performance settings M2JS_CHUNK_SIZE=100 M2JS_SHOW_PROGRESS=true m2js src --detect-unused # Generate configuration file npx m2js --init-config Features: ✓ High-confidence removal suggestions with automated commands ! Medium-risk items flagged for manual review !! High-risk items with detailed warnings Performance metrics and caching for large codebases Smart detection of public APIs, frameworks, and side-effects `; } //# sourceMappingURL=dead-code-cli.js.map