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
})
],
You can write a custom sort function:
https://github.com/ampedandwired/html-webpack-plugin/blob/master/index.js#L176
@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.
Most helpful comment
@Guedez solution worked for me as well, I just had to swap around
b.names[0]anda.names[0]and make itindexOfinstead ofindexof. Thank you for the share!