devghost
Version:
👻 Find dead code, dead imports, and dead dependencies before they haunt your project
250 lines • 8.75 kB
JavaScript
;
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