UNPKG

mira-consciousness

Version:

Memory & Intelligence Retention Archive - Preserving The Spark

166 lines 6.88 kB
/** * Bundle Performance Analyzer * Analyzes JavaScript bundle sizes and optimization opportunities */ import fs from 'fs-extra'; import * as path from 'path'; export class BundleAnalyzer { projectRoot; constructor(projectRoot) { this.projectRoot = projectRoot; } async analyze(issues) { try { // Look for webpack build output const distPath = path.join(this.projectRoot, 'dist'); const buildPath = path.join(this.projectRoot, 'build'); let bundlePath = ''; if (await fs.pathExists(distPath)) { bundlePath = distPath; } else if (await fs.pathExists(buildPath)) { bundlePath = buildPath; } else { return undefined; } const files = await fs.readdir(bundlePath); const jsFiles = files.filter(f => f.endsWith('.js')); const chunkSizes = {}; const largestAssets = []; let totalSize = 0; for (const file of jsFiles) { const filePath = path.join(bundlePath, file); const stats = await fs.stat(filePath); const size = stats.size; chunkSizes[file] = size; totalSize += size; largestAssets.push({ name: file, size }); // Check for oversized bundles if (size > 1024 * 1024) { // 1MB issues.push({ file: `${bundlePath}/${file}`, type: 'large_bundle', message: `Bundle is large (${(size / 1024 / 1024).toFixed(1)}MB)`, impact: 'high', recommendation: 'Consider code splitting or tree shaking' }); } } // Sort largest assets largestAssets.sort((a, b) => b.size - a.size); // Analyze for duplicated modules const duplicatedModules = await this.findDuplicatedModules(bundlePath, jsFiles); // Generate suggestions const suggestions = this.generateBundleSuggestions(totalSize, jsFiles.length, largestAssets, duplicatedModules); // Estimate gzipped size const gzippedSize = await this.estimateGzippedSize(bundlePath, jsFiles); return { totalSize, gzippedSize, chunkSizes, largestAssets: largestAssets.slice(0, 10), duplicatedModules, suggestions }; } catch (error) { return undefined; } } async findDuplicatedModules(bundlePath, jsFiles) { const moduleSignatures = new Map(); const duplicates = []; for (const file of jsFiles) { try { const filePath = path.join(bundlePath, file); const content = await fs.readFile(filePath, 'utf-8'); // Look for common module patterns const modulePatterns = [ /\/\*\*\*\/ \"([\w-]+)\"/g, // Webpack module comments /define\("([\w-]+)",/g, // AMD modules /exports\["([\w-]+)"\]/g // CommonJS exports ]; for (const pattern of modulePatterns) { let match; while ((match = pattern.exec(content)) !== null) { const moduleName = match[1]; if (!moduleSignatures.has(moduleName)) { moduleSignatures.set(moduleName, []); } moduleSignatures.get(moduleName).push(file); } } } catch (error) { // Skip file if can't read } } // Find modules that appear in multiple files for (const [moduleName, files] of moduleSignatures) { if (files.length > 1) { duplicates.push(`${moduleName} (in ${files.join(', ')})`); } } return duplicates; } generateBundleSuggestions(totalSize, fileCount, largestAssets, duplicatedModules) { const suggestions = []; // Size-based suggestions if (totalSize > 5 * 1024 * 1024) { // 5MB total suggestions.push('Total bundle size is large - consider aggressive code splitting'); } if (totalSize > 10 * 1024 * 1024) { // 10MB total suggestions.push('Bundle size is excessive - review dependencies and enable tree shaking'); } // File count suggestions if (fileCount === 1) { suggestions.push('Single bundle detected - consider code splitting for better caching'); } if (fileCount > 20) { suggestions.push('Many bundle chunks - consider consolidating for fewer HTTP requests'); } // Largest asset suggestions if (largestAssets.length > 0 && largestAssets[0].size > 2 * 1024 * 1024) { suggestions.push(`Largest bundle (${largestAssets[0].name}) is over 2MB - split critical path`); } // Duplication suggestions if (duplicatedModules.length > 0) { suggestions.push(`Found ${duplicatedModules.length} duplicated modules - configure webpack optimization.splitChunks`); } // Common library suggestions const hasReact = largestAssets.some(a => a.name.includes('react')); const hasVendor = largestAssets.some(a => a.name.includes('vendor')); if (!hasVendor && fileCount > 1) { suggestions.push('Consider extracting vendor libraries into separate chunk'); } if (hasReact) { suggestions.push('Ensure React is in production mode for smaller bundle size'); } return suggestions; } async estimateGzippedSize(bundlePath, jsFiles) { let totalGzippedSize = 0; for (const file of jsFiles) { try { const filePath = path.join(bundlePath, file); const stats = await fs.stat(filePath); // Check if .gz version exists const gzPath = filePath + '.gz'; if (await fs.pathExists(gzPath)) { const gzStats = await fs.stat(gzPath); totalGzippedSize += gzStats.size; } else { // Estimate gzip size as 30% of original totalGzippedSize += Math.floor(stats.size * 0.3); } } catch (error) { // Skip file if can't read } } return totalGzippedSize; } } //# sourceMappingURL=BundleAnalyzer.js.map