UNPKG

devghost

Version:

👻 Find dead code, dead imports, and dead dependencies before they haunt your project

250 lines • 8.75 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.fixUnusedImports = fixUnusedImports; exports.getFixPreview = getFixPreview; exports.fixUnusedFunctions = fixUnusedFunctions; const fs = __importStar(require("node:fs")); const ts = __importStar(require("typescript")); /** * Group unused imports by file for efficient processing */ function groupByFile(unusedImports) { const grouped = new Map(); for (const imp of unusedImports) { if (!grouped.has(imp.file)) { grouped.set(imp.file, []); } grouped.get(imp.file)?.push(imp); } return grouped; } /** * Remove unused imports from a single file */ function fixFileImports(filePath, unusedImports) { try { const content = fs.readFileSync(filePath, 'utf-8'); const lines = content.split('\n'); // Sort by line number descending so we can remove from bottom to top // This prevents line number shifts const sortedImports = [...unusedImports].sort((a, b) => b.line - a.line); let linesRemoved = 0; for (const imp of sortedImports) { // Line numbers from analysis are 1-indexed, array is 0-indexed const lineIndex = imp.line - 1; if (lineIndex >= 0 && lineIndex < lines.length) { // Check if this is the entire import statement const line = lines[lineIndex]; // Simple heuristic: if the line is an import statement, remove it if (line.trim().startsWith('import ')) { lines.splice(lineIndex, 1); linesRemoved++; } } } // Write the fixed content back fs.writeFileSync(filePath, lines.join('\n'), 'utf-8'); return { file: filePath, linesRemoved, success: true, }; } catch (error) { return { file: filePath, linesRemoved: 0, success: false, error: error instanceof Error ? error.message : 'Unknown error', }; } } /** * Create a backup of a file */ function createBackup(filePath) { const backupPath = `${filePath}.devghost-backup`; fs.copyFileSync(filePath, backupPath); return backupPath; } /** * Auto-fix unused imports in all files */ async function fixUnusedImports(unusedImports, options = {}) { const { dryRun = false, createBackup: shouldBackup = false } = options; const results = []; // Group by file const grouped = groupByFile(unusedImports); for (const [file, imports] of grouped.entries()) { if (dryRun) { // In dry-run mode, just report what would be done results.push({ file, linesRemoved: imports.length, success: true, }); } else { // Create backup if requested if (shouldBackup) { try { createBackup(file); } catch (_error) { results.push({ file, linesRemoved: 0, success: false, error: 'Failed to create backup', }); continue; } } // Fix the file const result = fixFileImports(file, imports); results.push(result); } } return results; } /** * Get a preview of what will be fixed */ function getFixPreview(unusedImports) { const grouped = groupByFile(unusedImports); let preview = ''; for (const [file, imports] of grouped.entries()) { preview += `\n${file}:\n`; for (const imp of imports) { preview += ` - Line ${imp.line + 1}: ${imp.entireLine.trim()}\n`; } } return preview; } /** * Remove unused functions from a single file */ function fixFileFunctions(filePath, unusedFunctions) { try { const content = fs.readFileSync(filePath, 'utf-8'); const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true); // Find nodes to remove const nodesToRemove = []; function visit(node) { // Check if this node corresponds to one of our unused functions for (const func of unusedFunctions) { const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart()); // Match by line number (approximate) and name if (line === func.line) { if ((ts.isFunctionDeclaration(node) && node.name?.text === func.functionName) || (ts.isVariableStatement(node) && node.declarationList.declarations.some((d) => ts.isIdentifier(d.name) && d.name.text === func.functionName)) || (ts.isMethodDeclaration(node) && ts.isIdentifier(node.name) && node.name.text === func.functionName)) { nodesToRemove.push({ start: node.getFullStart(), end: node.getEnd() }); } } } ts.forEachChild(node, visit); } visit(sourceFile); // Sort by start position descending to remove from bottom up nodesToRemove.sort((a, b) => b.start - a.start); let newContent = content; for (const range of nodesToRemove) { newContent = newContent.substring(0, range.start) + newContent.substring(range.end); } fs.writeFileSync(filePath, newContent, 'utf-8'); return { file: filePath, linesRemoved: nodesToRemove.length, // Approximation success: true, }; } catch (error) { return { file: filePath, linesRemoved: 0, success: false, error: error instanceof Error ? error.message : 'Unknown error', }; } } /** * Auto-fix unused functions in all files */ async function fixUnusedFunctions(unusedFunctions, options = {}) { const { dryRun = false, createBackup: shouldBackup = false } = options; const results = []; // Group by file const grouped = new Map(); for (const func of unusedFunctions) { if (!grouped.has(func.file)) { grouped.set(func.file, []); } grouped.get(func.file)?.push(func); } for (const [file, functions] of grouped.entries()) { if (dryRun) { results.push({ file, linesRemoved: functions.length, success: true, }); } else { if (shouldBackup) { try { createBackup(file); } catch (_error) { results.push({ file, linesRemoved: 0, success: false, error: 'Failed to create backup', }); continue; } } const result = fixFileFunctions(file, functions); results.push(result); } } return results; } //# sourceMappingURL=index.js.map