UNPKG

ai-debug-local-mcp

Version:

🎯 ENHANCED AI GUIDANCE v4.1.2: Dramatically improved tool descriptions help AI users choose the right tools instead of 'close enough' options. Ultra-fast keyboard automation (10x speed), universal recording, multi-ecosystem debugging support, and compreh

273 lines • 11.2 kB
/** * NextJSBundleAnalyzer - Analyzes Next.js bundle structure and performance * * This module handles: * - Bundle size analysis per route * - Code splitting effectiveness measurement * - Unused dependency detection * - Performance optimization recommendations * - Comprehensive bundle reporting */ export class NextJSBundleAnalyzer { /** * Analyze the complete bundle structure of a Next.js application */ async analyzeBundleStructure(page) { try { const bundleData = await page.evaluate(() => { const debug = window.__NEXTJS_ADVANCED_DEBUG__; if (!debug?.bundleAnalysis) { return { bundleAnalysis: { firstLoadJS: {}, chunks: [] } }; } return { bundleAnalysis: debug.bundleAnalysis }; }); const { bundleAnalysis } = bundleData; // Calculate total bundle size const totalSize = Object.values(bundleAnalysis.firstLoadJS || {}) .reduce((sum, size) => sum + (typeof size === 'number' ? size : 0), 0); // Analyze code splitting effectiveness const chunks = bundleAnalysis.chunks || []; const sharedChunks = chunks .filter((chunk) => chunk.route === 'shared' || chunk.name.includes('vendor')) .map((chunk) => chunk.name); const sharedSize = chunks .filter((chunk) => sharedChunks.includes(chunk.name)) .reduce((sum, chunk) => sum + (chunk.size || 0), 0); const codeSplittingEffectiveness = totalSize > 0 ? (sharedSize / totalSize) * 100 : 0; // Create route chunks mapping const routeChunks = {}; Object.keys(bundleAnalysis.firstLoadJS || {}).forEach(route => { routeChunks[route] = chunks .filter((chunk) => chunk.route === route || chunk.route === 'shared') .map((chunk) => chunk.name); }); // Detect unused dependencies const unusedDependencies = await this.detectUnusedDependencies(page); return { firstLoadJS: bundleAnalysis.firstLoadJS || {}, totalBundleSize: totalSize, unusedDependencies, codeSplitting: { effectiveness: codeSplittingEffectiveness, sharedChunks, routeChunks } }; } catch (error) { console.error('Error analyzing bundle structure:', error); return { firstLoadJS: {}, totalBundleSize: 0, unusedDependencies: [], codeSplitting: { effectiveness: 0, sharedChunks: [], routeChunks: {} } }; } } /** * Detect unused dependencies by comparing package.json with actual bundle usage */ async detectUnusedDependencies(page) { try { const depData = await page.evaluate(() => { // In a real implementation, this would analyze the actual bundle chunks // For now, we'll simulate dependency analysis return { packageData: null, // Would need to read package.json bundleChunks: [] // Would need to analyze actual chunk contents }; }); if (!depData.packageData) { return []; } const dependencies = Object.keys(depData.packageData.dependencies || {}); const usedDeps = new Set(depData.bundleChunks); return dependencies.filter(dep => !usedDeps.has(dep)); } catch (error) { console.error('Error detecting unused dependencies:', error); return []; } } /** * Analyze bundle performance and identify issues */ analyzePerformance(bundleData) { const issues = []; const recommendations = []; // Calculate metrics const firstLoadSizes = Object.values(bundleData.firstLoadJS); const avgFirstLoadSize = firstLoadSizes.length > 0 ? firstLoadSizes.reduce((a, b) => a + b, 0) / firstLoadSizes.length : 0; const largestRoute = Object.entries(bundleData.firstLoadJS) .reduce((largest, [route, size]) => size > largest.size ? { route, size } : largest, { route: '', size: 0 }); const sharedCodeRatio = bundleData.codeSplitting.effectiveness; // Performance analysis let score = 100; // Check total bundle size if (bundleData.totalBundleSize > 1000000) { // 1MB issues.push('Large bundle size detected'); recommendations.push('Consider code splitting and lazy loading'); score -= 30; } else if (bundleData.totalBundleSize > 500000) { // 500KB issues.push('Bundle size could be optimized'); recommendations.push('Review bundle composition for optimization opportunities'); score -= 15; } // Check individual route sizes if (largestRoute.size > 500000) { // 500KB issues.push('Large individual route bundle'); recommendations.push(`Optimize route ${largestRoute.route} - consider component lazy loading`); score -= 20; } // Check code splitting effectiveness if (bundleData.codeSplitting.effectiveness < 30) { issues.push('Poor code splitting effectiveness'); recommendations.push('Implement better code splitting strategies'); score -= 25; } // Check unused dependencies if (bundleData.unusedDependencies.length > 0) { issues.push('Unused dependencies detected'); recommendations.push(`Remove unused dependencies: ${bundleData.unusedDependencies.join(', ')}`); score -= bundleData.unusedDependencies.length * 5; } return { score: Math.max(0, Math.min(100, score)), issues, recommendations, metrics: { avgFirstLoadSize, largestRoute, sharedCodeRatio } }; } /** * Get specific optimization suggestions based on bundle analysis */ getOptimizationSuggestions(bundleData) { const suggestions = []; // Code splitting suggestions if (bundleData.codeSplitting.effectiveness < 40) { suggestions.push('Implement dynamic imports for route-based code splitting'); suggestions.push('Consider lazy loading non-critical components'); } // Large bundle suggestions if (bundleData.totalBundleSize > 1000000) { suggestions.push('Enable tree shaking in build configuration'); suggestions.push('Analyze bundle with webpack-bundle-analyzer'); suggestions.push('Consider using a lighter alternative to large dependencies'); } // Unused dependencies if (bundleData.unusedDependencies.length > 0) { suggestions.push(`Remove unused dependencies: ${bundleData.unusedDependencies.join(', ')}`); } // Route-specific suggestions const largestRoutes = Object.entries(bundleData.firstLoadJS) .filter(([_, size]) => size > 300000) .map(([route, _]) => route); if (largestRoutes.length > 0) { suggestions.push(`Optimize large routes: ${largestRoutes.join(', ')}`); } // Shared chunk suggestions if (bundleData.codeSplitting.sharedChunks.length === 0) { suggestions.push('Create shared vendor chunks for common dependencies'); } return suggestions; } /** * Generate a comprehensive bundle analysis report */ async generateReport(page) { try { const bundleData = await this.analyzeBundleStructure(page); const performance = this.analyzePerformance(bundleData); const suggestions = this.getOptimizationSuggestions(bundleData); const routeCount = Object.keys(bundleData.firstLoadJS).length; const avgSize = routeCount > 0 ? Object.values(bundleData.firstLoadJS).reduce((a, b) => a + b, 0) / routeCount : 0; const summary = `Bundle Analysis Summary: Total Bundle: ${(bundleData.totalBundleSize / 1024).toFixed(2)}KB Routes: ${routeCount} Average First Load: ${(avgSize / 1024).toFixed(2)}KB Code Splitting: ${bundleData.codeSplitting.effectiveness.toFixed(1)}% effective Performance Score: ${performance.score}/100`; return { summary, bundle: bundleData, performance, suggestions }; } catch (error) { console.error('Error generating bundle report:', error); throw error; } } /** * Compare bundle metrics against Next.js performance baselines */ compareAgainstBaselines(bundleData) { const details = []; let score = 0; // Bundle size baseline (recommended < 250KB first load) const avgFirstLoad = Object.values(bundleData.firstLoadJS).length > 0 ? Object.values(bundleData.firstLoadJS).reduce((a, b) => a + b, 0) / Object.values(bundleData.firstLoadJS).length : 0; if (avgFirstLoad < 250000) { score += 25; details.push('✓ First load size within recommended limits'); } else { details.push('⚠ First load size exceeds 250KB recommendation'); } // Code splitting baseline (recommended > 50%) if (bundleData.codeSplitting.effectiveness > 50) { score += 25; details.push('✓ Good code splitting effectiveness'); } else { details.push('⚠ Code splitting could be improved'); } // Unused dependencies baseline (should be 0) if (bundleData.unusedDependencies.length === 0) { score += 25; details.push('✓ No unused dependencies detected'); } else { details.push(`⚠ ${bundleData.unusedDependencies.length} unused dependencies found`); } // Total bundle baseline (recommended < 1MB) if (bundleData.totalBundleSize < 1000000) { score += 25; details.push('✓ Total bundle size within limits'); } else { details.push('⚠ Total bundle size exceeds 1MB'); } let status; if (score >= 90) status = 'excellent'; else if (score >= 70) status = 'good'; else if (score >= 50) status = 'needs-improvement'; else status = 'poor'; return { status, details }; } } //# sourceMappingURL=nextjs-bundle-analyzer.js.map