Html-webpack-plugin: Order of scripts

Created on 10 Dec 2015  路  18Comments  路  Source: jantimon/html-webpack-plugin

I am creating 3 bundles and need to be able to insert them in a specific order:

This is what is generated:

<script src="common.8eab23e9e55f8c47699c.bundle.js"></script>
<script src="app.8eab23e9e55f8c47699c.bundle.js"></script>
<script src="angular2.8eab23e9e55f8c47699c.bundle.js"></script></body>

but I need to have my app.bundle as the last one.

My config is:

module.exports = {
    entry: {
        "angular2": [
            "core-js",
            "rxjs",
            "zone.js",
            "reflect-metadata",
            "angular2/angular2",
            "angular2/core",
            "angular2/router",
            "angular2/http"
        ],
        "app": "./src/app/bootstrap.ts" // our app
    },
    output: {
        path: root('__build__'),
        filename: '[name].[hash].bundle.js',
        chunkFilename: '[id].chunk.js'
    },
    plugins: [
        new CommonsChunkPlugin('angular2', '[name].[hash].bundle.js'),
        new CommonsChunkPlugin('common', '[name].[hash].bundle.js'),
        new HtmlWebpackPlugin({
            template: './src/public/index.html',
            inject: 'body',
            minify: false
        })
    ],
question

Most helpful comment

@Guedez solution worked for me as well, I just had to swap around b.names[0] and a.names[0] and make it indexOf instead of indexof. Thank you for the share!

All 18 comments

@kampdomoj can this be closed?

The thing is that I am a javascript newbie, so I need an example to be able to test this. I have been googling on chunkSortMode to find one, but apparently nobody published a custom sort function that I can use as a starting point... :(

The default chunkSortMode function is this one:

(function orderEntryLast(a, b) {
      if (a.entry !== b.entry) {
        return b.entry ? 1 : -1;
      } else {
        return b.id - a.id;
      }
    })

Use https://github.com/s-a/iron-node with debugger; or console.log(a,b); to get the details for a and b

More details on the array sorting can be found here:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort

I'm probably a bit late, but maybe someone else finds this helpful. In order to get the correct bundle order, you should set chunksSortMode to 'none' and add Webpack's OccurrenceOrderPlugin to Webpack's configuration. Given @kampdomoj Angular 2 setup, I would suggest something like

plugins: [
        new OccurenceOrderPlugin(true),
        new CommonsChunkPlugin({
            name: 'angular2',
            filename: 'angular2.js',
            minChunks: Infinity
        }),
        new CommonsChunkPlugin({
            name: 'common',
            filename: 'common.js'
        }),
        new HtmlWebpackPlugin({
            filename: '../index.html',
            hash: false,
            template: config.sourcePath + '/index.html',
            inject: 'body',
            chunksSortMode: 'none'
        })
    ];

This should result in the proper bundle order without using a custom sorter function.

@ruehl thanks for the information
could you please go a little bit in detail and explain how the OccurrenceOrderPlugin might help here?

@jantimon Actually, it is more of a crude workaround than a real solution. The OOP rearranges order of modules (and therefore implicitly chunks as well) by their link counter. Due to the nature of Angular2 setup common.js is most often linked, followed by angular2.js followed by app.js, thus resulting in a working structure.

Only my 2 cents and being a Webpack rookie: after fiddling around with the sort function (chunkSortMode) and inspecting the arguments passed to that function, I wonder if it wouldn't be an option to sort chunks by their parent relationship rather than by their individual ids: if module A is listed as parent of B, then A should be embedded before B. Or more general: if module A is an ancestor (ie. parent of grade N) of module B, then A should be embedded before B. So basically we would perform a topological sort on the dependency graph of the chunks. Of course this would not work on graphs including one or more dependency cycles.

What are your thoughts on such an approach?

I haven't looked into the sorting of the chunks at all. But your suggestion sounds like it would for almost every thinkable use case. Feel free to open a pull request :)

Then I'm looking forward my first pull request on Github ever ;-)

don't understand why, but ruehl's solution works

This one worked for me
chunksSortMode: function(a, b) { var order = ["polyfills", "libs", "js", "vendor", "main"]; return order.indexof(b.names[0]) - order.indexof(a.names[0]); }

for reference, my entries is

entry: { 'libs': ['./src/js/util.js', './src/libs/RSA/rsa_compiled.js', './src/libs/forge.min.0.6.12.js', './src/libs/FileSaver.js', './src/libs/DataStream.js', './src/libs/pako.js', './src/libs/lodash.core.js' ], 'js': ['./src/js/server_message.js', './src/js/messages/files.js', './src/js/messages/login.js', './src/js/messages/tabs.js', './src/js/message_decoder.js', './src/js/message_encoder.js' ], 'polyfills': './src/polyfills.browser.ts', 'vendor': './src/vendor.browser.ts', 'main': './src/main.browser.ts', }, .

@Guedez solution worked for me as well, I just had to swap around b.names[0] and a.names[0] and make it indexOf instead of indexof. Thank you for the share!

worked solution (verified using webpack 2)

webpack.config.js:

...
entry: {
  vendors: [
    './src/polyfills.ts',
    './src/vendors.ts',
  ],
  app: './src/main.ts',
},
...
plugins: {
  ...
  new CommonsChunkPlugin({
    // order in array does matters: 0 - commons, 1 - vendors
    names: ['commons', 'vendors'],
    minChunks: 2
  }),
  new HtmlWebpackPlugin({
    // order in array here doesn't matters
    chunks: [ // I ordered all chunks, just for readability...
      'commons',
      'vendors',
      'app',
    ],
    ...
  }),
  ...
},
...

result output: index.html:

<body>
  ...
  <script type="text/javascript" src="/angular2-webpack2-aot/commons.js" defer="defer"></script>
  <script type="text/javascript" src="/angular2-webpack2-aot/vendors.js" defer="defer"></script>
  <script type="text/javascript" src="/angular2-webpack2-aot/app.js" defer="defer"></script>
</body>

if your commons bundle is too fat, you can use another solution:

...
entry: {
  polyfills:  './src/polyfills.ts',
  vendors: './src/vendors.ts',
  app: './src/main.ts',
},
...
plugins: {
  ...
  // code splitting:
  new CommonsChunkPlugin({ name: 'commons' }), // 0, split commons from all entries
  new CommonsChunkPlugin({ name: 'polyfills', chunks: ['polyfills', 'vendors',], }), // 1
  new CommonsChunkPlugin({ name: 'vendors', chunks: ['vendors', 'app',], }), // 2
  // html
  new HtmlWebpackPlugin({
    chunks: [
      'commons', // 0
      'polyfills', // 1
      'vendors', // 2
      'app', // rest
    ],
    ...
  }),
  ...
},
...

result:

<body>
  ...
  <script type="text/javascript" src="/angular2-webpack2-aot/commons.js" defer="defer"></script>
  <script type="text/javascript" src="/angular2-webpack2-aot/polyfills.js" defer="defer"></script>
  <script type="text/javascript" src="/angular2-webpack2-aot/vendors.js" defer="defer"></script>
  <script type="text/javascript" src="/angular2-webpack2-aot/app.js" defer="defer"></script>
</body>

here are few example with such configurations:
commons-vendors-app
commons-polyffils-vendors-app

A simpler solution is to use a template for your html so you can tell Webpack exactly where to inject your script tags in the page. Your template would look something like:

<!-- ./src/index.template.html -->
<html>
<body>
<script src="<%= htmlWebpackPlugin.files.chunks.common.entry %>"></script>                                                            
<script src="<%= htmlWebpackPlugin.files.chunks.angluar2.entry %>"></script>
<script src="<%= htmlWebpackPlugin.files.chunks.app.entry %>"></script>
</body>
</html>

And the config:

```
new HTMLWebpackPlugin({
template: './src/index.template.html',
inject: false,
}),

This configuration save my life:

        new HtmlWebpackPlugin({
            template: 'src/index.html',
            chunksSortMode: function(a, b) {
                var order = ["polyfills", "app"];
                return order.indexOf(a.names[0]) - order.indexOf(b.names[0]);
            }
        })

Thanks @Guedez for the leads 馃槃

@Nek- this is also possible using

  chunksSortMode: 'manual',
  chunks: ['polyfills", "app'],

@jantimon thanks,but it's chunksSortMode with a s.

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

NeverwinterMoon picture NeverwinterMoon  路  3Comments

hackteck picture hackteck  路  3Comments

GerkinDev picture GerkinDev  路  3Comments

laruiss picture laruiss  路  3Comments

klinki picture klinki  路  3Comments