Managing jQuery plugin dependency in webpack

JavascriptJqueryAmdWebpack

Javascript Problem Overview


I'm using Webpack in my application, in which I create two entry points - bundle.js for all my JavaScript files/codes, and vendors.js for all libraries like jQuery and React. What do I do in order to use plugins which have jQuery as their dependencies and I want to have them also in vendors.js? What if those plugins have multiple dependencies?

Currently I'm trying to use this jQuery plugin here - https://github.com/mbklein/jquery-elastic. The Webpack documentation mentions providePlugin and imports-loader. I used providePlugin, but still the jQuery object is not available. Here is how my webpack.config.js looks like-

var webpack = require('webpack');
var bower_dir = __dirname + '/bower_components';
var node_dir = __dirname + '/node_modules';
var lib_dir = __dirname + '/public/js/libs';

var config = {
    addVendor: function (name, path) {
        this.resolve.alias[name] = path;
        this.module.noParse.push(new RegExp(path));
    },
    plugins: [
        new webpack.ProvidePlugin({
            $: "jquery",
            jquery: "jQuery",
            "window.jQuery": "jquery"
        }),
        new webpack.optimize.CommonsChunkPlugin('vendors', 'vendors.js', Infinity)
    ],
    entry: {
        app: ['./public/js/main.js'],
        vendors: ['react','jquery']
    },
    resolve: {
        alias: {
            'jquery': node_dir + '/jquery/dist/jquery.js',
            'jquery.elastic': lib_dir + '/jquery.elastic.source.js'
        }
    },
    output: {
        path: './public/js',
        filename: 'bundle.js'
    },
    module: {
        loaders: [
            { test: /\.js$/, loader: 'jsx-loader' },
            { test: /\.jquery.elastic.js$/, loader: 'imports-loader' }
        ]
    }
};
config.addVendor('react', bower_dir + '/react/react.min.js');
config.addVendor('jquery', node_dir + '/jquery/dist/jquery.js');
config.addVendor('jquery.elastic', lib_dir +'/jquery.elastic.source.js');

module.exports = config;

But in spite of this, it still throws an error in the browser console:

> Uncaught ReferenceError: jQuery is not defined

Similarly, when I use the imports-loader, it throws an error,

> require is not defined'

in this line:

var jQuery = require("jquery")

However, I could use the same plugin when I don't add it to my vendors.js file and instead required it in the normal AMD way as how I include my other JavaScript code files, like-

define(
[
    'jquery',
    'react',
    '../../common-functions',
    '../../libs/jquery.elastic.source'
],function($,React,commonFunctions){
    $("#myInput").elastic() //It works

});

But this is not what I want to do, as this would mean that jquery.elastic.source.js is bundled along with my JavaScript code in bundle.js, and I want all my jQuery plugins to be in the vendors.js bundle. So how do I go about achieving this?

Javascript Solutions


Solution 1 - Javascript

You've mixed different approaches how to include legacy vendor modules. This is how I'd tackle it:

1. Prefer unminified CommonJS/AMD over dist

Most modules link the dist version in the main field of their package.json. While this is useful for most developers, for webpack it is better to alias the src version because this way webpack is able to optimize dependencies better (e.g. when using the DedupePlugin).

// webpack.config.js

module.exports = {
    ...
    resolve: {
        alias: {
            jquery: "jquery/src/jquery"
        }
    }
};

However, in most cases the dist version works just fine as well.


2. Use the ProvidePlugin to inject implicit globals

Most legacy modules rely on the presence of specific globals, like jQuery plugins do on $ or jQuery. In this scenario you can configure webpack, to prepend var $ = require("jquery") everytime it encounters the global $ identifier.

var webpack = require("webpack");

    ...
    
    plugins: [
        new webpack.ProvidePlugin({
            $: "jquery",
            jQuery: "jquery"
        })
    ]

3. Use the imports-loader to configure this

Some legacy modules rely on this being the window object. This becomes a problem when the module is executed in a CommonJS context where this equals module.exports. In this case you can override this with the imports-loader.

Run npm i imports-loader --save-dev and then

module: {
    loaders: [
        {
            test: /[\/\\]node_modules[\/\\]some-module[\/\\]index\.js$/,
            loader: "imports-loader?this=>window"
        }
    ]
}

The imports-loader can also be used to manually inject variables of all kinds. But most of the time the ProvidePlugin is more useful when it comes to implicit globals.


4. Use the imports-loader to disable AMD

There are modules that support different module styles, like AMD, CommonJS and legacy. However, most of the time they first check for define and then use some quirky code to export properties. In these cases, it could help to force the CommonJS path by setting define = false.

module: {
    loaders: [
        {
            test: /[\/\\]node_modules[\/\\]some-module[\/\\]index\.js$/,
            loader: "imports-loader?define=>false"
        }
    ]
}

5. Use the script-loader to globally import scripts

If you don't care about global variables and just want legacy scripts to work, you can also use the script-loader. It executes the module in a global context, just as if you had included them via the <script> tag.


6. Use noParse to include large dists

When there is no AMD/CommonJS version of the module and you want to include the dist, you can flag this module as noParse. Then webpack will just include the module without parsing it, which can be used to improve the build time. This means that any feature requiring the AST, like the ProvidePlugin, will not work.

module: {
    noParse: [
        /[\/\\]node_modules[\/\\]angular[\/\\]angular\.js$/
    ]
}

Solution 2 - Javascript

For global access to jquery then several options exist. In my most recent webpack project, I wanted global access to jquery so I added the following to my plugins declarations:

 plugins: [
    new webpack.ProvidePlugin({
      $: "jquery",
      jQuery: "jquery"
    })
  ]

This then means that jquery is accessible from within the JavaScript source code via global references $ and jQuery.

Of course, you need to have also installed jquery via npm:

$ npm i jquery --save

For a working example of this approach please feel free to fork my app on github

Solution 3 - Javascript

I don't know if I understand very well what you are trying to do, but I had to use jQuery plugins that required jQuery to be in the global context (window) and I put the following in my entry.js:

var $ = require('jquery');
window.jQuery = $;
window.$ = $;

The I just have to require wherever i want the jqueryplugin.min.js and window.$ is extended with the plugin as expected.

Solution 4 - Javascript

I got things working nicely while exposing $ and jQuery as global variables with Webpack 3.8.1 and the following.

Install jQuery as a project dependency. You can omit @3.2.1 to install the latest version or specify another version.

npm install --save jquery@3.2.1

Install expose-loader as a development dependency if not installed already.

npm install expose-loader --save-dev

Configure Webpack to load and expose jQuery for us.

// webpack.config.js
const webpack = require('webpack')

module.exports = {
  entry: [
    // entry bits
  ],
  output: {
    // output bits
  },
  module: {
    rules: [
      // any other rules
      {
        // Exposes jQuery for use outside Webpack build
        test: require.resolve('jquery'),
        use: [{
          loader: 'expose-loader',
          options: 'jQuery'
        },{
          loader: 'expose-loader',
          options: '$'
        }]
      }
    ]
  },
  plugins: [
    // Provides jQuery for other JS bundled with Webpack
    new webpack.ProvidePlugin({
      $: 'jquery',
      jQuery: 'jquery'
    })
  ]
}

Solution 5 - Javascript

In your webpack.config.js file add below:

 var webpack = require("webpack");
 plugins: [
    new webpack.ProvidePlugin({
        $: "jquery",
        jQuery: "jquery"
    })
 ],

Install jQuery using npm:

$ npm i jquery --save

In app.js file add below lines:

import $ from 'jquery';
window.jQuery = $;
window.$ = $;

This worked for me. :)

Solution 6 - Javascript

Add this to your plugins array in webpack.config.js

new webpack.ProvidePlugin({
    'window.jQuery': 'jquery',
    'window.$': 'jquery',
})

then require jquery normally

require('jquery');

If pain persists getting other scripts to see it, try explicitly placing it in the global context via (in the entry js)

window.$ = jQuery;

Solution 7 - Javascript

I tried some of the supplied answers but none of them seemed to work. Then I tried this:

new webpack.ProvidePlugin({
    'window.jQuery'    : 'jquery',
    'window.$'         : 'jquery',
    'jQuery'           : 'jquery',
    '$'                : 'jquery'
});

Seems to work no matter which version I'm using

Solution 8 - Javascript

This works in webpack 3:

in the webpack.config.babel.js file:

resolve: {
	alias: {
		 jquery: "jquery/src/jquery"
	},
 ....
}

And use ProvidePlugin

new webpack.ProvidePlugin({
		'$': 'jquery',
		'jQuery': 'jquery',
	})

Solution 9 - Javascript

Edit: Sometimes you want to use webpack simply as a module bundler for a simple web project - to keep your own code organized. The following solution is for those who just want an external library to work as expected inside their modules - without using a lot of time diving into webpack setups. (Edited after -1)

Quick and simple (es6) solution if you’re still struggling or want to avoid externals config / additional webpack plugin config:

<script src="cdn/jquery.js"></script>
<script src="cdn/underscore.js"></script>
<script src="etc.js"></script>
<script src="bundle.js"></script>

inside a module:

const { jQuery: $, Underscore: _, etc } = window;

Solution 10 - Javascript

The best solution I've found was:

https://github.com/angular/angular-cli/issues/5139#issuecomment-283634059

Basically, you need to include a dummy variable on typings.d.ts, remove any "import * as $ from 'jquery" from your code, and then manually add a tag to jQuery script to your SPA html. This way, webpack won't be in your way, and you should be able to access the same global jQuery variable in all your scripts.

Solution 11 - Javascript

This works for me on the webpack.config.js

    new webpack.ProvidePlugin({
        $: 'jquery',
        jQuery: 'jquery',
        'window.jQuery': 'jquery'
    }),

in another javascript or into HTML add:

global.jQuery = require('jquery');

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionbooleanhunterView Question on Stackoverflow
Solution 1 - JavascriptJohannes EwaldView Answer on Stackoverflow
Solution 2 - JavascriptarcseldonView Answer on Stackoverflow
Solution 3 - JavascriptsanfilippopabloView Answer on Stackoverflow
Solution 4 - JavascriptHarlemSquirrelView Answer on Stackoverflow
Solution 5 - JavascriptashwiniView Answer on Stackoverflow
Solution 6 - JavascriptKhaledMohamedPView Answer on Stackoverflow
Solution 7 - JavascriptCam TullosView Answer on Stackoverflow
Solution 8 - JavascriptSharpCoderView Answer on Stackoverflow
Solution 9 - JavascriptfrdnrdbView Answer on Stackoverflow
Solution 10 - JavascriptFabricioView Answer on Stackoverflow
Solution 11 - JavascriptJPRLColView Answer on Stackoverflow