UNPKG

sicua

Version:

A tool for analyzing project structure and dependencies

190 lines (189 loc) 9.42 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TypeAnalyzer = void 0; const typescript_1 = __importDefault(require("typescript")); const typeCollector_1 = require("./analyzers/typeCollector"); const componentTypeAnalyzer_1 = require("./analyzers/componentTypeAnalyzer"); const complexTypeAnalyzer_1 = require("./analyzers/complexTypeAnalyzer"); const typeUsageAnalyzer_1 = require("./analyzers/typeUsageAnalyzer"); const duplicateTypeAnalyzer_1 = require("./analyzers/duplicateTypeAnalyzer"); const typeStatisticsAnalyzer_1 = require("./analyzers/typeStatisticsAnalyzer"); const typeSimilarityUtils_1 = require("./utils/typeSimilarityUtils"); const unifiedTypeGenerator_1 = require("./generators/unifiedTypeGenerator"); /** * Main analyzer for TypeScript types in a project */ class TypeAnalyzer { constructor(scanResult, typeChecker, options = {}) { this.scanResult = scanResult; this.typeChecker = typeChecker; this.options = { analyzeDuplicates: true, analyzeComplexTypes: true, analyzeComponentProps: true, analyzeUsage: true, generateUnifiedTypes: true, maxFilesToAnalyze: 1000, maxTypesToAnalyze: 5000, batchSize: 50, maxReportedIssues: 100, maxDuplicateSuggestions: 20, maxUnifiedTypeSuggestions: 10, ...options, }; } /** * Run the type analysis */ async analyze() { // Initialize the result const result = { interfacesCount: 0, typesCount: 0, componentsWithPropTypes: [], componentsWithoutPropTypes: [], regularFunctionsWithoutReturnType: 0, suggestedImprovements: [], anyUsageCount: 0, broadUnionTypesCount: 0, complexTypes: [], typeSimplificationSuggestions: [], duplicatedTypes: [], unifiedTypesSuggestions: [], typesByDirectory: {}, typesWithoutNamespace: [], }; // Step 1: Collect all types const typeCollector = new typeCollector_1.TypeCollector(this.scanResult); const typeData = await typeCollector.collectAllTypes(); const { typeRegistry, signatureToTypes, typesByDirectory } = typeData; // Step 2: Analyze component types and props if (this.options.analyzeComponentProps) { const componentAnalyzer = new componentTypeAnalyzer_1.ComponentTypeAnalyzer(this.scanResult); const componentAnalysis = await componentAnalyzer.analyzeComponentTypes(); result.componentsWithPropTypes = componentAnalysis.componentsWithPropTypes || []; result.componentsWithoutPropTypes = componentAnalysis.componentsWithoutPropTypes || []; result.regularFunctionsWithoutReturnType = componentAnalysis.regularFunctionsWithoutReturnType || 0; result.suggestedImprovements.push(...(componentAnalysis.suggestedImprovements || [])); } // Step 3: Analyze type usage if (this.options.analyzeUsage) { const typeUsageAnalyzer = new typeUsageAnalyzer_1.TypeUsageAnalyzer(this.scanResult, typeRegistry); const relevantFiles = Array.from(this.scanResult.sourceFiles.keys()).slice(0, this.options.maxFilesToAnalyze); const usageResult = await typeUsageAnalyzer.analyzeTypeUsages(relevantFiles); const typesWithoutNamespace = typeUsageAnalyzer.identifyTypesWithoutNamespace(); result.anyUsageCount = usageResult.anyUsageCount; result.suggestedImprovements.push(...usageResult.suggestedImprovements); result.typesWithoutNamespace = typesWithoutNamespace; } // Step 4: Analyze duplicate types if (this.options.analyzeDuplicates) { const duplicateAnalyzer = new duplicateTypeAnalyzer_1.DuplicateTypeAnalyzer(typeRegistry, signatureToTypes); result.duplicatedTypes = duplicateAnalyzer.detectTypeDuplications(this.options.maxDuplicateSuggestions); } // Step 5: Generate unified type suggestions if (this.options.generateUnifiedTypes) { // Get the most frequently used types const typeCandidates = Array.from(typeRegistry.values()) .filter((t) => t.usages.size > 1 || t.name.includes("Props") || t.name.includes("State")) .slice(0, 100); // Find similar types and generate groups const similarTypesGroups = typeSimilarityUtils_1.TypeSimilarityUtils.groupSimilarTypes(typeCandidates).map((group) => ({ types: group.types, similarityScore: group.similarity, })); // Generate unified type suggestions result.unifiedTypesSuggestions = unifiedTypeGenerator_1.UnifiedTypeGenerator.generateAllUnifiedTypes(similarTypesGroups, this.options.maxUnifiedTypeSuggestions); } // Step 6: Analyze complex types if (this.options.analyzeComplexTypes) { const complexResults = await this.analyzeComplexTypes(); result.complexTypes = complexResults.complexTypes; result.typeSimplificationSuggestions = complexResults.typeSimplificationSuggestions; result.broadUnionTypesCount = complexResults.broadUnionTypesCount; } // Step 7: Calculate type statistics const typeStatisticsAnalyzer = new typeStatisticsAnalyzer_1.TypeStatisticsAnalyzer(typesByDirectory); const statistics = typeStatisticsAnalyzer.calculateTypeStatistics(); result.interfacesCount = statistics.interfacesCount; result.typesCount = statistics.typesCount; result.typesByDirectory = typesByDirectory; return result; } /** * Analyze complex types */ async analyzeComplexTypes() { const complexTypeAnalyzer = new complexTypeAnalyzer_1.ComplexTypeAnalyzer(this.options.maxReportedIssues || 100); const filePaths = Array.from(this.scanResult.sourceFiles.keys()); const batchSize = this.options.batchSize || 50; // Process files in batches for (let i = 0; i < filePaths.length; i += batchSize) { const batch = filePaths.slice(i, i + batchSize); await Promise.all(batch.map(async (filePath) => { const sourceFile = this.scanResult.sourceFiles.get(filePath); if (sourceFile) { this.visitNodesForComplexTypes(sourceFile, filePath, complexTypeAnalyzer); } })); } return complexTypeAnalyzer.getResults(); } /** * Visit nodes to find and analyze complex types */ visitNodesForComplexTypes(sourceFile, filePath, complexTypeAnalyzer, context = "") { const visit = (node, currentContext = "") => { // Process potential complex types if (typescript_1.default.isTypeNode(node)) { complexTypeAnalyzer.analyzeComplexType(node, filePath, currentContext); } // Extract context for better reporting let newContext = currentContext; if (typescript_1.default.isInterfaceDeclaration(node) || typescript_1.default.isTypeAliasDeclaration(node)) { newContext = node.name.text; } else if (typescript_1.default.isPropertySignature(node) && typescript_1.default.isIdentifier(node.name)) { newContext = currentContext ? `${currentContext}.${node.name.text}` : node.name.text; } else if (typescript_1.default.isMethodSignature(node) && typescript_1.default.isIdentifier(node.name)) { newContext = currentContext ? `${currentContext}.${node.name.text}()` : `${node.name.text}()`; } else if (typescript_1.default.isFunctionDeclaration(node) && node.name) { newContext = `${node.name.text}()`; } else if (typescript_1.default.isParameter(node) && typescript_1.default.isIdentifier(node.name)) { newContext = currentContext ? `${currentContext}(${node.name.text})` : `param:${node.name.text}`; } // Only visit certain node types to save time if (typescript_1.default.isTypeNode(node) || typescript_1.default.isInterfaceDeclaration(node) || typescript_1.default.isTypeAliasDeclaration(node) || typescript_1.default.isMethodSignature(node) || typescript_1.default.isPropertySignature(node) || typescript_1.default.isFunctionDeclaration(node) || typescript_1.default.isParameter(node) || typescript_1.default.isSourceFile(node)) { typescript_1.default.forEachChild(node, (child) => visit(child, newContext)); } }; visit(sourceFile, context); } } exports.TypeAnalyzer = TypeAnalyzer;