I want to keep my website's original HTML files in /src and have HtmlWebPackPlugIn create its version of these files 1:1 in \dist.
Having my own, untouched copy of HTML files I can keep control of my HTML files and won't find myself with lost information in case I misconfigured HtmlWebPackPlugIn.
Add a pathMapping option to have HtmlWebPackPlugIn copy and transform all files from pathMapping.srcRoot to pathMapping.destRoot.
Suggest configuration object:
interface pathMapping
{
srcRoot: string;
destRoot: string;
}
AFAICS, you need to create a separate configuration entry for each HTML file you want HtmlWebPackPlugIn to create in a folder different from the source folder. Creating a separate configuration for each and every HTML file becomes error prone, tedious and hard to maintain. There should be a single, solution wide option.
This issue had no activity for at least half a year. It's subject to automatic issue closing if there is no activity in the next 15 days.
ping
Does https://github.com/webpack-contrib/copy-webpack-plugin solve this?
@jantimon: Thanks for replying.
How can I make sure the copy-webpack-plugin will copy the HTML file prior to html-webpack-plugin processing the copied versions?
Oh sorry
I guess I didn't understand your question correctly.
Are you are basically trying to compile multiple files like this?
src/index.html
src/main.html
src/about.html
Hi, @jantimon, yes, that's right.
Unfortunately this is not handled well by the html-webpack-plugin.
However you could do the following:
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
plugins: plugins: [
...["index", "main", "about"].map((htmlFile) => new HtmlWebpackPlugin({
template: `src/${htmlFile}.html`,
filename: `${htmlFile}.html`
}))
]
};
Instead of hardcoding the filenames you could also use a npm package like glob.
Hi, @jantimon,
sounds intriguing! :+1:
Though, perhaps the contributors may like the idea of adding this as a first-class citizen option?
Still, I found my solution now. Shall I close? Or do you want me to keep it open in case someone might want to add this as a new option to html-webpack-plugin?
Your requirement is common and we could introduce a quite nice api e.g. filename: "[name].html".
The complexity comes with defining which javscript/css files should go into which file.
For example if you have an about.html maybe you need only css but no js.
However we could also wrap the plugin by another plugin which focusses on multiple pages and would use a very similar code to the one I shared above.
Getting the list of CSS and JS needed file can be done by extracting it from the HTML stream (getting all JS and CSS resources from the src folder for example).
Right now, it is quite difficult as asset chunk are defined at the beginning of the emit hook (based on self.options.chunks). This means we need to extract the chunk before the emit phase and set them into self.options.chunks.
For raw HTML file, this can be done in any phase before. Then, JS and CSS definition from the source file need to be replace with the generated one (include processed JS and CSS files / chunks and remove those from the src stream). The remove part can be something like this :
compiler.hooks.compilation.tap('Woot', (compilation) => {
getHtmlWebpackPluginHooks(compilation).afterTemplateExecution.tapAsync('Woot', (htmlData,cb) => {
htmlData.html = htmlData.html.replace(/.*<link .* href="%PUBLIC_URL%.*.css" .*\/>.*\n/g, "")
htmlData.html = htmlData.html.replace(/.*<script .*src="%PUBLIC_URL%\/.*.js"><\/script>.*\n/g, "")
cb(null, htmlData);
})
})
Then the plugin natural process will inject the generated resources.
For file other than raw HTML (for example with template preprocessor like EJS, nunjucks, ...), we can get the needed resource list after file has been processed. Which mean, we need to wait for file processing (completion of compilationPromise) to get HTML result stream. From that stream we can set self.options.chunks with chunks extracted from the stream :
const pattern = /"%PUBLIC_URL%\/(.*)\..*"/g;
function matchAll(str, regex) {
const res = [];
let m;
while (m = regex.exec(str)) {
res.push(m[1]);
}
return res;
}
compiler.hooks.emit.tapAsync('Woot', (compilation, callback) => {
compilationPromise
.then(compiledTemplate => self.evaluateCompilationResult(compilation, compiledTemplate))
.then(compilationResult => {
self.options.chunks = matchAll(compilationResult, pattern);
callback();
});
});
To sum up :
Being able to access compilationResult content (computed from a promise triggered into the make hooks) could ease working with html stream. Currently, only the hash and outputName is stored into the plugin instance. If we store the compilationResult.content, we can access it into step after "make" and before "emit" (like "afterCompile" for example) (if we can make sure that the compileTemplate process is complete after the "make" step).
@jantimon In version 3.2.0, there was a sync hook html-webpack-plugin-alter-chunks. Any reason why it was removed ?
Also, any reason why the compilationPromise is resolved in the emit phase ? Any other way to retrieve the result content of a potential template ?
It is now before asset tag generation:

Why would you need the results at a different point?
Thanks for the quick reply.
TLDR : i love you man, the plugin does exactly what i need !
My initial concern was to push some new assets and let html-webpack-plugin do its magic.
For exampel, as i am a lazy programmer, i would have liked to push foobar.js as a script dependency and i wanted the plugin to generate :
{ tagName: 'script',
voidTag: false,
attributes: { src: '/foobar.js' } }
But, pushing the dependency path is not enough in real life, i will need to push some additionnal attribute as well (for example, media attribute for ptint stylesheet or type=module and nomodule to do differential JS loading).
In that case, you are right, the alterAssetTags hook is better suited and would allow me to do finer tuning : this is great news !
Though, one thing is missing for my use case : being able to access the template result before defining the asset.
Currently, the plugin does this sequence :
In order to extract the needed dependency from the template result, i'd need the compiledTemplate (compilationPromise result) in the alterAssetTags hook.
But looking deeper into the sequence graph show that there is another hook for me : afterTemplateExecution.
It exposes html (compilationPromise result) and every tags through headTags and bodyTags.
This is just perfect.
I have everything i need.
Thank you so much !
Thank you so much for your feedback :)
This plugin api is now also part of the stable 4.0.0 release
Most helpful comment
Your requirement is common and we could introduce a quite nice api e.g.
filename: "[name].html".The complexity comes with defining which javscript/css files should go into which file.
For example if you have an
about.htmlmaybe you need only css but no js.However we could also wrap the plugin by another plugin which focusses on multiple pages and would use a very similar code to the one I shared above.