1

I am trying to get a legacy app that uses the Jquery modal plugin which requires bootstrap's javascript to work within an Angular 6 and Webpack 4 project that uses a custom Webpack configuration. Everything is working except that the tree shaking that takes place during bundling is removing the bootstrap imports that are in my vendor.ts file because no where in my app do I explicitly use the bootstrap import.

This then causes my bootstrap modals and bootstrap dropdowns to break.

Note: if I add something like:

import * as bootstrap from "bootstrap";
console.log(bootstrap);

to my main.ts file, webpack doesn't remove bootstrap and everything works fine.

I have already tried adding multiple entries to my package.json sideEffects property that has been suggested in the documentation but I am thinking that this property is used to mark code that should be removed. Is there a way to mark imported modules as not to be removed from the tree shaking process?

I have also tried the ProvidePlugin like so:

new ProvidePlugin({
    $: "jquery",
    jQuery: "jquery",
    "window.jQuery": "jquery",
    _: "underscore",
    bootstrap: "bootstrap",
    moment: "moment",
    momenttimezone: "moment-timezone",
    // Collapse: "exports-loader?Collapse!bootstrap/js/dist/collapse",
    // Dropdown: "exports-loader?Dropdown!bootstrap/js/dist/dropdown",
    Modal: "exports-loader?Modal!bootstrap/js/dist/modal",
})

Thanks for your time.

Justin
  • 683
  • 8
  • 24

4 Answers4

2

I had the same problem and solution was following:

instead of using "import", use "require".

Change this

import * as bootstrap from "bootstrap";

to this

require("bootstrap");

Unfortunately, I have not figured out, how to make it work with the "import".

hostar
  • 166
  • 2
2

After doing some more digging into this issue, I was getting similar issues with other imports, the root problem was that I was referencing third party libraries in more then one place.

In other words, I was importing JQuery, bootstrap, and other libraries in my main.ts, my vendor.ts, and in my ProvidePlugin. Once the app was bundled the different references were conflicting with each other and this caused problems in my application.

I started by cleaning up all of the references and having them all live in once place, the ProvidePlugin. I then also added the runtimeChunk to my webpack configuration.

optimization: {
    runtimeChunk: "single"
}

This ensures that all of the code is referencing the same libraries. Checkout the documentation here. I'm going to leave the answer as accepted for @hostar because he did solve my initial problem though.

Justin
  • 683
  • 8
  • 24
  • It's interesting though, I just read a comment from one of the core members of webpack that said you shouldn't use the runtimeChunk unless you need to. [See this comment](https://github.com/webpack/webpack/issues/6647#issuecomment-369868055). Once I have my angularJS app completely converted to Angular we should be able to remove all these third party libraries so ideally this is a short-term fix. – Justin Sep 24 '18 at 23:20
  • Good find @justin ! Referencing same library from few places should not be a problem, webpack is clever enough to find out it is the same thing. Using optimization.splitChunks webpack will include this library just once and create needed references in modules. I think this might be bug in webpack, because using "runtimeChunk" seems overly complicated. – hostar Sep 25 '18 at 08:53
1

As of version 4.5 of Bootstrap I think that the better solution is being explicit about the plugins that you are using as is explained in the documentation:

import 'bootstrap/js/dist/util';
import 'bootstrap/js/dist/modal';

in this way you are not loading the full bootstrap js but only what you need. Afaik, there is not need to set bootstrap as a global with ProvidePlugin if you are using this, neither special tree-shaking configuration.

Also if you are only using jQuery for Bootstrap, there is no need to make a special config for jQuery. bootstrap/js/dist/util.js already does import $ "from jquery", so webpack will handle the import.

If you prefer to use jquery-slim instead of "jquery", create an alias, so import "jquery" will be resolved to the slim module. In fact I always configure the alias pointing to the full version or the slim version. I found that this can avoid problems like import jQuery two times by accident..

resolve: {
    alias: {
        // or "jquery/dist/jquery.js" for the full version
        jquery: 'jquery/dist/jquery.slim.js',
    }
},
Francisco Puga
  • 23,869
  • 5
  • 48
  • 64
0

Try webpack.ProvidePlugin in your webpack config:

Automatically load modules instead of having to import or require them everywhere.

Example:

// In webpack config
...
plugins: [
  new webpack.ProvidePlugin({
    bootstrap: 'bootstrap'
  })
]
...

More info about "shimming" in the documentation.

jered
  • 11,220
  • 2
  • 23
  • 34
  • I have tried this as well, and have even tried to provide specific bootstrap module as suggested in [this article](https://volaresystems.com/blog/post/2018/02/24/Tree-shaking-with-Bootstrap-4-and-webpack). – Justin Sep 21 '18 at 23:48