I need to re-structure an existing AMD module to make it both usable by pages with/without RequireJS presented. How should I do it, and is there any example code? Preferably, it would be a way without polluting the global namespace, though not a strict requirement.
I need to re-structure an existing AMD module to make it both usable by pages with/without RequireJS presented. How should I do it, and is there any example code? Preferably, it would be a way without polluting the global namespace, though not a strict requirement.
Share Improve this question asked Sep 24, 2012 at 3:31 gskleegsklee 4,9444 gold badges40 silver badges56 bronze badges 2- Why would you do this? How much of a hassle is including RequireJS or building 2 versions of your script, one for RequireJS and another standalone? – Joseph Commented Sep 24, 2012 at 3:52
- github.com/gfranko/amdclean – user985399 Commented Aug 12, 2019 at 0:13
6 Answers
Reset to default 8This is not a bad idea at all, quite often JS libs are required to support a AMD/non AMD environment. Here is one variation of the solution:
!function (name, definition) {
if (typeof module != 'undefined') module.exports = definition()
else if (typeof define == 'function' && define.amd) define(name, definition)
else this[name] = definition()
}('reqwest', function () {
// Module here
});
The only downside is you can't request other dependencies, so this would only be useful in stand alone libraries, like the ones below
- Dustin Diaz's Reqwest
- Mustache
I recently wrote a gist regarding this topic so I have copied the relevant bits for you below; however, feel free to check out the original Gist for more info.
The following boilerplate allows you to write your module once and have it work in a CJS/NodeJs, AMD, or Browser Global environment.
Best Used When...
- You are migrating from namespaced (err, globals) code to either AMD or CJS modules or both.
- You can't yet factor out browser globals but also need to test your code via NodeJS (e.g. Mocha)
Benefits & Trade-offs
- A single module format allowing you to target AMD, CJS/NodeJS, and legacy browser globals like
window.*
. - Allows multiple dependencies to be defined.
- Run unit tests via CLI/NodeJS runner (e.g. Mocha).
- Less pain while incrementally migrating to CJS/NodeJS or AMD modules.
- You give up the Java-like namespaces (e.g. com.company.package.module) -- meh, they are a mess anyway.
- This (UMD) isn't a standard; to be fair, neither is AMD (it's a convention with a well-defined spec).
- Non-trivial amount of boilerplate (and Ugly).
Example
/**
* Creates a an "AppWidget" module that imports a "SingleDependency" constructor and exposes an "AppWidget" constructor.
*
* Allows you to access AppWidget as a CJS/AMD module (i.e. NodeJS or RequireJs):
*
* @example
* var AppWidget = require('app-widget')
*
* Allows you to access AppWidget as a legacy browser global:
*
* @example
* var AppWidget = window.AppWidget
*/
!(function (name, context, definition) {
if (typeof exports === 'object') { module.exports = definition(require); } else if (typeof define === 'function' && define.amd) { define(definition); } else { context[name] = definition(); }
}).call(this, 'AppWidget', this, function (require) {
'use strict'
// imports
var SingleDependency = (typeof require === 'function') ? require('./single-dependency') : window.SingleDependency;
var singleDependency = new SingleDependency();
function AppWidget() {}
AppWidget.prototype.start = function () {};
// exports
return AppWidget;
});
Check out UMD. You should find a pattern suitable for your purposes there. The templates are somewhat ugly but work.
I think that this is quite a bad idea.
Your possible steps that you have to take to make sure that it works:
- ensure that all module's dependencies are loaded on that page (jQuery, Backbone and others)
- include your module(s) on the page in the order you know they should be executed
- ensure that any module that is a dependency of another module creates a global variable with the same name the "importing" module expects and refers to in its code
- ensure that your module refers to the dependencies (including other modules) by the same name
- override/create a global method
define
that executes your module's factory function
And that is only a part of what you'd have to do. What about 3rd parties that you need to be AMD-compliant for your RequireJS pages (or at least be in its shim
configuration), but also should be global for your non-RequireJS pages?
Simply put, IMO, it'd be easier to rework existing code into AMD version, that to make your modules non-AMD
Based on Simon Smiths answer I dealed with module dependencies:
(function (root, factory) {
if (typeof exports === "object") {
module.exports = factory();
} else if (typeof define === "function" && define.amd) {
define(['jquery', 'ol'], factory);
} else {
root.module_name = factory();
}
}(this, function (_$, _ol) {
return new function () {
// supposed that window.ol and window.$ are defined
var ol = goog.isDef(_ol) ? _ol : window.ol;
var $ = goog.isDef(_$) ? _$ : window.$;
}
}));
Where goog.isDef
is Google Closure
function:
goog.isDef = function(val) {
return val !== void 0;
};
Hope it will help someone.
Check out Browserify, which will create single, standalone .js file with all dependencies embedded from your AMD/JS code.
You could then ship 2 versions of your code, one for AMD users and one for "oldschool" js users.