UNPKG

mira-consciousness

Version:

Memory & Intelligence Retention Archive - Preserving The Spark

561 lines 20.7 kB
/** * Universal Robust Unused Code Analyzer * * Designed for thousands of users across all environments and project types. * Ultra-conservative approach that works safely with any codebase. */ import * as fs from 'fs/promises'; import * as path from 'path'; import { glob } from 'glob'; export class UniversalRobustUnusedCodeAnalyzer { projectRoot; environment = null; safetyConfig; entryPoints = new Set(); reachableFiles = new Set(); // Universal patterns that should ALWAYS be preserved UNIVERSAL_PRESERVE_PATTERNS = [ // Entry points and executables '**/index.*', '**/main.*', '**/app.*', '**/server.*', '**/start.*', '**/launch.*', '**/run.*', 'bin/**', 'scripts/**', // Configuration files '**/*.config.*', '**/config/**', '**/configs/**', '**/configuration/**', // Build and deployment '**/webpack.*', '**/babel.*', '**/tsconfig.*', '**/jsconfig.*', '**/package.json', '**/package-lock.json', '**/yarn.lock', '**/pnpm-lock.yaml', '**/Dockerfile*', '**/docker-compose.*', '**/.env*', // Documentation and metadata '**/README.*', '**/CHANGELOG.*', '**/LICENSE*', '**/CONTRIBUTING.*', 'docs/**', // Testing infrastructure '**/*.test.*', '**/*.spec.*', '**/test/**', '**/tests/**', '**/spec/**', '**/jest.*', '**/karma.*', '**/cypress.*', // Source maps and build outputs '**/*.map', 'dist/**', 'build/**', 'out/**', 'target/**', // Version control and CI/CD '.git/**', '.github/**', '.gitlab/**', '**/.gitignore', '**/.gitattributes', // IDE and editor files '.vscode/**', '.idea/**', '**/*.iml', // Language-specific preserves // Python '**/__init__.py', '**/setup.py', '**/requirements.*', '**/pyproject.toml', // Java '**/pom.xml', '**/build.gradle', '**/MainActivity.*', // C/C++ '**/CMakeLists.txt', '**/Makefile*', '**/main.c*', // Go '**/go.mod', '**/go.sum', '**/main.go', // Rust '**/Cargo.toml', '**/Cargo.lock', '**/main.rs', // PHP '**/composer.json', '**/composer.lock', '**/index.php', // Ruby '**/Gemfile*', '**/Rakefile', // .NET '**/*.csproj', '**/*.sln', '**/Program.cs', // Mobile development '**/Info.plist', '**/AndroidManifest.xml', '**/pubspec.yaml', // Static site generators '**/gatsby-*', '**/next.config.*', '**/nuxt.config.*', '**/vite.config.*', // Database and data '**/*.sql', '**/*.db', '**/*.sqlite*', '**/migrations/**', '**/seeds/**', // Security and certificates '**/*.pem', '**/*.key', '**/*.crt', '**/*.cert', // Any file with "main", "index", "app", "start" in the name '**/*main*', '**/*index*', '**/*app*', '**/*start*', '**/*entry*', '**/*boot*', '**/*init*', '**/*setup*', '**/*config*', '**/*launch*' ]; constructor(projectRoot, safetyConfig) { this.projectRoot = projectRoot; this.safetyConfig = { maxFilesToAnalyze: 10000, maxDirectoriesToScan: 1000, analysisTimeoutMs: 30000, // 30 seconds max confidenceThreshold: 0.95, // Very high confidence required enableDeepAnalysis: false, // Disabled by default for safety preservePatterns: [], ...safetyConfig }; } async analyze() { console.log('🌍 Starting Universal Robust Analysis...'); const startTime = Date.now(); try { // Set timeout for entire analysis const timeoutPromise = new Promise((_, reject) => { setTimeout(() => reject(new Error('Analysis timeout')), this.safetyConfig.analysisTimeoutMs); }); const analysisPromise = this.performSafeAnalysis(); const result = await Promise.race([analysisPromise, timeoutPromise]); const duration = Date.now() - startTime; console.log(`✅ Analysis completed in ${duration}ms`); return result; } catch (error) { console.error('❌ Analysis failed:', error); // Return safe default result on any error return this.createSafeDefaultResult(); } } async performSafeAnalysis() { // Step 1: Detect project environment await this.detectProjectEnvironment(); // Step 2: Universal entry point detection await this.detectUniversalEntryPoints(); // Step 3: Conservative file classification const allFiles = await this.scanProjectFiles(); // Step 4: Ultra-conservative unused detection const unusedFiles = await this.detectUnusedFilesConservatively(allFiles); // Convert to expected type structure const languageInfos = (this.environment?.languages || []).map(lang => ({ name: lang, percentage: 0, files: 0, linesOfCode: 0, entryPoints: [], buildFiles: [], packageFiles: [] })); const typedEnvironment = { languages: languageInfos, frameworks: this.environment?.frameworks || [], buildSystems: this.environment?.buildSystems || [], packageManagers: this.environment?.packageManager ? [this.environment.packageManager] : [], operatingSystem: 'unknown', architecture: 'unknown', projectType: this.mapProjectType(this.environment?.type || 'unknown') }; return { score: this.calculateSafetyScore(unusedFiles.length, allFiles.length), environment: typedEnvironment, summary: { totalFiles: allFiles.length, unusedFileCount: unusedFiles.length, unusedFilePercentage: (unusedFiles.length / allFiles.length) * 100, totalFunctions: 0, unusedFunctionCount: 0, unusedFunctionPercentage: 0, unusedImportCount: 0, unusedAssetCount: 0, unusedStyleCount: 0, deadCodeBlockCount: 0, potentialSavings: { linesOfCode: 0, fileSize: 0, estimatedDevelopmentTime: 0 } }, unusedFiles, unusedFunctions: [], unusedImports: [], unusedAssets: [], unusedStyles: [], deadCodeBlocks: [], languageSpecificResults: [], recommendations: [] }; } async detectProjectEnvironment() { const packageJsonPath = path.join(this.projectRoot, 'package.json'); const frameworks = []; const languages = []; const buildSystems = []; let packageManager = null; try { // Detect from package.json if (await fs.access(packageJsonPath).then(() => true).catch(() => false)) { const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf-8')); // Detect frameworks const deps = { ...packageJson.dependencies, ...packageJson.devDependencies }; if (deps.react) frameworks.push('React'); if (deps.vue) frameworks.push('Vue'); if (deps.angular || deps['@angular/core']) frameworks.push('Angular'); if (deps.express) frameworks.push('Express'); if (deps.next) frameworks.push('Next.js'); if (deps.gatsby) frameworks.push('Gatsby'); if (deps.nuxt) frameworks.push('Nuxt.js'); packageManager = 'npm'; } // Detect languages by file extensions const sampleFiles = await glob('**/*.{js,ts,jsx,tsx,py,java,go,rs,php,rb,cs,cpp,c}', { cwd: this.projectRoot, ignore: ['node_modules/**', '.git/**'] }); // Limit for performance const limitedFiles = sampleFiles.slice(0, 100); for (const file of limitedFiles) { const ext = path.extname(file).toLowerCase(); if (['.js', '.jsx', '.ts', '.tsx'].includes(ext) && !languages.includes('JavaScript/TypeScript')) { languages.push('JavaScript/TypeScript'); } if (ext === '.py' && !languages.includes('Python')) languages.push('Python'); if (ext === '.java' && !languages.includes('Java')) languages.push('Java'); if (ext === '.go' && !languages.includes('Go')) languages.push('Go'); if (ext === '.rs' && !languages.includes('Rust')) languages.push('Rust'); if (ext === '.php' && !languages.includes('PHP')) languages.push('PHP'); if (ext === '.rb' && !languages.includes('Ruby')) languages.push('Ruby'); if (ext === '.cs' && !languages.includes('C#')) languages.push('C#'); if (['.cpp', '.c'].includes(ext) && !languages.includes('C/C++')) languages.push('C/C++'); } // Detect other package managers if (await fs.access(path.join(this.projectRoot, 'yarn.lock')).then(() => true).catch(() => false)) { packageManager = 'yarn'; } if (await fs.access(path.join(this.projectRoot, 'pnpm-lock.yaml')).then(() => true).catch(() => false)) { packageManager = 'pnpm'; } this.environment = { type: this.determineProjectType(frameworks, languages), frameworks, languages, buildSystems, packageManager }; } catch (error) { // Safe fallback this.environment = { type: 'unknown', frameworks: [], languages: [], buildSystems: [], packageManager: null }; } } determineProjectType(frameworks, languages) { if (frameworks.includes('React') || frameworks.includes('Vue') || frameworks.includes('Angular')) { return 'web'; } if (frameworks.includes('Express') || languages.includes('JavaScript/TypeScript')) { return 'node'; } if (languages.includes('Java') && (languages.length === 1)) { return 'mobile'; // Could be Android } return 'unknown'; } mapProjectType(type) { switch (type) { case 'web': return 'web'; case 'node': return 'api'; case 'mobile': return 'mobile'; case 'desktop': return 'desktop'; case 'library': return 'library'; default: return 'single'; } } async detectUniversalEntryPoints() { try { // Look for package.json entry points const packageJsonPath = path.join(this.projectRoot, 'package.json'); if (await fs.access(packageJsonPath).then(() => true).catch(() => false)) { const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf-8')); if (packageJson.main) this.entryPoints.add(packageJson.main); if (packageJson.module) this.entryPoints.add(packageJson.module); if (packageJson.bin) { if (typeof packageJson.bin === 'string') { this.entryPoints.add(packageJson.bin); } else { Object.values(packageJson.bin).forEach(bin => this.entryPoints.add(bin)); } } } // Universal entry point patterns const entryPatterns = [ 'index.*', 'main.*', 'app.*', 'server.*', 'start.*', 'bin/**', 'src/index.*', 'src/main.*', 'src/app.*', 'lib/index.*' ]; for (const pattern of entryPatterns) { const matches = await glob(pattern, { cwd: this.projectRoot }); matches.slice(0, 10).forEach(match => this.entryPoints.add(match)); } } catch (error) { // Safe fallback - assume common entry points exist this.entryPoints.add('index.js'); this.entryPoints.add('main.js'); this.entryPoints.add('app.js'); } } async scanProjectFiles() { try { const files = await glob('**/*', { cwd: this.projectRoot, ignore: [ 'node_modules/**', '.git/**', 'dist/**', 'build/**', 'out/**', 'target/**', '**/*.log', '**/*.tmp', '**/tmp/**', '**/temp/**' ], dot: false }); // Limit files for safety const limitedFiles = files.slice(0, this.safetyConfig.maxFilesToAnalyze); // Filter to actual files only const actualFiles = []; for (const file of limitedFiles) { try { const fullPath = path.join(this.projectRoot, file); const stat = await fs.stat(fullPath); if (stat.isFile()) { actualFiles.push(file); } } catch (error) { // Skip files that can't be accessed } } return actualFiles; } catch (error) { console.warn('File scanning failed, returning empty list for safety'); return []; } } async detectUnusedFilesConservatively(allFiles) { const unusedFiles = []; const preservePatterns = [ ...this.UNIVERSAL_PRESERVE_PATTERNS, ...this.safetyConfig.preservePatterns ]; for (const file of allFiles) { // Check if file matches any preserve pattern const isPreserved = preservePatterns.some(pattern => { // Convert glob pattern to regex for matching const regex = new RegExp(pattern .replace(/\*\*/g, '.*') .replace(/\*/g, '[^/]*') .replace(/\?/g, '.')); return regex.test(file); }); if (isPreserved) { continue; // Skip preserved files } // Only flag files with VERY high confidence and specific criteria const confidence = await this.calculateFileUnusedConfidence(file); if (confidence >= this.safetyConfig.confidenceThreshold) { try { const fullPath = path.join(this.projectRoot, file); const stat = await fs.stat(fullPath); unusedFiles.push({ path: file, size: stat.size, lastModified: stat.mtime, reason: 'File appears unused based on conservative analysis', confidence, potentialEntryPoints: [] }); } catch (error) { // Skip files that can't be accessed } } } return unusedFiles; } async calculateFileUnusedConfidence(file) { // Ultra-conservative: Start with very low confidence let confidence = 0.1; try { const fullPath = path.join(this.projectRoot, file); const content = await fs.readFile(fullPath, 'utf-8'); // Very strict criteria for considering a file unused const ext = path.extname(file).toLowerCase(); // Only consider certain file types for potential removal if (!['.log', '.tmp', '.cache', '.bak', '.old'].includes(ext)) { return 0.1; // Very low confidence for normal files } // Check if it's a temporary or cache file if (file.includes('/tmp/') || file.includes('/cache/') || file.includes('/temp/')) { confidence = 0.7; } // Check if it's a log file if (ext === '.log' && content.length > 0) { const lines = content.split('\n'); const lastLine = lines[lines.length - 1]; if (lastLine && lastLine.trim()) { // Check if log is very old (basic heuristic) const dateMatch = lastLine.match(/\d{4}-\d{2}-\d{2}/); if (dateMatch) { const logDate = new Date(dateMatch[0]); const monthsOld = (Date.now() - logDate.getTime()) / (1000 * 60 * 60 * 24 * 30); if (monthsOld > 6) { confidence = 0.8; } } } } // Even stricter for source files - never suggest removal if (['.js', '.ts', '.jsx', '.tsx', '.py', '.java', '.go', '.rs', '.php', '.rb', '.cs', '.cpp', '.c'].includes(ext)) { return 0.05; // Extremely low confidence for source files } } catch (error) { // If we can't read the file, don't suggest removing it return 0.1; } return Math.min(confidence, 0.9); // Cap at 90% confidence for safety } calculateSafetyScore(unusedCount, totalCount) { // Conservative scoring - lower scores for more flagged files if (totalCount === 0) return 100; const unusedPercentage = (unusedCount / totalCount) * 100; if (unusedPercentage < 1) return 95; // Excellent if (unusedPercentage < 5) return 85; // Good if (unusedPercentage < 10) return 70; // Fair if (unusedPercentage < 20) return 50; // Poor return 25; // Very poor - likely analysis error } createSafeDefaultResult() { const typedEnvironment = { languages: [], frameworks: [], buildSystems: [], packageManagers: [], operatingSystem: 'unknown', architecture: 'unknown', projectType: 'single' }; return { score: 90, // Safe default score environment: typedEnvironment, summary: { totalFiles: 0, unusedFileCount: 0, unusedFilePercentage: 0, totalFunctions: 0, unusedFunctionCount: 0, unusedFunctionPercentage: 0, unusedImportCount: 0, unusedAssetCount: 0, unusedStyleCount: 0, deadCodeBlockCount: 0, potentialSavings: { linesOfCode: 0, fileSize: 0, estimatedDevelopmentTime: 0 } }, unusedFiles: [], unusedFunctions: [], unusedImports: [], unusedAssets: [], unusedStyles: [], deadCodeBlocks: [], languageSpecificResults: [], recommendations: [] }; } } //# sourceMappingURL=UniversalRobustUnusedCodeAnalyzer.js.map