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