Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature request: support multiple configurations with one HTML file. eg type=module. #782

Closed
graingert opened this issue Sep 11, 2017 · 8 comments

Comments

@graingert
Copy link
Contributor

graingert commented Sep 11, 2017

Description

support multiple configurations with one HTML file. eg <script type=module>

Specifically I'd like to create a config with babel loader set to:

{
  "presets": [
    ["env", {
      "targets": {
        "chrome": 61, "safari": "10.2"
      }
    }]
  ]
}

and one with babel loader set to:

{
  "presets": [
    ["env", {
      "targets": {
        "ie": 11
      }
    }]
  ]
}

then render both entry points in the same page with:

<script type="module" src="<- config[0].entry.js ->"><script>
<script nomodule src="<- config[1].entry.js ->"><script>
@mike-engel
Copy link

mike-engel commented Sep 19, 2017

This would be awesome! I think it's important to keep it open to accepting multiple script sources. In my build, for example, three script tags get added because of code splitting:

<script type="text/javascript" src="/runtime.d4c01c143617f5833c44.js"></script>
<script type="text/javascript" src="/vendor.54efa82295fbe6e5beca.js"></script>
<script type="text/javascript" src="/main.cd860d906240d47980f6.js"></script>

So I guess the solution there would be something like (obviously I'm somewhat making up the syntax here):

<- config[0].output.map(src => `<script type="module" src="${src}></script>`) ->
<- config[1].output.map(src => `<script nomodule src="${src}></script>`) ->

@mastilver
Copy link
Collaborator

I'm all in for a multiple config setup: #780 (comment)

Can put down the api you had in mind? (ie: which options would you pass down to new HtmlPlugin())

@mike-engel
Copy link

@mastilver That's a really good question! I don't think this can work unless there's a shared cache 😞. Here's how I ended up hacking this together for our (specific) webpack config. It was complicated a bit by us emitting several modules instead of a single main.js or bundle.js. This also doesn't really handle all CSS cases (it worked for our single CSS bundle).

let sharedAssets = { chunks: {}, js: [] }

function HtmlMultiBuildWebpackPlugin() {}

HtmlMultiBuildWebpackPlugin.prototype.apply = function(compiler) {
  compiler.plugin('compilation', function(compilation) {
    compilation.plugin('html-webpack-plugin-before-html-processing', function(data, cb) {
      // Avoid chunk name collisions, since they can be named the same between builds
      // { 'main': {} } to { 'main-modern': {} } if the filename matches
      const obj = mapKeys(data.assets.chunks, (v, k) => {
        if (v.entry.includes('-modern')) { return `${k}-modern` }

        return k
      })

      sharedAssets.js = uniq(concat(sharedAssets.js, data.assets.js))
      sharedAssets.chunks = merge(sharedAssets.chunks, obj)

      const newData = merge(data.assets, sharedAssets)

      cb(null, newData)
    })

    compilation.plugin('html-webpack-plugin-alter-asset-tags', function(args, cb) {
      const newBody = args.body.map((arg) => {
        if (arg.attributes.src.includes('-modern')) {
          return merge(arg, {
            attributes: { type: 'module' }
          })
        } else {
          return merge(arg, {
            attributes: { nomodule: true }
          })
        }
      })

      cb(null, merge(args, { body: newBody }))
    })
  })
}

So I guess an api to accommodate that would look something like this? It's definitely weird!

new HtmlWebpackPlugin({
  multiBuild: {
    cacheName: 'unique-name', // used to cache all chunks that go together
    suffixes: ['-modern'], // list of suffixes to watch out for. Could just be a string
    attributes: { // attributes to append based on the suffixes above. Would NEED to match
      '-modern': { type: 'module' }, // attribute overrides for the modern files
      default: { nomodule: true } // attribute overrides for everything else
    }
  }
})

@robatwilliams
Copy link

I tried out @mike-engel 's plugin from above - it works great.

I created a gist with a few tweaks (functionally pretty much the same), and an usage example.

What use cases are there for making this more generic than the scenario described in the opening post?

@stale
Copy link

stale bot commented Jun 1, 2018

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.

@stale stale bot added the wontfix label Jun 1, 2018
@alexparish
Copy link

Is there still appetite for this?

@stale stale bot removed the wontfix label Jun 11, 2018
@firsttris
Copy link

created a small plugin for html-webpack-plugin for webpack4 multi-build configuration which also support FF, IE11, Edge, Chrome, Safari 11.
wrote down my findings...
https://github.com/firsttris/html-webpack-multi-build-plugin

@housseindjirdeh
Copy link

Just wanted to add my 2 cents here. I agree with @graingert in that using module/nomodule can help developers trim their bundle sizes by quite a bit (and Babel makes this transpilation process even easier with the esmodules target).

It's so great to see plugins and workarounds created to solve this problem (such as the many already linked in this thread 🎉), but it would be awesome to have this supported by HTMLWebpackPlugin. Happy to help where possible!

@lock lock bot locked as resolved and limited conversation to collaborators Nov 3, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

8 participants