devghost
Version:
👻 Find dead code, dead imports, and dead dependencies before they haunt your project
213 lines • 8.42 kB
JavaScript
"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.analyzeUnusedTypes = analyzeUnusedTypes;
const fs = __importStar(require("node:fs"));
const ts = __importStar(require("typescript"));
const tsparser_1 = require("../utils/tsparser");
/**
* Analyze TypeScript files for unused types, interfaces, type aliases, and enums
*/
function analyzeUnusedTypes(files) {
const unusedTypes = [];
const allTypeDeclarations = new Map();
const typeReferences = new Set();
// First pass: collect all type declarations
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);
collectTypeDeclarations(sourceFile, file, allTypeDeclarations);
}
// Second pass: find type references
for (const file of files) {
if (!fs.existsSync(file))
continue;
const content = fs.readFileSync(file, 'utf-8');
const sourceFile = (0, tsparser_1.createSourceFile)(file, content);
collectTypeReferences(sourceFile, typeReferences);
}
// Find unused types
for (const [_typeId, typeDecl] of allTypeDeclarations) {
if (!typeReferences.has(typeDecl.name)) {
unusedTypes.push({
file: typeDecl.file,
line: typeDecl.line,
column: typeDecl.column,
typeName: typeDecl.name,
typeKind: typeDecl.kind,
isExported: typeDecl.isExported,
entireLine: typeDecl.entireLine,
});
}
}
return unusedTypes;
}
function collectTypeDeclarations(sourceFile, filePath, declarations) {
function visit(node) {
// Interface declarations
if (ts.isInterfaceDeclaration(node) && node.name) {
const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
const text = node.getText(sourceFile);
const firstLine = text.split('\n')[0];
declarations.set(`${filePath}:${node.name.text}`, {
file: filePath,
name: node.name.text,
kind: 'interface',
line: line + 1,
column: character,
isExported: hasExportModifier(node),
entireLine: firstLine,
});
}
// Type alias declarations
if (ts.isTypeAliasDeclaration(node) && node.name) {
const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
const text = node.getText(sourceFile);
const firstLine = text.split('\n')[0];
declarations.set(`${filePath}:${node.name.text}`, {
file: filePath,
name: node.name.text,
kind: 'type',
line: line + 1,
column: character,
isExported: hasExportModifier(node),
entireLine: firstLine,
});
}
// Enum declarations
if (ts.isEnumDeclaration(node) && node.name) {
const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
const text = node.getText(sourceFile);
const firstLine = text.split('\n')[0];
declarations.set(`${filePath}:${node.name.text}`, {
file: filePath,
name: node.name.text,
kind: 'enum',
line: line + 1,
column: character,
isExported: hasExportModifier(node),
entireLine: firstLine,
});
}
// Class declarations (as types)
if (ts.isClassDeclaration(node) && node.name) {
const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
const text = node.getText(sourceFile);
const firstLine = text.split('\n')[0];
declarations.set(`${filePath}:${node.name.text}`, {
file: filePath,
name: node.name.text,
kind: 'class',
line: line + 1,
column: character,
isExported: hasExportModifier(node),
entireLine: firstLine,
});
}
ts.forEachChild(node, visit);
}
visit(sourceFile);
}
function collectTypeReferences(sourceFile, references) {
function visit(node) {
// Type references
if (ts.isTypeReferenceNode(node)) {
const typeName = node.typeName.getText(sourceFile);
references.add(typeName);
}
// Heritage clauses (extends, implements)
if (ts.isHeritageClause(node)) {
for (const type of node.types) {
const typeName = type.expression.getText(sourceFile);
references.add(typeName);
}
}
// Variable declarations with type annotations
if (ts.isVariableDeclaration(node) && node.type) {
extractTypeNamesFromTypeNode(node.type, sourceFile, references);
}
// Function parameters and return types
if (ts.isFunctionLike(node)) {
// Parameters
for (const param of node.parameters) {
if (param.type) {
extractTypeNamesFromTypeNode(param.type, sourceFile, references);
}
}
// Return type
if (node.type) {
extractTypeNamesFromTypeNode(node.type, sourceFile, references);
}
}
// Type assertions (as expressions)
if (ts.isAsExpression(node)) {
extractTypeNamesFromTypeNode(node.type, sourceFile, references);
}
ts.forEachChild(node, visit);
}
visit(sourceFile);
}
function extractTypeNamesFromTypeNode(typeNode, sourceFile, references) {
if (ts.isTypeReferenceNode(typeNode)) {
const typeName = typeNode.typeName.getText(sourceFile);
references.add(typeName);
}
if (ts.isUnionTypeNode(typeNode) || ts.isIntersectionTypeNode(typeNode)) {
for (const type of typeNode.types) {
extractTypeNamesFromTypeNode(type, sourceFile, references);
}
}
if (ts.isArrayTypeNode(typeNode)) {
extractTypeNamesFromTypeNode(typeNode.elementType, sourceFile, references);
}
if (ts.isTypeLiteralNode(typeNode)) {
for (const member of typeNode.members) {
if (ts.isPropertySignature(member) && member.type) {
extractTypeNamesFromTypeNode(member.type, sourceFile, references);
}
}
}
}
function hasExportModifier(node) {
const modifiersNode = node;
if (!modifiersNode.modifiers)
return false;
return modifiersNode.modifiers.some((mod) => mod.kind === ts.SyntaxKind.ExportKeyword);
}
//# sourceMappingURL=unusedTypes.js.map