javascriptwebpackbabeljswebpack-dev-serverbundling-and-minification

Reload Webpack Html Template on Certain Files Change


I'm using revealjs to create responsive presentations. The problem with revealjs is that all the slides code is written in a single HTML file which can be messy to some level (Some presentations' HTML code reached about 3500 lines of HTML in that single file).

I'm now restructuring this system and I would like to have a directory named slides that contains each slide HTML file. Each of these files is named slidenumber.html. Finally, I want to bundle all of the files with webpack 5 into a single HTML file in dist. I managed to achieve this but it has an issue with the dev server.

webpack.config.js

// ... imports .... 

module.exports = {
  ...,
  plugins: [
    ....,
    new HtmlWebpackPlugin({
      filename: "index.html",
      inject: true,
      templateContent: getTemplate(),
    }),
    new WatchExternalFilesPlugin({
      files: ["./slides/*.html"],
    }),
  ],
  module: {
    rules: [...],
  },
  devServer: {
    port: 8080,
  },
};

The getTemplate function loops over the HTML files in the slides directory and returns them wrapped with the template boilerplate

This is the function for reference getTemplate.js

const fs = require("fs/promises");
const path = require("path");
const { parse } = require("node-html-parser");

module.exports = async () => {
  const template = parse(
    await fs.readFile(path.join(__dirname, "../templates/index.html"))
  );
  const files = await fs.readdir(path.join(__dirname, "../slides"));

  for await (const fileName of files) {
    const slide = parse(
      await fs.readFile(path.join(__dirname, `../slides/${fileName}`))
    );
    template.querySelector("#slides").appendChild(slide);
  }

  return template.toString();
};

all of the above code is working fine on build but when running the dev server, I can't get the HtmlWebpackPlugin to re-execute the templateContent: getTemplate() on the change of any HTML slide file in the slides directory and as a result, when I edit any file of the slides HTML files in the slides directory, I don't get any update.

I'm aware that templateContent is supposed to run only on the start of the server but I'm asking if there is any other feature that can get me to the required behavior.

Thanks if you made it to here and excuse my English, I'm not a native speaker.


Solution

  • I could achieve the behavior I described in the question by setting a middleware from the dev server options that catches any request and returns the output of the getTemplate function.

    This is the configurations for the dev server webpack.config.dev.js

    // ...imports...
    
    module.exports = {
      mode: "development",
      entry: { index: "./main.js" },
      output: {...},
      module: {
        rules: [...],
      },
      devServer: {
        port: 8080,
        watchFiles: ["./slides/*.html"],
        hot: true,
        onBeforeSetupMiddleware: function (devServer) {
          devServer.app.get("/", async function (req, res) {
            res.send(await getTemplate());
          });
        },
      },
    };
    

    These are the configurations for the production server webpack.config.production.js

    // ...imports...
    
    module.exports = {
      mode: "production",
      entry: { index: "./main.js" },
      output: {...},
      plugins: [
        new HtmlWebpackPlugin({
          filename: "index.html",
          templateContent: getTemplate(),
          inject: false,
        }),
      ],
      module: {
        rules: [...],
      },
    };
    
    

    I used the webpackHtmlPlugin in production as usual but in development, I didn't use it at all since it can't reload the templates on the build

    In development, though I lost the ability to add the hash number to the compiled js file as I won't be able to predict the hash and inject its script tag. The compiled file had the same name as the original file and I added the script tag manually in the HTML template file.

    Hope this helps anyone!