Load the src version of Select2
Sean Larkin, one of the webpack developers says:
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...1
Following this advice, I prefer to require files under the src
folder:
import "select2/src/js/jquery.select2.js";
import "select2/src/scss/core.scss";
Load language files statically
You'll then find there are various language-related hurdles to overcome. As soon as you insert $(".dropdown").select2()
into your code, you'll see
Uncaught Error: Cannot find module './i18n/en'
. This is because the dynamic require designed for RequireJS is not working. It comes from the loadPath
function in translation.js
:
if (!(path in Translation._cache)) {
var translations = require(path);
Translation._cache[path] = translations;
}
In webpack parlance this is called a 'require expression'. My solution is to avoid ever reaching that line by priming the cache first. In my app code I put:
import EnglishTranslation from "select2/src/js/select2/i18n/en.js";
import { _cache } from "select2/src/js/select2/translation.js";
_cache["./i18n/en"]=EnglishTranslation;
_cache["en"]=EnglishTranslation;
You will need to do this for all the languages you wish to use.
Then you can use the language features as documented, including $.fn.select2.defaults.set('language',"en")
or language: en
during initialisation. Overrides like language: { noResults: function() { return "abc"; } }
work also.
Disable the contextless require
The above instructions give you a working select2, but Webpack will be complaining, Critical dependency: the request of a dependency is an expression
. This means, "webpack needs to include all files inside the current folder and all files in child folders"2, which would be everything under select2/src/js/select2
!
I found I could use imports-loader to disable the require()
in the translation module completely, whilst leaving the define()
call intact, so that it could still do its exports. Here's an excerpt from my webpack.config.js
:
For webpack 5, imports-loader 4
module: {
rules: [
{
test: /select2\/src\/js\/select2\/translation\.js$/,
use: {
loader: "imports-loader",
options: {
additionalCode: "var require=false;"
}
}
},
]
}
For webpack 4 and imports-loader 0.8
module: {
rules: [
{
test: /select2\/src\/js\/select2\/translation\.js$/,
use: {
loader: "imports-loader",
options: "require=>false"
}
]
}
Writing Custom Adapters
You can use webpack to import various components to write your own data adapters. The select2 adapter documentation assumes you will use something like the supplied Almond loader (e.g. $.fn.select2.amd.require
), so it took me a while to realise I could do this kind of thing:
import * as Utils from "select2/src/js/select2/utils";
import * as ArrayAdapter from "select2/src/js/select2/data/array";
Hints
- Switch on
$.fn.select2.defaults.set('debug',true);
when diagnosing language issues
- On versions below 4.09, avoid strange language defaults by adding
$("html").removeAttr("lang");
to your app.5