Back to blog
InspectionJanuary 1, 2024· 16 min read

JavaScript Bundle Analysis and Optimization: A Developer's Guide

A practical Fusebox guide to javascript bundle analysis and optimization.

JavaScript Bundle Analysis and Optimization: A Developer's Guide

JavaScript bundles are the cholesterol of web performance—they accumulate silently until they block everything. The average web page loads 450KB of JavaScript, but studies show users only interact with 30% of it. This guide provides tools to analyze bundle composition, identify bloat, and implement code splitting strategies that actually work.

Why Bundle Size Matters

In 2022, a major e-commerce platform discovered their 2MB JavaScript bundle was causing a 40% cart abandonment rate on mobile. After implementing aggressive code splitting and tree shaking, they reduced the initial bundle to 150KB and increased mobile conversions by 35%. Every kilobyte of JavaScript costs you users.

Bundle Analyzer Tool

Comprehensive bundle analysis in the browser:

// JavaScript Bundle Analyzer
class BundleAnalyzer {
    constructor() {
        this.scripts = [];
        this.modules = new Map();
        this.dependencies = new Map();
        this.coverage = null;
    }
    
    // Analyze all loaded scripts
    async analyzePageBundles() {
        const analysis = {
            scripts: await this.analyzeScripts(),
            coverage: await this.analyzeCoverage(),
            duplicates: this.findDuplicates(),
            optimization: this.calculateOptimizationPotential(),
            recommendations: []
        };
        
        analysis.recommendations = this.generateRecommendations(analysis);
        return analysis;
    }
    
    // Analyze loaded scripts
    async analyzeScripts() {
        const scripts = Array.from(document.querySelectorAll('script'));
        const scriptData = [];
        
        for (const script of scripts) {
            if (script.src) {
                try {
                    const data = await this.analyzeScript(script.src);
                    scriptData.push(data);
                } catch (error) {
                    console.error(`Failed to analyze ${script.src}:`, error);
                }
            } else if (script.textContent) {
                // Inline scripts
                scriptData.push({
                    type: 'inline',
                    content: script.textContent,
                    size: new Blob([script.textContent]).size,
                    location: 'inline',
                    async: script.async,
                    defer: script.defer
                });
            }
        }
        
        // Also check for dynamically loaded scripts
        const resources = performance.getEntriesByType('resource');
        for (const resource of resources) {
            if (resource.initiatorType === 'script' || 
                resource.name.endsWith('.js')) {
                const existing = scriptData.find(s => s.url === resource.name);
                if (!existing) {
                    scriptData.push({
                        url: resource.name,
                        size: resource.transferSize || resource.encodedBodySize,
                        loadTime: resource.duration,
                        type: 'dynamic',
                        startTime: resource.startTime
                    });
                }
            }
        }
        
        this.scripts = scriptData;
        return scriptData;
    }
    
    // Analyze individual script
    async analyzeScript(url) {
        const response = await fetch(url);
        const text = await response.text();
        
        const analysis = {
            url,
            size: new Blob([text]).size,
            gzipSize: response.headers.get('content-length') || 0,
            type: this.detectBundleType(text),
            modules: this.extractModules(text),
            libraries: this.detectLibraries(text),
            async: document.querySelector(`script[src="${url}"]`)?.async || false,
            defer: document.querySelector(`script[src="${url}"]`)?.defer || false,
            loadTime: this.getScriptLoadTime(url),
            parseTime: this.estimateParseTime(text.length),
            isMinified: this.isMinified(text),
            sourceMaps: text.includes('//# sourceMappingURL=')
        };
        
        // Detect potential issues
        analysis.issues = this.detectIssues(text, analysis);
        
        return analysis;
    }
    
    // Detect bundle type
    detectBundleType(code) {
        if (code.includes('webpackJsonp') || code.includes('__webpack_require__')) {
            return 'webpack';
        } else if (code.includes('System.register') || code.includes('System.import')) {
            return 'systemjs';
        } else if (code.includes('define.amd') || code.includes('requirejs')) {
            return 'amd';
        } else if (code.includes('parcelRequire')) {
            return 'parcel';
        } else if (code.includes('__vite__')) {
            return 'vite';
        } else if (code.includes('_rollupPluginBabelHelpers')) {
            return 'rollup';
        }
        return 'unknown';
    }
    
    // Extract modules from bundle
    extractModules(code) {
        const modules = [];
        
        // Webpack module detection
        const webpackModules = code.matchAll(/\/\*\*\*\/ "([^"]+)":/g);
        for (const match of webpackModules) {
            modules.push({
                id: match[1],
                type: 'webpack'
            });
        }
        
        // ES6 imports
        const imports = code.matchAll(/import\s+.*?\s+from\s+["']([^"']+)["']/g);
        for (const match of imports) {
            modules.push({
                id: match[1],
                type: 'es6'
            });
        }
        
        // CommonJS requires
        const requires = code.matchAll(/require\(["']([^"']+)["']\)/g);
        for (const match of requires) {
            modules.push({
                id: match[1],
                type: 'commonjs'
            });
        }
        
        return modules;
    }
    
    // Detect common libraries
    detectLibraries(code) {
        const libraries = [];
        const signatures = {
            'react': /\bReact\.createElement\b|\buseState\b|\buseEffect\b/,
            'react-dom': /\bReactDOM\.render\b|\bReactDOM\.createRoot\b/,
            'vue': /\bnew Vue\b|\bVue\.component\b|\bVue\.use\b/,
            'angular': /\bangular\.module\b|\b@angular\/core\b/,
            'jquery': /\bjQuery\b|\b\$\(.+\)\./,
            'lodash': /\b_\.\w+\b|\blodash\b/,
            'moment': /\bmoment\(\b|\bmoment\.locale\b/,
            'axios': /\baxios\.get\b|\baxios\.post\b/,
            'd3': /\bd3\.select\b|\bd3\.scale\b/,
            'three': /\bTHREE\.\w+\b|\bnew THREE\./,
            'chart.js': /\bnew Chart\b|\bChart\.defaults\b/,
            'rxjs': /\bObservable\b|\bfrom\b|\bof\b.*pipe\b/,
            'redux': /\bcreateStore\b|\bcombineReducers\b/,
            'express': /\bexpress\(\)\b|\bapp\.get\b/,
            'webpack': /\b__webpack_require__\b/,
            'babel': /\b_interopRequireDefault\b/,
            'typescript': /\b__extends\b|\b__awaiter\b/,
            'polyfill': /\bregeneratorRuntime\b|\bPromise\.prototype\.finally\b/
        };
        
        for (const [library, pattern] of Object.entries(signatures)) {
            if (pattern.test(code)) {
                // Try to find version
                const versionPattern = new RegExp(`${library}@([\\d.]+)`, 'i');
                const versionMatch = code.match(versionPattern);
                
                libraries.push({
                    name: library,
                    version: versionMatch ? versionMatch[1] : 'unknown',
                    confidence: pattern.exec(code) ? 'high' : 'medium'
                });
            }
        }
        
        return libraries;
    }
    
    // Get script load time
    getScriptLoadTime(url) {
        const entry = performance.getEntriesByName(url)[0];
        return entry ? entry.duration : 0;
    }
    
    // Estimate parse time
    estimateParseTime(size) {
        // Rough estimate: 1MB takes ~25ms to parse on modern desktop
        // Mobile is ~4x slower
        const baseParseTime = size / 1024 / 1024 * 25;
        const isMobile = /mobile/i.test(navigator.userAgent);
        return isMobile ? baseParseTime * 4 : baseParseTime;
    }
    
    // Check if code is minified
    isMinified(code) {
        const lines = code.split('\n');
        const avgLineLength = code.length / lines.length;
        const hasShortVars = /\b[a-z]\b\s*=/g.test(code);
        const hasNoComments = !(/\/\*[\s\S]*?\*\/|\/\/.*/g.test(code));
        
        return avgLineLength > 200 && hasShortVars && hasNoComments;
    }
    
    // Detect issues in bundle
    detectIssues(code, analysis) {
        const issues = [];
        
        // Check for console logs in production
        if (analysis.isMinified && code.includes('console.log')) {
            issues.push({
                type: 'console-logs',
                severity: 'low',
                message: 'Console logs found in production bundle'
            });
        }
        
        // Check for source maps in production
        if (analysis.isMinified && analysis.sourceMaps) {
            issues.push({
                type: 'source-maps',
                severity: 'medium',
                message: 'Source maps exposed in production'
            });
        }
        
        // Check for development React
        if (code.includes('development.js') || 
            code.includes('This looks like a development build of React')) {
            issues.push({
                type: 'dev-build',
                severity: 'high',
                message: 'Development build detected in production'
            });
        }
        
        // Check for duplicate code patterns
        const codePatterns = code.match(/.{100,}/g) || [];
        const duplicates = codePatterns.filter((pattern, index) => 
            codePatterns.indexOf(pattern) !== index
        );
        
        if (duplicates.length > 10) {
            issues.push({
                type: 'duplicates',
                severity: 'medium',
                message: `${duplicates.length} duplicate code patterns found`
            });
        }
        
        return issues;
    }
    
    // Analyze code coverage
    async analyzeCoverage() {
        if (!window.__coverage__ && typeof window.Coverage === 'undefined') {
            // Try to use Chrome DevTools Protocol if available
            try {
                const coverage = await this.collectCoverage();
                return coverage;
            } catch (e) {
                return { supported: false, message: 'Coverage API not available' };
            }
        }
        
        return { supported: true, data: window.__coverage__ };
    }
    
    // Collect coverage data (simplified)
    async collectCoverage() {
        // This would require Chrome DevTools Protocol
        // Simplified version for demonstration
        const coverage = {
            js: [],
            css: []
        };
        
        // Estimate based on function calls
        this.scripts.forEach(script => {
            if (script.content || script.url) {
                coverage.js.push({
                    url: script.url || 'inline',
                    usedBytes: 0,
                    totalBytes: script.size,
                    unusedPercentage: 100 // Would need real data
                });
            }
        });
        
        return coverage;
    }
    
    // Find duplicate dependencies
    findDuplicates() {
        const modules = new Map();
        const duplicates = [];
        
        this.scripts.forEach(script => {
            if (script.modules) {
                script.modules.forEach(module => {
                    if (!modules.has(module.id)) {
                        modules.set(module.id, []);
                    }
                    modules.get(module.id).push(script.url || 'inline');
                });
            }
        });
        
        modules.forEach((locations, moduleId) => {
            if (locations.length > 1) {
                duplicates.push({
                    module: moduleId,
                    locations,
                    count: locations.length
                });
            }
        });
        
        return duplicates.sort((a, b) => b.count - a.count);
    }
    
    // Calculate optimization potential
    calculateOptimizationPotential() {
        let savingsBytes = 0;
        const opportunities = [];
        
        // Check for unminified code
        const unminified = this.scripts.filter(s => !s.isMinified);
        if (unminified.length > 0) {
            const savings = unminified.reduce((sum, s) => sum + (s.size * 0.7), 0);
            savingsBytes += savings;
            opportunities.push({
                type: 'minification',
                savings,
                scripts: unminified.map(s => s.url || 'inline')
            });
        }
        
        // Check for development builds
        const devBuilds = this.scripts.filter(s => 
            s.issues?.some(i => i.type === 'dev-build')
        );
        if (devBuilds.length > 0) {
            const savings = devBuilds.reduce((sum, s) => sum + (s.size * 0.3), 0);
            savingsBytes += savings;
            opportunities.push({
                type: 'production-builds',
                savings,
                scripts: devBuilds.map(s => s.url)
            });
        }
        
        // Check for duplicate modules
        const duplicateSavings = this.findDuplicates().reduce((sum, dup) => 
            sum + ((dup.count - 1) * 50000), 0 // Estimate 50KB per duplicate
        );
        if (duplicateSavings > 0) {
            savingsBytes += duplicateSavings;
            opportunities.push({
                type: 'deduplication',
                savings: duplicateSavings
            });
        }
        
        return {
            totalSavings: savingsBytes,
            percentage: (savingsBytes / this.getTotalSize()) * 100,
            opportunities
        };
    }
    
    // Get total bundle size
    getTotalSize() {
        return this.scripts.reduce((sum, script) => sum + (script.size || 0), 0);
    }
    
    // Generate recommendations
    generateRecommendations(analysis) {
        const recommendations = [];
        const totalSize = this.getTotalSize();
        
        // Size recommendations
        if (totalSize > 500000) { // 500KB
            recommendations.push({
                priority: 'high',
                type: 'bundle-size',
                message: `Total JavaScript size is ${(totalSize / 1024 / 1024).toFixed(2)}MB`,
                impact: 'Slow initial load, especially on mobile',
                solutions: [
                    'Implement code splitting',
                    'Lazy load non-critical features',
                    'Remove unused dependencies'
                ]
            });
        }
        
        // Check for missing async/defer
        const blockingScripts = analysis.scripts.filter(s => 
            s.url && !s.async && !s.defer && !s.type.includes('module')
        );
        if (blockingScripts.length > 0) {
            recommendations.push({
                priority: 'high',
                type: 'render-blocking',
                message: `${blockingScripts.length} render-blocking scripts detected`,
                scripts: blockingScripts.map(s => s.url),
                solution: 'Add async or defer attributes'
            });
        }
        
        // Duplicate dependencies
        if (analysis.duplicates.length > 0) {
            recommendations.push({
                priority: 'medium',
                type: 'duplicates',
                message: `${analysis.duplicates.length} duplicate modules detected`,
                impact: `Could save ~${(analysis.optimization.opportunities
                    .find(o => o.type === 'deduplication')?.savings / 1024).toFixed(0)}KB`,
                solution: 'Use webpack deduplication or module federation'
            });
        }
        
        // Library-specific recommendations
        const libraries = analysis.scripts.flatMap(s => s.libraries || []);
        const hasReact = libraries.some(l => l.name === 'react');
        const hasMoment = libraries.some(l => l.name === 'moment');
        
        if (hasMoment) {
            recommendations.push({
                priority: 'medium',
                type: 'large-library',
                message: 'Moment.js detected (67KB gzipped)',
                solution: 'Consider date-fns or day.js (2KB)'
            });
        }
        
        if (hasReact && analysis.scripts.some(s => 
            s.issues?.some(i => i.type === 'dev-build'))) {
            recommendations.push({
                priority: 'critical',
                type: 'dev-react',
                message: 'React development build in production',
                impact: '4x larger bundle size',
                solution: 'Set NODE_ENV=production'
            });
        }
        
        return recommendations;
    }
}

// Run bundle analysis
const analyzer = new BundleAnalyzer();
analyzer.analyzePageBundles().then(analysis => {
    console.log('Bundle Analysis:', analysis);
});

Code Splitting Detector

Detect and analyze code splitting implementation:

// Code Splitting Detector
class CodeSplittingDetector {
    constructor() {
        this.chunks = new Map();
        this.routes = new Map();
        this.lazyModules = [];
    }
    
    // Detect code splitting patterns
    detectCodeSplitting() {
        const detection = {
            hasCodeSplitting: false,
            strategy: 'none',
            chunks: [],
            lazyComponents: [],
            dynamicImports: [],
            routeBasedSplitting: false,
            recommendations: []
        };
        
        // Check for webpack chunks
        if (window.webpackJsonp || window.webpackChunk) {
            detection.hasCodeSplitting = true;
            detection.strategy = 'webpack';
            detection.chunks = this.analyzeWebpackChunks();
        }
        
        // Check for dynamic imports
        detection.dynamicImports = this.detectDynamicImports();
        if (detection.dynamicImports.length > 0) {
            detection.hasCodeSplitting = true;
            if (detection.strategy === 'none') {
                detection.strategy = 'dynamic-import';
            }
        }
        
        // Check for React lazy
        detection.lazyComponents = this.detectReactLazy();
        
        // Check for route-based splitting
        detection.routeBasedSplitting = this.detectRouteBasedSplitting();
        
        // Analyze effectiveness
        detection.effectiveness = this.analyzeEffectiveness(detection);
        
        // Generate recommendations
        detection.recommendations = this.generateRecommendations(detection);
        
        return detection;
    }
    
    // Analyze webpack chunks
    analyzeWebpackChunks() {
        const chunks = [];
        const jsonpArray = window.webpackJsonp || window.webpackChunk || [];
        
        if (Array.isArray(jsonpArray)) {
            jsonpArray.forEach((chunk, index) => {
                if (Array.isArray(chunk)) {
                    const [chunkIds, modules] = chunk;
                    chunks.push({
                        id: chunkIds,
                        moduleCount: Object.keys(modules || {}).length,
                        index
                    });
                }
            });
        }
        
        // Check for chunk loading functions
        const chunkLoadingGlobal = window.__webpack_require__;
        if (chunkLoadingGlobal && chunkLoadingGlobal.e) {
            chunks.push({
                type: 'lazy-chunks',
                hasChunkLoader: true
            });
        }
        
        return chunks;
    }
    
    // Detect dynamic imports
    detectDynamicImports() {
        const dynamicImports = [];
        const scripts = Array.from(document.querySelectorAll('script'));
        
        scripts.forEach(script => {
            const content = script.textContent || '';
            
            // Look for import() calls
            const importMatches = content.matchAll(/import\s*\(\s*["'`]([^"'`]+)["'`]\s*\)/g);
            for (const match of importMatches) {
                dynamicImports.push({
                    module: match[1],
                    type: 'dynamic-import'
                });
            }
            
            // Look for webpack's require.ensure
            const requireMatches = content.matchAll(/require\.ensure\s*\(\s*\[([^\]]*)\]/g);
            for (const match of requireMatches) {
                dynamicImports.push({
                    modules: match[1],
                    type: 'require-ensure'
                });
            }
            
            // Look for webpack magic comments
            const magicComments = content.matchAll(/\/\*\s*webpackChunkName:\s*"([^"]+)"\s*\*\//g);
            for (const match of magicComments) {
                dynamicImports.push({
                    chunkName: match[1],
                    type: 'webpack-magic-comment'
                });
            }
        });
        
        return dynamicImports;
    }
    
    // Detect React.lazy usage
    detectReactLazy() {
        const lazyComponents = [];
        const scripts = document.querySelectorAll('script');
        
        scripts.forEach(script => {
            const content = script.textContent || '';
            
            // React.lazy patterns
            const lazyMatches = content.matchAll(/React\.lazy\s*\(\s*\(\s*\)\s*=>\s*import\s*\(\s*["'`]([^"'`]+)["'`]\s*\)/g);
            for (const match of lazyMatches) {
                lazyComponents.push({
                    component: match[1],
                    type: 'react-lazy'
                });
            }
            
            // Next.js dynamic imports
            const nextDynamic = content.matchAll(/dynamic\s*\(\s*\(\s*\)\s*=>\s*import\s*\(\s*["'`]([^"'`]+)["'`]\s*\)/g);
            for (const match of nextDynamic) {
                lazyComponents.push({
                    component: match[1],
                    type: 'nextjs-dynamic'
                });
            }
        });
        
        return lazyComponents;
    }
    
    // Detect route-based code splitting
    detectRouteBasedSplitting() {
        // Check for common routing patterns
        const patterns = {
            reactRouter: /Route.*component=\{.*lazy|import\(/,
            vueRouter: /component:\s*\(\)\s*=>\s*import/,
            angularRouter: /loadChildren:\s*\(\)\s*=>\s*import/
        };
        
        let hasRouteBasedSplitting = false;
        const scripts = document.querySelectorAll('script');
        
        scripts.forEach(script => {
            const content = script.textContent || '';
            
            for (const [framework, pattern] of Object.entries(patterns)) {
                if (pattern.test(content)) {
                    hasRouteBasedSplitting = true;
                    this.routes.set(framework, true);
                }
            }
        });
        
        return hasRouteBasedSplitting;
    }
    
    // Analyze code splitting effectiveness
    analyzeEffectiveness(detection) {
        const effectiveness = {
            score: 0,
            issues: [],
            metrics: {}
        };
        
        // Check if initial bundle is still too large
        const initialBundle = performance.getEntriesByType('resource')
            .filter(r => r.initiatorType === 'script')
            .sort((a, b) => a.startTime - b.startTime)[0];
        
        if (initialBundle && initialBundle.transferSize > 200000) { // 200KB
            effectiveness.issues.push({
                type: 'large-initial-bundle',
                size: initialBundle.transferSize,
                message: 'Initial bundle still too large despite code splitting'
            });
        }
        
        // Check chunk loading performance
        const chunkRequests = performance.getEntriesByType('resource')
            .filter(r => r.name.includes('chunk') || r.name.includes('.lazy.'));
        
        effectiveness.metrics.chunkCount = chunkRequests.length;
        effectiveness.metrics.avgChunkSize = chunkRequests.length > 0 ?
            chunkRequests.reduce((sum, r) => sum + r.transferSize, 0) / chunkRequests.length : 0;
        
        // Calculate score
        if (detection.hasCodeSplitting) {
            effectiveness.score += 30;
        }
        if (detection.routeBasedSplitting) {
            effectiveness.score += 30;
        }
        if (detection.lazyComponents.length > 0) {
            effectiveness.score += 20;
        }
        if (effectiveness.metrics.avgChunkSize < 50000) { // 50KB average
            effectiveness.score += 20;
        }
        
        return effectiveness;
    }
    
    // Generate recommendations
    generateRecommendations(detection) {
        const recommendations = [];
        
        if (!detection.hasCodeSplitting) {
            recommendations.push({
                priority: 'critical',
                type: 'no-code-splitting',
                message: 'No code splitting detected',
                impact: 'Users download all code upfront',
                solution: 'Implement dynamic imports for routes and heavy components',
                example: `// Route-based splitting
const HomePage = lazy(() => import('./pages/HomePage'));
const ProfilePage = lazy(() => import('./pages/ProfilePage'));

// Component-based splitting
const HeavyChart = lazy(() => import('./components/HeavyChart'));

// Conditional splitting
if (user.isAdmin) {
    const AdminPanel = await import('./AdminPanel');
}`
            });
        }
        
        if (detection.hasCodeSplitting && !detection.routeBasedSplitting) {
            recommendations.push({
                priority: 'high',
                type: 'no-route-splitting',
                message: 'Code splitting not applied to routes',
                impact: 'All routes loaded upfront',
                solution: 'Split code by route for better initial load'
            });
        }
        
        if (detection.effectiveness.metrics.avgChunkSize > 100000) {
            recommendations.push({
                priority: 'medium',
                type: 'large-chunks',
                message: `Average chunk size is ${(detection.effectiveness.metrics.avgChunkSize / 1024).toFixed(0)}KB`,
                impact: 'Slow lazy loading',
                solution: 'Split chunks further or use granular imports'
            });
        }
        
        if (detection.dynamicImports.length === 0 && detection.hasCodeSplitting) {
            recommendations.push({
                priority: 'low',
                type: 'old-splitting-syntax',
                message: 'Using older code splitting syntax',
                solution: 'Migrate to dynamic import() for better browser support'
            });
        }
        
        return recommendations;
    }
}

// Detect code splitting
const splittingDetector = new CodeSplittingDetector();
console.log('Code Splitting Analysis:', splittingDetector.detectCodeSplitting());

Bundle Optimization Generator

Generate optimization strategies:

// Bundle Optimization Generator
class BundleOptimizer {
    constructor(bundleAnalysis) {
        this.analysis = bundleAnalysis;
        this.optimizations = [];
    }
    
    // Generate comprehensive optimization plan
    generateOptimizationPlan() {
        const plan = {
            immediate: [],
            shortTerm: [],
            longTerm: [],
            config: this.generateConfigs(),
            estimatedSavings: 0
        };
        
        // Immediate optimizations (can be done now)
        plan.immediate = this.getImmediateOptimizations();
        
        // Short-term optimizations (1-2 sprints)
        plan.shortTerm = this.getShortTermOptimizations();
        
        // Long-term optimizations (major refactoring)
        plan.longTerm = this.getLongTermOptimizations();
        
        // Calculate total savings
        plan.estimatedSavings = this.calculateTotalSavings([
            ...plan.immediate,
            ...plan.shortTerm,
            ...plan.longTerm
        ]);
        
        return plan;
    }
    
    // Get immediate optimizations
    getImmediateOptimizations() {
        const optimizations = [];
        
        // Production builds
        const devBuilds = this.analysis.scripts.filter(s => 
            s.issues?.some(i => i.type === 'dev-build')
        );
        if (devBuilds.length > 0) {
            optimizations.push({
                id: 'production-builds',
                title: 'Switch to Production Builds',
                effort: 'low',
                impact: 'high',
                savings: devBuilds.reduce((sum, s) => sum + s.size * 0.7, 0),
                implementation: `// package.json
"scripts": {
  "build": "NODE_ENV=production webpack --mode production"
}

// webpack.config.js
module.exports = {
  mode: process.env.NODE_ENV || 'production',
  optimization: {
    minimize: true,
    nodeEnv: 'production'
  }
}`
            });
        }
        
        // Tree shaking
        const hasUnusedExports = this.analysis.coverage?.js.some(c => 
            c.unusedPercentage > 30
        );
        if (hasUnusedExports) {
            optimizations.push({
                id: 'tree-shaking',
                title: 'Enable Tree Shaking',
                effort: 'low',
                impact: 'medium',
                savings: this.analysis.scripts.reduce((sum, s) => sum + s.size * 0.2, 0),
                implementation: `// webpack.config.js
module.exports = {
  optimization: {
    usedExports: true,
    sideEffects: false,
    providedExports: true
  }
};

// package.json
{
  "sideEffects": false, // or ["*.css", "*.scss"]
}`
            });
        }
        
        // Compression
        optimizations.push({
            id: 'compression',
            title: 'Enable Gzip/Brotli Compression',
            effort: 'low',
            impact: 'high',
            savings: this.analysis.scripts.reduce((sum, s) => sum + s.size * 0.6, 0),
            implementation: `// webpack.config.js
const CompressionPlugin = require('compression-webpack-plugin');

module.exports = {
  plugins: [
    new CompressionPlugin({
      algorithm: 'gzip',
      test: /\.(js|css|html|svg)$/,
      threshold: 8192,
      minRatio: 0.8
    }),
    new CompressionPlugin({
      algorithm: 'brotliCompress',
      test: /\.(js|css|html|svg)$/,
      threshold: 8192,
      minRatio: 0.8,
      filename: '[path][base].br'
    })
  ]
}`
        });
        
        return optimizations;
    }
    
    // Get short-term optimizations
    getShortTermOptimizations() {
        const optimizations = [];
        
        // Code splitting
        if (!this.analysis.hasCodeSplitting) {
            optimizations.push({
                id: 'code-splitting',
                title: 'Implement Code Splitting',
                effort: 'medium',
                impact: 'high',
                savings: this.analysis.scripts.reduce((sum, s) => sum + s.size * 0.4, 0),
                implementation: `// Routes
const routes = [
  {
    path: '/',
    component: () => import('./pages/Home.vue')
  },
  {
    path: '/profile',
    component: () => import('./pages/Profile.vue')
  }
];

// Components
const HeavyComponent = lazy(() => 
  import(/* webpackChunkName: "heavy" */ './HeavyComponent')
);

// Conditional loading
button.addEventListener('click', async () => {
  const { processData } = await import('./dataProcessor');
  processData();
});`
            });
        }
        
        // Replace large libraries
        const largeLibraries = this.analysis.scripts
            .flatMap(s => s.libraries || [])
            .filter(l => ['moment', 'lodash'].includes(l.name));
            
        if (largeLibraries.length > 0) {
            optimizations.push({
                id: 'replace-libraries',
                title: 'Replace Large Libraries',
                effort: 'medium',
                impact: 'medium',
                savings: 200000, // Estimate
                libraries: largeLibraries,
                implementation: `// Replace Moment.js with date-fns
// Before: import moment from 'moment'; // 67KB gzipped
// After:
import { format, parseISO } from 'date-fns'; // 2KB

// Replace Lodash with native methods
// Before: import _ from 'lodash'; // 24KB gzipped
// After:
import debounce from 'lodash-es/debounce'; // 1KB

// Or use native alternatives
const groupBy = (arr, key) => 
  arr.reduce((acc, item) => {
    (acc[item[key]] = acc[item[key]] || []).push(item);
    return acc;
  }, {});`
            });
        }
        
        // Bundle analysis
        optimizations.push({
            id: 'bundle-analysis',
            title: 'Regular Bundle Analysis',
            effort: 'low',
            impact: 'medium',
            implementation: `// webpack-bundle-analyzer
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  plugins: [
    new BundleAnalyzerPlugin({
      analyzerMode: 'static',
      reportFilename: 'bundle-report.html',
      openAnalyzer: false
    })
  ]
};

// package.json
"scripts": {
  "analyze": "webpack --profile --json > stats.json && webpack-bundle-analyzer stats.json"
}`
        });
        
        return optimizations;
    }
    
    // Get long-term optimizations
    getLongTermOptimizations() {
        const optimizations = [];
        
        // Micro frontends
        if (this.getTotalSize() > 2000000) { // 2MB
            optimizations.push({
                id: 'micro-frontends',
                title: 'Migrate to Micro Frontends',
                effort: 'high',
                impact: 'high',
                description: 'Split application into independent deployable units',
                implementation: `// Module Federation (Webpack 5)
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');

// Shell application
module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: 'shell',
      remotes: {
        header: 'header@http://localhost:3001/remoteEntry.js',
        catalog: 'catalog@http://localhost:3002/remoteEntry.js'
      }
    })
  ]
};

// Micro frontend
module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: 'header',
      filename: 'remoteEntry.js',
      exposes: {
        './Header': './src/components/Header'
      }
    })
  ]
};`
            });
        }
        
        // Modern build pipeline
        optimizations.push({
            id: 'modern-pipeline',
            title: 'Modern Build Pipeline',
            effort: 'high',
            impact: 'high',
            description: 'Serve modern JavaScript to modern browsers',
            implementation: `// Differential serving
module.exports = [
  // Modern browsers
  {
    entry: './src/index.js',
    output: {
      filename: '[name].[contenthash].modern.js'
    },
    target: ['web', 'es2020'],
    module: {
      rules: [{
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [
              ['@babel/preset-env', {
                targets: { esmodules: true },
                bugfixes: true
              }]
            ]
          }
        }
      }]
    }
  },
  // Legacy browsers
  {
    entry: './src/index.js',
    output: {
      filename: '[name].[contenthash].legacy.js'
    },
    target: ['web', 'es5'],
    module: {
      rules: [{
        test: /\.js$/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [
              ['@babel/preset-env', {
                targets: '> 0.25%, not dead'
              }]
            ]
          }
        }
      }]
    }
  }
];

// HTML
<script type="module" src="app.modern.js"></script>
<script nomodule src="app.legacy.js"></script>`
        });
        
        // Build caching
        optimizations.push({
            id: 'build-caching',
            title: 'Implement Build Caching',
            effort: 'medium',
            impact: 'medium',
            description: 'Speed up builds and ensure consistent hashing',
            implementation: `// webpack.config.js
module.exports = {
  cache: {
    type: 'filesystem',
    buildDependencies: {
      config: [__filename]
    }
  },
  optimization: {
    moduleIds: 'deterministic',
    runtimeChunk: 'single',
    splitChunks: {
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          priority: -10,
          reuseExistingChunk: true
        }
      }
    }
  }
};`
        });
        
        return optimizations;
    }
    
    // Generate configuration files
    generateConfigs() {
        return {
            webpack: this.generateWebpackConfig(),
            babel: this.generateBabelConfig(),
            tsconfig: this.generateTsConfig(),
            package: this.generatePackageScripts()
        };
    }
    
    // Generate optimized webpack config
    generateWebpackConfig() {
        return `const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');

module.exports = {
  mode: 'production',
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[contenthash].js',
    chunkFilename: '[name].[contenthash].chunk.js',
    clean: true
  },
  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({
        parallel: true,
        terserOptions: {
          compress: {
            drop_console: true,
            drop_debugger: true,
            pure_funcs: ['console.log']
          },
          mangle: true,
          format: {
            comments: false
          }
        },
        extractComments: false
      }),
      new CssMinimizerPlugin()
    ],
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          priority: -10,
          reuseExistingChunk: true
        },
        common: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true
        }
      }
    },
    runtimeChunk: 'single',
    moduleIds: 'deterministic',
    usedExports: true,
    sideEffects: false
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        removeComments: true,
        collapseWhitespace: true,
        removeAttributeQuotes: true
      }
    }),
    process.env.ANALYZE && new BundleAnalyzerPlugin()
  ].filter(Boolean),
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            cacheDirectory: true
          }
        }
      }
    ]
  }
};`;
    }
    
    // Generate Babel config
    generateBabelConfig() {
        return `module.exports = {
  presets: [
    ['@babel/preset-env', {
      targets: {
        browsers: ['> 1%', 'last 2 versions', 'not dead']
      },
      useBuiltIns: 'usage',
      corejs: 3,
      modules: false
    }],
    '@babel/preset-react'
  ],
  plugins: [
    '@babel/plugin-syntax-dynamic-import',
    '@babel/plugin-proposal-class-properties',
    ['@babel/plugin-transform-runtime', {
      corejs: false,
      helpers: true,
      regenerator: true
    }],
    process.env.NODE_ENV === 'production' && [
      'transform-remove-console',
      { exclude: ['error', 'warn'] }
    ]
  ].filter(Boolean)
};`;
    }
    
    // Generate TypeScript config
    generateTsConfig() {
        return `{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "lib": ["ES2020", "DOM"],
    "jsx": "react",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "skipLibCheck": true,
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "removeComments": true,
    "preserveConstEnums": false,
    "sourceMap": true,
    "importHelpers": true
  },
  "exclude": ["node_modules", "dist", "build"]
}`;
    }
    
    // Generate package.json scripts
    generatePackageScripts() {
        return `{
  "scripts": {
    "build": "NODE_ENV=production webpack --mode production",
    "build:analyze": "ANALYZE=true npm run build",
    "build:modern": "webpack --config webpack.modern.js",
    "build:legacy": "webpack --config webpack.legacy.js",
    "build:all": "npm run build:modern && npm run build:legacy",
    "bundle:check": "bundlesize",
    "bundle:analyze": "webpack-bundle-analyzer stats.json",
    "bundle:visualize": "webpack --profile --json > stats.json && open-cli webpack-bundle-analyzer stats.json"
  },
  "bundlesize": [
    {
      "path": "./dist/main.*.js",
      "maxSize": "100 kB"
    },
    {
      "path": "./dist/vendors.*.js",
      "maxSize": "200 kB"
    }
  ]
}`;
    }
    
    // Calculate total savings
    calculateTotalSavings(optimizations) {
        return optimizations.reduce((sum, opt) => sum + (opt.savings || 0), 0);
    }
    
    // Get total size
    getTotalSize() {
        return this.analysis.scripts.reduce((sum, s) => sum + (s.size || 0), 0);
    }
}

// Generate optimization plan
const bundleAnalysis = await new BundleAnalyzer().analyzePageBundles();
const optimizer = new BundleOptimizer(bundleAnalysis);
const plan = optimizer.generateOptimizationPlan();
console.log('Optimization Plan:', plan);

Best Practices for Bundle Optimization

  1. Code Split by Route: Each route in its own chunk
  2. Lazy Load Below Fold: Don't load what users don't see
  3. Tree Shake Aggressively: Remove all dead code
  4. Minimize Parse Time: Smaller bundles parse faster
  5. Use Production Builds: Development builds are 4x larger
  6. Implement Caching: Use content hashes for long-term caching
  7. Monitor Bundle Size: Set up size budgets in CI/CD
  8. Analyze Regularly: Bundle size creeps up over time
  9. Modern/Legacy Split: Serve modern JS to modern browsers
  10. Remove Unused Dependencies: Audit node_modules regularly

Common Bundle Optimization Mistakes

  • No Code Splitting: Everything in one giant bundle
  • Splitting Too Much: Hundreds of tiny chunks
  • Development Builds: React dev mode in production
  • Full Library Imports: Importing all of Lodash for one function
  • No Tree Shaking: Shipping dead code
  • Vendor Bundle Too Large: All dependencies in one chunk
  • No Compression: Serving uncompressed JavaScript
  • Poor Cache Strategy: Cache busting on every deploy
  • Polyfill Bloat: Polyfilling for IE11 when not needed
  • Source Maps in Production: Exposing source code

Remember: Every byte of JavaScript has a cost—download time, parse time, compile time, and execution time. Optimize not just for size but for the entire JavaScript processing pipeline.