UMD in JavaScript

UMD in JavaScript

·

3 min read

If you ever try to write a JavaScript library, you may consider bundle your code into a UMD format. UMD stands for Universal Module Definition for JavaScript. Set UMD as the bundling target is because it can be loaded directly in a script tag, can be loaded in AMD, and also can be loaded as a Node.js module. In this article, let see how to bundle code into UMD and how the format works.

Bundling into UMD

Bundling tools like webpack and rollup support set UMD as target directly. Now let see how to use rollup to do it.

First, initialize the project and install rollup.

npm init -y
npm install rollup

Then we create a simple source code index.js like below.

export const pi = 3.14;

export function add(a, b) {
    return a + b;
}

Next, we add a build command into npm script.

"build": "rollup index.js --file output.js --format umd --name math"

Here, we set bundling output as a file output.js, format as umd, output module name as math.

Now let run the build command.

npm run build

Then we should get a result output.js like below.

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

    const pi = 3.14;

    function add(a, b) {
        return a + b;
    }

    exports.add = add;
    exports.pi = pi;

}));

Now we can try to load it in html with script tag, load it in AMD or as a Node.js module to test.

The test for script tag looks like this.

Screenshot 2022-11-26 at 21.24.36.png

How it works

As you can see from the above bundling output, in UMD format our source code is wrapped inside with some other code. This wrapper is actually the one who provides this multiple format support.

The bundling output is a bit hard to read. I make some format change to make it clearer.

(function (global, factory) {

  // if using commonjs
  if (typeof exports === "object" && typeof module !== "undefined") {
    factory(exports);

  // if using amd
  } else if (typeof define === "function" && define.amd) {
    define(["exports"], factory);

  // if in browser
  } else {

    // if globalThis exists, use gloablThis
    // if not, use this(first parameter) or self
    if (typeof globalThis !== "undefined") {
      global = globalThis;
    } else {
      global = global || self;
    }

    // use math as module name
    factory((global.math = {}));
  }
})(this, function (exports) {
  "use strict";

  // this is the above factory function
  // parameter exports used as the global environment

  const pi = 3.14;

  function add(a, b) {
    return a + b;
  }

  exports.add = add;
  exports.pi = pi;
});

Once you read the code, you should feel that it is pretty simple. It first creates a factory function which accepts a parameter exports to used as the global environment. Inside this factory function, all the exported code is set. Then inside the main function, it try to guess current environment by testing if some special variables are exist. Once current environment is confirmed, it passes the corresponding global environment to the factory function. Then finally run the factory function and complete the bundling process.