UNPKG

devghost

Version:

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

186 lines • 6.85 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.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