Exemples de Configuration Webpack

Exemples de configuration Webpack incluant la configuration de base, l'optimisation, les loaders, les plugins et les patterns Webpack modernes

Key Facts

Category
Build Tools
Items
3
Format Families
image

Sample Overview

Exemples de configuration Webpack incluant la configuration de base, l'optimisation, les loaders, les plugins et les patterns Webpack modernes This sample set belongs to Build Tools and can be used to test related workflows inside Elysia Tools.

💻 Configuration de Base Webpack javascript

🟢 simple ⭐⭐

Configuration essentielle de Webpack pour les applications JavaScript avec environnements de développement et de production

⏱️ 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: {
            static: {
                directory: 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')
    ]
}

💻 Optimisation Avancée Webpack javascript

🟡 intermediate ⭐⭐⭐⭐

Optimisation des performances, division du code, tree shaking et fonctionnalités avancées de 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: {
        static: {
            directory: 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"
    }
]

💻 Loaders et Plugins Webpack javascript

🟡 intermediate ⭐⭐⭐⭐

Développement de loaders et plugins personnalisés Webpack et exemples d'intégration

⏱️ 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;