Search code examples
javascriptrequirejsamd

How are namespace objects used alongside AMD in javascript?


In an effort to make my libraries more portable I've been reading up on AMD and CommonJS. The one thing I have noticed above all is the way they use a directory structure and aim to have one module per file. From what I can tell their 'namespaces' coincide with the directory tree.

However, my own code uses a global object as a namespace, then among my various files, regardless of the directories, I add classes to this object.

(function (Twifty) {
    // Add objects to the Twifty namespace
    return Twifty;
}(Twifty || {}));

In the upgrade effort, I'm trying to support AMD and CommonJs. There are many articles out there about how to this, but I can't wrap my head around the AMD define function. Here is how I am transforming the above code:

(function (root, factory) {

    // Define the namespace if it doesn't exist
    var Twifty = root.Twifty || {};

    'use strict';
    if (typeof define === 'function' && define.amd) {
        // AMD is used - Register as an anonymous module.
        define([], factory);
    } else if (typeof exports === 'object') {
        // CommonJS is used - Twifty is a namespace and doesn't need to be required
        factory(Twifty);
    } else {
        // Neither AMD nor CommonJS used. Use global variables.
        if (!document) {
            throw 'twifty-map requires a DOM object';
        }
        root.Twifty = factory(Twifty);
    }

}(this, function(Twifty) {
    // Add objects to the Twifty namespace
    return Twifty;
}));

The problem is with the AMD define. The factory requires a Twifty object but since it is not a module I can't add it as a dependency. How should I handle this?

It's not really feasable to remove the namespace as this would break any code that depends on it.

Sorry in advance if this is a trivial question.


Solution

  • In an AMD or CommonJS context, your modules should not leak variables into the global space.

    So a strategy has to be used in order to have a common object (the namespace) that the various modules can add to.

    You can have a twifty.base module like this:

    (function (root) {
        'use strict';
    
        if (typeof define === 'function' && define.amd) {
            // AMD 
            define([], {});
        }
        else if (typeof module === "object" && module.exports) {
            // CommonJS
            module.exports = {};
        } 
        else {
            root.Twifty = root.Twifty || {};
        }
    }(this));
    

    All other modules would depend on twifty.base:

    (function (root, factory) {
    
        'use strict';
        if (typeof define === 'function' && define.amd) {
            // AMD
            define(['twifty.base'], factory);
        }
        else if (typeof module === "object" && module.exports) {
            // CommonJS
            module.exports = factory(require('twifty.base'));
        } 
        else {
            root.Twifty = factory(root.Twifty || {});
        }
    
    }(this, function(Twifty) {
        Twifty.foo = 1;
    
        Twifty.bar = function () {};
    
        return Twifty;
    }));
    

    In an environment where modules are not used, the script elements could load the scripts in any order and twifty.base would actually not be needed. (Because factory(root.Twifty || {}).) In an AMD or CommonJS environment, since all modules depend on twifty.base, and since modules in AMD and CommonJS environments are singletons, then all modules are extending the same JavaScript object.