Webpack 配置示例

Webpack配置示例,包括基础设置、优化、加载器、插件和现代webpack模式

💻 Webpack 基础设置 javascript

🟢 simple ⭐⭐

JavaScript应用程序的基本webpack配置,包含开发和生产环境

⏱️ 20 min 🏷️ webpack, configuration, build
Prerequisites: Node.js knowledge, JavaScript ES6+, Build tools concepts
// Webpack 5 Basic Configuration
// File: webpack.config.js

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = (env, argv) => {
    const isProduction = argv.mode === 'production';

    return {
        // Entry point
        entry: './src/index.js',

        // Output configuration
        output: {
            path: path.resolve(__dirname, 'dist'),
            filename: isProduction ? '[name].[contenthash].js' : '[name].js',
            chunkFilename: isProduction ? '[name].[contenthash].chunk.js' : '[name].chunk.js',
            clean: true, // Clean output directory before emit
            publicPath: '/'
        },

        // Module resolution
        resolve: {
            extensions: ['.js', '.jsx', '.ts', '.tsx'],
            alias: {
                '@': path.resolve(__dirname, 'src'),
                '@components': path.resolve(__dirname, 'src/components'),
                '@utils': path.resolve(__dirname, 'src/utils')
            }
        },

        // Loaders configuration
        module: {
            rules: [
                // JavaScript/TypeScript files
                {
                    test: /\.(js|jsx|ts|tsx)$/,
                    exclude: /node_modules/,
                    use: {
                        loader: 'babel-loader',
                        options: {
                            presets: [
                                '@babel/preset-env',
                                '@babel/preset-react',
                                '@babel/preset-typescript'
                            ],
                            plugins: [
                                '@babel/plugin-transform-runtime',
                                '@babel/plugin-proposal-class-properties'
                            ]
                        }
                    }
                },

                // CSS files
                {
                    test: /\.css$/,
                    use: [
                        isProduction ? MiniCssExtractPlugin.loader : 'style-loader',
                        'css-loader',
                        {
                            loader: 'postcss-loader',
                            options: {
                                postcssOptions: {
                                    plugins: [
                                        ['autoprefixer']
                                    ]
                                }
                            }
                        }
                    ]
                },

                // SASS/SCSS files
                {
                    test: /\.(scss|sass)$/,
                    use: [
                        isProduction ? MiniCssExtractPlugin.loader : 'style-loader',
                        'css-loader',
                        'sass-loader'
                    ]
                },

                // Images
                {
                    test: /\.(png|jpe?g|gif|svg|webp)$/,
                    type: 'asset',
                    parser: {
                        dataUrlCondition: {
                            maxSize: 8 * 1024 // 8kb
                        }
                    },
                    generator: {
                        filename: 'images/[name].[hash][ext]'
                    }
                },

                // Fonts
                {
                    test: /\.(woff|woff2|eot|ttf|otf)$/,
                    type: 'asset/resource',
                    generator: {
                        filename: 'fonts/[name].[hash][ext]'
                    }
                },

                // JSON files
                {
                    test: /\.json$/,
                    type: 'asset/resource'
                }
            ]
        },

        // Plugins
        plugins: [
            // HTML template
            new HtmlWebpackPlugin({
                template: './public/index.html',
                filename: 'index.html',
                inject: 'body',
                minify: isProduction ? {
                    removeComments: true,
                    collapseWhitespace: true,
                    removeRedundantAttributes: true,
                    useShortDoctype: true,
                    removeEmptyAttributes: true,
                    removeStyleLinkTypeAttributes: true,
                    keepClosingSlash: true,
                    minifyJS: true,
                    minifyCSS: true,
                    minifyURLs: true
                } : false
            }),

            // Environment variables
            new webpack.DefinePlugin({
                'process.env.NODE_ENV': JSON.stringify(argv.mode),
                'process.env.PUBLIC_URL': JSON.stringify(''),
                __DEV__: !isProduction,
                __PROD__: isProduction
            })
        ].concat(isProduction ? [
            // Production-only plugins
            new MiniCssExtractPlugin({
                filename: 'styles/[name].[contenthash].css',
                chunkFilename: 'styles/[name].[contenthash].chunk.css'
            }),

            // Bundle analyzer
            new BundleAnalyzerPlugin.BundleAnalyzerPlugin({
                analyzerMode: 'static',
                openAnalyzer: false
            })
        ] : []),

        // Development server configuration
        devServer: {
            contentBase: path.join(__dirname, 'dist'),
            compress: true,
            port: 3000,
            hot: true,
            open: true,
            historyApiFallback: true,
            overlay: {
                errors: true,
                warnings: false
            },
            proxy: {
                '/api': {
                    target: 'http://localhost:8080',
                    changeOrigin: true,
                    secure: false
                }
            }
        },

        // Optimization
        optimization: {
            minimize: isProduction,
            minimizer: [
                '...', // Use default minimizer
                new TerserPlugin({
                    parallel: true,
                    terserOptions: {
                        compress: {
                            drop_console: isProduction
                        }
                    }
                }),
                new CssMinimizerPlugin()
            ],

            // Code splitting
            splitChunks: {
                chunks: 'all',
                cacheGroups: {
                    vendor: {
                        test: /[\\/]node_modules[\\/]/,
                        name: 'vendors',
                        chunks: 'all',
                        priority: 10
                    },
                    common: {
                        name: 'common',
                        minChunks: 2,
                        chunks: 'all',
                        priority: 5,
                        reuseExistingChunk: true
                    }
                }
            },

            // Runtime chunk
            runtimeChunk: {
                name: 'runtime'
            },

            // Module IDs
            moduleIds: isProduction ? 'deterministic' : 'named',
            chunkIds: isProduction ? 'deterministic' : 'named'
        },

        // Source maps
        devtool: isProduction ? 'source-map' : 'eval-cheap-module-source-map',

        // Performance hints
        performance: {
            hints: isProduction ? 'warning' : false,
            maxEntrypointSize: 512000,
            maxAssetSize: 512000
        },

        // Stats configuration
        stats: {
            colors: true,
            modules: false,
            children: false,
            chunks: false,
            chunkModules: false
        },

        // Externals (optional)
        externals: {
            // jquery: 'jQuery',
            // lodash: '_'
        }
    };
};

// Required plugins for production
const webpack = require('webpack');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer');

// Package.json scripts
{
  "scripts": {
    "dev": "webpack serve --mode development",
    "build": "webpack --mode production",
    "build:analyze": "webpack --mode production --env analyze",
    "clean": "rimraf dist"
  }
}

// .babelrc configuration
{
  "presets": [
    ["@babel/preset-env", {
      "targets": {
        "browsers": ["last 2 versions", "not dead"]
      },
      "modules": false,
      "useBuiltIns": "usage",
      "corejs": 3
    }],
    "@babel/preset-react",
    "@babel/preset-typescript"
  ],
  "plugins": [
    ["@babel/plugin-transform-runtime", {
      "corejs": 3
    }],
    "@babel/plugin-proposal-class-properties",
    "@babel/plugin-proposal-object-rest-spread"
  ]
}

// tsconfig.json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "node",
    "lib": ["DOM", "DOM.Iterable", "ES6"],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "module": "ESNext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"],
      "@components/*": ["src/components/*"],
      "@utils/*": ["src/utils/*"]
    }
  },
  "include": [
    "src"
  ]
}

// postcss.config.js
module.exports = {
    plugins: [
        require('autoprefixer')
    ]
}

💻 Webpack 高级优化 javascript

🟡 intermediate ⭐⭐⭐⭐

性能优化、代码分割、tree shaking和webpack高级功能

⏱️ 40 min 🏷️ webpack, optimization, performance
Prerequisites: Webpack basics, JavaScript performance, Build optimization
// Webpack Advanced Optimization Configuration
// webpack.config.prod.js

const path = require('path');
const webpack = require('webpack');
const TerserPlugin = require('terser-webpack-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { PurgeCSSPlugin } = require('purgecss-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer');
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin');
const { NormalModuleReplacementPlugin } = require('webpack');

// Speed measurement wrapper
const smp = new SpeedMeasurePlugin();

module.exports = smp.wrap({
    mode: 'production',

    // Multiple entry points for better caching
    entry: {
        main: './src/index.js',
        vendor: ['react', 'react-dom'],
        polyfills: './src/polyfills.js'
    },

    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'js/[name].[contenthash:8].js',
        chunkFilename: 'js/[name].[contenthash:8].chunk.js',
        publicPath: '/',
        pathinfo: false,
        crossOriginLoading: 'anonymous' // For better caching
    },

    resolve: {
        extensions: ['.js', '.jsx', '.ts', '.tsx'],
        modules: [
            path.resolve(__dirname, 'src'),
            'node_modules'
        ],
        alias: {
            '@': path.resolve(__dirname, 'src'),
            react: 'preact/compat',
            'react-dom': 'preact/compat'
        }
    },

    module: {
        rules: [
            // JavaScript/TypeScript with advanced caching
            {
                test: /\.(js|jsx|ts|tsx)$/,
                include: path.resolve(__dirname, 'src'),
                exclude: /node_modules/,
                use: [
                    {
                        loader: 'thread-loader',
                        options: {
                            workers: require('os').cpus().length - 1,
                            poolTimeout: 2000
                        }
                    },
                    {
                        loader: 'babel-loader',
                        options: {
                            cacheDirectory: true,
                            cacheCompression: false,
                            compact: true,
                            presets: [
                                ['@babel/preset-env', {
                                    targets: {
                                        browsers: ['> 1%', 'last 2 versions', 'not dead']
                                    },
                                    modules: false,
                                    useBuiltIns: 'usage',
                                    corejs: 3
                                }]
                            ]
                        }
                    }
                ]
            },

            // CSS optimization
            {
                test: /\.css$/,
                use: [
                    {
                        loader: MiniCssExtractPlugin.loader,
                        options: {
                            esModule: false,
                            publicPath: (resourcePath) => {
                                return path.relative(resourcePath, path.resolve(__dirname, 'src'));
                            }
                        }
                    },
                    {
                        loader: 'css-loader',
                        options: {
                            importLoaders: 1,
                            sourceMap: false,
                            modules: {
                                auto: (resourcePath) => resourcePath.includes('.module.'),
                                localIdentName: '[name]__[local]--[hash:base64:5]'
                            }
                        }
                    },
                    {
                        loader: 'postcss-loader',
                        options: {
                            postcssOptions: {
                                plugins: [
                                    require('autoprefixer'),
                                    require('cssnano')({
                                        preset: 'advanced'
                                    })
                                ]
                            }
                        }
                    }
                ]
            },

            // Image optimization
            {
                test: /\.(png|jpe?g|gif|webp)$/i,
                type: 'asset',
                parser: {
                    dataUrlCondition: {
                        maxSize: 4 * 1024 // 4kb
                    }
                },
                generator: {
                    filename: 'images/[name].[contenthash:8][ext]'
                },
                use: [
                    {
                        loader: 'image-webpack-loader',
                        options: {
                            mozjpeg: { progressive: true, quality: 80 },
                            optipng: { enabled: false },
                            pngquant: { quality: [0.65, 0.90], speed: 4 },
                            gifsicle: { interlaced: false }
                        }
                    }
                ]
            },

            // SVG optimization
            {
                test: /\.svg$/,
                use: [
                    {
                        loader: 'svg-url-loader',
                        options: {
                            limit: 10 * 1024,
                            noquotes: true
                        }
                    }
                ]
            }
        ]
    },

    // Advanced optimization
    optimization: {
        minimize: true,
        minimizer: [
            // Custom Terser configuration
            new TerserPlugin({
                parallel: true,
                terserOptions: {
                    compress: {
                        drop_console: true,
                        drop_debugger: true,
                        pure_funcs: ['console.log', 'console.info'],
                        passes: 3
                    },
                    mangle: {
                        safari10: true
                    },
                    format: {
                        comments: false,
                        quote_style: 3
                    }
                },
                extractComments: false
            }),

            // CSS optimization
            new CssMinimizerPlugin({
                minimizerOptions: {
                    preset: [
                        'default',
                        {
                            discardComments: { removeAll: true },
                            normalizeWhitespace: false,
                            colormin: true
                        }
                    ]
                }
            })
        ],

        // Advanced code splitting
        splitChunks: {
            chunks: 'all',
            minSize: 20000,
            maxSize: 244000,
            minChunks: 1,
            maxAsyncRequests: 30,
            maxInitialRequests: 30,
            automaticNameDelimiter: '~',
            enforceSizeThreshold: 50000,
            cacheGroups: {
                // Frameworks
                frameworks: {
                    test: /[\\/]node_modules[\\/](react|react-dom|preact)[\\/]/,
                    name: 'frameworks',
                    priority: 40,
                    enforce: true
                },

                // Libraries
                libs: {
                    test: /[\\/]node_modules[\\/]/,
                    name(module) {
                        const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
                        return `npm.${packageName.replace('@', '')}`;
                    },
                    priority: 30,
                    minChunks: 1,
                    reuseExistingChunk: true
                },

                // Common modules
                common: {
                    name: 'common',
                    minChunks: 2,
                    priority: 20,
                    chunks: 'initial',
                    reuseExistingChunk: true
                },

                // CSS
                styles: {
                    test: /\.(css|scss|sass)$/,
                    name: 'styles',
                    priority: 10,
                    chunks: 'all',
                    enforce: true
                }
            }
        },

        // Runtime optimization
        runtimeChunk: {
            name: (entrypoint) => `runtime~${entrypoint.name}`
        },

        // Module concatenation (scope hoisting)
        concatenateModules: true,

        // Side effects optimization
        sideEffects: false,

        // Export optimization
        usedExports: true,

        // Provided exports for tree shaking
        providedExports: true
    },

    // Advanced plugins
    plugins: [
        // Environment variables with optimizations
        new webpack.DefinePlugin({
            'process.env.NODE_ENV': JSON.stringify('production'),
            'process.env.GENERATE_SOURCEMAP': JSON.stringify(false),
            'process.env.PUBLIC_URL': JSON.stringify(''),
            __DEV__: false,
            __PROD__: true
        }),

        // CSS extraction with optimizations
        new MiniCssExtractPlugin({
            filename: 'css/[name].[contenthash:8].css',
            chunkFilename: 'css/[name].[contenthash:8].chunk.css',
            ignoreOrder: true
        }),

        // Tree shake CSS
        new PurgeCSSPlugin({
            paths: glob.sync(`${path.join(__dirname, 'src')}/**/*`, { nodir: true }),
            defaultExtractor: (content) => content.match(/[\w-/:]+(?<!:)/g) || [],
            safelist: {
                standard: [/-(leave|enter|appear)(|-(to|from|active))$/],
                deep: [/^modal-/, /^tooltip-/, /^popover-/]
            }
        }),

        // Gzip compression
        new CompressionPlugin({
            algorithm: 'gzip',
            test: /\.(js|css|html|svg)$/,
            threshold: 10240,
            minRatio: 0.8,
            compressionOptions: {
                level: 9
            }
        }),

        // Brotli compression
        new CompressionPlugin({
            algorithm: 'brotliCompress',
            test: /\.(js|css|html|svg)$/,
            threshold: 10240,
            minRatio: 0.8,
            compressionOptions: {
                level: 11
            },
            filename: '[path][base].br'
        }),

        // Hard source caching for faster builds
        new HardSourceWebpackPlugin({
            cacheDirectory: path.join(__dirname, '.webpack-cache'),
            environmentHash: {
                root: process.cwd(),
                directories: [],
                files: ['package.json', 'yarn.lock']
            }
        }),

        // Bundle analyzer for analysis
        new BundleAnalyzerPlugin({
            analyzerMode: 'static',
            openAnalyzer: false,
            reportFilename: path.resolve(__dirname, 'dist/bundle-report.html')
        }),

        // Replace development modules with production equivalents
        new NormalModuleReplacementPlugin(
            /^react-refresh$/,
            'react-refresh/cjs/react-refresh-babel.development.js'
        ),

        // Progress bar
        new webpack.ProgressPlugin()
    ],

    // Performance hints
    performance: {
        hints: 'warning',
        maxEntrypointSize: 250000,
        maxAssetSize: 250000,
        assetFilter: (assetFilename) => {
            return !assetFilename.includes('\.map');
        }
    },

    // Source maps
    devtool: false,

    // Caching
    cache: {
        type: 'filesystem',
        buildDependencies: {
            config: [__filename]
        }
    },

    // Externals for CDN usage
    externals: {
        // react: 'React',
        // 'react-dom': 'ReactDOM',
        // lodash: '_'
    },

    // Advanced stats
    stats: {
        preset: 'normal',
        builtAt: true,
        moduleTrace: true,
        source: false,
        errorDetails: true,
        colors: true,
        modules: false,
        children: false,
        chunks: false,
        chunkModules: false,
        entrypoints: false,
        performance: true,
        hash: true,
        version: true,
        timings: true
    }
});

// webpack-dev.config.js (Development optimized)
module.exports = {
    mode: 'development',

    entry: {
        main: './src/index.js'
    },

    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].js',
        publicPath: '/'
    },

    devtool: 'eval-cheap-module-source-map',

    devServer: {
        contentBase: path.join(__dirname, 'dist'),
        hot: true,
        compress: true,
        port: 3000,
        historyApiFallback: true,
        overlay: {
            errors: true,
            warnings: true
        },
        watchOptions: {
            aggregateTimeout: 300,
            poll: 1000,
            ignored: /node_modules/
        },
        client: {
            logging: 'info',
            overlay: {
                errors: true,
                warnings: false
            }
        }
    },

    plugins: [
        new webpack.HotModuleReplacementPlugin(),
        new webpack.DefinePlugin({
            'process.env.NODE_ENV': JSON.stringify('development'),
            __DEV__: true,
            __PROD__: false
        })
    ],

    module: {
        rules: [
            {
                test: /\.(js|jsx|ts|tsx)$/,
                use: 'babel-loader',
                exclude: /node_modules/
            },
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader']
            }
        ]
    },

    resolve: {
        extensions: ['.js', '.jsx', '.ts', '.tsx'],
        alias: {
            '@': path.resolve(__dirname, 'src')
        }
    }
};

// webpack.config.js (Main configuration that merges based on environment)
const { merge } = require('webpack-merge');
const commonConfig = require('./webpack.common.js');

module.exports = (env, argv) => {
    const isProduction = argv.mode === 'production';
    const envConfig = isProduction
        ? require('./webpack.prod.js')
        : require('./webpack.dev.js');

    return merge(commonConfig, envConfig);
};

// Package.json scripts for advanced optimization
{
  "scripts": {
    "dev": "webpack serve --config webpack.dev.js",
    "build": "webpack --config webpack.prod.js",
    "build:analyze": "npm run build && open dist/bundle-report.html",
    "build:profile": "webpack --config webpack.prod.js --profile",
    "clean": "rimraf dist .webpack-cache",
    "size-limit": "size-limit",
    "lighthouse": "lighthouse http://localhost:3000 --output=html --output-path=./lighthouse-report.html"
  }
}

// .size-limit.json for bundle size monitoring
[
    {
        "path": "dist/js/main.*.js",
        "limit": "50 KB"
    },
    {
        "path": "dist/css/main.*.css",
        "limit": "20 KB"
    },
    {
        "path": "dist/js/vendor.*.js",
        "limit": "100 KB"
    }
]

💻 Webpack 加载器和插件 javascript

🟡 intermediate ⭐⭐⭐⭐

自定义webpack加载器、插件开发和集成示例

⏱️ 35 min 🏷️ webpack, loaders, plugins, development
Prerequisites: Webpack basics, Node.js streams, Plugin architecture
// Webpack Custom Loaders and Plugins Examples

// ===== CUSTOM LOADER EXAMPLES =====

// 1. Markdown to HTML Loader
// loaders/markdown-loader.js
const marked = require('marked');
const highlight = require('highlight.js');

module.exports = function(source) {
    // Configure marked with syntax highlighting
    marked.setOptions({
        highlight: (code, lang) => {
            if (lang && highlight.getLanguage(lang)) {
                return highlight.highlight(code, { language: lang }).value;
            }
            return highlight.highlightAuto(code).value;
        },
        breaks: true,
        gfm: true
    });

    // Convert markdown to HTML
    const html = marked(source);

    // Return as a JavaScript module
    return `export default ${JSON.stringify(html)};`;
};

// 2. Image Optimization Loader
// loaders/image-optimizer-loader.js
const sharp = require('sharp');
const path = require('path');
const crypto = require('crypto');

module.exports = function(buffer) {
    const callback = this.async();
    const { width, height, quality = 80, format = 'webp' } = this.getOptions();

    // Generate hash for caching
    const hash = crypto
        .createHash('md5')
        .update(buffer + JSON.stringify({ width, height, quality, format }))
        .digest('hex');

    const outputPath = path.join(
        this.outputPath,
        'optimized-images',
        `${this.resourcePath.split('/').pop().split('.')[0]}-${hash}.${format}`
    );

    // Process image with sharp
    const transformer = sharp(buffer);

    if (width || height) {
        transformer.resize(width, height, { fit: 'inside', withoutEnlargement: true });
    }

    transformer
        .toFormat(format, { quality })
        .toBuffer()
        .then(outputBuffer => {
            this.emitFile(outputPath, outputBuffer);
            callback(null, `export default "${outputPath}";`);
        })
        .catch(err => callback(err));
};

module.exports.raw = true;

// 3. Translation Loader
// loaders/i18n-loader.js
const fs = require('fs');
const path = require('path');

module.exports = function(source) {
    const content = JSON.parse(source);
    const translations = {};

    // Scan translation files
    const localesDir = path.resolve(this.context, 'locales');
    if (fs.existsSync(localesDir)) {
        const locales = fs.readdirSync(localesDir);

        locales.forEach(locale => {
            const localePath = path.join(localesDir, locale);
            if (fs.statSync(localePath).isDirectory()) {
                const filePath = path.join(localePath, `${path.basename(this.resource, '.json')}.json`);
                if (fs.existsSync(filePath)) {
                    translations[locale] = JSON.parse(fs.readFileSync(filePath, 'utf8'));
                }
            }
        });
    }

    // Generate translation functions
    const translationsCode = Object.keys(translations)
        .map(locale => {
            const localeTranslations = translations[locale];
            const translationsMap = JSON.stringify(localeTranslations, null, 2);

            return `const ${locale} = ${translationsMap};`;
        })
        .join('\n');

    return `
        // Default translations from the JSON file
        const defaultTranslations = ${JSON.stringify(content, null, 2)};

        // Locale-specific translations
        ${translationsCode}

        const translations = {
            en: defaultTranslations,
            ${Object.keys(translations).map(locale => `${locale}: ${locale}`).join(',\n            ')}
        };

        export function t(key, locale = 'en') {
            const keys = key.split('.');
            let translation = translations[locale];

            for (const k of keys) {
                if (translation && translation[k]) {
                    translation = translation[k];
                } else {
                    return key; // Fallback to key if translation not found
                }
            }

            return translation || key;
        }

        export default translations;
    `;
};

// 4. Environment Variable Loader
// loaders/env-loader.js
const fs = require('fs');
const path = require('path');

module.exports = function(source) {
    const envFile = path.resolve(this.context, '.env');
    let envVars = {};

    if (fs.existsSync(envFile)) {
        const envContent = fs.readFileSync(envFile, 'utf8');
        envContent.split('\n').forEach(line => {
            const match = line.match(/^([^=]+)=(.*)$/);
            if (match) {
                envVars[match[1]] = match[2];
            }
        });
    }

    const processEnv = { ...process.env, ...envVars };
    const envRegex = /process\.env\.([A-Z_]+)/g;

    let result = source.replace(envRegex, (match, envVar) => {
        const value = processEnv[envVar];
        return value ? JSON.stringify(value) : 'undefined';
    });

    return result;
};

// ===== CUSTOM PLUGIN EXAMPLES =====

// 1. Asset Versioning Plugin
// plugins/asset-version-plugin.js
const crypto = require('crypto');
const path = require('path');
const fs = require('fs');

class AssetVersionPlugin {
    constructor(options = {}) {
        this.options = {
            manifestPath: options.manifestPath || 'asset-manifest.json',
            versionKey: options.versionKey || 'version',
            ...options
        };
    }

    apply(compiler) {
        compiler.hooks.emit.tapAsync('AssetVersionPlugin', (compilation, callback) => {
            const manifest = {};
            const version = Date.now() + '-' + crypto.randomBytes(4).toString('hex');

            // Process all assets
            for (const [filename, asset] of Object.entries(compilation.assets)) {
                const content = asset.source();
                const hash = crypto.createHash('md5').update(content).digest('hex');

                manifest[filename] = {
                    version: hash,
                    url: filename,
                    [this.options.versionKey]: version
                };
            }

            // Write manifest file
            const manifestJson = JSON.stringify(manifest, null, 2);
            compilation.assets[this.options.manifestPath] = {
                source: () => manifestJson,
                size: () => manifestJson.length
            };

            callback();
        });
    }
}

module.exports = AssetVersionPlugin;

// 2. Bundle Size Analyzer Plugin
// plugins/bundle-size-analyzer-plugin.js
const fs = require('fs');
const path = require('path');

class BundleSizeAnalyzerPlugin {
    constructor(options = {}) {
        this.options = {
            outputPath: options.outputPath || 'bundle-size-report.json',
            thresholds: options.thresholds || {
                total: 1024 * 1024, // 1MB
                chunks: 1024 * 256   // 256KB per chunk
            }
        };
    }

    apply(compiler) {
        compiler.hooks.afterEmit.tap('BundleSizeAnalyzerPlugin', (compilation) => {
            const report = {
                timestamp: new Date().toISOString(),
                totalSize: 0,
                chunks: {},
                assets: {},
                warnings: [],
                errors: []
            };

            // Analyze chunks
            for (const [name, chunk] of compilation.chunks.entries()) {
                const size = chunk.files.reduce((total, file) => {
                    return total + compilation.assets[file].size();
                }, 0);

                report.chunks[name] = {
                    size,
                    files: chunk.files,
                    modules: chunk.modules.map(m => ({
                        identifier: m.identifier(),
                        size: m.size()
                    }))
                };

                report.totalSize += size;

                // Check thresholds
                if (size > this.options.thresholds.chunks) {
                    report.warnings.push(`Chunk "${name}" exceeds size threshold: ${(size / 1024).toFixed(2)}KB`);
                }
            }

            // Check total size threshold
            if (report.totalSize > this.options.thresholds.total) {
                report.errors.push(`Total bundle size exceeds threshold: ${(report.totalSize / 1024 / 1024).toFixed(2)}MB`);
            }

            // Analyze individual assets
            for (const [name, asset] of Object.entries(compilation.assets)) {
                report.assets[name] = {
                    size: asset.size()
                };
            }

            // Write report
            const reportPath = path.join(compilation.outputOptions.path, this.options.outputPath);
            fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));

            // Log warnings and errors
            if (report.warnings.length > 0) {
                console.warn('Bundle Size Warnings:');
                report.warnings.forEach(warning => console.warn(`  - ${warning}`));
            }

            if (report.errors.length > 0) {
                console.error('Bundle Size Errors:');
                report.errors.forEach(error => console.error(`  - ${error}`));
            }
        });
    }
}

module.exports = BundleSizeAnalyzerPlugin;

// 3. Component Documentation Generator Plugin
// plugins/component-docs-plugin.js
const fs = require('fs');
const path = require('path');
const parseJsdoc = require('jsdoc-to-markdown');

class ComponentDocsPlugin {
    constructor(options = {}) {
        this.options = {
            componentsPath: options.componentsPath || 'src/components',
            outputDir: options.outputDir || 'docs/components',
            ...options
        };
    }

    apply(compiler) {
        compiler.hooks.beforeCompile.tap('ComponentDocsPlugin', () => {
            const componentsPath = path.resolve(compiler.context, this.options.componentsPath);
            const outputDir = path.resolve(compiler.context, this.options.outputDir);

            // Ensure output directory exists
            if (!fs.existsSync(outputDir)) {
                fs.mkdirSync(outputDir, { recursive: true });
            }

            // Find all component files
            const componentFiles = this.findComponentFiles(componentsPath);

            // Generate documentation for each component
            componentFiles.forEach(filePath => {
                const componentName = path.basename(filePath, path.extname(filePath));
                const documentation = this.generateComponentDocumentation(filePath);
                const outputPath = path.join(outputDir, `${componentName}.md`);

                fs.writeFileSync(outputPath, documentation);
            });

            // Generate index file
            this.generateIndexDoc(componentFiles, outputDir);
        });
    }

    findComponentFiles(dir) {
        const files = [];

        function traverse(currentDir) {
            const items = fs.readdirSync(currentDir);

            for (const item of items) {
                const fullPath = path.join(currentDir, item);
                const stat = fs.statSync(fullPath);

                if (stat.isDirectory()) {
                    traverse(fullPath);
                } else if (path.extname(item) === '.js' || path.extname(item) === '.jsx') {
                    files.push(fullPath);
                }
            }
        }

        traverse(dir);
        return files;
    }

    generateComponentDocumentation(filePath) {
        const source = fs.readFileSync(filePath, 'utf8');
        const componentName = path.basename(filePath, path.extname(filePath));

        // Extract JSDoc comments
        const jsdoc = parseJsdoc.getTemplateData(source);

        // Generate markdown
        return `
# ${componentName}

## Description
${jsdoc.description || 'No description available.'}

## Props
${jsdoc.params ? jsdoc.params.map(param =>
    `- **${param.name}** (${param.type.names.join('|')}) - ${param.description}`
).join('\n') : 'No props documented.'}

## Examples
```jsx
import ${componentName} from './${componentName}';

function App() {
    return <${componentName} />;
}
```

## Source
[${path.basename(filePath)}](../${path.relative(process.cwd(), filePath)})
        `;
    }

    generateIndexDoc(files, outputDir) {
        const indexPath = path.join(outputDir, 'README.md');
        const content = `
# Component Documentation

This directory contains documentation for all components in the application.

## Components
${files.map(file => {
    const componentName = path.basename(file, path.extname(file));
    const relativePath = path.relative(outputDir, file);
    return `- [${componentName}](${componentName}.md)`;
}).join('\n')}

## Generated On
${new Date().toISOString()}
        `;

        fs.writeFileSync(indexPath, content);
    }
}

module.exports = ComponentDocsPlugin;

// ===== INTEGRATION EXAMPLES =====

// webpack.config.js with custom loaders and plugins
const path = require('path');
const { AssetVersionPlugin } = require('./plugins/asset-version-plugin');
const { BundleSizeAnalyzerPlugin } = require('./plugins/bundle-size-analyzer-plugin');
const { ComponentDocsPlugin } = require('./plugins/component-docs-plugin');

module.exports = {
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].[contenthash].js'
    },

    module: {
        rules: [
            // Custom markdown loader
            {
                test: /\.md$/,
                use: [
                    'html-loader',
                    path.resolve(__dirname, 'loaders/markdown-loader.js')
                ]
            },

            // Custom image optimizer loader
            {
                test: /\.(png|jpe?g|gif|svg)$/,
                oneOf: [
                    {
                        resourceQuery: /optimize/,
                        use: path.resolve(__dirname, 'loaders/image-optimizer-loader.js')
                    },
                    {
                        type: 'asset/resource'
                    }
                ]
            },

            // Custom i18n loader
            {
                test: /\.translation\.json$/,
                type: 'javascript/auto',
                use: path.resolve(__dirname, 'loaders/i18n-loader.js')
            },

            // Custom env loader
            {
                test: /\.env\.(js|mjs)$/,
                use: path.resolve(__dirname, 'loaders/env-loader.js')
            }
        ]
    },

    plugins: [
        new AssetVersionPlugin({
            manifestPath: 'asset-manifest.json'
        }),

        new BundleSizeAnalyzerPlugin({
            outputPath: 'bundle-analysis.json',
            thresholds: {
                total: 1024 * 512,  // 512KB
                chunks: 1024 * 128  // 128KB
            }
        }),

        new ComponentDocsPlugin({
            componentsPath: 'src/components',
            outputDir: 'docs/components'
        })
    ],

    resolveLoader: {
        alias: {
            'markdown-loader': path.resolve(__dirname, 'loaders/markdown-loader.js'),
            'image-optimizer-loader': path.resolve(__dirname, 'loaders/image-optimizer-loader.js'),
            'i18n-loader': path.resolve(__dirname, 'loaders/i18n-loader.js'),
            'env-loader': path.resolve(__dirname, 'loaders/env-loader.js')
        }
    }
};

// Example usage in components
import React, { useState } from 'react';
import './styles.css';

// Using custom image optimizer
import optimizedImage from './image.png?optimize';

// Using custom i18n loader
import { t } from './translation.json';

const MyComponent = () => {
    const [message] = useState(t('welcome.message'));

    return (
        <div>
            <h1>{message}</h1>
            <img src={optimizedImage} alt="Optimized image" />
        </div>
    );
};

export default MyComponent;