Skip to content

Differences between the simplified CommonJS wrapper and standard AMD define

Gert Rietveld edited this page Dec 21, 2013 · 9 revisions

Differences between the Simplified CommonJS wrapper and AMD define

RequireJS has many ways of defining a module, this document will cover the 2 ways of defining a module which contain dependencies, the standard AMD define and the simplified CommonJS wrapper, both ways can be used on the same project and toggled at will, but it's important to understand how both patterns differs and the problems that may arise.



The basic definition of an AMD module is like this:

define(['foo', 'foo/bar'], function(foo, bar){
    return {
        doSomething : function(){
            console.log(foo + bar);
        }
    };
});

This means that modules foo and foo/bar will be loaded (if not available yet) before the definition function gets called and it will pass both modules as arguments to the definition function. More about it on the RequireJS API page

So how does it work?

  1. RequireJS reads the dependency array.
  2. Then it checks if each module was already registered for the current context:
    1. If module was registered uses it;
    2. Otherwise:
      1. resolve the ID into a URI by checking the paths.config and current module path;
      2. loads script;
      3. If AMD module, repeat steps till all the dependencies are loaded.
  3. After all the dependencies are ready it calls the definition function passing the dependencies as arguments, it will then register the module as the value returned by the definition function.

If you prefer to author your modules as if it was a CommonJS module (specially if you have a very large dependency array) you can use the simplified CommonJS wrapper.

define(function(require, exports, module){
    var foo = require('foo'),
        bar = require('foo/bar');

    exports.doSomething = function(){
        console.log(foo + bar);
    };
});

A module is only treated as a CJS module if it doesn't contain a dependency array and the definition function contain at least one parameter.

More info about the Simplified CJS Wrapper at RequireJS documentation.

How does it work?

  1. RequireJS checks if define was called without a dependency array.
  2. Then it checks if the definition function contains any parameters by reading Function.prototype.length.
    1. If it contains, treats the module as CJS:
      1. Call Function.prototype.toString() and read all the synchronous require() calls inside the definition function.
      2. Load all dependencies like if it was on the dependency list of a standard AMD module (see step 2 of "standard AMD define").
      3. After dependencies are ready it calls the definition function passing the special modules require, exports and module as arguments, then registers module as returned value or as value set as module.exports or the exports object itself.
    2. If not, execute definition function and register module as returned value. (standard AMD without any dependencies)

On a standard AMD module you can use a synchronous require() as long as the module was already registered for that context:

define(['require', 'lorem/ipsum'], function(require){
    // since 'lorem/ipsum' is on the dependency list it will be registered
    // before calling the definition function, so the synchronous require will
    // also work
    console.log( require('lorem/ipsum') );
});

But this would fail if the module dolor/amet wasn't registered yet:

define(['require'], function(require){
    console.log( require('dolor/amet') );
});

Since we are passing a dependency array as first argument of the define call it will treat the module as a standard AMD module, so it won't read the definition function content to figure out the dependencies before calling it. You would get the following error:

Uncaught Error: Module name 'dolor/amet' has not been loaded yet for context: _

more info

But if you use the Simplified CommonJS Wrapper instead it would load the dependency before calling the definition function:

define(function(require){
    // this will work since RequireJS will treat module as CJS
    console.log( require('dolor/amet') );
});

The special modules require, exports and module are also available for standard AMD modules.

define(['exports'], function(exports){
    //add properties to the exports object as if the module was a CJS module
    exports.foo = 'bar';
    exports.lorem = function(){
        console.log('ipsum');
    };
});

Properties added to the exports object will be on the public interface of the module, no need to return any value.

This module gives you information about the module ID and location of the current module:

define(['module'], function(module){
    console.log(module.id);
    console.log(module.uri);
});

The previous examples used the require module to load other dependencies but it also contains.

  • require.toUrl(moduleName) : It will return a full path to the resource, obeying any RequireJS configuration.

If you have more questions check RequireJS mailing list and the #requirejs IRC channel on freenode.net.