I recently upgraded to Webpack 5 and I'm having some trouble importing UMD modules.
In particular, I'm trying to import Leaflet. Leaflet seems to export a UMD module created by rollup.js, which looks like this:
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(factory((global.L = {})));
}(this, (function (exports) { 'use strict';
[...]
})));
As you can see, it first tries to use CommonJS (if exports
and module
is defined), then it tries to use AMD (if define
is defined), and otherwise it tries to put itself to the global scope by referencing this
from the module context.
Webpack (without any loaders) compiles it to this:
(function (global, factory) {
typeof exports === 'object' && "object" !== 'undefined' ? factory(exports) :
typeof define === 'function' && __webpack_require__.amdO ? define(['exports'], factory) :
(factory((global.L = {})));
}(undefined, (function (exports) { 'use strict';
[...]
})));
Concretely, what it did was:
typeof module
with "object"
define.amd
with __webpack_require__.amd0
this
with undefined
During the build, webpack gives me a warning: export 'default' (imported as 'L') was not found in 'leaflet' (possible exports: )
. When I open the compiled bundle in the browser, I get the error Uncaught TypeError: Cannot set property 'L' of undefined
.
What happens is that CommonJS fails (because exports
is not defined), AMD fails (because define
is not defined) and finally setting the global also fails because this
is replaced with undefined
.
According to the Webpack docs, Webpack should universally support importing CommonJS and AMD modules, but it seems in this case neither of the two work.
What can I do to fix this?
I narrowed the problem down and discovered that the problem was caused by the fact that I was using an imports-loader
in a certain way.
Before the update, I used an imports-loader
to add any needed transitive dependencies that my dependencies were not importing, in particular CSS files. The configuration looked something like this:
{ module: { rules: [ {
test: /\/node_modules\/leaflet\/.*\.js$/,
loader: "imports-loader?asdf=leaflet/dist/leaflet.css"
} ] } }
After the upgrade, webpack didn't accept the string syntax for loaders anymore, and imports-loader also changed its syntax, so I changed it to:
{ module: { rules: [ {
test: /\/node_modules\/leaflet\/.*\.js$/,
loader: "imports-loader",
options: {
imports: "default leaflet/dist/leaflet.css asdf"
}
} ] } }
This seemed to do the job and properly included the necessary CSS file. However, when I discovered that this part of the config is the cause of the problem, I dug deeper into the documentation of imports-loader
. It turns out the configuration I was using was now generating the following line of code:
import asdf from "leaflet/dist/leaflet.css";
(and I could have used "side-effects leaflet/dist/leaflet.css"
to get the same result without having to use asdf
as a bogus import name). While this properly imported the CSS file, it probably caused webpack to think that this file is a proper ES module, disabling support for CommonJS/AMD/UMD modules. I changed the configuration of imports-loader to the following:
{ module: { rules: [ {
test: /\/node_modules\/leaflet\/.*\.js$/,
loader: "imports-loader",
options: {
imports: "pure leaflet/dist/leaflet.css",
type: "commonjs"
}
} ] } }
According to the docs, this creates the following code line:
require("leaflet/dist/leaflet.css");
After making this change, it seems to work without problems.
Interestingly, it seems that webpack actually detects the whole UMD block rather than just providing the exports
and define
variables, so it compiles the Leaflet code into this now:
(function (global, factory) {
true ? factory(exports) :
0;
}(this, (function (exports) { 'use strict';
[...]
})));