Modules/Transport/D

From CommonJS Spec Wiki
Jump to: navigation, search

STATUS: PROPOSAL

Implementations
[[Implementations/Yabble|]]

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.


Example

require.define({ // transport the math/add module
  "math/add": function(require, exports, module){
    var sum = require("./general").sum;
    exports.plusTwo = function(a){
      return sum(a, 2);
    };
  }
},["math/general"]); // list deps

Specification

This specification defines a require.define function that can be called to provide modules to a loader.

  1. JavaScript script may consist of any number of calls to a "require.define" function.
  2. "require.define" accepts a module set, which is an object where each property name represents a top-level module identifier and every value is a "module descriptor".
  3. A "module descriptor" can be a function or an object:
    1. If the "module descriptor" is a function, it is is the factory function.
    2. If the "module descriptor" is an object, it can have the following properties
    3. An "injects" array of the known free-variables that are applicable ("require", "exports", or "module"). Each of these variables should be passed to the factory function at the corresponding parameter positions.
    4. A "factory" function. The factory function for the module. The "injects" property may define the arguments passed to the factory function. If "injects" is omitted then the arguments should default to the free variables for the module format ("require", "exports", or "module" for CommonJS modules).
  4. The second parameter of require.define is an array of top-level module identifiers corresponding to the shallow dependencies of the given module factory, those that it directly "requires". Module factories 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.define". This parameter is optional. If omitted, the set of dependencies is empty.

Calling require.define does not guarantee that the module's factory will be immediately executed even if the dependencies are available. Module factories generally should not be executed until they are requested by a require, require.ensure, or other module requesting mechanism. If multiple require.define exists in one script, care must be taken to ensure the correct order, module transport writers must not assume that the modules from future require.define (later in a script) calls will be available to previous calls. In other words this will likely trigger an unnecessary request for module "b":

    require.define({'a': function() {}}, ['b']);
    require.define({'b': function() {}});

Non-Normative Optional Extensions

These capabilities are not required, but some loaders may support:

  1. If the factory value is a string, some loaders may support evaluating the string to a factory function
  2. Some loaders may support module-specific dependency lists, read from the module descriptor object's "dependencies" property.

Format

require.define({
  "MODULE_ID": FACTORY_FUNCTION or {injects: [INJECTIONS], factory: FACTORY_FUNCTION}, 
  ...
}, [ARRAY_OF_DEPENDENCY_MODULE_IDS]);

Examples

Transporting a set of CommonJS modules

Transports CommonJS modules "alpha" and "beta", which depend on "gamma":

require.define({
  "alpha": function (require, exports, module) {
    exports.verb = function() {
      return require("beta").action();
    }
  },
  "beta": function (require, exports, module) {
    exports.action = function() {
      return require("gamma").something();
    }
  },
}, ["gamma"]);

Defining a module

Defining a module using module injection:

require.define({
  "alpha": {
    injects: ["require", "module", "exports"],
    factory: function (require, module, exports) {
      exports.verb = function() {
        return require("beta").action();
      }
    }
  }
});

Implementation/Usage Notes

Just like any JavaScript object literal, the use of property names that correspond to the property names on Object.prototype will not be enumerable in older versions of Internet Explorer, and modules with such names might not be detected by a loader unless it specifically checks for them. Also, duplicate module ids MUST not be included in object literals, different JavaScript VMs treat duplicates differently.

Philosophy

Module Transport Format is optimized for auto-generated transportation of modules, but can be written by hand if necessary. It is intended that modules conforming to the CommonJS Modules/1.1 specification can be programmatically wrapped and bundled to Module Transport Format. Crafting by hand in this format introduces fragility because individual modules and trees of modules become autonomous and therefore tightly coupled to their module name space.

  • modules can be dynamically compiled to Module Transport Format by simply wrapping, internal modification to modules is not necessary
  • all module factories can be registered in a single call with minimal boilerplate, but also
  • module transport format is concatenatable.
  • module namespace domain for declaring modules and specifying dependencies is not reduced by free variables (like require, exports, module).
  • modules can be injected as arguments to the factory to ease referencing other modules.
  • the module descriptor is an extensible name space.
  • dependency sets can be defined after modules to reduce memory usage in generation implementors.

Considerations for transport when used in the browser

  • Relative module names that start with "." and ".." are not supported in the top level module IDs, particularly if many modules are being transported from different paths.

Show of Hands

  • In favor of Transport/D:
    • Kris Zyp
    • Kris Kowal
  • Opposed to Transport/D:
    • Tobie Langel

If opposed or in favor with reservations, vote on changes that you want:

  • A bundle should consist of a single call to a "require.define" function.
    • Tobie Langel
  • require.define should only take a single module (instead of module set hash). require.pause and require.resume should be added to define modules with circular dependencies.
  • Each module descriptor should allow for a dependency list
    • Tobie Langel (this list should be somehow made optional)
  • The dependency list should be the first argument
  • Strings should be accepted as factory values (and eval'd with a CommonJS wrapper)
  • Allow for module ids in the injects array