UNPKG

sicua

Version:

A tool for analyzing project structure and dependencies

133 lines (132 loc) 5.34 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.DeduplicationAnalyzer = void 0; const deduplication_types_1 = require("./types/deduplication.types"); const componentFilter_1 = require("./utils/componentFilter"); const similarity_1 = require("./utils/similarity"); const componentInfoExtractor_1 = require("./extractors/componentInfoExtractor"); const grouping_1 = require("./utils/grouping"); /** * Analyzer for detecting component duplication */ class DeduplicationAnalyzer { constructor(components, scanResult) { this.scanResult = scanResult; this.sourceFiles = (0, componentInfoExtractor_1.createSourceFiles)(components, scanResult); } /** * Analyzes components to find similarities and duplication */ async analyzeComponents(components, thresholds = deduplication_types_1.DEFAULT_SIMILARITY_THRESHOLDS) { try { const validComponents = components.filter((comp) => this.isValidForDeduplication(comp)); let processedCount = 0; let comparisonCount = 0; // Enhance components with detailed info const enhancedComponents = await Promise.all(validComponents.map(async (component) => { processedCount++; await new Promise((resolve) => setTimeout(resolve, 0)); const sourceFile = this.sourceFiles.get(component.fullPath); if (!sourceFile) { return component; } return (0, componentInfoExtractor_1.enhanceComponentInfo)(component, sourceFile, this.scanResult); })); // Find similarities between components const similarities = []; const chunkSize = 100; for (let i = 0; i < enhancedComponents.length; i++) { for (let j = i + 1; j < enhancedComponents.length; j++) { if (!(0, componentFilter_1.shouldCompareComponents)(enhancedComponents[i], enhancedComponents[j], thresholds.nameDistanceThreshold)) { continue; } // Skip components from very different contexts if (!shouldCompareByContext(enhancedComponents[i], enhancedComponents[j])) { continue; } comparisonCount++; if (comparisonCount % chunkSize === 0) { await new Promise((resolve) => setTimeout(resolve, 0)); } const similarity = (0, similarity_1.compareComponents)(enhancedComponents[i], enhancedComponents[j], thresholds); similarities.push(similarity); } } // Filter for significant matches const significantMatches = (0, similarity_1.filterSignificantSimilarities)(similarities, thresholds.minSimilarityScore); // Group similar components const groupedSimilarities = (0, grouping_1.groupSimilarComponents)(significantMatches); // Apply consolidation const consolidatedResults = (0, grouping_1.consolidateSimilarities)(groupedSimilarities); return consolidatedResults; } finally { this.sourceFiles.clear(); } } /** * Component validation using scan result metadata */ isValidForDeduplication(component) { if (!(0, componentFilter_1.isValidComponentForComparison)(component)) { return false; } const metadata = this.scanResult.fileMetadata.get(component.fullPath); if (metadata) { // Skip test files if (metadata.isTest) { return false; } // Require React patterns if (!metadata.hasReactImport && !metadata.hasJSX && metadata.componentCount === 0) { return false; } } return true; } } exports.DeduplicationAnalyzer = DeduplicationAnalyzer; /** * Check if components should be compared based on their context/purpose */ function shouldCompareByContext(comp1, comp2) { // Extract context from file paths const context1 = extractContext(comp1.fullPath); const context2 = extractContext(comp2.fullPath); // Don't compare components from very different contexts const differentContexts = ["auth", "marketing", "admin", "dashboard"]; if (differentContexts.includes(context1) && differentContexts.includes(context2) && context1 !== context2) { return false; } return true; } /** * Extract context from file path */ function extractContext(filePath) { const pathParts = filePath.split("/"); // Look for Next.js route groups like (auth), (marketing) for (const part of pathParts) { if (part.startsWith("(") && part.endsWith(")")) { return part.slice(1, -1); } } // Look for common directory patterns const contextPatterns = [ "auth", "marketing", "admin", "dashboard", "components", ]; for (const part of pathParts) { if (contextPatterns.includes(part.toLowerCase())) { return part.toLowerCase(); } } return "general"; }