Skip to content

Instantly share code, notes, and snippets.

@millermedeiros
Forked from ryanflorence/universal-module.js
Created September 29, 2011 19:21
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save millermedeiros/1251668 to your computer and use it in GitHub Desktop.
Save millermedeiros/1251668 to your computer and use it in GitHub Desktop.
Universal JavaScript Module, supports AMD (RequireJS), Node.js, and the browser.
(function(def){
def('myModule', ['someDependency', 'somethingElse'], function(someDependency, somethingElse){
//return the module's API
return {};
});
}(
// wrapper to run code everywhere
typeof define === 'function' && define.amd?
//AMD
function(name, deps, factory){
define(deps, factory); //registering as an unnamed module, more flexible and match CommonJS
} :
(typeof require === 'function' && typeof module !== 'undefined' && module.exports ?
//CommonJS
function(deps, factory){
module.exports = factory.apply(this, deps.map(require));
} :
//Browser (regular script tag)
function(name, deps, factory){
var d, i = 0, global = this, old = global[name], mod;
while(d = deps[i]){
deps[i++] = this[d];
}
global[name] = mod = factory.apply(global, deps);
mod.noConflict = function(){
global[name] = old;
return mod;
};
}
)
));
// AMD
require(['myModule'], function (myModule){
// use myModule here
});
// Node.js
var myModule = require('myModule');
// Global
myModule
// if myModule is already defined, `noConflict` gives it back
var myNonConflictingModule = myModule.noConflict();
@millermedeiros
Copy link
Author

if the dependencies are on the "same level" (not on a nested path) it is easy to map names to globals, if not it would need some code to convert the path names into global dependencies, a naive example:

while(d = deps[i]){
    deps[i++] = eval( d.replace(/\//g, '.') );  //eval is evil!  :P
}

the same thing would happen to register a global module that is on a nested path, like:

define('myLib/foo/bar/myModule', [], function(){...});

If the dependency is on a relative path it would be even harder..

If you really need the code to run on all environments and it has dependencies I suggest doing the dependency management for globals manually, so you can control the name of the modules and how they should map, if a module depends on jQuery for instance the AMD convention is to use jquery as the dependency name, while the global is called jQuery...

don't use globals anymore, embrace AMD and be happy :D

@millermedeiros
Copy link
Author

I've been using a simplified version of this pattern: https://github.com/millermedeiros/crossroads.js/blob/master/dev/src/wrapper.js - specially since I don't think that noConflict() is that important (unless you use a very common name like $ - which is a very bad idea BTW..) and also because I don't "trust" the automatic dependency conversion for globals..

@unscriptable
Copy link

line 10 should be typeof define === 'undefined' no? Actually, typeof define == 'function' && define.amd is the only way to truly detect AMD. require is not standardized (nor should it be, imho).

I'm with you: "embrace AMD and be happy", but ppl can embrace CommonJS and be happy, too. :)

@millermedeiros
Copy link
Author

line 10 should be typeof define === 'undefined' && typeof require === 'undefined' (AMD have define and CommonJS have require).. in fact the logic should be the opposite check for define first (and use AMD), than check for require and module.exports than fallback to browser..

the code above makes a lot of assumptions like checking for require when it is only using define for AMD, check for exports when it uses module.exports... I was only worried about making it work with RequireJS/Node.js and being concise.. even checking for module.exports we can't be sure that it is really what we expect it to be, define.amd is a "safe" check, will start using it from now on..

I only recommend using a wrapper like this if it is a library that you are going to distribute and have no idea how people may want to use it, I like this simple wrapper way better:

(function(def){
def(['someDependency', 'foo/bar/somethingElse'], function(someDependency, somethingElse){
    //return the module's API
    return {};
});
}(
   typeof define === 'function' && define.amd?
    //AMD
    function(name, deps, factory){
        define(deps, factory);
    } :
    //CommonJS
    function(deps, factory){
        module.exports = factory.apply(this, deps.map(require));
    }
));

who uses globals anyways? :P

@unscriptable
Copy link

I like how your boilerplate is on the bottom and the module definition at the top.

imho even define && define.amd is safe enough. Doesn't hurt to do the additional check though.

@millermedeiros
Copy link
Author

in case someone reaches this gist, I just published a new post explaining why AMD rocks: http://blog.millermedeiros.com/2011/09/amd-is-better-for-the-web-than-commonjs-modules/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment