UNPKG

sicua

Version:

A tool for analyzing project structure and dependencies

336 lines (335 loc) • 19.2 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 (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ProjectAnalyzer = void 0; // General Imports const configManager_1 = require("./configManager"); const progressTracker_1 = require("./progressTracker"); const fs = __importStar(require("fs/promises")); const typescript_1 = __importDefault(require("typescript")); // Parsers Imports const directoryScanner_1 = require("../parsers/directoryScanner"); const translationSourceScanner_1 = require("../parsers/translationSourceScanner"); const fileParser_1 = require("../parsers/fileParser"); // Utils & Types Imports const analysisUtils_1 = require("../utils/common/analysisUtils"); const contentProcessor_1 = require("../utils/common/contentProcessor"); // Analyzers imports const ComponentAnalyzer_1 = require("../analyzers/component/ComponentAnalyzer"); const FunctionAnalyzer_1 = require("../analyzers/function/FunctionAnalyzer"); const TypeAnalyzer_1 = require("../analyzers/type/TypeAnalyzer"); const ComplexityAnalyzer_1 = require("../analyzers/complexity/ComplexityAnalyzer"); const DeduplicationAnalyzer_1 = require("../analyzers/deduplication/DeduplicationAnalyzer"); const ErrorHandlingAnalyzer_1 = require("../analyzers/errorHandling/ErrorHandlingAnalyzer"); const SeoAnalyzer_1 = require("../analyzers/seo/SeoAnalyzer"); const TranslationAnalyzer_1 = require("../analyzers/translation/TranslationAnalyzer"); const SecurityAnalyzer_1 = require("../analyzers/security/SecurityAnalyzer"); // Graph generators const graphGenerator_1 = require("../generators/graphGenerator"); const structureGraphGenerator_1 = require("../generators/structureGraphGenerator"); const ComponentFlowAnalyzer_1 = require("../analyzers/componentFlow/ComponentFlowAnalyzer"); const GeneralAnalyzer_1 = require("../analyzers/general/GeneralAnalyzer"); const ComponentScoringAnalyzer_1 = require("../analyzers/scoring/ComponentScoringAnalyzer"); const AccessibilityAnalyzer_1 = require("../analyzers/accessibility/AccessibilityAnalyzer"); const path_1 = __importDefault(require("path")); const componentLookupService_1 = require("./componentLookupService"); const pathResolver_1 = require("../parsers/pathResolver"); class ProjectAnalyzer { constructor(projectPath) { this.scanResult = null; this.typeChecker = null; this.program = null; this.config = new configManager_1.ConfigManager(projectPath); this.contentProcessor = new contentProcessor_1.ContentProcessor(); this.progressTracker = new progressTracker_1.ProgressTracker([ "Loading configuration", "šŸ” Detecting project structure...", "šŸ“ Scanning project directory...", "šŸ“ Parsing component files...", "šŸ“Š Analyzing general metrics...", "šŸ”— Analyzing component dependencies...", "⚔ Analyzing functions...", "šŸŽÆ Analyzing TypeScript types...", "🧮 Calculating complexity metrics...", "šŸ•øļø Generating component dependency graph...", "šŸ—ļø Generating file structure graph...", "šŸ”„ Detecting component duplications...", "āš ļø Analyzing error handling patterns...", "šŸ”Ž Analyzing SEO implementation...", "🌐 Analyzing translation coverage...", "🌊 Analyzing component flow patterns...", "šŸ” Analyzing accessibility compliance...", "šŸ”’ Analyzing security vulnerabilities...", "šŸ† Calculating component scores...", "šŸ’¾ Writing analysis results...", ]); } getProjectPath() { return this.config.projectPath; } async analyze() { try { // Load configuration with dynamic project structure detection await this.config.loadConfig(); this.progressTracker.start(); this.progressTracker.incrementProgress(); // Loading configuration // Validate configuration and show warnings if any const warnings = this.config.validateConfig(); if (warnings.length > 0) { console.warn("āš ļø Configuration warnings:"); warnings.forEach((warning) => console.warn(` ${warning}`)); } this.progressTracker.incrementProgress(); // Detecting project structure // Perform unified scanning of the project with enhanced structure detection this.scanResult = await (0, directoryScanner_1.scanDirectory)(this.config.projectPath, this.config); this.progressTracker.incrementProgress(); // Scanning project directory // Initialize TypeScript program and type checker with dynamic paths this.initializeTypeScriptProgram(); // Filter parseable files but keep all for security analysis const parseableScanResult = this.createParseableScanResult(); // Parse components using the enhanced scan result const components = await (0, fileParser_1.parseFiles)(parseableScanResult, this.config.srcDir, this.config); this.progressTracker.incrementProgress(); // Parsing component files // Initialize optimized services once for all analyzers const componentLookupService = new componentLookupService_1.ComponentLookupService(components); const pathResolver = new pathResolver_1.PathResolver(this.config, this.scanResult); // General Analysis const generalAnalyzer = new GeneralAnalyzer_1.GeneralAnalyzer(this.scanResult); const generalAnalysis = await generalAnalyzer.analyze(); this.progressTracker.incrementProgress(); // Analyzing general metrics // Dependency analysis with optimized services const componentAnalyzer = new ComponentAnalyzer_1.ComponentAnalyzer(components, this.config, componentLookupService, pathResolver); const advancedAnalysis = await componentAnalyzer.analyze(); this.progressTracker.incrementProgress(); // Analyzing component dependencies // Function analysis const functionAnalyzer = new FunctionAnalyzer_1.FunctionAnalyzer(components); const functionAnalysis = await functionAnalyzer.analyze(); this.progressTracker.incrementProgress(); // Analyzing functions // Type analysis using the enhanced type analyzer const typeAnalyzer = new TypeAnalyzer_1.TypeAnalyzer(this.scanResult, this.typeChecker); const typeAnalysis = await typeAnalyzer.analyze(); this.progressTracker.incrementProgress(); // Analyzing TypeScript types // Complexity analysis const complexityAnalyzer = new ComplexityAnalyzer_1.ComplexityAnalyzer(components, componentLookupService, pathResolver, this.scanResult); const complexityAnalysis = await complexityAnalyzer.analyze(); this.progressTracker.incrementProgress(); // Calculating complexity metrics // Graph generation for components const componentGraphGenerator = (0, graphGenerator_1.generateGraphData)(components, this.config); this.progressTracker.incrementProgress(); // Generating component dependency graph // Graph generation for file structure with enhanced metadata const structureGraphGenerator = (0, structureGraphGenerator_1.generateStructureGraphData)(this.scanResult, this.config); this.progressTracker.incrementProgress(); // Generating file structure graph // Ensure all component details are loaded components.forEach((component) => componentGraphGenerator.loadComponentDetails(component.name)); // Deduplication analysis const analyzer = new DeduplicationAnalyzer_1.DeduplicationAnalyzer(components, this.scanResult); const similarities = await analyzer.analyzeComponents(components); // Filter significant matches const significantMatches = similarities.filter((s) => s.similarityScore > 0.7); this.progressTracker.incrementProgress(); // Detecting component duplications // Error handling analysis const errorHandlingAnalyzer = new ErrorHandlingAnalyzer_1.ErrorHandlingAnalyzer(this.scanResult.filePaths, components); const errorHandlingAnalysis = errorHandlingAnalyzer.analyze(); this.progressTracker.incrementProgress(); // Analyzing error handling patterns // SEO analysis const seoAnalyzer = new SeoAnalyzer_1.SEOAnalyzer(components); const seoAnalysis = await seoAnalyzer.analyze(); this.progressTracker.incrementProgress(); // Analyzing SEO implementation // Translation analysis const translationSourceFiles = (0, translationSourceScanner_1.getTranslationSourceFiles)(this.scanResult); const translationAnalyzer = new TranslationAnalyzer_1.TranslationAnalyzer(this.config.projectPath, translationSourceFiles); const translationAnalysis = await translationAnalyzer.analyze(); this.progressTracker.incrementProgress(); // Analyzing translation coverage // Component flow analysis with enhanced path handling const componentFlowAnalyzer = new ComponentFlowAnalyzer_1.ComponentFlowAnalyzer(this.config.projectPath, this.config.srcDir, components, componentLookupService, pathResolver, this.scanResult); const componentFlowAnalysis = await componentFlowAnalyzer.analyze(); this.progressTracker.incrementProgress(); // Analyzing component flow patterns const accessibilityAnalyzer = new AccessibilityAnalyzer_1.AccessibilityAnalyzer(this.scanResult, components); const accessibilityAnalysis = await accessibilityAnalyzer.analyze(); this.progressTracker.incrementProgress(); // Analyzing accessibility compliance // Security analysis with comprehensive scan result const securityAnalyzer = new SecurityAnalyzer_1.SecurityAnalyzer(); const securityAnalysis = await securityAnalyzer.analyze(this.scanResult, { projectPath: this.config.projectPath, sourcePath: this.config.srcDir, filesToAnalyze: this.scanResult.filePaths, }); this.progressTracker.incrementProgress(); // Analyzing security vulnerabilities // Component scoring const componentScoringAnalyzer = new ComponentScoringAnalyzer_1.ComponentScoringAnalyzer(); const topComponents = await componentScoringAnalyzer.calculateTopScoringComponents(components, { generalAnalysis, dependencyAnalysis: advancedAnalysis, errorHandlingAnalysis, complexityAnalysis, typeAnalysis, seoAnalysis, translationAnalysis, componentFlowAnalysis, deduplicationAnalysis: significantMatches, }, 20 // Top 20 components ); this.progressTracker.incrementProgress(); // Calculating component scores // Build the final analysis result const analysisResult = { generalAnalysis, componentDependencyGraph: componentGraphGenerator.getSigmaData(), fileStructureGraph: structureGraphGenerator.getStructureData(), advancedAnalysis, functionAnalysis, typeAnalysis, complexityAnalysis, deduplicationAnalysis: significantMatches, errorHandlingAnalysis, seoAnalysis, translationAnalysis, componentFlowAnalysis, accessibilityAnalysis, securityAnalysis, topScoringComponents: topComponents, }; // Write output file await fs.writeFile(this.config.outputFileName, JSON.stringify(analysisResult, analysisUtils_1.replacer, 2)); this.progressTracker.incrementProgress(); // Processing and writing output this.progressTracker.complete(); // Show any configuration warnings again at the end if (warnings.length > 0) { console.log(`\nāš ļø ${warnings.length} configuration warning(s) - see above for details`); } } catch (error) { console.error("\nāŒ An error occurred during analysis:", error); // Provide helpful error context if (error instanceof Error) { if (error.message.includes("ENOENT") || error.message.includes("does not exist")) { console.error("šŸ’” This might be a path or file access issue. Please check:"); console.error(" - The project path exists and is accessible"); console.error(" - You have read permissions for the directory"); console.error(" - The detected source directory is correct"); } if (error.message.includes("package.json")) { console.error("šŸ’” Package.json issue detected. Please ensure:"); console.error(" - package.json exists in the project root"); console.error(" - The file contains valid JSON"); console.error(" - You're running the analyzer from the correct directory"); } } throw error; } } /** * Create a filtered scan result for parseable files while keeping security data intact */ createParseableScanResult() { const parseableExtensions = [".ts", ".tsx", ".js", ".jsx"]; const parseableFiles = this.scanResult.filePaths.filter((filePath) => { const ext = path_1.default.extname(filePath).toLowerCase(); return parseableExtensions.includes(ext); }); return { ...this.scanResult, filePaths: parseableFiles, // Filter the sourceFiles and fileContents maps to parseable files only sourceFiles: new Map(Array.from(this.scanResult.sourceFiles.entries()).filter(([filePath]) => { const ext = path_1.default.extname(filePath).toLowerCase(); return parseableExtensions.includes(ext); })), fileContents: new Map(Array.from(this.scanResult.fileContents.entries()).filter(([filePath]) => { const ext = path_1.default.extname(filePath).toLowerCase(); return parseableExtensions.includes(ext); })), fileMetadata: new Map(Array.from(this.scanResult.fileMetadata.entries()).filter(([filePath]) => { const ext = path_1.default.extname(filePath).toLowerCase(); return parseableExtensions.includes(ext); })), }; } /** * Initialize TypeScript program and type checker for static analysis with enhanced path handling */ initializeTypeScriptProgram() { if (!this.scanResult) { throw new Error("Scan result is not available"); } try { // Look for tsconfig.json in project root or source directory const possibleTsConfigPaths = [ path_1.default.join(this.config.projectPath, "tsconfig.json"), path_1.default.join(this.config.srcDir, "tsconfig.json"), ]; let tsConfigPath; let compilerOptions = { target: typescript_1.default.ScriptTarget.Latest, module: typescript_1.default.ModuleKind.ESNext, jsx: typescript_1.default.JsxEmit.React, allowJs: true, esModuleInterop: true, skipLibCheck: true, noEmit: true, incremental: false, }; // Try to find and parse tsconfig.json for (const configPath of possibleTsConfigPaths) { try { const configFile = typescript_1.default.readConfigFile(configPath, typescript_1.default.sys.readFile); if (!configFile.error) { tsConfigPath = configPath; const parsedConfig = typescript_1.default.parseJsonConfigFileContent(configFile.config, typescript_1.default.sys, path_1.default.dirname(configPath)); if (!parsedConfig.errors.length) { compilerOptions = { ...compilerOptions, ...parsedConfig.options }; break; } } } catch (error) { // Continue to next possible config path } } if (!tsConfigPath) { console.log("šŸ“„ No tsconfig.json found, using default TypeScript configuration"); } // Create program with all source files this.program = typescript_1.default.createProgram(Array.from(this.scanResult.sourceFiles.keys()), compilerOptions); this.typeChecker = this.program.getTypeChecker(); } catch (error) { console.warn("āš ļø Failed to initialize TypeScript program:", error); console.log("šŸ”§ Continuing with basic analysis (some type features may be limited)"); // Create a minimal program as fallback this.program = typescript_1.default.createProgram(Array.from(this.scanResult.sourceFiles.keys()), { target: typescript_1.default.ScriptTarget.Latest, module: typescript_1.default.ModuleKind.ESNext, allowJs: true, noEmit: true, }); this.typeChecker = this.program.getTypeChecker(); } } } exports.ProjectAnalyzer = ProjectAnalyzer;