Skip to content
This repository has been archived by the owner on Mar 15, 2018. It is now read-only.

AMD Usage

ngokevin edited this page Oct 9, 2014 · 6 revisions

The backbone to Commonplace is its use of AMD modules to handle dependencies.

What is AMD?

AMD stands for "Asynchronous Module Definition". The API that's used by Commonplace is based on the official AMD API.

AMD enables the following:

  • Single initialization: Modules are only initialized once per page load.
  • Dependency management: Modules may only use external resources that they have requested, meaning dependencies can (usually) be determined statically.
  • Improved debugging: If a module fails initialization, the source of the error can be determined and the dependency tree can be logged.
  • Standard module interface: Each module is exposed in a single, standard way: by requiring it. You don't need to worry about where the code lives, where it's namespaced, or how to run it.
  • Mocking: Modules can be mocked out for testing.

AMD Loaders

We use two AMD module loaders, one for development and one for production.

Development - Require.js

Require.js is used for local development and debugging and isn't included in the compiled source. The require.js file loads each module in the order that it's requested by writing a script tag to the page. As each script loads, Require adds each dependency until the entire source has been loaded.

Require.js is unsuitable for production use because of the volume of HTTP requests that it produces as well as the size of the Require.js library. Additionally, errors that occur as a result of dependency errors or syntax errors can be confusing or even misleading.

Since Commonplace has a lot of re-used libraries that are placed into directories, our Gulp build scripts generate a require.js file that appends a require.js config call containing paths and shims. This config call can be called again in main.js to be overridden.

Production - Almond

Commonplace uses almond.js, a lightweight AMD loader, in production. Almond, written by the same author of require.js, is much smaller and handles all the different types of define calls such as those that don't specify dependencies.

In production, our JS is minified and concatenated into one file, include.js. Our Gulp build tasks use amd-optimize to build our project's JS. AMD optimizers like amd-optimize handle anonymously-defined modules, dependency tree inspection, shimming, and paths. Another optimizer is r.js, written by the same author of require.js, but it's an entire build system that doesn't fit well into our Gulp system.

Configuration

Require.js, which depends on filenames and paths, takes a config with require.config({}). It takes an object that includes paths and shims. In production, our optimizer can take the same configuration we use in development to build our JS such that it still works.

Paths

The paths config tells Require.js if a module is located in a different path than its module name indicates. Such as with our requests module, it lives in lib/commonplace, so we set:

paths: {requests: 'lib/commonplace'}

into our config.

Shim

Many third-party libraries don't specifically do a define call. Such libraries include Underscore.js, which expose a global variable. This means we cannot traditionally require the module without help. We can shim the module into Require.js by setting a shim config. Such as with Underscore.js, it exports _, so we set:

shim: {underscore: {exports: '_'}}

into our config.

Commonplace Base Config

Since we have many Commonplace projects that share a lot of common modules, all of the projects would have necessary require.js configs that would need to be done. Since we don't want to repeat ourselves, we have a base config that lives within Commonplace at lib/config.js.

This config module includes a config for Require.js. The config is read by our Gulp build tasks to automatically build a custom require.js file that does a require.config so projects work out of the box. The config is also passed into our AMD optimizer.

Since Commonplace projects may want to specify their own paths and shims, they have their own config.js in their project roots that can be used to override or extend the Commonplace base config.