Modules/Transport/B
STATUS: PROPOSAL
- Draft 2
- Draft 1
Module transport format is a content format that can be used to send module factories from a server to an asynchronous client-side module loader. A module factory can then be used on the client to instantiate modules.
Specification
Content in module transport format is called a "bundle".
- A bundle is a JavaScript program and consists only of any number of calls to a "require.def" function.
- "require.def" accepts a top-level module identifier and a "module descriptor" object.
- A "module descriptor" consists of:
- A "factory" function. The factory function accepts an object that permits the loader to inject arbitrary dependencies, particularly "require", "exports", and "module" for CommonJS modules. It is the purview of the module text to extract these values.
- A "requires" array of top-level module identifiers corresponding to the shallow dependencies of the given module factory, those that it directly "requires". A module factory must not be called until all of the module desciptors mentioned in the transitive "requires" have been given to a loader by calls to "require.def".
Non-normative Form
The following is an example of the structure of a module transport. All capitals denote meta-syntactic variables; names that would be replaced. Elisions imply continuation, either by repetition or extension as applicable in context. Destructuring notation, as proposed for future ECMAScript is used here to communicate that the injection argument to the module factory function does not have any particular name, and may not necessarily be bound as anything other than arguments[0], but that it can carry arbitrary values for use by the module; "require", "exports", and "module" are names that must be injected particularly and only necessarily for the case of CommonJS modules.
require.def(ID, { "factory": function ({require, exports, module, …}) { MODULE_TEXT }, "requires": […MODULE_IDS], … }); …
Example
Given a CommonJS module "example/point":
var Scalar = require("./scalar").Scalar; exports.Point = function (x, y) { return Object.create(exports.Point.prototype, { "x": {"get": function () {retun Scalar(x)}}, "y": {"get": function () {retun Scalar(y)}} }); }
This would be the bundle of "example/point":
require.def("example/point", { "factory": function () { var exports = arguments[0].exports, require = arguments[0].require, module = arguments[0].module; var Scalar = require("./scalar").Scalar; exports.Point = function (x, y) { return Object.create(exports.Point.prototype, { "x": {"get": function () {retun Scalar(x)}}, "y": {"get": function () {retun Scalar(y)}} }); } }, "requires": ["example/scalar"] });
Philosophy
Module Transport Format is not intended to be written by hand; it is intended that modules conforming to the CommonJS Modules/1.1 specification can be programmatically wrapped and bundled to Module Transport Format. It would be harmful for modules to be crafted by hand in this format because individual modules and trees of modules would become atonomous and therefore tightly coupled to their module name space.
- modules can be dynamically complied to Module Transport Format
- all module factories can be registered in a single call, but also
- module transport format is concatenatable.
- the module descriptor is intentionally an extensible name space. One obvious extension would be a "deepRequires" list that could be used by an asynchronous, client-side module loader that has not been provided fore-knowledge about the transitive dependencies of a module to initiate early and optimially ordered HTTP requests for those dependencies before their shallow dependees are downloaded and registered by "require.def". A good heuristic for optimal order of download for modules is largest to smallest.
Draft 1 called for "require.def" to accept an object mapping multiple top-level module identifiers to respective module descriptors. In response to Tobie Langel's comment that this would necessitate onerous complication for loaders on Internet Explorer because of the "DontEnum" bug, this proposal was revised to accept only one module for each call to "require.def".