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

Order of scripts #140

Closed
ghost opened this issue Dec 10, 2015 · 18 comments
Closed

Order of scripts #140

ghost opened this issue Dec 10, 2015 · 18 comments
Labels

Comments

@ghost
Copy link

ghost commented Dec 10, 2015

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
        })
    ],
@jantimon
Copy link
Owner

jantimon commented Dec 13, 2015

You can write a custom sort function:

https://github.com/ampedandwired/html-webpack-plugin/blob/master/index.js#L176

@jantimon
Copy link
Owner

jantimon commented Dec 17, 2015

@kampdomoj can this be closed?

@ghost
Copy link
Author

ghost commented Dec 17, 2015

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

@jantimon
Copy link
Owner

jantimon commented Dec 17, 2015

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

@jantimon jantimon closed this as completed Jan 6, 2016
@ruehl
Copy link
Contributor

ruehl commented Jan 23, 2016

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.

@jantimon
Copy link
Owner

jantimon commented Jan 24, 2016

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

@ruehl
Copy link
Contributor

ruehl commented Jan 24, 2016

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

@jantimon
Copy link
Owner

jantimon commented Jan 25, 2016

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

@ruehl
Copy link
Contributor

ruehl commented Jan 25, 2016

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

@daggerok
Copy link

daggerok commented Nov 19, 2016

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

@Guedez
Copy link

Guedez commented Nov 30, 2016

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', },.

@jperasmus
Copy link

jperasmus commented Mar 3, 2017

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

@daggerok
Copy link

daggerok commented Mar 4, 2017

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

@rosscreighton
Copy link

rosscreighton commented Nov 7, 2017

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,
}),

@Nek-
Copy link

Nek- commented Mar 26, 2018

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 😄

@jantimon
Copy link
Owner

jantimon commented Mar 27, 2018

@Nek- this is also possible using

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

@aboutqx
Copy link

aboutqx commented May 3, 2018

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

@lock
Copy link

lock bot commented Jun 5, 2018

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.

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

No branches or pull requests

8 participants