Html-webpack-plugin: Add options to read html from `src` and create in `dist`

Created on 10 Mar 2019  路  15Comments  路  Source: jantimon/html-webpack-plugin

Is your feature request related to a problem? Please describe.

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.

Describe the solution you'd like

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;
}


Describe alternatives you've considered

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.

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.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.

All 15 comments

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

@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 :

  • need setting options.chunks dynamically
  • need setting self.options.chunks before the plugin emit phase
  • need the processed html stream to extract resources before the plugin emit phase

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:

Flow

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 :

  • // Turn the js and css paths into grouped HtmlTagObjects
  • // Turn the compiled tempalte into a nodejs function or into a nodejs string

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

Was this page helpful?
0 / 5 - 0 ratings

Related issues

var-bp picture var-bp  路  3Comments

azat-io picture azat-io  路  4Comments

rokoroku picture rokoroku  路  3Comments

yyx990803 picture yyx990803  路  4Comments

hackteck picture hackteck  路  3Comments