Webpack bundles my files in the wrong order (CommonsChunkPlugin)
Asked Answered
G

4

30

What I want is to bundle my JavaScript vendor files in a specific order via CommonsChunkPlugin from Webpack.

I'm using the CommonsChunkPlugin for Webpack. The usage from the official documentation is straight forward and easy. It works as intended but I believe the plugin is bundling my files in alphabetical order (could be wrong). There are no options for the plugin to specify the order they should be bundled.

Note: For those who are not familiar with Bootstrap 4, it currently requires a JavaScript library dependency called Tether. Tether must be loaded before Bootstrap.

webpack.config.js

module.exports = {
  entry: {
    app: './app.jsx',
    vendor: ['jquery', 'tether', 'bootstrap', 'wowjs'],
  },

  output: {
    path: __dirname + '/dist',
    filename: 'bundle.js',
  },

  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
        name: 'vendor',
        filename: 'vendor.bundle.js'
    }),

    new webpack.optimize.UglifyJsPlugin(),
  ],
};

Two things are happening here:

  1. vendor.bundle.js contains bootstrap, jquery, tether, wowjs
  2. bundle.js contains the rest of my application

Bundling order:
correct: jquery, tether, bootstrap, wowjs
incorrect: bootstrap, jquery, tether, wowjs

Notice in my webpack.config.js I ordered them exactly as they should but they are bundled in the incorrect order. It doesn't matter if I rearrange them randomly the result is the same.

After I use Webpack to build my application, the vendor.bundle.js shows me the incorrect order.

I know they're bundled incorrectly cause Chrome Dev. Tools tell me there are dependency issues. When I view the file through the tool and my IDE, it is bundled in the incorrect order.


My other approach also resulted in the same issue

I also tried import and require in my entry file (in this case, app.jsx) without the use of the CommonChunkPlugin and that also loads my JavaScript libraries in alphabetical order for some reason.

webpack.config.js

module.exports = {
  entry: './app.jsx',

  output: {
    path: __dirname + '/dist',
    filename: 'bundle.js',
  },

  plugins: [
    new webpack.optimize.UglifyJsPlugin(),
  ],
};

app.jsx (entry)

import './node_modules/jquery/dist/jquery.min';
import './node_modules/tether/dist/js/tether.min';
import './node_modules/bootstrap/dist/js/bootstrap.min';
import './node_modules/wowjs/dist/wow.min';

or

require('./node_modules/jquery/dist/jquery.min');
require('./node_modules/tether/dist/js/tether.min');
require('./node_modules/bootstrap/dist/js/bootstrap.min');
require('./node_modules/wowjs/dist/wow.min');

The result?
Bootstrap > jQuery > Tether > wowjs


How do I load my vendor files in the correct order?

Gusgusba answered 23/3, 2017 at 22:42 Comment(4)
Does alphabetical order make it any wrong? It is done with purpose, so that the order did not affect the contents. "cause Chrome Dev. Tools tell me there are dependency issues. " --- any details on that?Chaetognath
@Chaetognath Well, there are huge issues if it is done in alphabetical order. One being that Bootstrap requires jQuery and Tether to be loaded before itself otherwise it can't operate properly.Gusgusba
The placement order in the bundle does not in any way affect the order they are loaded in runtime. The problem you're having is caused by something else.Chaetognath
I have the same problem, BUT how can I understand in which order my packages are loaded ?Broadax
R
8

You can try https://webpack.js.org/guides/shimming/#script-loader - it looks like it will execute scripts in order and in global context.

Rafael answered 23/3, 2017 at 23:11 Comment(4)
Interestingly enough, I used script-loader and it bundled everything incorrectly. I decided to git clone various React boilerplates and examples to test out script-loader, CommonsChunkPlugin and import / require statements but they all fall under the same issue.Gusgusba
So I strictly used script-loader and it definitely works but the end result is 1 bundled file with all my JS. I wanted to avoid having 1 large file since 2 small files (1 vendor-libs and 1 application) loaded via async would be much more ideal. I'll continue experimenting with splitting/chunking with script-loader to see if I can get that resultGusgusba
@OllieCee it's not hard to make a separate bundle out of libraries that have to define globals and load it separately before all other bundles. Of course you will have implicit (no require or import) dependencies on e.g. jquery in other bundlesRafael
Updated link: webpack.js.org/guides/shimming/#other-utilities or: webpack.js.org/loaders/script-loaderLole
G
20

Success!

webpack.config.js

module.exports = {
  entry: {
    app: './app.jsx',
    vendor: [
        "script-loader!uglify-loader!jquery",
        "script-loader!uglify-loader!tether",
        "script-loader!uglify-loader!bootstrap",
        "script-loader!uglify-loader!wowjs",
    ]
  },

  output: {
    path: __dirname + '/dist',
    filename: 'bundle.js',
  },

  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
        name: 'vendor',
        filename: 'vendor.bundle.js'
    }),

    new webpack.optimize.UglifyJsPlugin(),
  ],
};

What magic is happening here?

  1. Webpack creates vendor.bundle.js by minifying & bundling my vendor files which now execute in the global context.
  2. Webpack creates bundle.js with all of its application code

entry file (app.jsx in this case)

import './script';

This script is just custom JavaScript that uses jQuery, Bootstrap, Tether and wowjs. It executes after vendor.bundle.js, allowing it to run successfully.

A mistake I made trying to execute my script.js was that I thought it had to be in the global context. So I imported it with script-loader like this: import './script-loader!script';. In the end, you don't need to because if you're importing through your entry file it will end up in the bundle file regardless.


Everything is all good.

Thanks @Ivan for the script-loader suggestion. I also noticed that the CommonsChunkPlugin was pulling the non-minified vendor versions so I chained uglify-loader into the process.

Although, I do believe some .min.js are created differently to get rid of extra bloat. Though that is for me to figure out. Thanks!

Gusgusba answered 24/3, 2017 at 17:7 Comment(1)
I have the same problem, BUT how can I understand in which order my packages are loaded ? @itamar and Ollie CeeBroadax
R
8

You can try https://webpack.js.org/guides/shimming/#script-loader - it looks like it will execute scripts in order and in global context.

Rafael answered 23/3, 2017 at 23:11 Comment(4)
Interestingly enough, I used script-loader and it bundled everything incorrectly. I decided to git clone various React boilerplates and examples to test out script-loader, CommonsChunkPlugin and import / require statements but they all fall under the same issue.Gusgusba
So I strictly used script-loader and it definitely works but the end result is 1 bundled file with all my JS. I wanted to avoid having 1 large file since 2 small files (1 vendor-libs and 1 application) loaded via async would be much more ideal. I'll continue experimenting with splitting/chunking with script-loader to see if I can get that resultGusgusba
@OllieCee it's not hard to make a separate bundle out of libraries that have to define globals and load it separately before all other bundles. Of course you will have implicit (no require or import) dependencies on e.g. jquery in other bundlesRafael
Updated link: webpack.js.org/guides/shimming/#other-utilities or: webpack.js.org/loaders/script-loaderLole
B
2

Worked with htmlWebpackPlugin from official tutorials and switched the order form entry key. ( vendor then app )

In webpack.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: {
      vendor: [
          'angular'
      ],
      app: [
          './src/index.js',
          './src/users/users.controller.js',
          './src/users/users.directive.js',
      ]
  },
   plugins: [
       new CleanWebpackPlugin(['dist']),
       new HtmlWebpackPlugin({
           template: './src/index-dev.html'
       }),
       new webpack.NamedModulesPlugin()
  ...
}

Now in the generated index.html file I have the correct order

<script src='vendor.bundle.js'></script>
<script src='app.bundle.js'></scrip
Beamer answered 24/4, 2018 at 15:46 Comment(0)
Z
0

This worked for me https://www.npmjs.com/package/webpack-cascade-optimizer-plugin

const CascadeOptimizer = require('webpack-cascade-optimizer-plugin');

module.exports = {
  entry: {
    app: './app.jsx',
    vendor: ['jquery', 'tether', 'bootstrap', 'wowjs'],
  },

  output: {
    path: __dirname + '/dist',
    filename: 'bundle.js',
  },

  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
        name: 'vendor',
        filename: 'vendor.bundle.js'
    }),

    new webpack.optimize.UglifyJsPlugin(),

    new CascadeOptimizer({
        fileOrder: ['jquery', 'tether', 'bootstrap', 'wowjs']
    })
  ],
};
Zoie answered 23/9, 2020 at 14:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.