Integrating jQuery Chosen with Webpack using imports-loader
Modularisation system is a part of every modern language that is used for creating complex applications. JavaScript is special here - modularisation system is not a part of language itself. We got great standards (CommonJS or AMD) and tools to make modularisation work. One of the most popular is Webpack - a module bundler which comes with variety of so-called loaders, transforming the input code.
Unfortunately modularisation systems not within the language creates compatibility problems. There are libraries that can’t understand modules and assumes that dependencies are available globally under predefined names.
I had this issue today. I wanted to use the great Chosen library for multi-select inputs. Unfortunately Chosen expects to have jQuery defined in the global namespace and is unaware of modules at all. In this article I’d like to show you how to integrate such module-unaware libraries with Webpack without introducing global variables.
Goal & Initial State
My goal was to allow Chosen to extend my jQuery
instance without exposing the jQuery instance as a global. I have jQuery in my project installed using npm install --save jquery
command - as a module. So every time I need to use jQuery I need to import it manually (here, using ES2015 syntax):
import $ from 'jquery';
Chosen can come as a bower package or as a standalone JS file with some CSS files and images attached. I decided to take the latter approach and put it into a vendor/
folder in my project. So I put the chosen.jquery.js
file into the vendor
folder.
As have been said before, chosen.jquery.js
does not understand modules - it needs to have jQuery defined under jQuery
name in global namespace. With many libraries like that it is also desirable to have this
pointing to the window
variable which is not granted in many environments. For example Babel.js assumes that every input it takes is a ES2015 module and this
is pointing to null
in such case (more about it here). So if you integrate babel-loader
to use ES2015 syntax you may experience problems with such libraries.
Having this problem defined, let’s take a look how the perfect solution can be achieved - so have jQuery still as a module without exposing it as a global but being able to use libraries that assumes jQuery is defined as a global.
Solution - imports-loader
Webpack has a special loader for handling cases like that. It is called imports-loader
and you need to install it by yourself - it doesn’t come by default when you install webpack
. Fortunately the installation process is straightforward:
npm install --save-dev imports-loader
Then you need to modify your webpack config. In loaders
section of your config you need to add the following code:
{ test: /vendor\/.+\.(jsx|js)$/,
loader: 'imports?jQuery=jquery,$=jquery,this=>window'
}
Let’s get through this loader
definition. The first part, imports
defines that we want to use imports-loader
. After ?
there is a list of values, separated by a comma. First one jQuery=jquery
defines that under the jQuery
variable in loaded code there will be a result of require("jquery")
expression - in our case, our jQuery library from the module. The second one is the same, but the name is different. If you want to name your variable the same as your module, you can skip the part after the equation sign and the equation sign itself. So jquery
would create a jquery
variable with require("jquery")
value in the loaded code.
The last value is about redefining the global variable. So global this
will point to window
in the loaded code. It is an equivalent of wrapping the whole contents of the file with the function(this) { ... }
and calling this function in-place with window
as an argument. You can do the same with any global variables and it is indicated by an arrow (=>
).
That’s it! Now I can do:
import $ from 'jquery';
import "vendor/chosen.jquery";
And I can use chosen
jQuery extension without any problems!
Summary
As you can see, integrating module-unaware libraries without exposing its dependencies as globals is a rather simple task. In fact I thought it’ll be harder - I’ve seen many webpack configurations where popular libraries/frameworks like jQuery or Angular.js were exposed as global variables. I hope that you’ll be able to integrate your favourite libraries that way just like I did with Chosen.