cram.js Concepts
February 9, 2014 ยท View on GitHub
AMD
Asynchronous Module Definition is a proposed standard file format for writing Javascript modules. It has built-in closures to help developers write modular code and has support for asynchronous loading, which is required in browser environments.
CommonJS Modules
CommonJS Modules are another proposed format for Javascript modules. Unlike amd, CommonJS modules don't have built-in closures since they rely on a run-time environment that limits access to the global object. They also don't handle asynchrony since they assume the environment can load modules synchronously.
There are several build tools that can be used to convert CommonJS Modules to a format suitable for use in a browser. cram.js can be used for this, too. For simpler development cycles, curl.js can load CommonJS modules without a build step, as well.
Optimization and Bundles
Small files are much easier to develop and test. However, large files load more efficiently over HTTP connections. The process of optimization is the conversion of many smaller files to one -- or a small number of -- larger files. We call these larger files "bundles".
The smaller files are typically Javascript files, but could also be any
text-based resources such as CSS and HTML files, as well. Actually, any type
of text file can be used, as long as there's an cram.js plugin for it.
When combined into a bundle, the text based resources are wrapped in an
AMD define.
Ideally, all of the Javascript files would be AMD or CommonJS modules, but legacy, non-modular Javascript files are also allowed via the js! plugin.
The simplest approach to bundling would be to create a single bundle that comprises the entire app -- Javascript, CSS, and HTML. This is also a very performant approach for small and medium-sized applications. However, very large apps almost always benefit from more sophisticated strategies, involving many, interdependent bundles.
AMD Bundles
If a bundle is reliant on an external AMD loader, we call it an "AMD Bundle". To an AMD loader, it looks a lot like an AMD module with several stowaways.
Integrated Bundles
An AMD Bundle may contain run-time configuration or code to kick-start an application. If an AMD loader is also included, the bundle is self-sufficient and no longer needs any type of external loader.
run.js
One strategy for launching an AMD application is via a global script that
contains configuration and code to kick start the application. In cujoJS,
we call this a "run.js file". However, the actual name of the file doesn't
matter. During development, the run.js file is loaded by the browser via a
normal <script> element, but during production, the run.js file may be part
of an integrated bundle.
Compile to AMD
Since AMD is such a browser-friendly format, it's convenient to wrap other types of things in AMD "wrappers". Many of curl.js's cram plugins use this concept at build time whenever possible. For instance, curl's text! plugin takes the text resource and transforms it to a module that exports a string:
define('myTextResource', function () { return 'text is here'; });
curl.js's transforms also use this feature to transform a "foriegn" format, such as CommonJS Modules, into AMD.
Code Inference
It can be a bit of a maintenance hassle to have to keep two different copies of your application's AMD config: one for the loader and one for the bundler. cram.js lets you reuse your loader config even if it's embedded in an HTML file or a run.js.
cram.js will inspect your HTML and/or Javascript files for configuration information and modules to run. These will serve as the base configuration for building a bundle. You can override the configuration by providing overrides to cram like this, for example:
node path/to/cram myapp/index.html build_overrides.json
Read up on configuration options for more information.