devghost
Version:
👻 Find dead code, dead imports, and dead dependencies before they haunt your project
186 lines • 6.85 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.analyzeUnusedVariables = analyzeUnusedVariables;
const fs = __importStar(require("node:fs"));
const ts = __importStar(require("typescript"));
const tsparser_1 = require("../utils/tsparser");
/**
* Analyze TypeScript files for unused variables
*/
function analyzeUnusedVariables(files) {
const unusedVariables = [];
for (const file of files) {
if (!fs.existsSync(file))
continue;
const content = fs.readFileSync(file, 'utf-8');
if ((0, tsparser_1.shouldIgnoreFile)(content))
continue;
const sourceFile = (0, tsparser_1.createSourceFile)(file, content);
const fileVariables = findUnusedVariablesInFile(sourceFile, file);
unusedVariables.push(...fileVariables);
}
return unusedVariables;
}
function findUnusedVariablesInFile(sourceFile, filePath) {
const unusedVariables = [];
const declaredVariables = [];
// First pass: collect all variable declarations
function collectVariables(node, scopeType) {
// Variable declarations: const, let, var
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name)) {
const _parent = node.parent?.parent;
let variableType = 'const';
if (ts.isVariableDeclarationList(node.parent)) {
const flags = node.parent.flags;
if (flags & ts.NodeFlags.Let)
variableType = 'let';
else if (flags & ts.NodeFlags.Const)
variableType = 'const';
else
variableType = 'var';
}
const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.name.getStart());
const text = sourceFile.text.split('\n')[line] || '';
declaredVariables.push({
name: node.name.text,
line: line + 1,
column: character,
variableType,
scopeType,
entireLine: text.trim(),
node: node.name,
});
}
// Function parameters
if (ts.isFunctionLike(node)) {
for (const param of node.parameters) {
if (ts.isIdentifier(param.name)) {
const { line, character } = sourceFile.getLineAndCharacterOfPosition(param.name.getStart());
const text = sourceFile.text.split('\n')[line] || '';
declaredVariables.push({
name: param.name.text,
line: line + 1,
column: character,
variableType: 'parameter',
scopeType: 'function',
entireLine: text.trim(),
node: param.name,
});
}
}
// Visit function body with function scope
ts.forEachChild(node, (child) => collectVariables(child, 'function'));
return; // Don't traverse again
}
// Block scope
if (ts.isBlock(node)) {
ts.forEachChild(node, (child) => collectVariables(child, 'block'));
return;
}
ts.forEachChild(node, (child) => collectVariables(child, scopeType));
}
// Start collection at module level
collectVariables(sourceFile, 'module');
// Second pass: check which variables are actually used
for (const variable of declaredVariables) {
if (!isVariableUsed(sourceFile, variable)) {
unusedVariables.push({
file: filePath,
line: variable.line,
column: variable.column,
variableName: variable.name,
variableType: variable.variableType,
scopeType: variable.scopeType,
entireLine: variable.entireLine,
});
}
}
return unusedVariables;
}
function isVariableUsed(_sourceFile, variable) {
let isUsed = false;
const targetName = variable.name;
// Find the scope of this variable (function, block, or module)
const scope = findScope(variable.node);
function visit(node) {
// Skip the declaration itself
if (node === variable.node) {
return;
}
// Check if this is an identifier with the same name
if (ts.isIdentifier(node) && node.text === targetName) {
// Make sure we're in the same scope
if (isInScope(node, scope)) {
isUsed = true;
return;
}
}
ts.forEachChild(node, visit);
}
visit(scope);
return isUsed;
}
function findScope(node) {
let current = node.parent;
while (current) {
// Function scope
if (ts.isFunctionLike(current)) {
return current;
}
// Block scope
if (ts.isBlock(current)) {
return current;
}
// Module scope
if (ts.isSourceFile(current)) {
return current;
}
current = current.parent;
}
// Fallback to source file
return node.getSourceFile();
}
function isInScope(node, scope) {
let current = node;
while (current) {
if (current === scope) {
return true;
}
current = current.parent;
}
return false;
}
//# sourceMappingURL=unusedVariables.js.map