Skip to content

Instantly share code, notes, and snippets.

@kirbysayshi
Created August 24, 2012 22:16
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save kirbysayshi/3456345 to your computer and use it in GitHub Desktop.
Save kirbysayshi/3456345 to your computer and use it in GitHub Desktop.
JS Module Guards

This Is Absolutely Tiring

Every time I start a node module, or a new JS project, I'm confronted with this same question: how do I structure it to allow it to be used in the browser or nodejs, without requiring a build step, and without getting in the way of my development?

While developing, I typically create some type of browser-based environment; Firebug and Web Inspector are still miles ahead of anything else we've got. Ideally I want to be able to add a file via a script tag, and have that be the only requirement.

As @visionmedia points out, this is ridiculous.

This gist is meant to compile all of the various ways I've seen of guarding against/for a variety of module systems. Fork it and add your own, I'll try to bring them into this one.

;(function(window, undefined) {
/** Detect free variable `exports` */
var freeExports = typeof exports == 'object' && exports &&
(typeof global == 'object' && global && global == global.global && (window = global), exports);
/*--------------------------------------------------------------------------*/
// expose Lo-Dash
// some AMD build optimizers, like r.js, check for specific condition patterns like the following:
if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {
// Expose Lo-Dash to the global object even when an AMD loader is present in
// case Lo-Dash was injected by a third-party script and not intended to be
// loaded as a module. The global assignment can be reverted in the Lo-Dash
// module via its `noConflict()` method.
window._ = lodash;
// define as an anonymous module so, through path mapping, it can be
// referenced as the "underscore" module
define(function() {
return lodash;
});
}
// check for `exports` after `define` in case a build optimizer adds an `exports` object
else if (freeExports) {
// in Node.js or RingoJS v0.8.0+
if (typeof module == 'object' && module && module.exports == freeExports) {
(module.exports = lodash)._ = lodash;
}
// in Narwhal or RingoJS v0.7.0-
else {
freeExports._ = lodash;
}
}
else {
// in a browser or Rhino
window._ = lodash;
}
}(this));
// https://github.com/RobertWHurst/LucidJS/blob/master/lucid.js
(function(factory) {
//AMD
if(typeof define === 'function' && define.amd) {
define(factory);
//NODE
} else if(typeof module === 'object' && module.exports) {
module.exports = factory();
//GLOBAL
} else {
window.LucidJS = factory();
}
})(function() {
var api;
//return the api
api = {
"emitter": EventEmitter
};
return api;
function EventEmitter(object) {}
});
// https://github.com/chjj/marked/blob/master/lib/marked.js
;(function() {
var marked = function(){}
if (typeof module !== 'undefined') {
module.exports = marked;
} else {
this.marked = marked;
}
}).call(function() {
return this || (typeof window !== 'undefined' ? window : global);
}());
// This pattern requires an additional index.js to include the above file for nodejs:
module.exports = require('./lib/marked');
;(function(exports) {
/*global define:true,EventEmitter:true*/
'use strict';
// EventEmitter is an external dependency - ed.
function construct(EventEmitter) {
function StateMachine(options) {}
// followed by actual class def - ed.
}
// We need to build the StateMachine class using the construct function
// This construction will occur differently depending on the existence of AMD
if(typeof define === 'function' && define.amd) {
// In this case, AMD is present on the page
// We will assume (if wrong, I'm sorry) that the script is being loaded with it
// So we should load EventEmitter from the components directory via AMD too
var preBuilt;
define(['./components/eventEmitter/EventEmitter.js'], function(EventEmitter) {
// This function will try to use a previous result of the construct function
// This saves rebuilding every time
// If it has not been built yet then it will be
if(!preBuilt) {
preBuilt = construct(EventEmitter);
}
// Now return the built version which may have been loaded from the cache thing
return preBuilt;
});
}
else {
// Without AMD on the page they must load EventEmitter manually
// Which also means StateMachine must be built and exposed manually
// So we use the global EventEmitter instance and drop StateMachine into the global object too
exports.StateMachine = construct(EventEmitter);
// If the dependency is not met the user will probably get an undefined warning
}
}(this));
;(function(vash){
// this pattern was inspired by LucidJS,
// https://github.com/RobertWHurst/LucidJS/blob/master/lucid.js
if(typeof define === 'function' && define['amd']){
define(vash); // AMD
} else if(typeof module === 'object' && module['exports']){
module['exports'] = vash; // NODEJS
} else {
window['vash'] = vash; // BROWSER
}
})(function(exports){
exports.whatever = function(){}
return exports;
}({}));
@jdalton
Copy link

jdalton commented Aug 24, 2012

This is how I do it in Lo-Dash, Benchmark.js, and Platform.js

The pattern works well with AMD optimizers, works in Ringo, Rhino, Narwhal, and Node.js too.

@kirbysayshi
Copy link
Author

Thanks for the examples, I added lodash!

I think what really gets to me is that every file needs something like this. And yes, I realize that a build step can fix it, but having a build step required for development is a significant time expense due to repetition.

I'm working on splitting Vash into two files, the compiler and the runtime, and was hoping for a way to avoid having a huge guard in each, but looks like it's unavoidable.

@jdalton
Copy link

jdalton commented Aug 24, 2012

@kirbysayshi not every files needs this though, only the file containing the chunk you want to expose. The rest could be AMD modules that are loaded in dev and then compiled/optimized for build.

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