Learn more about Russian war crimes in Ukraine.

What are UMD modules? One final module system to rule them all (except ES modules which are a different thing)

I’ve recently written about four “module” systems in JavaScript:

But there is, of course, a fifth and final module system. One module system to rule them all. It’s called Universal Module Definition, or “UMD”.

Basically, a UMD module is a JavaScript file that tries to guess at runtime which module system it’s being used in, and then it acts as that kind of module. So you can load the file in a plain <script>, or you can load it from an AMD module loader, or you can load it as a Node.js module, and it will always do something sensible.

You might reasonably ask why this is useful. Surely the developer knows ahead of time which module system is being used, and so can load a version specifically for that module system? So we’d have react-script.js for raw <script> users, react-commonjs.js for CommonJS users, react-amd.js for AMD users, and react-es6.js for ECMAScript module users. Honestly, I don’t know the answer. I suppose it allows library authors and library consumers to live in some level of ignorance.

But for whatever reason, UMD is popular. Here is the basic pattern, in all its glory:

// myModuleName.js
(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(['exports', 'b'], factory);
    } else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') {
        // CommonJS
        factory(exports, require('b'));
    } else {
        // Browser globals
        factory((root.myModuleName = {}), root.b);
}(typeof self !== 'undefined' ? self : this, function (exports, b) {
    // Use b in some fashion.

    // attach properties to the exports object to define
    // the exported module properties.
    exports.action = function () {};

Near the bottom is the factory function, which contains the meat of the module. As arguments, it is given its imported modules, and an exports object that it is expected to populate. We then have some magic tests for different module systems. If typeof define === 'function' && define.amd, we’re probably in an AMD system, so we should define a module. If typeof exports === 'object' && typeof exports.nodeName !== 'string', we’re probably in a CommonJS system, so we should require the depdencies and put our exports on the global exports object. Otherwise, we should just act like a <script>: attempt to take our dependencies from the global object, and spit our exports back onto the global object.

There are lots of variations on this theme in different packages. But you know you’re looking at UMD if you see these runtime checks for typeof define, typeof exports, etc. For example, here’s a file in the react package, which begins:

(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
  typeof define === 'function' && define.amd ? define(['exports'], factory) :
  (global = global || self, factory(global.React = {}));
}(this, (function (exports) { 'use strict';
// ...

Despite claiming to be “universal”, there’s one big module system that UMD doesn’t support: ECMAScript modules, i.e. The Future! You can’t write import {foo,bar} from './someUmdModule.js' and expect it to work. I actually think it would be impossible, because ECMAScript modules define their imports and exports statically at the top level, which contradicts the idea of dynamic runtime switching between module systems.

What can computers do? What are the limits of mathematics? And just how busy can a busy beaver be? This year, I’m writing Busy Beavers, a unique interactive book on computability theory. You and I will take a practical and modern approach to answering these questions — or at least learning why some questions are unanswerable!

It’s only $19, and you can get 50% off if you find the discount code ... Not quite. Hackers use the console!

After months of secret toil, I and Andrew Carr released Everyday Data Science, a unique interactive online course! You’ll make the perfect glass of lemonade using Thompson sampling. You’ll lose weight with differential equations. And you might just qualify for the Olympics with a bit of statistics!

It’s $29, but you can get 50% off if you find the discount code ... Not quite. Hackers use the console!

More by Jim

Tagged #programming, #javascript, #web. All content copyright James Fisher 2020. This post is not associated with my employer. Found an error? Edit this page.