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

Bootstrap's modal.js + utile.js import #338

Closed
CyrilKrylatov opened this issue Jun 21, 2018 · 23 comments
Closed

Bootstrap's modal.js + utile.js import #338

CyrilKrylatov opened this issue Jun 21, 2018 · 23 comments

Comments

@CyrilKrylatov
Copy link

CyrilKrylatov commented Jun 21, 2018

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

@stof
Copy link
Member

stof commented Jun 21, 2018

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)

@CyrilKrylatov
Copy link
Author

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?

@stof
Copy link
Member

stof commented Jun 21, 2018

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

@CyrilKrylatov
Copy link
Author

CyrilKrylatov commented Jun 21, 2018

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?

@stof
Copy link
Member

stof commented Jun 21, 2018

@DaPo but these are not the files you are importing

@Lyrkan
Copy link
Collaborator

Lyrkan commented Jun 21, 2018

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

@stof
Copy link
Member

stof commented Jun 21, 2018

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

@Lyrkan
Copy link
Collaborator

Lyrkan commented Jun 21, 2018

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

@CyrilKrylatov
Copy link
Author

CyrilKrylatov commented Jun 21, 2018

@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

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

😟

@Lyrkan
Copy link
Collaborator

Lyrkan commented Jun 21, 2018

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

@CyrilKrylatov
Copy link
Author

@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. 😢

@CyrilKrylatov
Copy link
Author

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.

@stof
Copy link
Member

stof commented Jun 21, 2018

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)

@CyrilKrylatov
Copy link
Author

😭

@CyrilKrylatov
Copy link
Author

CyrilKrylatov commented Jun 22, 2018

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

@Johann-S
Copy link

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 : twbs/bootstrap#26767

@Johann-S
Copy link

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 😄

@CyrilKrylatov
Copy link
Author

CyrilKrylatov commented Jul 25, 2018

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!

@donni106
Copy link

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

@Lyrkan
Copy link
Collaborator

Lyrkan commented Oct 30, 2019

@CyrilKrylatov
Copy link
Author

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

@donni106
Copy link

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

@donni106
Copy link

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants