npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

html-bundler-webpack-plugin

v4.10.4

Published

Generates complete single-page or multi-page website from source assets. Build-in support for Markdown, Eta, EJS, Handlebars, Nunjucks, Pug. Alternative to html-webpack-plugin.

Downloads

20,959

Readme

npm node node Test codecov node

This plugin is all you need to generate a complete single- or multi-page website from your source assets.

The plugin automates the processing of source files such as JS/TS, SCSS, images and other assets referenced in an HTML or template file. This plugin will generate an HTML file containing all the necessary links to JS, CSS, images and other resources.

Why use the HTML Bundler Plugin?

This plugin is a powerful alternative to html-webpack-plugin and a replacement for many plugins and loaders.

The HTML Bundler Plugin works a bit differently than html-webpack-plugin. It doesn't just inject JavaScript and CSS into an HTML. Instead, it resolves all the source files of the assets referenced directly in the template and ensures the generated HTML contains the correct output URLs of resources after Webpack processes them. Additionally, CSS extracted from styles imported in JS can be injected into HTML as a <link> tag or as an inlined CSS.



💡 Highlights

  • An entry point is any HTML template. Start from HTML, not from JS.
  • Automatically processes templates found in the entry directory.
  • Build-in support for template engines: Eta, EJS, Handlebars, Nunjucks, Pug, Tempura, TwigJS, LiquidJS.
  • Build-in support for Markdown *.md files in templates, see Markdown demo in browser.
  • Source files of script and style can be specified directly in HTML:
    • <link href="./style.scss" rel="stylesheet">
      No longer need to define source style files in Webpack entry or import styles in JavaScript.
    • <script src="./app.ts" defer="defer"></script>
      No longer need to define source JavaScript files in Webpack entry.
  • Resolves source files of assets in attributes such as href src srcset using relative path or alias:
    • <link href="../images/favicon.svg" type="image/svg" rel=icon />
    • <img src="@images/pic.png" srcset="@images/pic400.png 1x, @images/pic800.png 2x" />
      Source files will be resolved, processed and auto-replaced with correct URLs in the generated HTML.
  • Inlines JS, CSS and Images into HTML. See how to inline all resources into single HTML file.
  • Supports the HMR for CSS to update CSS in browser without a full reload.
  • Watches for changes in the data file linked to the template in the plugin option.
  • Generates the preload tags for fonts, images, video, scripts, styles.
  • Generates the integrity attribute in the link and script tags.
  • Generates the favicons of different sizes for various platforms.
  • You can create custom plugins using the provided Plugin Hooks.
  • Over 600 tests for various use cases.

See full list of features.

HTML template as entry point

The HTML Bundler generates static HTML or template function from various templates containing source files of scripts, styles, images, fonts and other resources, similar to how it works in Vite. This plugin looks at the template files in entry option to start building the bundle. The source files of dependencies (scripts, styles, etc.) can be defined directly in the template.

The plugin resolves source files of assets in templates and replaces them with correct output URLs in the generated HTML. The resolved assets will be processed via Webpack plugins/loaders and placed into the output directory. You can use a relative path or Webpack alias to a source file.

A template imported in JS will be compiled into template function. You can use the template function in JS to render the template with variables in runtime on the client-side in the browser.

For example, using source asset files is HTML template ./src/views/index.html:

<html>
  <head>
    <!-- relative path to SCSS source file -->
    <link href="../scss/style.scss" rel="stylesheet" />
    <!-- relative path to TypeScript source file -->
    <script src="../app/main.ts" defer="defer"></script>
  </head>
  <body>
    <h1>Hello World!</h1>
    <!-- relative path to image source file -->
    <img src="../assets/images/picture1.png" />
    <!-- Webpack alias as path (src/assets/images/) to image source file -->
    <img src="@images/picture2.png" />
  </body>
</html>

The folder structure of the example:

./src/views/index.html
./src/app/main.ts
./src/scss/style.scss
./src/assets/images/picture1.png
./src/assets/images/picture2.png

Simple and clear Webpack configuration:

const path = require('path');
const HtmlBundlerPlugin = require('html-bundler-webpack-plugin');

module.exports = {
  output: {
    path: path.resolve(__dirname, 'dist'),
  },

  plugins: [
    new HtmlBundlerPlugin({
      // specify the entry points for HTML pages (or a template)
      entry: {
        index: 'src/views/index.html', // save generated HTML into dist/index.html
      },
      js: {
        filename: 'js/[name].[contenthash:8].js', // JS output filename
      },
      css: {
        filename: 'css/[name].[contenthash:8].css', // CSS output filename
      },
    }),
  ],
  
  module: {
      rules: [
        {
          test: /\.s?css$/,
          use: ['css-loader', 'sass-loader'],
        },
        {
          test: /\.(ico|png|jp?g|svg)/,
          type: 'asset/resource',
        },
      ],
    },
};

See full list of all plugin options.

Open an example in StackBlitz


🦖 Mozilla already uses this plugin to build static HTML files for the Mozilla AI GUIDE site.

The plugin has been actively developed for more than 2 years, and since 2023 it is open source.
Please support this project by giving it a star ⭐.


❤️ Sponsors & Patrons

Thank you to all our sponsors and patrons!


↑ top | ↓ contents

⚙️ How the plugin works

The plugin resolves references in the HTML template and adds them to the Webpack compilation. Webpack will automatically process the source files, and the plugin replaces the references with their output filenames in the generated HTML. See how the plugin works under the hood.

❓Question / Feature Request / Bug

If you have discovered a bug or have a feature suggestion, feel free to create an issue on GitHub.

📚 Read it

🔆 What's New in v4

  • NEW you can include Markdown *.md files in your HTML template (since v4.6.0).
  • NEW added supports the HMR for CSS (since v4.5.0).
  • NEW added supports the multiple configurations.
  • SUPPORTS Webpack version 5.96+ (since v4.2.0).
  • SUPPORTS Webpack version 5.81+ (since v4.0.0).
  • SUPPORTS Node.js version 18+.
  • BREAKING CHANGES see in the changelog.

🔆 What's New in v3

  • NEW added supports the template function in JS runtime on the client-side.
  • NEW added Pug preprocessor.
  • NEW added Twig preprocessor.
  • NEW added supports the dynamic import of styles.
  • NEW added supports the CSS Modules for styles imported in JS.
  • NEW added CSS extraction from styles used in *.vue files.
  • NEW added Hooks & Callbacks. Now you can create own plugin to extend this plugin.
  • NEW added the build-in FaviconsBundlerPlugin to generate and inject favicon tags.

🔆 What's New in v2

  • NEW added importing style files in JavaScript.
  • NEW added support the integrity.
  • NEW you can add/delete/rename a template file in the entry path without restarting Webpack.

For full release notes see the changelog.

⚠️ Limitations

Cache type

The current version works stably with cache.type as 'memory' (Webpack's default setting).

Support for the 'filesystem' cache type is in beta. It works stably in standard use cases, but it cannot be guaranteed to work in all use cases. If you have any problems, feel free to create an issue.


↑ top | ↓ contents

Install and Quick start

Install the html-bundler-webpack-plugin:

npm install html-bundler-webpack-plugin --save-dev

It's recommended to combine html-bundler-webpack-plugin with the css-loader and the sass-loader.
Install additional packages for styles:

npm install css-loader sass-loader sass --save-dev

Start with an HTML template. Add the <link> and <script> tags. You can include asset source files such as SCSS, JS, images, and other media files directly in an HTML template.

The plugin resolves <script src="..."> <link href="..."> and <img src="..." srcset="..."> that references your script, style and image source files.

For example, there is the template ./src/views/home.html:

<html>
  <head>
    <!-- variable from Webpack config -->
    <title><%= title %></title>
    <!-- relative path to favicon source file -->
    <link href="./favicon.ico" rel="icon" />
    <!-- relative path to SCSS source file -->
    <link href="./style.scss" rel="stylesheet" />
    <!-- relative path to JS source file -->
    <script src="./main.js" defer="defer"></script>
  </head>
  <body>
    <h1>Hello World!</h1>
    <!-- relative path to image source file -->
    <img src="./picture.png" />
  </body>
</html>

All source filenames should be relative to the entrypoint template, or you can use Webpack alias. The references are rewritten in the generated HTML so that they point to the correct output files.

The generated HTML contains URLs of the output filenames:

<html>
  <head>
    <title>Homepage</title>
    <link href="img/favicon.3bd858b4.ico" rel="icon" />
    <link href="css/style.05e4dd86.css" rel="stylesheet" />
    <script src="js/main.f4b855d8.js" defer="defer"></script>
  </head>
  <body>
    <h1>Hello World!</h1>
    <img src="img/picture.58b43bd8.png" />
  </body>
</html>

Pages can be defined in the entry option. JS and CSS can be configured using the js and css options.

If the entry option is a path, the plugin finds all templates automatically and keep the same directory structure in the output directory.

If the entry option is an object, the key is an output filename without .html extension and the value is a template file.

const HtmlBundlerPlugin = require('html-bundler-webpack-plugin');

module.exports = {
  plugins: [
    new HtmlBundlerPlugin({
      // automatically processing all templates in the path
      entry: 'src/views/',
      
      // - OR - define pages manually (key is output filename w/o `.html`)
      entry: {
        index: 'src/views/home.html', // => dist/index.html
        'news/sport': 'src/views/news/sport/index.html', // => dist/news/sport.html
      },
      
      // - OR - define pages with variables
      entry: [
        {
          import: 'src/views/home.html', // template file
          filename: 'index.html', // => dist/index.html
          data: { title: 'Homepage' }, // pass variables into template
        },
        {
          import: 'src/views/news/sport/index.html', // template file
          filename: 'news/sport.html', // => dist/news/sport.html
          data: { title: 'Sport news' }, // pass variables into template
        },
      ],
      
      // - OR - combine both the pages with and w/o variables in one entry
      entry: {
        // simple page config w/o variables
        index: 'src/views/home.html', // => dist/index.html
        // advanced page config with variables
        'news/sport': { // => dist/news/sport.html
          import: 'src/views/home.html', // template file
          data: { title: 'Sport news' }, // pass variables into template
        },
      },

      js: {
        // JS output filename, used if `inline` option is false (defaults)
        filename: 'js/[name].[contenthash:8].js',
        //inline: true, // inlines JS into HTML
      },
      css: {
        // CSS output filename, used if `inline` option is false (defaults)
        filename: 'css/[name].[contenthash:8].css',
        //inline: true, // inlines CSS into HTML
      },
    }),
  ],
  module: {
    rules: [
      {
        test: /\.(css|sass|scss)$/,
        use: ['css-loader', 'sass-loader'],
      },
      {
        test: /\.(ico|png|jp?g|webp|svg)$/,
        type: 'asset/resource',
        generator: {
          filename: 'img/[name].[hash:8][ext][query]',
        },
      },
    ],
  },
};

ℹ️ Note

To define the JS output filename, use the js.filename option of the plugin.
Don't use Webpack's output.filename, hold all relevant settings in one place - in plugin options.
Both places have the same effect, but js.filename has priority over output.filename.

For splitChunks see How to configure splitChunks.

No additional template loader is required. The plugin handles templates with base EJS-like syntax automatically. The default templating engine is Eta.

For using the native EJS syntax see Templating with EJS.
For using the Handlebars see Templating with Handlebars.
For other templates see Template engines.

For custom templates, you can use the preprocessor option to handle any template engine.

Open in StackBlitz

Open in StackBlitz

See boilerplate


↑ top

Table of Contents

  1. Features
  2. Install and Quick start
  3. Webpack options
  4. Build-in Plugins
  5. Third-party Plugins
  6. Hooks & Callbacks
  7. Plugin options
  8. Loader options
  9. Using template engines
  10. Using template in JavaScript
  11. Setup Live Reload
  12. Recipes
  13. Problems & Solutions
  14. Demo sites
  15. Usage examples

↑ back to contents

Features

  • HTML template is the entry point for all resources
  • extracts JS from the source script filename specified in HTML via a <script> tag
  • extracts CSS from the source style filename specified in HTML via a <link> tag
  • importing style files in JavaScript
  • resolves source asset files in HTML attributes and in the CSS url(), without using resolve-url-loader
  • supports styles used in *.vue files
  • generated HTML contains output filenames
  • supports the module types asset/resource asset/inline asset asset/source (*)
  • inline CSS in HTML
  • inline JavaScript in HTML
  • inline image as base64 encoded data-URL for PNG, JPG, etc. in HTML and CSS
  • inline SVG as SVG tag in HTML, e.g.: <svg>...</svg>
  • inline SVG as utf-8 data-URL in CSS, e.g.: url("data:image/svg+xml,<svg>...</svg>")
  • auto generation of <link rel="preload"> to preload assets
  • supports the auto publicPath
  • enable/disable extraction of comments to *.LICENSE.txt file
  • supports template engines such as Eta, EJS, Handlebars, Nunjucks, Pug, TwigJS, LiquidJS and others
  • supports a template function for usage in JS on the client-side
  • supports Markdown *.md files in templates
  • supports both async and sync preprocessor
  • auto processing multiple HTML templates using the entry path
  • pass data into template from the plugin config
  • dynamically loading template variables using the data option, change data w/o restarting
  • generates the integrity hashes and adds the integrity attribute to the link and script tags
  • minification of generated HTML
  • allows extending base functionality using hooks & callbacks
  • generates favicons of different sizes for various platforms and injects them into HTML

(*) - asset/source works currently for SVG only, in a next version will work for other files too

Why do many developers switch from Webpack to other bundlers?

One of the reasons they cite is the complex configuration many different plugins and loaders for one simple thing - rendering an HTML page with assets.

The HTML bundler plugin "changes the rule of the game", making configuration very simple and clear. Just one plugin replaces the functionality of the plugins and loaders:

| Package | Features | |---------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------| | html-webpack-plugin | creates HTML and inject script tag for compiled JS file into HTML | | mini-css-extract-plugin | injects link tag for processed CSS file into HTML | | webpack-remove-empty-scripts | removes generated empty JS files | | html-loader | exports HTML, resolving attributes | | style-loader | injects an inline CSS into HTML | | html-webpack-inject-preload | inject preload link tags | | preload-webpack-plugin | inject preload link tags | | html-webpack-inline-source-plugin | inline JS and CSS into HTML | | html-inline-css-webpack-plugin | inline CSS into HTML | | posthtml-inline-svg | injects an inline SVG icon into HTML | | resolve-url-loader | resolves a relative URL in CSS | | svg-url-loader | encodes a SVG data-URL as utf8 | | webpack-subresource-integrity | enables Subresource Integrity | | favicons-webpack-plugin | generates favicons and icons | | handlebars-webpack-plugin | renders Handlebars templates | | handlebars-loader | compiles Handlebars templates | | pug-loader | compiles Pug templates | | nunjucks-loader | compiles Nunjucks templates |

↑ back to contents

Webpack options

Important Webpack options used to properly configure this plugin.

output.path

Type: string Default: path.join(process.cwd(), 'dist')

The root output directory for all processed files, as an absolute path.
You can omit this option, then all generated files will be saved under dist/ in your project directory.

output.publicPath

Type: string|function Default: auto

The value of the option is prefixed to every URL created by this plugin. If the value is not the empty string or auto, then the option must end with /.

The possible values:

  • publicPath: 'auto' - automatically determines a path of an asset relative of their issuer. The generated HTML page can be opened directly form the local directory and all js, css and images will be loaded in a browser.
  • publicPath: '' - a path relative to an HTML page, in the same directory. The resulting path is different from a path generated with auto.
  • publicPath: '/' - a path relative to document root directory on a server
  • publicPath: '/assets/' - a sub path relative to document root directory on a server
  • publicPath: '//cdn.example.com/' - an external URL with the same protocol (http:// or https://)
  • publicPath: 'https://cdn.example.com/' - an external URL with the https:// protocol only

output.filename

Type: string|function Default: [name].js

The output name of a generated JS file.
Highly recommended to define the filename in the Plugin option js.filename.

The output name of a generated CSS file is determined in the Plugin option css.filename.

Define output JS and CSS filenames in the Plugin option, in one place:

const HtmlBundlerPlugin = require('html-bundler-webpack-plugin');
module.exports = {
  plugins: [
    new HtmlBundlerPlugin({
      js: {
        // define the output name of a generated JS file here
        filename: 'js/[name].[contenthash:8].js',
      },
      css: {
        // define the output name of a generated CSS file here
        filename: 'css/[name].[contenthash:8].css',
      },
    }),
  ],
};

↑ back to contents

entry

The starting point to build the bundle.

ℹ️ Note

Using this plugin an entry point is an HTML template. All script and style source files must be specified in the HTML template.

You can use the Webpack entry option to define HTML templates, but it is highly recommended to define all templates in plugin option entry, because it has an additional data property (not available in the Webpack entry) to pass custom variables into the HTML template.

For details see the plugin option entry.

↑ back to contents

Build-in Plugins

There are the most useful plugins available "out of the box". The build-in plugins maintained by the HtmlBundlerPlugin.

All build-in plugins are in the /plugins subdirectory of the HtmlBundlerPlugin.

FaviconsBundlerPlugin

The FaviconsBundlerPlugin generates favicons for different devices and injects favicon tags into HTML head.

Install

This plugin requires the additional favicons package.

npm install favicons -D

Config

const HtmlBundlerPlugin = require('html-bundler-webpack-plugin');
const { FaviconsBundlerPlugin } = require('html-bundler-webpack-plugin/plugins');

module.exports = {
  plugins: [
    new HtmlBundlerPlugin({
      entry: {
        // source favicon file must be specified directly in HTML using link tag
        index: './src/views/index.html',
      },
    }),
    // add the favicons plugin
    new FaviconsBundlerPlugin({
      enabled: 'auto', // true, false, auto - generate favicons in production mode only
      // favicons configuration options, see https://github.com/itgalaxy/favicons#usage
      faviconOptions: {
        path: '/img/favicons', // favicons output path relative to webpack output.path
        icons: {
          android: true, // Create Android homescreen icon.
          appleIcon: true, // Create Apple touch icons.
          appleStartup: false, // Create Apple startup images.
          favicons: true, // Create regular favicons.
          windows: false, // Create Windows 8 tile icons.
          yandex: false, // Create Yandex browser icon.
        },
      },
    }),
  ],
  module: {
    rules: [
      {
        test: /\.(png|jpe?g|ico|svg)$/,
        type: 'asset/resource',
      },
    ],
  },
};

FaviconsBundlerPlugin options

  • enabled: boolean | 'auto'
    if is 'auto' then generate favicons in production mode only, in development mode will be used original favicon processed via webpack asset module.
  • faviconOptions: FaviconOptions - options of the favicons module. See configuration options.

Usage

The source file of your favicon must be specified directly in HTML as the link tag with rel="icon" attribute.

If the FaviconsBundlerPlugin is disabled or as auto in development mode, then the source favicon file will be processed via webpack.

If the FaviconsBundlerPlugin is enabled or as auto in production mode, then the source favicon file will be processed via favicons module and the original link tag with favicon will be replaced with generated favicon tags.

For example, there is the src/views/index.html

<!DOCTYPE html>
<html>
<head>
  <!-- source favicon file relative to this HTML file, or use a webpack alias -->
  <link href="./myFavicon.png" rel="icon" />
</head>
<body>
  <h1>Hello World!</h1>
</body>
</html>

The generated HTML when FaviconsBundlerPlugin is disabled:

<!DOCTYPE html>
<html>
<head>
  <!-- output favicon file -->
  <link href="assets/img/myFavicon.1234abcd.png" rel="icon" />
</head>
<body>
  <h1>Hello World!</h1>
</body>
</html>

The generated HTML when FaviconsBundlerPlugin is enabled:

<!DOCTYPE html>
<html>
<head>
  <!-- original tag is replaced with tags generated by favicons module -->
  <link rel="apple-touch-icon" sizes="1024x1024" href="/img/favicons/apple-touch-icon-1024x1024.png">
  <link rel="apple-touch-icon" sizes="114x114" href="/img/favicons/apple-touch-icon-114x114.png">
  <link rel="apple-touch-icon" sizes="120x120" href="/img/favicons/apple-touch-icon-120x120.png">
  <link rel="apple-touch-icon" sizes="144x144" href="/img/favicons/apple-touch-icon-144x144.png">
  <link rel="apple-touch-icon" sizes="152x152" href="/img/favicons/apple-touch-icon-152x152.png">
  <link rel="apple-touch-icon" sizes="167x167" href="/img/favicons/apple-touch-icon-167x167.png">
  <link rel="apple-touch-icon" sizes="180x180" href="/img/favicons/apple-touch-icon-180x180.png">
  <link rel="apple-touch-icon" sizes="57x57" href="/img/favicons/apple-touch-icon-57x57.png">
  <link rel="apple-touch-icon" sizes="60x60" href="/img/favicons/apple-touch-icon-60x60.png">
  <link rel="apple-touch-icon" sizes="72x72" href="/img/favicons/apple-touch-icon-72x72.png">
  <link rel="apple-touch-icon" sizes="76x76" href="/img/favicons/apple-touch-icon-76x76.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/img/favicons/favicon-16x16.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/img/favicons/favicon-32x32.png">
  <link rel="icon" type="image/png" sizes="48x48" href="/img/favicons/favicon-48x48.png">
  <link rel="icon" type="image/x-icon" href="/img/favicons/favicon.ico">
  <link rel="manifest" href="/img/favicons/manifest.webmanifest">
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
  <meta name="apple-mobile-web-app-title" content="My App">
  <meta name="application-name" content="My App">
  <meta name="mobile-web-app-capable" content="yes">
  <meta name="theme-color" content="#fff">
</head>
<body>
  <h1>Hello World!</h1>
</body>
</html>

↑ back to contents

Third-party Plugins

The third-party plugins not maintained by the HtmlBundlerPlugin. It potentially does not have the same support, security policy or license as Build-in Plugins.

You can create own plugin using the plugin hooks. As a reference plugin, you can use the FaviconsBundlerPlugin.

If you have a useful plugin, create a PR with the link to you plugin.

The plugin name must end with -bundler-plugin, e.g. hello-world-bundler-plugin.

Currently there are no plugins yet. Be the first to create one.

↑ back to contents

Hooks & Callbacks

Using hooks and callbacks, you can extend the functionality of this plugin.

The hook can be defined in an external plugin. The callback is defined as an option in the HTMLBundlerPlugin.

Most hooks have a callback with the same name. Each callback is called after hook with the same name. So with a callback, you can change the result of the hook.

When using callbacks

If you have small code just for your project or are doing debugging, you can use callbacks.

When using hooks

Using hooks you can create your own plugin.

How the plugin works under the hood.

How to use hooks

The simplest way, add the { apply() { ... } } object to the array of the Webpack plugins:

const HtmlBundlerPlugin = require('html-bundler-webpack-plugin');

module.exports = {
  plugins: [
    new HtmlBundlerPlugin({
      entry: {
        index: './src/index.html',
      },
    }),
    // your plugin
    {
      apply(compiler) {
        const pluginName = 'MyPlugin';

        compiler.hooks.compilation.tap(pluginName, (compilation) => {
          const hooks = HtmlBundlerPlugin.getHooks(compilation);

          // modify generated HTML of the index.html template
          hooks.beforeEmit.tap(pluginName, (content, { name, sourceFile, assetFile }) => {
            return content.replace('something...', 'other...')
          });
        });
      },
    },
  ],
};

You can use this template as the basis for your own plugin:

const HtmlBundlerPlugin = require('html-bundler-webpack-plugin');

class MyPlugin {
  pluginName = 'my-plugin';
  options = {};

  /**
   * @param {{ enabled: boolean | 'auto'}} options The options of your plugin.
   */
  constructor(options = {}) {
    this.options = options;
  }

  apply(compiler) {
    // you can use the API of the HtmlBundlerPlugin.option
    const enabled = HtmlBundlerPlugin.option.toBool(this.options?.enabled, true, 'auto');
    const outputPath = HtmlBundlerPlugin.option.getWebpackOutputPath();

    if (!enabled) {
      return;
    }

    const { pluginName } = this;
    const { webpack } = compiler; // instance of the Webpack
    const fs = compiler.inputFileSystem.fileSystem; // instance of the Webpack FyleSystem

    // start your plugin from the webpack compilation hook
    compiler.hooks.compilation.tap(pluginName, (compilation) => {
      const hooks = HtmlBundlerPlugin.getHooks(compilation);
      
      // usage of the sync, async and promise hooks

      // sync hook
      hooks.<hookName>.tap(pluginName, (...arguments) => {
        // do somthing here ...
        const result = 'your result';
        // return the result
        return result;
      });
      
      // async hook
      hooks.<hookName>.tapAsync(pluginName, (...arguments, callback) => {
        // do somthing here ...
        const result = 'your result';
        // call the callback function to resolve the async hook
        callback(result);
      });
      
      // promise hook
      hooks.<hookName>.tapPromise(pluginName, (...arguments) => {
        // do somthing here ...
        const result = 'your result';
        // return the promise with the result
        return Promise.resolve(result);
      });
    });
  }
}

module.exports = MyPlugin;

Then add your plugin in the webpack config:

const HtmlBundlerPlugin = require('html-bundler-webpack-plugin');
const MyBundlerPlugin = require('my-bundler-plugin');

module.exports = {
  plugins: [
    new HtmlBundlerPlugin({
      entry: {
        index: './src/index.html',
      },
    }),
    // your plugin
    new MyBundlerPlugin({ enabled: true });
  ],
};

For an example implementation see FaviconsBundlerPlugin.

↑ back to contents

beforePreprocessor

AsyncSeriesWaterfallHook<[
  content: string,
  loaderContext: LoaderContext<Object> & { data: { [key: string]: any } | string }
]>;

For details on AsyncSeriesWaterfallHook see the hook interface.

For details on hook parameters, see in the beforePreprocessor callback option.

↑ back to contents

preprocessor

AsyncSeriesWaterfallHook<[
  content: string,
  loaderContext: LoaderContext<Object> & { data: { [key: string]: any } | string }
]>;

For details on AsyncSeriesWaterfallHook see the hook interface.

For details on hook parameters, see in the preprocessor callback option.

↑ back to contents

resolveSource

SyncWaterfallHook<[
  source: string,
  info: {
    type: 'style' | 'script' | 'asset';
    tag: string;
    attribute: string;
    value: string;
    resolvedFile: string;
    issuer: string
  },
]>;

no calback

Called after resolving of a source attribute defined by source loader option.

For details on SyncWaterfallHook see the hook interface.

Hook parameters:

  • source - a source of the tag where are parsed attributes, e.g. <link href="./favicon.png" rel="icon">
  • info - an object with parsed information:
    • type - the type of the tag
    • tag - the tag name, e.g. 'link', 'script', 'img', etc.
    • attribute - the attribute name, e.g. 'src', 'href', etc.
    • value - the attribute value
    • resolvedFile - the resolved file from the value
    • issuer - the template file

Return a string to override the resolved value of the attribute or undefined to keep the resolved value.

↑ back to contents

postprocess

AsyncSeriesWaterfallHook<[content: string, info: TemplateInfo]>;

For details on AsyncSeriesWaterfallHook see the hook interface.

For details on hook parameters, see in the postprocess callback option.

↑ back to contents

beforeEmit

AsyncSeriesWaterfallHook<[content: string, entry: CompileEntry]>;

For details on AsyncSeriesWaterfallHook see the hook interface.

For details on hook parameters, see in the beforeEmit callback option.

↑ back to contents

afterEmit

AsyncSeriesHook<[entries: CompileEntries]>;

For details on AsyncSeriesHook see the hook interface.

For details on hook parameters, see in the afterEmit callback option.

↑ back to contents

integrityHashes

AsyncSeriesHook<{
  // the map of the output asset filename to its integrity hash
  hashes: Map<string, string>;
}>;

Called after all assets have been processed and hashes have finite values and cannot be changed, at the afterEmit stage. This can be used to retrieve the integrity values for the asset files.

For details on AsyncSeriesHook see the hook interface.

Callback Parameter: hashes is the map of the output asset filename to its integrity hash. The map only contains JS and CSS assets that have a hash.

You can write your own plugin, for example, to extract integrity values into the separate file:

const fs = require('fs');
const path = require('path');
const HtmlBundlerPlugin = require('html-bundler-webpack-plugin');
module.exports = {
  output: {
    crossOriginLoading: 'anonymous', // required for Subresource Integrity
  },
  plugins: [
    new HtmlBundlerPlugin({
      entry: {
        index: './src/index.html',
      },
      js: {
        filename: '[name].[contenthash:8].js',
        chunkFilename: '[name].[contenthash:8].chunk.js',
      },
      css: {
        filename: '[name].[contenthash:8].css',
        chunkFilename: '[name].[contenthash:8].chunk.css',
      },
      integrity: 'auto',
    }),
    // your plugin to extract the integrity values
    {
      apply(compiler) {
        compiler.hooks.compilation.tap('MyPlugin', (compilation) => {
          const hooks = HtmlBundlerPlugin.getHooks(compilation);
          hooks.integrityHashes.tapAsync(
            'MyPlugin', 
            (hashes) => Promise.resolve().then(() => {
                if (hashes.size > 0) {
                  const saveAs = path.join(__dirname, 'dist/integrity.json');
                  const json = Object.fromEntries(hashes);
                  fs.writeFileSync(saveAs, JSON.stringify(json, null, '  ')); // => save to file
                  console.log(hashes); // => output to console
                }
              })
            );
          }
        );
      },
    },
  ],
};

The content of the dist/integrity.json file looks like:

{
  "815.49b3d882.chunk.js": "sha384-dBK6nNrKKk2KjQLYmHZu6tuWwp7kBzzEvdX+4Ni11UzxO2VHvP4A22E/+mmeduul",
  "main.9c043cce.js": "sha384-AbfLh7mk6gCp0nhkXlAnOIzaHeJSB8fcV1/wT/FWBHIDV7Blg9A0sukZ4nS3xjtR"
  "main.dc4ea4af.chunk.css": "sha384-W/pO0vwqqWBj4lq8nfe+kjrP8Z78smCBttkCvx1SYKrVI4WEdJa6W6i0I2hoc1t7",
  "style.47f4da55.css": "sha384-gaDmgJjLpipN1Jmuc98geFnDjVqWn1fixlG0Ab90qFyUIJ4ARXlKBsMGumxTSu7E",
}

↑ back to contents

Plugin options

test

Type: RegExp Default: /\.(html|eta)$/

The test option allows to handel only those templates as entry points that match the name of the source file.

For example, if you have other templates, e.g. *.liquid, as entry points, then you can set the option to match custom template files: test: /\.(html|liquid)$/.

The test value is used in the default loader.

ℹ️ Note

Using the preprocessor options will be added the templating engine extensions in the test automatically. Defaults preprocessor is Eta therefore is used the /\.(html|eta)$/ RegExp.

For example, if you define the preprocessor option as the handlebars, then will be used the /\.(html|hbs|handlebars)$/ RegExp automatically.

Why is it necessary to define it? Can't it be automatically processed?

This plugin is very powerful and has many experimental features not yet documented. One of the next features will be the processing scripts and styles as entry points for library bundles without templates. To do this, the plugin must differentiate between a template entry point and a script/style entry point. This plugin can completely replace the functionality of mini-css-extract-plugin and webpack-remove-empty-scripts in future.

entry

Type: EntryObject | Array<EntryDescription> | string.

The EntryObject is identical to Webpack entry plus additional data property to pass custom variables into the HTML template.

Defines the entry points for HTML files. You can specify a path to a template directory to automatically process all templates, or specify multiple entries if you want to manually manage templates files.

ℹ️ Note

You can define templates both in Webpack entry and in the entry option of the plugin. The syntax is identical. But the data property can only be used in the entry option of the plugin.

💡 Tip

Specify template files as entry points in the entry plugin option.

An HTML template is a starting point for collecting all the dependencies used in your web application. Specify source scripts (JS, TS) and styles (CSS, SCSS, LESS, etc.) directly in HTML. The plugin automatically extracts JS and CSS whose source files are specified in an HTML template.

type EntryObject = {
  [name: string]: EntryDescription | string;
};

The key of the EntryObject is the output filename without an extension, relative to the outputPath option.

Simple syntax

When the entry point value is a string, it must be an absolute or relative template file. For example:

{
  entry: {
    index: path.join(__dirname, 'src/views/home/index.html'), // => dist/index.html
    'news/sport': 'src/views/news/sport/index.html', // => dist/news/sport.html
  },
}

Advanced syntax

If you need to pass data to a template or want to dynamically generate an output filename regardless of the entry key, you can define the value of an entry as an EntryDescription object.

type EntryDescription = {
  /**
   * Template file, relative of context or absolute.
   */
  import: string;
  /**
   * Specifies the filename of the output file.
   */
  filename?: FilenameTemplate;
  /**
   * The template data.
   */
  data?: { [key: string]: any } | string;
};

type FilenameTemplate =
  | string
  | ((pathData: import('webpack/Compilation').PathData, assetInfo?: import('webpack/Compilation').AssetInfo) => string);
import

The import is a path to a template file, absolute or relative to the Webpack context option.

filename

When the filename is defined as a string, it will be used as the output html filename. In this case, the entry key can be any unique string.

For example:

{
  entry: {
    page01: {
      import: 'src/views/news/sport/index.html', // <= source template
      filename: 'news/sport.html', // => output ./dist/news/sport.html
    },
  },
}

When the filename is defined as a template string, then the entry key will be used as the [name] in the template string. Defaults, the filename is the [name].html template string.

For example:

{
  entry: {
    'news/sport': {
      import: 'src/views/news/sport/index.html', // <= source template
      filename: '[name].html', // => output ./dist/news/sport.html
    },
  },
}

The example above is equivalent to the simple syntax:

{
  entry: {
    'news/sport': 'src/views/news/sport/index.html',
  },
}

data

The data is passed into preprocessor to render the template with variables.

data as object

When the data is an object, it will be loaded once with Webpack start. After changing the data, you need to restart Webpack.

For example:

{
  entry: {
    index: {
      import: 'src/views/index.html',
      // pass data as an object
      data: {
        title: 'Home',
      }
    },
}

data as JSON file

When the data is a string, it must be an absolute or relative path to a file. The file can be a JSON file or a JS file that exports the data as an object. Use the data as a file if you want to get dynamic data in a template. The data file will be reloaded after changes, without restarting Webpack.

For example, using *.json data file:

{
  entry: {
    index: {
      import: 'src/views/index.html',
      // load data from JSON file
      data: 'src/data/home.json',
    },
  },
}

The data file src/data/home.json:

{
  "title": "Home"
}

data as JS file

To load data into a template dynamically you can use JS file. In JS file you can load data from a database.

For example, using *.js data file:

{
  entry: {
    index: {
      import: 'src/views/index.html',
      // load data from JS file
      data: 'src/data/home.js',
    },
  },
}

The data file src/data/home.js:

// here you can load data from a database
const data = {
  title: 'Home',
};

module.exports = data;

💡 Tip

To pass global variables in all templates use the data plugin option.

Entry as an array

If the entry is the array of the EntryDescription then the filename property is required.

{
  entry: [
    {
      filename: 'index.html', // => output filename dist/index.html
      import: 'src/views/index.html', // template file
      data: { title: 'Homepage' }, // page specifically variables
    },
    {
      filename: 'about.html',
      import: 'src/views/about.html',
      data: { title: 'About' },
    },
    {
      filename: 'news/sport.html',
      import: 'src/views/news/sport.html',
      data: { title: 'Sport' },
    },
  ],
}

Entry as an object

The absolute equivalent to the example above using an object is:

{
  entry: {
    index: { // => output filename dist/index.html
      import: 'src/views/index.html', // template file
      data: { title: 'Homepage' }, // page specifically variables
    },
    about: {
      import: 'src/views/about.html',
      data: { title: 'About'