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

301 lines 11.1 kB
"use strict"; /** * Safe Removal Suggestions Engine for M2JS * Generates intelligent removal suggestions with confidence levels */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.generateRemovalSuggestions = generateRemovalSuggestions; exports.assessExportRemovalRisk = assessExportRemovalRisk; exports.assessImportRemovalRisk = assessImportRemovalRisk; const path_1 = __importDefault(require("path")); /** * Generate safe removal suggestions for dead code */ function generateRemovalSuggestions(deadExports, unusedImports, projectPath) { const suggestions = []; // Generate suggestions for dead exports deadExports.forEach((deadExport, index) => { const suggestion = createExportRemovalSuggestion(deadExport, index, projectPath); suggestions.push(suggestion); }); // Generate suggestions for unused imports unusedImports.forEach((unusedImport, index) => { const suggestion = createImportRemovalSuggestion(unusedImport, index + deadExports.length, projectPath); suggestions.push(suggestion); }); // Sort suggestions by priority and safety return suggestions.sort((a, b) => { const priorityOrder = { high: 3, medium: 2, low: 1 }; const safetyOrder = { safe: 3, 'review-needed': 2, risky: 1 }; if (a.priority !== b.priority) { return priorityOrder[b.priority] - priorityOrder[a.priority]; } return safetyOrder[b.safety] - safetyOrder[a.safety]; }); } /** * Create removal suggestion for a dead export */ function createExportRemovalSuggestion(deadExport, index, projectPath) { const relativePath = path_1.default.relative(projectPath, deadExport.file); const isHighConfidence = deadExport.confidence === 'high'; const hasRisks = deadExport.riskFactors.length > 0; const suggestion = { id: `export-${index}`, priority: mapConfidenceToPriority(deadExport.confidence), safety: mapConfidenceToSafety(deadExport.confidence, hasRisks), action: `Remove ${deadExport.type}: ${deadExport.name}`, file: relativePath, line: deadExport.line, impact: generateExportImpact(deadExport), type: 'remove-export', }; // Add command for high confidence removals if (isHighConfidence && !hasRisks) { suggestion.command = generateRemovalCommand(deadExport); } // Add warnings for risky removals if (hasRisks) { suggestion.warnings = deadExport.riskFactors; } return suggestion; } /** * Create removal suggestion for an unused import */ function createImportRemovalSuggestion(unusedImport, index, projectPath) { const relativePath = path_1.default.relative(projectPath, unusedImport.file); const isHighConfidence = unusedImport.confidence === 'high'; const hasRisks = unusedImport.riskFactors.length > 0; const suggestion = { id: `import-${index}`, priority: mapConfidenceToPriority(unusedImport.confidence), safety: mapConfidenceToSafety(unusedImport.confidence, hasRisks), action: `Remove unused import: ${unusedImport.name}`, file: relativePath, line: unusedImport.line, impact: generateImportImpact(unusedImport), type: 'remove-import', }; // Add command for high confidence removals if (isHighConfidence && !hasRisks) { suggestion.command = generateImportRemovalCommand(unusedImport); } // Add warnings for risky removals if (hasRisks) { suggestion.warnings = unusedImport.riskFactors; } return suggestion; } /** * Generate confidence levels and risk assessment for dead exports */ function assessExportRemovalRisk(deadExport, projectPath) { const riskFactors = []; const fileName = path_1.default.basename(deadExport.file); const exportName = deadExport.name; // Check for public API patterns if (isPublicAPI(deadExport, projectPath)) { riskFactors.push('Appears to be public API - may have external consumers'); } // Check for common naming patterns that suggest external usage if (isLikelyExternalAPI(exportName)) { riskFactors.push('Export name suggests it may be used by external packages'); } // Check for test files if (isTestFile(fileName)) { riskFactors.push('Located in test file - may be used by test framework'); } // Check for type definitions if (deadExport.type === 'interface' || deadExport.type === 'type') { riskFactors.push('Type definition - may be used in type annotations'); } // Check for default exports if (exportName === 'default') { riskFactors.push('Default export - may be imported with different names'); } // Check for configuration/setup files if (isConfigFile(fileName)) { riskFactors.push('Configuration file - may be loaded dynamically'); } // Determine confidence based on risk factors let confidence; if (riskFactors.length === 0) { confidence = 'high'; } else if (riskFactors.length <= 2) { confidence = 'medium'; } else { confidence = 'low'; } return { confidence, riskFactors }; } /** * Generate confidence levels and risk assessment for unused imports */ function assessImportRemovalRisk(unusedImport) { const riskFactors = []; const importName = unusedImport.name; const fromPath = unusedImport.from; // Check for side-effect imports if (isSideEffectImport(fromPath)) { riskFactors.push('May be imported for side effects'); } // Check for polyfills or setup imports if (isPolyfillImport(fromPath, importName)) { riskFactors.push('Appears to be polyfill or setup import'); } // Check for type-only usage (TypeScript) if (isTypeImport(importName)) { riskFactors.push('May be used in type annotations only'); } // Check for common framework patterns if (isFrameworkImport(fromPath, importName)) { riskFactors.push('Framework import - may be used implicitly'); } // External packages are generally safe to remove if unused const isExternal = !fromPath.startsWith('./') && !fromPath.startsWith('../'); // Determine confidence let confidence; if (riskFactors.length === 0 && isExternal) { confidence = 'high'; } else if (riskFactors.length <= 1) { confidence = 'medium'; } else { confidence = 'low'; } return { confidence, riskFactors }; } /** * Helper functions for risk assessment */ function isPublicAPI(deadExport, projectPath) { const relativePath = path_1.default.relative(projectPath, deadExport.file); // Check if it's in an index file (common pattern for public APIs) if (path_1.default.basename(deadExport.file).startsWith('index.')) { return true; } // Check if it's in a 'lib', 'public', or 'api' directory at root level // Note: 'src' is too broad - most internal code is in src const segments = relativePath.split(path_1.default.sep); if (segments.length > 0 && ['lib', 'public', 'api'].includes(segments[0])) { return true; } // Check if it's in top-level src/index.ts or similar public entry points if (segments.length === 2 && segments[0] === 'src' && path_1.default.basename(deadExport.file).startsWith('index.')) { return true; } return false; } function isLikelyExternalAPI(exportName) { // Common patterns for external APIs const apiPatterns = [ /^create[A-Z]/, // createComponent, createStore /^use[A-Z]/, // useHook, useEffect (React) /^get[A-Z]/, // getConfig, getData /^set[A-Z]/, // setConfig, setData /^init[A-Z]/, // initializeApp, initConfig (not just "init") /[A-Z]Config$/, // AppConfig, UserConfig (not internal configs) /[A-Z]Utils$/, // StringUtils, DateUtils (not internal utils) /[A-Z]Helper$/, // ApiHelper, DataHelper (not internal helpers) ]; return apiPatterns.some(pattern => pattern.test(exportName)); } function isTestFile(fileName) { return /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(fileName); } function isConfigFile(fileName) { const configPatterns = [ /^config\./, /\.config\./, /^setup\./, /^bootstrap\./, /^polyfill\./, ]; return configPatterns.some(pattern => pattern.test(fileName)); } function isSideEffectImport(fromPath) { const sideEffectPatterns = [ /polyfill/, /setup/, /bootstrap/, /core-js/, // Add core-js as a side-effect import /\.css$/, /\.scss$/, /\.less$/, ]; return sideEffectPatterns.some(pattern => pattern.test(fromPath)); } function isPolyfillImport(fromPath, importName) { return (/polyfill|core-js|regenerator/.test(fromPath) || /polyfill|regenerator/.test(importName)); } function isTypeImport(importName) { // Common type naming patterns return /^[A-Z].*Type$|^[A-Z].*Interface$|^I[A-Z]/.test(importName); } function isFrameworkImport(fromPath, importName) { // React patterns if (fromPath === 'react' && ['React', 'Component', 'PureComponent'].includes(importName)) { return true; } // Vue patterns if (fromPath === 'vue' && ['Vue', 'createApp'].includes(importName)) { return true; } return false; } /** * Utility functions for mapping confidence to priority/safety */ function mapConfidenceToPriority(confidence) { return confidence; // Direct mapping for now } function mapConfidenceToSafety(confidence, hasRisks) { if (confidence === 'low') { return 'risky'; } if (hasRisks) { return 'review-needed'; // Both high and medium confidence with risks need review } return confidence === 'high' ? 'safe' : 'review-needed'; } function generateExportImpact(deadExport) { const typeDescriptions = { function: 'function', class: 'class definition', variable: 'variable/constant', interface: 'type interface', type: 'type alias', }; return `Remove unused ${typeDescriptions[deadExport.type]} (~${estimateLines(deadExport.type)} lines)`; } function generateImportImpact(unusedImport) { return `Remove unused ${unusedImport.type} import (~1 line)`; } function estimateLines(type) { const estimates = { function: 10, class: 20, variable: 1, interface: 5, type: 2, }; return estimates[type] || 5; } function generateRemovalCommand(deadExport) { return `# Remove lines around ${deadExport.line} in ${path_1.default.basename(deadExport.file)}`; } function generateImportRemovalCommand(unusedImport) { return `# Remove import on line ${unusedImport.line} in ${path_1.default.basename(unusedImport.file)}`; } //# sourceMappingURL=suggestion-engine.js.map