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

304 lines • 11.1 kB
/** * NextJS Performance & Optimization Module * * Handles bundle analysis, font optimization, and performance monitoring. * This module provides insights for optimizing Next.js application performance. */ export class NextJSPerformanceOptimization { page; bundleAnalysis = { firstLoadJS: {}, totalBundleSize: 0, unusedDependencies: [], codeSplitting: { effectiveness: 0, sharedChunks: [], routeSpecificChunks: {} } }; async attachToPage(page) { this.page = page; await this.setupBundleAnalysis(); await this.setupFontLoadingAnalysis(); await this.setupPerformanceMonitoring(); } async getBundleAnalysis() { return this.bundleAnalysis || { firstLoadJS: {}, totalBundleSize: 0, unusedDependencies: [], codeSplitting: { effectiveness: 0, sharedChunks: [], routeSpecificChunks: {} } }; } async getFontLoadingAnalysis() { if (!this.page) { return { strategy: 'auto', fontsUsingNextFont: 0, fontsNotOptimized: [], variableFonts: [], cls: 0, loadTime: 0 }; } try { const fontAnalysis = await this.page.evaluate(() => { const analysis = { strategy: 'auto', fontsUsingNextFont: 0, fontsNotOptimized: [], variableFonts: [], cls: 0, loadTime: 0 }; // Analyze font loading strategy const fontDisplays = document.querySelectorAll('link[rel="stylesheet"]'); fontDisplays.forEach(link => { const href = link.getAttribute('href'); if (href?.includes('fonts.googleapis.com')) { analysis.fontsNotOptimized.push(href); } }); // Check for Next.js font optimization const nextFontLinks = document.querySelectorAll('link[data-next-font]'); analysis.fontsUsingNextFont = nextFontLinks.length; return analysis; }); return fontAnalysis || { strategy: 'auto', fontsUsingNextFont: 0, fontsNotOptimized: [], variableFonts: [], cls: 0, loadTime: 0 }; } catch (error) { return { strategy: 'auto', fontsUsingNextFont: 0, fontsNotOptimized: [], variableFonts: [], cls: 0, loadTime: 0 }; } } async getPerformanceScore() { if (!this.page) { return { overall: 0, fcp: 0, lcp: 0, cls: 0, fid: 0, ttfb: 0, score: 0, metrics: { fcp: 0, lcp: 0, cls: 0, fid: 0, ttfb: 0 }, recommendations: [], breakdown: { nextjsOptimizations: 0, bundleSize: 0, caching: 0, rendering: 0 } }; } try { const performanceData = await this.page.evaluate(() => { const navigationTiming = performance.getEntriesByType('navigation')[0]; const paintTiming = performance.getEntriesByType('paint'); const fcp = paintTiming.find((entry) => entry.name === 'first-contentful-paint')?.startTime || 0; const ttfb = navigationTiming.responseStart - navigationTiming.requestStart; // Calculate scores (simplified) const fcpScore = Math.max(0, 100 - (fcp / 18)); const ttfbScore = Math.max(0, 100 - (ttfb / 6)); const bundleScore = 80; const cachingScore = 70; const renderingScore = 85; const overall = (fcpScore + ttfbScore + bundleScore + cachingScore + renderingScore) / 5; return { overall, fcp, lcp: fcp + 500, cls: 0, fid: 0, ttfb, score: overall, metrics: { fcp, lcp: fcp + 500, cls: 0, fid: 0, ttfb }, recommendations: overall < 75 ? ['Optimize bundle size', 'Improve caching', 'Optimize rendering'] : [], breakdown: { nextjsOptimizations: 75, bundleSize: bundleScore, caching: cachingScore, rendering: renderingScore } }; }); return performanceData || { overall: 0, fcp: 0, lcp: 0, cls: 0, fid: 0, ttfb: 0, score: 0, metrics: { fcp: 0, lcp: 0, cls: 0, fid: 0, ttfb: 0 }, recommendations: [], breakdown: { nextjsOptimizations: 0, bundleSize: 0, caching: 0, rendering: 0 } }; } catch (error) { return { overall: 0, fcp: 0, lcp: 0, cls: 0, fid: 0, ttfb: 0, score: 0, metrics: { fcp: 0, lcp: 0, cls: 0, fid: 0, ttfb: 0 }, recommendations: [], breakdown: { nextjsOptimizations: 0, bundleSize: 0, caching: 0, rendering: 0 } }; } } async analyzeBundle() { if (!this.page) { return { totalSize: 0, gzippedSize: 0, chunks: [], recommendations: [] }; } const bundleInfo = await this.page.evaluate(() => { const scripts = document.querySelectorAll('script[src*="_next/static/chunks/"]'); const chunks = []; let totalSize = 0; scripts.forEach((script, index) => { const src = script.getAttribute('src'); if (src) { const name = src.split('/').pop() || `chunk-${index}`; const estimatedSize = 50000; chunks.push({ name, size: estimatedSize, type: src.includes('runtime') ? 'runtime' : src.includes('pages') ? 'initial' : 'async' }); totalSize += estimatedSize; } }); const recommendations = []; if (chunks.length > 10) { recommendations.push('Consider code splitting to reduce initial bundle size'); } if (totalSize > 500000) { recommendations.push('Bundle size is large - consider tree shaking and dynamic imports'); } return { totalSize, gzippedSize: Math.round(totalSize * 0.3), chunks, recommendations }; }); return bundleInfo; } async setupBundleAnalysis() { if (!this.page) return; const bundleData = await this.page.evaluate(() => { const scripts = document.querySelectorAll('script[src*="_next/static/chunks/"]'); const analysis = { firstLoadJS: {}, totalBundleSize: 0, unusedDependencies: [], codeSplitting: { effectiveness: 0, sharedChunks: [], routeSpecificChunks: {} } }; scripts.forEach((script) => { const src = script.getAttribute('src'); if (src) { const route = 'current-route'; const estimatedSize = 50000; if (!analysis.firstLoadJS[route]) { analysis.firstLoadJS[route] = 0; } analysis.firstLoadJS[route] += estimatedSize; analysis.totalBundleSize += estimatedSize; if (src.includes('chunks/')) { analysis.codeSplitting.sharedChunks.push(src); } } }); const totalChunks = scripts.length; const sharedChunks = analysis.codeSplitting.sharedChunks.length; analysis.codeSplitting.effectiveness = totalChunks > 0 ? (sharedChunks / totalChunks) * 100 : 0; return analysis; }); this.bundleAnalysis = bundleData; } async setupFontLoadingAnalysis() { if (!this.page) return; await this.page.evaluate(() => { if (!window.document.fonts) return; const fontAnalysis = { loadTime: 0, fontsLoaded: 0 }; const fontLoadPromises = []; const fontIterator = window.document.fonts.values(); let font; while ((font = fontIterator.next()).value) { fontLoadPromises.push(font.loaded.then(() => { fontAnalysis.fontsLoaded++; })); } Promise.all(fontLoadPromises).then(() => { fontAnalysis.loadTime = performance.now(); window.__nextjs_font_analysis = fontAnalysis; }); }); } async setupPerformanceMonitoring() { if (!this.page) return; await this.page.evaluate(() => { if ('PerformanceObserver' in window) { const observer = new PerformanceObserver((list) => { const entries = list.getEntries(); for (const entry of entries) { if (entry.entryType === 'layout-shift') { const layoutShiftEntry = entry; if (!layoutShiftEntry.hadRecentInput) { window.__nextjs_cls = (window.__nextjs_cls || 0) + layoutShiftEntry.value; } } } }); observer.observe({ entryTypes: ['layout-shift'] }); } }); } } //# sourceMappingURL=nextjs-performance-optimization.js.map