Simple webpack plugin to identify the cause of repeated rebuild issue

Webpack-dev-server serves as an integrated tool, that offers hot reloading of modified assets in Webpack module builder system. It watches all the files specified by the glob pattern specified in the webpack.config and auto-refreshes the asset URL if it identifies a change. such an all-in-one setup has made Webpack a most preferred module builder solution in many famous Open source projects and has become a default engine for Angular CLI.

With all its goodies packed in, recently I've faced a strange issue which I couldn't figure out easily. The problem was that webpack seemed to be repeatedly building on and on in a never ending loop. The reason was obvious that some file change has caused it to go on a rebuild spree. I first narrowed down the include folder option in tsconfig.json file. But the issue persisted. I kept on narrowing down the scope of the webpack watcher, but still I couldn't get webpack to stop.

With task runners like gulp, it was dead simple as I could pipe to a watch function/task to output the information. So I expected it to be simpler in Webpack and started googling for webpack documentation, on how to log & view details of files that causes an event to happen i.e., rebuild. I was disappointment to see that no relevant API document pages turned out. Everything about Webpack API seemed to be hidden. So finally I landed up in a StackOverflow post, which suggested a watcher object from which the required information could be logged.

I wrapped that sample code part as a simple standalone webpack-plugin like below

// WebpackWatchRunPlugin.js
/*
 * This simple webpack plugin helps to identify the list of file changes, that 
 * triggered webpack re-compilation/re-build
 */
"use strict";

class WebpackWatchRunPlugin {
    constructor(options) {
        if (typeof options !== "object") options = {};
        this.options = options;
    }

    apply(compiler) {
        const options = this.options;
        compiler.plugin("watch-run",
            function (watching, done) {
                const changedTimes = watching.compiler.watchFileSystem.watcher.mtimes;
                const changedFiles = Object.keys(changedTimes)
                    .map(file => `\n  ${file}`)
                    .join("");
                if (changedFiles.length) {
                    console.log("Files modified:", changedFiles);
                }
                done();
            });
    }
}

module.exports = WebpackWatchRunPlugin;

To use in your webpack system, require above module in your webpack.config and add it to your plugins list as below

// Webpack.config

const WebpackWatchRunPlugin = require('../../tools/WebpackWatchRunPlugin');

module.exports = function(){
  return {
    // ...
    // plugins: [
          new WebpackWatchRunPlugin() 
       ]
  }
}

Finally I modified a watched file & saved it. It immediately triggered a rebuild and also logged debug information about i.e., the list of files that triggered the rebuild event.

The information helped me to the identify that the rebuilds were infact due to the code coverage report plugin which erroneously created HTML coverage report inside one of the project folder. With that folder added to tsconfig exclusion, the issue was sortedout as expected.

In the course of problem root cause analysis, I realized 2 things

  1. How simple & effortless to create a new webpack plugin extension.
  2. How webpack docs seemed to be missing the required webpack API information and how useful it was to analyze and identify a cause.