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
Maintainers
Readme
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
andstyle
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
andscript
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.
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.
🦖 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
- Using HTML Bundler Plugin for Webpack to generate HTML files
- Keep output directory structure in Webpack
- Auto generate an integrity hash for
link
andscript
tags - Use a HTML file as an entry point? (Webpack issue, #536)
- Comparison and Benchmarks of Node.js libraries to colorize text in terminal (offtopic)
🔆 What's New in v4
- NEW you can include Markdown
*.md
files in your HTML template (sincev4.6.0
). - NEW added supports the HMR for CSS (since
v4.5.0
). - NEW added supports the multiple configurations.
- SUPPORTS Webpack version
5.96+
(sincev4.2.0
). - SUPPORTS Webpack version
5.81+
(sincev4.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'soutput.filename
, hold all relevant settings in one place - in plugin options.
Both places have the same effect, butjs.filename
has priority overoutput.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.
See boilerplate
↑ top
Table of Contents
- Features
- Install and Quick start
- Webpack options
- Build-in Plugins
- FaviconsBundlerPlugin (generates favicon tags)
- Third-party Plugins
- Hooks & Callbacks
- Plugin options
- test (RegEx to handle matching templates)
- entry (template as entry point)
- entry as an array (array notation)
- entry as an object (object notation)
- entry as a path (find templates in a directory recursively)
- entry data (pass data in the template)
- data as object (pass data in the template as an object)
- data as
JSON
file (load data from JSON file) - data as
JS
file (dynamically load data from JS where can be used DB)
- entry dynamic (entry as a path to template files)
- entryFilter (filter for entry dynamic)
- outputPath (output path of HTML file)
- filename (output filename of HTML file)
- js (options for JS)
- css (options for CSS)
- data (🔗reference to loaderOptions.data)
- beforePreprocessor (callback, 🔗reference to loaderOptions.beforePreprocessor)
- preprocessor (callback or string, 🔗reference to loaderOptions.preprocessor)
- preprocessorOptions (🔗reference to loaderOptions.preprocessorOptions)
- postprocess (callback)
- beforeEmit (callback)
- afterEmit (callback)
- preload (inject preload link tags)
- integrity (inject subresource integrity hash into script and style tags)
- minify and minifyOptions (minification of generated HTML)
- extractComments
- watchFiles
- hotUpdate
- verbose
- loaderOptions (reference to loader options)
- Loader options
- sources (resolving asset references in a specific tag attribute)
- filter (allow fine-tuning to resolve specific cases)
- root (allow to resolve a path with leading
/
) - context (allow to resolve a path w/o leading
/
) - beforePreprocessor (callback)
- preprocessor (callback or string) and preprocessorOptions (templating)
- data (pass global data into all templates as an object or a file)
- sources (resolving asset references in a specific tag attribute)
- Using template engines
- Using template in JavaScript
- Setup Live Reload
- Recipes
- How to keep source directory structure for HTML
- How to keep source directory structure for assets (fonts, images, etc.)
- How to use source images in HTML
- How to resize and generate responsive images
- How to preload fonts
- How to inline CSS in HTML
- How to inline JS in HTML
- How to inline SVG, PNG images in HTML
- How to inline all resources into single HTML file
- How to resolve source assets in an attribute containing JSON value
- How to resolve source image in the
style
attribute - How to resolve source image in the
href
attribute (<a href="image.jpg">
) - How to load CSS file dynamically (lazy loading CSS)
- How to import CSS class names in JS (CSS modules)
- How to import CSS stylesheet in JS (CSSStyleSheet)
- How to load JS and CSS from
node_modules
in template - How to import CSS or SCSS from
node_modules
in SCSS - How to process a PHP template
- How to pass data into multiple templates
- How to use some different template engines
- How to configure
splitChunks
- How to keep package name for split chunks from node_modules
- How to split CSS files
- Problems & Solutions
- Demo sites
-
Usage examples
- Simple example "Hello World!" View in browser | source
- Simple example "Hello World!" using
Pug
View in browser | source - Automatically processing multiple HTML templates View in browser | source
- Bootstrap with Webpack View in browser | source
- Tailwind CSS with Webpack View in browser | source
- Twig with Webpack View in browser
- Handlebars with Webpack View in browser | source
- Extend Handlebars layout with blocks View in browser | source
- Auto generate integrity hash for
link
andscript
tags View in browser | source - Inline multiple SVG files w/o ID collision View in browser | source
- Bundle Vue app into single HTML file with embedded JS, CSS, images View in browser | source
- Using Markdown
*.md
files in templates View in browser | source
↑ 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
andsync
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 thelink
andscript
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 withauto
.publicPath: '/'
- a path relative todocument root
directory on a serverpublicPath: '/assets/'
- a sub path relative todocument root
directory on a serverpublicPath: '//cdn.example.com/'
- an external URL with the same protocol (http://
orhttps://
)publicPath: 'https://cdn.example.com/'
- an external URL with thehttps://
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 tagtag
- the tag name, e.g.'link'
,'script'
,'img'
, etc.attribute
- the attribute name, e.g.'src'
,'href'
, etc.value
- the attribute valueresolvedFile
- the resolved file from the valueissuer
- 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. Defaultspreprocessor
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 theentry
option of the plugin. The syntax is identical. But thedata
property can only be used in theentry
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'