Webpack-encore: Bootstrap's modal.js + utile.js import

Created on 21 Jun 2018  Â·  23Comments  Â·  Source: symfony/webpack-encore

Hello team,

first of all, I want to thank you for having webpack-encore created. Webpack was a big issue for me but with Encore, everything seems to be easy to use. Very good job to y'all. Thank you again.

However, it seems like I have a trouble with Bootstrap's modal.js, its utile.js he is using and compilation with Webpack.

I'll try to make myself as clear as possible. Webpack and JavaScript are not exactly my thing and English is not my mother tongue, so, please, do tell me if you need any clarification or something.

In a first place, I tried to follow this tutorial about Bootstrap x Encore. But bootstrap-sass is no longer supported for bootstrap v4 as you can see in their readme and I'm working with bootstrap 4.1.1 ("bootstrap": "^4.1.1", from my package.json).

So, my webpack.config.js looks like this:

const Encore = require('@symfony/webpack-encore');
Encore
…
   .addEntry(
        'base', [
            …
            'bootstrap/js/dist/util.js',
            'bootstrap/js/dist/modal.js',
            …
        ]
    )
…
;

It compils successfully as we can see here:

Running webpack ...

 DONE  Compiled successfully in 6215ms                                    14:41:09

 I  34 files written to web/built
✨  Done in 7.46s.

But when I try to open a modal on my website, I have this error: Uncaught ReferenceError: Util is not defined.

So I assume that the Util global variable that bootstrap is using is missing, since Webpack create functions around file it is importing and all.

So I added the following code in my webpack.conf.js:

    .autoProvideVariables({
        Util: 'bootstrap/js/dist/util.js',
    });

It works well for Popper (required to by Bootstrap) but not with Util since I have the same Uncaught ReferenceError: Util is not defined.

Soooo, after a few searches on GitHub and all, I found this comment.

I removed Util from both autoProvideVariables() and my addEntry(), modal.js as well to put them both in a bootstrap.js file:

const $ = require('jquery');
import Util from 'bootstrap/js/src/util'
import Modal from 'bootstrap/js/src/modal'

and added an entry for it in webpack.config.js:

    .addEntry('bootstrap', './assets/js/webpack/bootstrap.js')

And now, I have a error compilation:

 ERROR  Failed to compile with 1 errors                                   14:48:39

 error  in ./node_modules/bootstrap/js/src/modal.js

Module parse failed: Unexpected token (222:8)
You may need an appropriate loader to handle this file type.
|     _getConfig(config) {
|       config = {
|         ...Default,
|         ...config
|       }

 @ ./assets/js/webpack/bootstrap.js 3:0-43
 ```

So I tried to add babel following [this documentation](https://symfony.com/doc/4.0/frontend/encore/babel.html) since it seems like it don't like the spread operator and it's not going well:

Running webpack ...

Error: configureBabel() cannot be called because your app already has Babel conf iguration (a .babelrc file, .babelrc.js file or babel key in package.json). Either put all of your Babel configuration in that file, or delete it and us e this function.
```

So here I am, opening an issue, asking for help.

I don't know what can I do more. If anyone had the same issue I'd be glad to hear for a solution.

Thank you for your time.

Edit I tried this one but I still have a Uncaught TypeError: Util.getSelectorFromElement is not a function when I try to open my modal.

Most helpful comment

So, I think I found something out.

In a bootstrap.js entry:

import Util from 'bootstrap/js/src/util';
global.Util = Util;
import 'bootstrap/js/dist/modal';

and it's working! 🎉

All 23 comments

AFAIK, Bootstrap itself is not really friendly with CommonJS, especially when using individual files. So I suggest you to keep importing the main bootstrap package instead (and request them to distribute UMD, CommonJS or ES6 modules for the separate components instead of files relying on global variables only, which won't even be global in CommonJS due to the way they are written)

Thank you for your reply, @stof!

Sorry for the following question but since JavaScript is not reality my thing, I'll need some clarification from you.

Are you suggesting that I should make a global boostrap import via something like require('bootstrap'); in Webpack, and then what should I do? Then, import modal.js / toggle.js etc. in a bootstrap.js file as you see in my first comment?

@DaPo Webpack relies on CommonJS and ES6 modules. And individual Bootstrap files are not using CommonJS nor ES6 to define their dependencies, but use the old-school global variables. This means that their npm package is not compatible with the webpack ecosystem (at least regarding their individual files).

The main bootstrap bundle on the other handle does not have such extra dependencies between Bootstrap parts, as everything is in a single part. There is still the issue about accessing jQuery itself, but Encore has you covered there thanks to autoProvidejQuery().

What you would do in your own project would only be:

const $ = require('jquery');
require('bootstrap');

This goes in your own file. And you don't need to put Bootstrap files themselves in the webpack config. Your entry should generally have only 1 file in it, which requires everything else.

Okay, I understand. Thank you for your clarification, I'll test that.

However, when you say

And individual Bootstrap files are not using CommonJS nor ES6 to define their dependencies, but use the old-school global variables

Bootstrap seems to make some ES6 export as you can see here, are you talking about that?

@DaPo but these are not the files you are importing

@DaPo The ES6 issue you have when importing Bootstrap source files (instead of "dist" ones) is also discussed in #139 if you want more info about it.

@Lyrkan in this bug report, imports are done from js/dist/. But these files are not modules.

@stof My comment was related to a single part of the first message in which @DaPo also tried to import modules from js/src:

const $ = require('jquery');
import Util from 'bootstrap/js/src/util'
import Modal from 'bootstrap/js/src/modal'

@Lyrkan I added the whole ugliJS thing as you described in the other issue and I have the following compilation error:
webpack.conf.js

.addEntry('bootstrap', './assets/js/webpack/bootstrap.js')
…
const webpackConfig = Encore.getWebpackConfig();

// Remove the old version first
webpackConfig.plugins = webpackConfig.plugins.filter(
    plugin => !(plugin instanceof webpack.optimize.UglifyJsPlugin)
);

// Add the new one
webpackConfig.plugins.push(new UglifyJsPlugin());

// export the final configuration
module.exports = webpackConfig;

bootstrap.js
```const $ = require('jquery');
import Util from 'bootstrap/js/src/util'
import Modal from 'bootstrap/js/src/modal'

From the compilation:

error in ./node_modules/bootstrap/js/src/modal.js

Module parse failed: Unexpected token (222:8)
You may need an appropriate loader to handle this file type.
| _getConfig(config) {
| config = {
| ...Default,
| ...config
| }

@ ./assets/js/webpack/bootstrap.js 3:0-43
```

😟

@DaPo That's probably caused by some missing babel plugins... as I said in my other post that's an annoying thing when importing from source files instead of dist:

Since Babel will then use the .babelrc file from Bootstrap you'll also need to yarn add --dev babel-preset-es2015 transform-es2015-modules-strip.

Edit: Be aware that this last method will slow down your builds and that if another module also uses a .babelrc file you'll also need to add the presets/plugins it uses (or disable .babelrc files). In my opinion you should use the dist files whenever it's possible to do so, and when it isn't slightly modify the exclude rule to add an exception instead of removing it entirely.

Right now Bootstrap's babelrc.js file asks for transform-es2015-modules-strip and @babel/proposal-object-rest-spread (that last one was not needed when I wrote the previous comment).

I'd still recommend you to use dist files... that's also Bootstrap's official website recommendation.

@Lyrkan yep, that's what I was afraid of. Thank you for your time.

Now, back to my original comment with that Util thing and all. 😢

I don't understand why:
webpack.conf.js

.autoProvideVariables({
        Util: 'bootstrap/js/dist/util',

bootstrap.js

import 'bootstrap/js/dist/modal';

results with a Uncaught TypeError: Util.getSelectorFromElement is not a function when I try to open my modal.

Meeeeeh.

That's because bootstrap/js/dist/util does not expose a module properly (and so Util ends up being nothing in the provided variable). these files are not compatible with webpack (and they are really bad for a global-based environment, as they leak lots of stuff)

😭

So, I think I found something out.

In a bootstrap.js entry:

import Util from 'bootstrap/js/src/util';
global.Util = Util;
import 'bootstrap/js/dist/modal';

and it's working! 🎉

Hi @DaPo,

I made a PR in Bootstrap to build our plugins in UMD, so you'll be able to choose what you want to import.

See the PR, you'll find examples : https://github.com/twbs/bootstrap/pull/26767

A new released of Bootstrap is out (https://github.com/twbs/bootstrap/releases/tag/v4.1.3) with the change I made to convert our plugins in UMD, so you won't have to use our sources files you'll have to use our bootstrap/js/dist files and it'll be fine 😄

Hello @Johann-S,

Thank you for coming by!

So I tried to do as you said in your PR, in a bootstrap.js file:

import Util from 'bootstrap/js/dist/util';
import 'bootstrap/js/dist/modal';
import 'bootstrap/js/dist/dropdown.js';
import 'bootstrap/js/dist/tab.js';

And it works like a charm, don't need to use global.Util = Util; any further.

Thank you for this PR and thank you for your work on Bootstrap.

Cheers!

@DaPo how did you managed jquery to work? i always get TypeError: $ is undefined (referenced in util.js:64:5)

@donni106 Try to follow this guide: https://symfony.com/doc/current/frontend/encore/legacy-applications.html#fixing-jquery-plugins-that-expect-jquery-to-be-global

@donni106 please see my comment below yours it should help you.

@DaPo I did that, of course, before I wrote.

Ok, sorry I figured out that I posted in a wrong project. I have the same error but with different configurations. I am using Rollup and not Webpack/Encore.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

weaverryan picture weaverryan  Â·  4Comments

o-alquimista picture o-alquimista  Â·  3Comments

Growiel picture Growiel  Â·  4Comments

MatthD picture MatthD  Â·  4Comments

JohnnyEvo picture JohnnyEvo  Â·  3Comments