UNPKG

@rayburst/sharity

Version:

Analyze shared package usage across monorepos - calculate symbol sharing percentages, track exclusive imports, and identify unused exports

143 lines 5.07 kB
"use strict"; /** * Symbol tracker - tracks exports and their usage across consumers */ Object.defineProperty(exports, "__esModule", { value: true }); exports.SymbolTracker = void 0; const typescript_parser_1 = require("../utils/typescript-parser"); const workspace_scanner_1 = require("../utils/workspace-scanner"); /** * Symbol registry to track all exports and imports */ class SymbolTracker { constructor() { this.exportedSymbols = new Map(); this.symbolUsages = new Map(); } /** * Scan target package and build export registry */ async scanPackageExports(packagePath, packageName, includeTests = false) { const files = await (0, workspace_scanner_1.findSourceFiles)(packagePath, [], includeTests); for (const file of files) { try { const analysis = (0, typescript_parser_1.parseFile)(file); for (const exportedSymbol of analysis.exports) { this.exportedSymbols.set(exportedSymbol.name, exportedSymbol); // Initialize usage tracking if (!this.symbolUsages.has(exportedSymbol.name)) { this.symbolUsages.set(exportedSymbol.name, []); } } } catch (error) { console.warn(`Warning: Failed to parse ${file}:`, error); } } } /** * Scan consumer package for imports from target */ async scanConsumerImports(consumerPath, consumerName, targetPackageName, includeTests = false) { const files = await (0, workspace_scanner_1.findSourceFiles)(consumerPath, [], includeTests); const importCounts = new Map(); for (const file of files) { try { const analysis = (0, typescript_parser_1.parseFile)(file); // Filter imports from target package const targetImports = analysis.imports.filter((imp) => this.isImportFromTarget(imp.source, targetPackageName)); for (const imp of targetImports) { for (const symbolName of imp.specifiers) { // Track this usage if (this.exportedSymbols.has(symbolName)) { importCounts.set(symbolName, (importCounts.get(symbolName) || 0) + 1); } } } } catch (error) { console.warn(`Warning: Failed to parse ${file}:`, error); } } // Add usages to registry for (const [symbolName, fileCount] of importCounts) { const usages = this.symbolUsages.get(symbolName) || []; usages.push({ symbolName, consumerName, consumerPath, importPath: targetPackageName, fileCount, }); this.symbolUsages.set(symbolName, usages); } } /** * Check if import is from the target package */ isImportFromTarget(importSource, targetPackageName) { // Direct import: import from "@exaring/ui" if (importSource === targetPackageName) { return true; } // Subpath import: import from "@exaring/ui/components/Button" if (importSource.startsWith(`${targetPackageName}/`)) { return true; } return false; } /** * Analyze all symbols and generate analysis results */ analyzeSymbols() { const analyses = []; for (const [symbolName, symbol] of this.exportedSymbols) { const usages = this.symbolUsages.get(symbolName) || []; // Get unique consumers const uniqueConsumers = new Set(usages.map((u) => u.consumerName)); const consumerCount = uniqueConsumers.size; analyses.push({ symbol, usages, consumerCount, isShared: consumerCount >= 2, isExclusive: consumerCount === 1, isUnused: consumerCount === 0, }); } return analyses; } /** * Get all exported symbols */ getExportedSymbols() { return Array.from(this.exportedSymbols.values()); } /** * Get usage for a specific symbol */ getSymbolUsage(symbolName) { return this.symbolUsages.get(symbolName) || []; } /** * Get all consumers that import from the target package */ getAllConsumers() { const consumers = new Set(); for (const usages of this.symbolUsages.values()) { for (const usage of usages) { consumers.add(usage.consumerName); } } return Array.from(consumers); } /** * Clear all tracked data */ clear() { this.exportedSymbols.clear(); this.symbolUsages.clear(); } } exports.SymbolTracker = SymbolTracker; //# sourceMappingURL=symbol-tracker.js.map