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
JavaScript
/**
* 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