WittCode💻

Webpack Plugins

By

Learn what webpack plugins are and how to use them to work with the webpack lifecycle. We will go over the HTML webpack plugin, ProgressPlugin, and create our own custom webpack plugin.

Table of Contents 📖

What are webpack Plugins?

A plugin is a feature of webpack that allows us to access, or plug in to, webpack's lifecycle. This allows us to do things such as configure global constants at compile time, monitor webpack's compilation progress, create HTML files to serve the bundles webpack creates, and more. Essentially, plugins allow us to do anything that webpack loaders can't do.

Working with webpack Plugins

To work with plugins, we use the plugins key in a webpack configuration file.

module.exports = {
    entry: './src/index.js',
    output: {
        filename: 'my-bundle.js',
        path: `${__dirname}/my-output`
    },
    plugins: []
}

The plugins key takes an array of plugins. Webpack plugins can be local, hosted on npm, or even built into webpack itself. No matter the case, a webpack plugin is simply a JavaScript object.

Built in Plugins

An example of a built in webpack plugin is the ProgressPlugin. This plugin reports on the webpack compilation progress. To use it, simply require webpack, access it as a property, and then instantiate it.

const webpack = require('webpack');

module.exports = {
    entry: './src/index.js',
    output: {
        filename: 'my-bundle.js',
        path: `${__dirname}/my-output`
    },
    plugins: [new webpack.ProgressPlugin()]
}

Now if we run webpack with this configuration we will see logging written to the console.

webpack --config webpack.config.js
93% sealing assets...

Also, as webpack plugins are JavaScript objects, we can configure them. For example, the ProgressPlugin constructor can accept a function.

const webpack = require('webpack');

const handler = (percentage, message, ...args) => {
    console.log(percentage, message, args);
}

module.exports = {
    entry: './src/index.js',
    output: {
        filename: 'my-bundle.js',
        path: `${__dirname}/my-output`
    },
    plugins: [new webpack.ProgressPlugin(handler)]
}

This function accepts 3 properties.

  • percentage - indicates the compilation completion, a value between 0 - 1
  • message - a message describing the currently executing task
  • ...args - additional descriptions of the current task

If we run this webpack configuration we will see the percentage values along with some other compilation information printed to the console.

webpack --config webpack.config.js
0  []
0.01 setup [ 'before run' ]
0.01 setup [ 'before run', 'NodeEnvironmentPlugin' ]
...
0.99 cache [ 'shutdown' ]
1  []

Plugins on npm

There are also webpack plugins present on npm. For example, there is the HTML webpack plugin which generates an HTML file containing our bundled JavaScript file. This is useful if we have a content hash in our output bundle filename and want it automatically added to an index.html file. It is installable from npm under the package name html-webpack-plugin and should be installed as a development depdendency.

npm i html-webpack-plugin -D

After installing it, we need to import it and then instantiate it in our webpack configuration file.

const webpack = require('webpack');
const htmlWebpackPlugin = require('html-webpack-plugin');

const handler = (percentage, message, ...args) => {
    console.log(percentage, message, args);
}

module.exports = {
    entry: './src/index.js',
    output: {
        filename: 'my-bundle.[contenthash].js',
        path: `${__dirname}/my-output`
    },
    plugins: [
        new webpack.ProgressPlugin(handler),
        new htmlWebpackPlugin()
    ]
}

Next run webpack.

webpack --config webpack.config.js

Then look for the output folder.

[object Object]

Checking the contents of the index.html file we will see the outputted bundle in a script tag.

<!doctype html><html><head><meta charset="utf-8"><title>Webpack App</title><meta name="viewport" content="width=device-width,initial-scale=1"><script defer="defer" src="my-bundle.4c1d4a8024a356be322d.js"></ script></head><body></body></html>

Creating a Plugin

Under the hood, a webpack plugin is simply a JavaScript object with an apply method. This apply method has access to the webpack compiler which is what ultimately provides access to webpack's lifecycle. For example, if we click into the HTML webpack plugin we can see the apply method.

declare class HtmlWebpackPlugin {
    constructor(options?: HtmlWebpackPlugin.Options);   
    ...   
    apply(compiler: Compiler): void;
    ...
}

To demonstrate, lets create our own plugin called MyPlugin.

class MyPlugin {
    apply(compiler) {
        compiler.hooks.done.tap('MyPlugin', (stats) => {
            console.log('Tapped into done!');
        });
        compiler.hooks.initialize.tap('MyPlugin', () => {
            console.log('Tapped into initialize!');
        })
    }
}

module.exports = {
    entry: './src/index.js',
    output: {
        filename: 'my-bundle.[contenthash].js',
        path: `${__dirname}/my-output`
    },
    plugins: [
        new MyPlugin()
    ]
}

Here we tap into webpack's lifecycle using the tap method on the desired part of the lifecycle. For example, initialize is called when a compiler object is initialized and done is called when the compilation has completed. Different lifecycle hooks have different arguments provided to their callback functions. For example, the done hook has stats provided to the callback function while initialize doesn't have any parameters. Next, run webpack.

webpack --config webpack.config.js
Tapped into initialize!
Tapped into done!

Based on the output, we can see how each callback function is ran when that part of the webpack lifecycle is reached.