Buy

Delete the window.jQuery line. Instead, go to webpack.js.org. This time, skip straight to the Documentation, Plugins, then find the ProvidePlugin. This plugin is crazy cool: it's both massively useful and shows off the power of the dark side... I mean Webpack.

At the top, it says:

Automatically load modules instead of having to require them everywhere.

Let's see what that means. In webpack.config.js, there is a new key we can put here called plugins, set to an array. Add new webpack.ProvidePlugin() with {}. Inside, pass a key called jQuery set to the string jquery in all lowercase... like the module's name:

21 lines webpack.config.js
... lines 1 - 3
module.exports = {
... lines 5 - 13
plugins: [
new webpack.ProvidePlugin({
jQuery: 'jquery',
... line 17
})
]
};

The ProvidePlugin() is bananas. Thanks to this line, whenever Webpack finds a variable in any file named jQuery that has not been initialized - in other words, some module like bootstrap where it's trying to use jQuery as a global variable - it will automatically require the jquery module and set the jQuery variable to that in the dumped file.

This is a game-changer: whenever we try to use any module that relies on jQuery as a global variable, this plugin will rewrite that code to use a proper require statement. Let's do the same thing for the $ variable, which should also use the jquery module:

21 lines webpack.config.js
... lines 1 - 3
module.exports = {
... lines 5 - 13
plugins: [
new webpack.ProvidePlugin({
jQuery: 'jquery',
$: 'jquery'
})
]
};

Tip

There is also something called the imports-loader, where you can do something similar to the ProvidePlugin on a module-by-module basis.

Oh, but PhpStorm says the webpack element "is not exported". At the top, add const webpack = require('webpack'):

21 lines webpack.config.js
... line 1
const webpack = require('webpack');
module.exports = {
... lines 5 - 13
plugins: [
new webpack.ProvidePlugin({
... lines 16 - 17
})
]
};

The plugin comes from Webpack, which we need to require like anything else. My editor still doesn't look super happy, but it will work, I promise!

Let's give it a shot! Go back and restart webpack:

./node_modules/.bin/webpack --watch

Then, find your browser and refresh. Ha! It's alive!!!

Take a look at the built layout.js file. These files are not meant to be easy to read: Webpack adds a lot of magic to get things working. But, it can be really interesting while learning. At the top, Webpack adds its bootstrap code. Next, search for the word "bootstrap".

Woh, check out this "VAR INJECTION" stuff: Webpack wraps Bootstrap in a self-executing function and passes jQuery as an argument. Inside, instead of Bootstrap using a global jQuery variable, it's using whatever is being passed as jQuery.

And... what is that? If you follow the green line on the left to the bottom of the self-executing function, it calls itself with .call() and passes a cryptic __webpack_require__(0) as the first argument. Internally, this is the require call to the jquery module. When it dumps the file, Webpack gives each module a number, and if you did some digging, you'd find out that 0 means jquery in this case.

Like I said... these files aren't meant to be read by humans, but it's really amazing to see how Webpack manages to build this one, final file.

Leave a comment!

  • 2018-01-22 weaverryan

    Hey Chris!

    Ah, thanks for following up with me on this! This is interesting - I don't like it to be honest :). As I mentioned, the date picker is already "written well": if it detects it's in a module environment (like Webpack), then it literally uses require('jquery'). That's awesome! Because it means that it will require the same jquery that we're using and modify it to add the bootstrap functionality.

    Except... apparently... it is *not* using the same jquery :/. By adding the alias above, you're explicitly telling webpack: "Hey: when someone requires jquery, *always* use this file". It should be doing that already... but for some reason, the jquery that is being passed to the date picker and the one being passed to your code when you use require appears to be different. I'll need to look into this further - it's not something I've seen before. But, it at least makes *some* sense: unlike PHP, in Node, it's possible for 2 different dependencies of your project to have their *own* private dependencies. It would be as if, in PHP, your project used version 1.2 of Monolog while Doctrine (a dependency of your project) simultaneously uses version 1.3. That's not possible in PHP, but (for better or worse, it has pros and cons) that *is* possible in Node land :).

    But you *are* safe with that fix - it's no magic. Hopefully now it makes a bit more sense at least!

    Cheers!

  • 2018-01-18 Chris

    Hi weaverryan,

    it almost feels like magic, ... with almost no effort. I already have used the updated version but it starts working right after inserting the following code, as you have suggested:


    resolve: {
    alias: {
    'jquery': path.join(__dirname, 'node_modules/jquery/dist/jquery'),
    }
    }

    This is a good progress for me, since I'm indeed struggling with jQuery plugins to make it work in my project.

    Thanks and have a nice day!

  • 2018-01-17 weaverryan

    Hiya Chris

    Yea, jQuery plugins are one of the most difficult things with Webpack (it's just the way they both work). But actually, I did some digging, and it looks like the Bootstrap date picker *has* been updated to work nicely with Webpack (if you're curious, it's this part: https://github.com/uxsoluti....

    So, this plugin should work even if you did *not* use the ProvidePlugin (but that plugin should not hurt anything). So... I'm not sure! But, I have one thing for you to try: https://github.com/Eonasdan... (if that doesn't work, also try the comment below it, but I believe the *first* comment is more correct).

    If this fixes the problem, can you let me know? It would actually be a very interesting thing that I wasn't aware of and would need to dig into a bit further. If it doesn't fix it, let me know too!

    Cheers!

  • 2018-01-17 Chris

    Hey guys,

    my problem should be simple enough, but I'm stuck :-) :

    I want to add the bootstrap-datepicker form input widget to one of my forms as we have done in the Symfony-Forms Datepicker Field Tutorial, but now with the power of webpack.

    This is, how it looks right now:

    import $ from 'jquery';
    import 'jquery-ui';
    import 'bootstrap';

    import('bootstrap-datepicker');
    import 'bootstrap-datepicker/dist/css/bootstrap-datepicker.css';

    $(document).ready(function() {
    $('#myDatepicker').datepicker();
    });

    In the console I get the following error:
    "Uncaught TypeError: (0 , _jquery2.default)(...).datepicker is not a function".

    Thanks for any help,

    Cheers!

  • 2017-08-10 Ángel Manuel Marqués Ruiz

    Perfect, that's nice to know. Thank you for all the hard work!

  • 2017-08-10 weaverryan

    Hey!

    We're going to cover Encore in a different tutorial - I want to show Webpack by itself first :).

    About the big file sizes - are you asking about the files in this tutorial? I mean, that jQuery is packaged inside each entry? If so, it will be around 2 weeks from now - it involves the CommonsChunkPlugin - it's not on the chapter list yet, but you'll see it on the list in about a week, then released about a week later. Sorry for the wait - we release chapters while we're still recording and finishing other parts of the tutorial :).

    Cheers!

  • 2017-08-10 Ángel Manuel Marqués Ruiz

    Great videos Ryan! I got some questions, is webpack encore a topic for this course? and.. Do we have a date for when the problem of big file sizes will be fixed? I took a glance at the programmed chapters of this course and didn't see it mentioned.