UNPKG

devghost

Version:

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

223 lines • 7.58 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.parseFile = parseFile; exports.extractImports = extractImports; exports.extractExports = extractExports; exports.isIdentifierUsed = isIdentifierUsed; exports.getImportedPackages = getImportedPackages; exports.getLineText = getLineText; exports.getLineAndColumn = getLineAndColumn; exports.getImportedIdentifiers = getImportedIdentifiers; exports.isTypeOnlyImport = isTypeOnlyImport; exports.isSideEffectImport = isSideEffectImport; exports.createSourceFile = createSourceFile; exports.shouldIgnoreFile = shouldIgnoreFile; exports.shouldIgnoreLine = shouldIgnoreLine; const fs = __importStar(require("node:fs")); const ts = __importStar(require("typescript")); /** * Parse a TypeScript/JavaScript file using the TS Compiler API */ function parseFile(filePath) { try { const content = fs.readFileSync(filePath, 'utf-8'); return ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true); } catch (error) { console.error(`Error parsing file ${filePath}:`, error); return null; } } /** * Extract all import declarations from a source file */ function extractImports(sourceFile) { const imports = []; function visit(node) { if (ts.isImportDeclaration(node)) { imports.push(node); } ts.forEachChild(node, visit); } visit(sourceFile); return imports; } /** * Extract all export declarations from a source file */ function extractExports(sourceFile) { const exports = []; function visit(node) { if (ts.isExportDeclaration(node)) { exports.push(node); } ts.forEachChild(node, visit); } visit(sourceFile); return exports; } /** * Check if an identifier is used in the source file */ function isIdentifierUsed(sourceFile, identifierName) { let isUsed = false; function visit(node) { // Skip import declarations themselves if (ts.isImportDeclaration(node)) { return; } // Check if this is an identifier with the name we're looking for if (ts.isIdentifier(node) && node.text === identifierName) { isUsed = true; return; } ts.forEachChild(node, visit); } visit(sourceFile); return isUsed; } /** * Extract package names from all imports in a source file */ function getImportedPackages(sourceFile) { const packages = new Set(); const imports = extractImports(sourceFile); for (const importDecl of imports) { if (ts.isStringLiteral(importDecl.moduleSpecifier)) { const moduleName = importDecl.moduleSpecifier.text; // Extract package name (handle scoped packages) if (moduleName.startsWith('.') || moduleName.startsWith('/')) { // Relative import, skip continue; } // Extract the package name let packageName; if (moduleName.startsWith('@')) { // Scoped package: @scope/package const parts = moduleName.split('/'); packageName = parts.slice(0, 2).join('/'); } else { // Regular package: package or package/subpath packageName = moduleName.split('/')[0]; } packages.add(packageName); } } return packages; } /** * Get the text of a specific line in the source file */ function getLineText(sourceFile, lineNumber) { const lines = sourceFile.text.split('\n'); return lines[lineNumber] || ''; } /** * Get line and column from a position in the source file * Returns 1-indexed line numbers (line 1 = first line) for consistency */ function getLineAndColumn(sourceFile, pos) { const { line, character } = sourceFile.getLineAndCharacterOfPosition(pos); // TypeScript returns 0-indexed line numbers, convert to 1-indexed return { line: line + 1, column: character }; } /** * Get all identifiers imported from an import declaration */ function getImportedIdentifiers(importDecl) { const identifiers = []; if (!importDecl.importClause) { // Side-effect import: import './file' return []; } const { importClause } = importDecl; // Default import: import Foo from 'bar' if (importClause.name) { identifiers.push(importClause.name.text); } // Named imports: import { a, b } from 'bar' if (importClause.namedBindings) { if (ts.isNamedImports(importClause.namedBindings)) { for (const element of importClause.namedBindings.elements) { identifiers.push(element.name.text); } } else if (ts.isNamespaceImport(importClause.namedBindings)) { // Namespace import: import * as foo from 'bar' identifiers.push(importClause.namedBindings.name.text); } } return identifiers; } /** * Check if an import is a type-only import */ function isTypeOnlyImport(importDecl) { return importDecl.importClause?.isTypeOnly || false; } /** * Check if an import is a side-effect import (no imported identifiers) */ function isSideEffectImport(importDecl) { return !importDecl.importClause; } /** * Create a source file from file path and content */ function createSourceFile(filePath, content) { const fileContent = content || fs.readFileSync(filePath, 'utf-8'); return ts.createSourceFile(filePath, fileContent, ts.ScriptTarget.Latest, true); } /** * Check if a file should be ignored based on devghost-ignore-file comment */ function shouldIgnoreFile(content) { const firstLines = content.split('\n').slice(0, 10).join('\n'); return firstLines.includes('devghost-ignore-file'); } /** * Check if a line should be ignored based on devghost-ignore-next-line comment */ function shouldIgnoreLine(content, lineNumber) { const lines = content.split('\n'); if (lineNumber > 0 && lineNumber <= lines.length) { const previousLine = lines[lineNumber - 1]; return previousLine.includes('devghost-ignore-next-line'); } return false; } //# sourceMappingURL=tsparser.js.map