Back to index

Understanding Javascript Modules

Diving into the murky waters of Javascript development yet again. Hopefully someone’s as lost as I was and this writing can be of help to them. Oh, and this is my first blog post …ever.

Updates:
2016–08–08: Less bull, more direct, re-organize.
2017–01–24: Minor edits. Moved to new site.
2017–07–02: Clarifications, fact edits, remove “2016” from title.

What are Modules?

Code that’s bundled into a reusable package is called a module. Code outside of the module communicates with the module via the module’s external interface, yet doesn’t care (and possibly knows nothing) about the module’s internal workings.

Scripts ≠ Modules

The script tag enables reusable pieces of code in the browser, but such code is far from straightforward to access from outside of that script. This is because code executing from one script tag doesn’t automatically have a reference to code in other script tags, via which they could communicate.

Modules can also have a complex dependency graph which is difficult to model with script tags, because script tags are executed sequentially.

Ideally we’d like to specify which modules to use – in code – without worrying about how they’re loaded, such as:

import { capitalize } from './string_utils.js'
console.log(capitalize('hello world'))

Module Formats

Module format specifies the syntax in which a Javascript module must be written in. This syntax is the way via which modules interact with module loaders. The interaction can happen, for example, by a module calling the “define” function specified by the AMD module format.

Although some parts of the proposed native module format (ES6) have been standardized, the specifications are not finished (the runtime semantics were removed before finalization). Thus as of 72016 there exists no native implementation of modules in Javascript.

The lack of native modules in Javascript has given rise to numerous module formats, such as AMD (Asynchronous Module Definition), CJS (CommonJS Modules), UMD (Universal Module Definition) and “System.register“. This is where transpilers come in.

Transpilers

The job of a transpiler is to take code in, transform it, and then spew out the transformed code. Usually it’s used to support Javascript language features that aren’t natively implemented yet. It does this by rewriting code that’s using unsupported features with equivalent code that’s not.

Good example being the ES6 module syntax. These days one can write their code as if the import and export syntax of the future was already supported, then transpile their code into one of the supported module formats.

Babel is one such transpiler.

Module Loaders

Module loader’s job is to load modules and their dependencies in the correct order. The loader is pointed to the first module to start loading from. It then recursively proceeds to load each module’s dependencies and execute them.

Upon being commanded to load a module, the module loader needs to find out where the module is. The module loader is free to load the module in any way it sees fit. It could load some external URL or it could construct the module on the fly, for example.

When the module’s dependencies have been satisfied, the module can be executed. The module loader is again free to choose how it does this.

For example in the case of a Javascript module written in the AMD module format, the following would take place:
The module makes itself known to the module loader by calling a function named define, which is specified by the AMD module format. Arguments given to the function are the module’s dependencies and a callback to the module’s code. When the module’s dependencies have been satisfied, the module loader executes the callback function with the satisfied dependencies as it’s arguments plus one extra argument: an exports object. The contents of the exports object can be set in the module’s callback function, and will be made available to other modules that depend on this module.

Notice that a module loader is not limited to loading only Javascript modules. Because the loader is free to interpret “loading” and “executing” however it wants, a Javascript code importing a CSS file could cause the module loader to load the CSS over the network, inject it as a script tag to the currently active HTML page and provide all the css selector names as a Javascript object to the dependent Javascript module.

Some examples of module loaders are Almond, curl.js, RequireJS and SystemJS. Webpack also includes a module loader which is automatically included in it’s output.

Resolving Confusion

The following caused me some headache, so I’ll try to unravel these mysteries.

SystemJS is the same as es6-module-loader (below), except SystemJS has been extended to support AMD (Asynchronous Module Definition) and CJS (CommonJS) module formats.

es6-module-loader is a module loader polyfill for the draft version of a proposed ES6 Module Loader Specification (Sections 15, 26.2 and 26.3). The proposed module loader can be interacted with to, for example, change the algorithm that determines where modules can be found, or to manipulate the module dependency tree. [1]

The proposed specification has apparently been replaced with another proposed specification (WhatWG Javascript Loader Standard), which in turn has been put on hold pending further investigation into how the implementation should look like. [2]

Feature-wise this module loader only supports loading modules in “System.register” module format (explained below) and can load ES6 modules at run-time by transpiling them to said format on-the-fly.

jspm is a package manager built around SystemJS. It delegates locating and retrieving packages to plugins that use it’s “registry api”. It provides implementations for npm’s package registry and jspm’s own. After a package has been installed, it can be imported as a dependency in code just like with SystemJS. jspm uses es6-module-loader’s “paths” functionality to map module names to urls so that resources are correctly loaded from the “jspm_packages” folder.

UMD is a module format, which makes modules written in it compatible with AMD, CommonJS and “browser globals” module formats.

Resolving even more confusion

How do ES6 Module Loader Specification, System, SystemJS, and System.register relate?

(Now what would “System Module Format” be? Because as far as I know, it doesn’t mean anything. “System” is just the name of the loader instance in the draft specification.) Gooby plz..

Closing thoughts

I apologize for any inaccuracies; I’m no expert in this field. I welcome all feedback, so please leave some if you feel like it. Corrections are especially welcome.

That’s all.

Attribution and sources

Thanks to Jani for some suggestion.

[1] Source of this information is the creator of SystemJS, Guy Bedford.
[2] Source of this paragraph is es6-module-loader’s GitHub page.

Authored on 2016-08-01, modified on 2017-08-02.
Filed under: [javascript].