Fading Coder

One Final Commit for the Last Sprint

Home > Tools > Content

Implementing Custom Loaders and Plugins for Webpack

Tools 1

Loaders

A loader acts as a transformer that processes and converts source code.

Workflow

  1. A module's loader is configured in webpack.config.js.
  2. When the corresponding module file is encountered, its loader is triggered.
  3. The loader receives the module's file content as a source string.
  4. Using APIs provided by webpack, the loader transforms the source to produce a result.
  5. The result is returnde or passed to the next loader in the chain for further processing.

Implementation Example

Simple String Replacement Loader This loader changes all occurrences of the string "user" to "admin".

src/string-replace-loader.js

module.exports = function (sourceCode) {
  return sourceCode.replace(/user/g, 'admin');
}

webpack.config.js

const path = require('path');

module.exports = {
    mode: 'development',
    entry: './src/main.js',
    output: {
        filename: 'app.js',
        path: path.resolve(__dirname, 'build')
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                use: [
                    {
                        loader: path.resolve('./src/string-replace-loader.js'),
                    }
                ]
            }
        ]
    }
};

src/main.js

function initApp() {
  console.log('Current user logged in.');
}
initApp();

After bundling, the output file app.js will contain console.log('Current admin logged in.');.

Asynchronous Loaders

Loaders can perform asynchronous operations using the this.async() method.

src/async-loader.js

module.exports = function (inputSource) {
  const done = this.async(); // Retrieves the callback function

  setTimeout(() => { // Simulates an async task like a network request
    const transformedSource = inputSource.replace(/user/g, 'admin');
    done(null, transformedSource); // Pass result to webpack
  }, 1000);
}

Plugins

Plugins can tap into key events throughout the webpack compilation lifecycle. They have access to webpack's API and can influence the output bundle directly, offering broader capabilities than loaders which primarily transform source code.

Core Concepts

Two primary objects are crucial for plugin development:

  • Compiler: Represents the complete, persistent webpack environment configuration. It is instantiated once when webpack starts. Plugins receive a reference to it to access the main environment.
  • Compilation: Represents a single build of assets. A new Compilation object is created each time file changes are detected (e.g., during watch mode). It contains the current module resources, generated assets, changed files, and dependency state.

Plugin Structure

  1. A JavaScript class or named function.
  2. An apply method defined on its prototype.
  3. Binding to a specific webpack event hook within the apply method.
  4. Manipulation of the compilation data or assets.
  5. Invocation of a webpack-provided callback when the plugin's logic is complete.

Basic Plugin Example

src/log-message-plugin.js

function LogMessagePlugin() { }

LogMessagePlugin.prototype.apply = function (compiler) {
    // Hook into the 'emit' event, which occurs right before assets are emitted.
    compiler.plugin('emit', function (compilation, callback) {
        console.log('[Webpack] Assets are about to be emitted.');
        callback(); // Signal completion
    });
};

module.exports = LogMessagePlugin;

Usage in webpack.config.js

const LogMessagePlugin = require('./src/log-message-plugin');

module.exports = {
    // ... other config
    plugins: [
        new LogMessagePlugin()
    ]
};

Advanced Plugin: HTML Asset Generation

This plugin creates an index.html file that automatically includes the bundled JavaScript.

src/html-generator-plugin.js

function HtmlGeneratorPlugin() { }

HtmlGeneratorPlugin.prototype.apply = function (compiler) {
    compiler.plugin('emit', function (compilation, finishCallback) {
        // Access the configured output filename
        const jsBundleName = compiler.options.output.filename;

        // Generate HTML content
        const htmlContent = `
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Generated App</title>
</head>
<body>
    <div id="root"></div>
    <script src="${jsBundleName}"></script>
</body>
</html>`;

        // Add the new HTML file to the compilation assets
        compilation.assets['index.html'] = {
            source: function() {
                return htmlContent;
            },
            size: function() {
                return htmlContent.length;
            }
        };

        finishCallback();
    });
};

module.exports = HtmlGeneratorPlugin;

Using this plugin will result in an index.html file being generated in the output directory alongside the app.js bundle.

Related Articles

Efficient Usage of HTTP Client in IntelliJ IDEA

IntelliJ IDEA incorporates a versatile HTTP client tool, enabling developres to interact with RESTful services and APIs effectively with in the editor. This functionality streamlines workflows, replac...

Installing CocoaPods on macOS Catalina (10.15) Using a User-Managed Ruby

System Ruby on macOS 10.15 frequently fails to build native gems required by CocoaPods (for example, ffi), leading to errors like: ERROR: Failed to build gem native extension checking for ffi.h... no...

Resolve PhpStorm "Interpreter is not specified or invalid" on WAMP (Windows)

Symptom PhpStorm displays: "Interpreter is not specified or invalid. Press ‘Fix’ to edit your project configuration." This occurs when the IDE cannot locate a valid PHP CLI executable or when the debu...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.