UNPKG

devghost

Version:

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

177 lines • 7.6 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.analyzeUnusedFunctions = analyzeUnusedFunctions; const fs = __importStar(require("node:fs")); const ts = __importStar(require("typescript")); const fs_1 = require("../utils/fs"); const tsparser_1 = require("../utils/tsparser"); /** * Analyze all files for unused functions */ async function analyzeUnusedFunctions(files) { const unusedFunctions = []; for (const file of files) { const sourceFile = (0, tsparser_1.parseFile)(file); if (!sourceFile) continue; const fileContent = fs.readFileSync(file, 'utf-8'); const functions = []; const usages = []; function visit(node) { // 1. Detect Function Declarations if (ts.isFunctionDeclaration(node) && node.name) { const isExported = node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) || false; if (!sourceFile) return; const { line, column } = (0, tsparser_1.getLineAndColumn)(sourceFile, node.name.getStart()); if (!(0, fs_1.hasIgnoreComment)(fileContent, line)) { functions.push({ name: node.name.text, start: node.getStart(), end: node.getEnd(), line, column, type: 'function', isExported, entireLine: (0, tsparser_1.getLineText)(sourceFile, line), }); } } // 2. Detect Arrow Functions / Function Expressions in Variables if (ts.isVariableDeclaration(node) && node.name && ts.isIdentifier(node.name) && node.initializer) { if (ts.isArrowFunction(node.initializer) || ts.isFunctionExpression(node.initializer)) { // Check if exported (need to check parent VariableStatement) let isExported = false; let current = node.parent; while (current) { if (ts.isVariableStatement(current)) { isExported = current.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) || false; break; } current = current.parent; } if (!sourceFile) return; const { line, column } = (0, tsparser_1.getLineAndColumn)(sourceFile, node.name.getStart()); if (!(0, fs_1.hasIgnoreComment)(fileContent, line)) { functions.push({ name: node.name.text, start: node.getStart(), end: node.getEnd(), line, column, type: 'arrow', isExported, entireLine: (0, tsparser_1.getLineText)(sourceFile, line), }); } } } // 3. Detect Usages (Identifiers) if (ts.isIdentifier(node)) { if (isUsage(node)) { usages.push({ name: node.text, pos: node.getStart() }); } } ts.forEachChild(node, visit); } visit(sourceFile); // Analyze for (const func of functions) { if (func.isExported) continue; // Skip exported functions (handled by unusedExports) // Check for usages outside the function itself (handles simple recursion) const isUsed = usages.some((u) => u.name === func.name && (u.pos < func.start || u.pos > func.end)); if (!isUsed) { unusedFunctions.push({ file, line: func.line, column: func.column, functionName: func.name, functionType: func.type, isExported: false, entireLine: func.entireLine, }); } } } return unusedFunctions; } /** * Check if an identifier is a usage (reference) rather than a declaration */ function isUsage(node) { const parent = node.parent; // Declarations if (ts.isFunctionDeclaration(parent) && parent.name === node) return false; if (ts.isVariableDeclaration(parent) && parent.name === node) return false; if (ts.isClassDeclaration(parent) && parent.name === node) return false; if (ts.isInterfaceDeclaration(parent) && parent.name === node) return false; if (ts.isTypeAliasDeclaration(parent) && parent.name === node) return false; if (ts.isEnumDeclaration(parent) && parent.name === node) return false; if (ts.isParameter(parent) && parent.name === node) return false; if (ts.isMethodDeclaration(parent) && parent.name === node) return false; // Property access: obj.prop -> prop is not a usage of variable 'prop' if (ts.isPropertyAccessExpression(parent) && parent.name === node) return false; // Property assignment: { prop: val } -> prop is not a usage if (ts.isPropertyAssignment(parent) && parent.name === node) return false; // Import/Export specifiers if (ts.isImportSpecifier(parent) && (parent.propertyName === node || parent.name === node)) return false; if (ts.isExportSpecifier(parent) && (parent.propertyName === node || parent.name === node)) return false; // Binding elements (destructuring): const { prop } = obj if (ts.isBindingElement(parent) && parent.propertyName === node) return false; // { prop: alias } -> prop is property name if (ts.isBindingElement(parent) && parent.name === node) return false; // { prop } -> prop is declaration return true; } //# sourceMappingURL=unusedFunctions.js.map