This guide builds on the Overview Example from the top-level conversion guide.
./src/test.js
:
var Whatever = require("./src/whatever.js");
var { Something } = require("./src/something.js");
var anotherVal = require("./src/another.js").another();
module.exports.whatever = Whatever();
Object.assign(module.exports,{
Something,
Another: anotherVal,
});
Let's take a look at the UMD output produced, in two separate parts: the auto-generated UMD wrapper boilerplate, and the conversion of the ./src/test.js
module's code.
First, the UMD wrapper:
(function UMD(name, context, dependencies, definition) {
if (typeof define === "function" && define.amd) {
dependencies = Object.keys(dependencies).map(p => p.replace(/^\.\//, ""));
define(name, dependencies, definition);
} else if (typeof module !== "undefined" && module.exports) {
dependencies = Object.keys(dependencies).map(p => require(p));
module.exports = definition(...dependencies);
} else {
dependencies = Object.values(dependencies).map(n => context[n]);
context[name] = definition(...dependencies);
}
})("TestModule", typeof globalThis != "undefined" ? globalThis : typeof global != "undefined" ? global : typeof window != "undefined" ? window : typeof self != "undefined" ? self : new Function("return this")(), {
"./src/whatever.js": "Whatever",
"./src/something.js": "Mz_540737562",
"./src/another.js": "Another"
}, function DEF(Whatever, Mz_540737562, Another) {
// ..
});
In this UMD wrapper, first notice that TestModule
is the exported name for this overall module. That name came from the dependency-map (depMap
) in the configuration for the build, as shown in the Overview Example. Moreover, the names Whatever
and Another
are used for the ./src/whatever.js
and ./src/another.js
modules, respectively; again these names come from depMap
.
However, ./src/something.js
isn't listed in the dependency map (it probably should be!), so it's treated as an unknown dependency. Normally, that would throw an error, but in this case it doesn't because the config included ignoreUnknownDependency: true
. It still has to have a name, so the name Mz_540737562
was randomly generated for it, and will be used throughout all files in this current build run for any matching ./src/something.js
dependency import.
The UMD wrapper ensures this module will load properly in all of the following environments:
-
Node.js (if you didn't want to use the original CJS, for some reason)
-
A browser, using an AMD-style loader such as RequireJS
-
A browser, using normal
<script src=..>
style loading, or a basic script loader (ie, LABjs)
Now, let's look at the ./src/test.js
module's converted code:
var {
Something
} = Mz_540737562;
var anotherVal = Another.another();
let _exp2 = {};
_exp2.whatever = Whatever();
Object.assign(_exp2, {
Something,
Another: anotherVal
});
return _exp2;
Let's break that conversion down.
The UMD wrapper automatically takes care of the entirety of a default import, like the first Whatever
import; that statement is effectively removed (having been replaced by just the Whatever
named parameter -- see the UMD wrapper boilerplate).
The named import for Something
is computed against the imported module binding (auto-named Mz_540737562
above).
And the computation of anotherVal
is performed against the imported Another
module binding.
All assignments to module.exports
are rewritten to be performed against an auto-generated variable (_exp2
above), such as _exp2.whatever = ..
, including other non-obvious assignment computations/expressions, like the Object.assign(..)
.
Finally, the _exp2
intermediary is returned as the single exported value for the module.
Let's also explore a variety of other forms of import and export conversion.
When require("..")
is encountered in the original source, the UMD wrapper takes care of loading this dependency, and setting it into the scope of the UMD module (via parameter) by its dependency name, or is auto-generated (like Mz_43812323
), if unknown.
Note: In the following snippets, DependencyName
stands in for this identifier (whether by its configured name, or auto-generated).
require("..");
// handled by the UMD wrapper
var x = require("..");
// handled by the UMD wrapper
// var x = ..
x = require("..");
// converts to:
x = DependencyName;
Note:
var x = require("..").something;
// converts to:
var x = DependencyName.something;
// var x = ..
x = require("..").something;
// converts to:
x = DependencyName.something;
var { something } = require("..");
// converts to:
var { something } = DependencyName;
var { something: x } = require("..");
// converts to:
var { something: x } = DependencyName;
// var something = ..
({ something } = require(".."));
// converts to:
({ something } = DependencyName);
// var x = ..
({ something: x } = require(".."));
// converts to:
({ something: x } = DependencyName);
var x = require("..").something(42);
// converts to:
var x = DependencyName.something(42);
// var x = ..
x = require("..").something(42);
// converts to:
x = DependencyName.something(42);
var x = require("..")(42);
// converts to:
var x = DependencyName(42);
// var x = ..
x = require("..")(42);
// converts to:
x = DependencyName(42);
something( require("..") );
// converts to:
something( DependencyName );
In all the following forms, module.exports
is recognized the same as just exports
, so they're interchangeable in the conversion.
In UMD builds, any operations against module.exports
are actually performed against an auto-generated substitute (ie, _exp
), and then at the end of the module, a return _exp
is inserted.
Note: multiple assignments to module.exports
(ie, module.exports = ..
, not just module.exports.x = ..
) are permitted in UMD builds. However, you should avoid this, as it's creating confusion by overriding earlier assignments. In the ESM build format, an exception will be thrown, because multiple default exports are not allowed by ESM. It is however common (and fine!) to have a single assignment to module.exports
and then subsequently assign additional properties (ie, module.exports.x = ..
).
module.exports = something;
// converts to:
let _exp = {};
// ..
_exp = something;
//..
return _exp;
module.exports = 42;
// converts to:
let _exp = {};
// ..
_exp = 42;
// ..
return _exp;
module.exports = something(42);
// converts to:
let _exp = {};
// ..
_exp = something(42);
// ..
return _exp;
module.exports = function something() { .. };
// converts to:
let _exp = {};
// ..
_exp = function something() { .. };
// ..
return _exp;
module.exports.x = something;
// converts to:
let _exp = {};
// ..
_exp.x = something;
// ..
return _exp;
module.exports.x = something.y;
// converts to:
let _exp = {};
// ..
_exp.x = something.y;
// ..
return _exp;
module.exports.x = 42;
// converts to:
let _exp = {};
// ..
_exp.x = 42;
// ..
return _exp;
module.exports.x = function something() { .. };
// converts to:
let _exp = {};
// ..
_exp.x = function something() { .. };
// ..
return _exp;
Object.assign(module.exports,{
something() { .. },
x: 42
});
// converts to:
let _exp = {};
// ..
Object.assign(_exp,{
something() { .. },
x: 42
});
// ..
return _exp;
something(module.exports);
// converts to:
let _exp = {};
// ..
something(_exp);
// ..
return _exp;
The combined import/export forms in UMD builds act like compositions of the above conversions, as there's no specific handling to combine them.