I'm adding AMD support to a javascript library I develop.
This library may use jquery but it will still work if jquery isn't loaded.
When defining the module dependency there's a way to set a dependency as 'optional' so that if that library is missing the module will still work?
I've had exactly the same problem recently, and here's how I fixed it. I defined a RequireJS plugin called optional
which ignores modules that fail to load by explicitly defining them as an empty object (but I suppose you could also define it as null or anything else if you wanted).
Here is the code (tested with RequireJS 2.1.15):
define("optional", [], {
load : function (moduleName, parentRequire, onload, config){
var onLoadSuccess = function(moduleInstance){
// Module successfully loaded, call the onload callback so that
// requirejs can work its internal magic.
onload(moduleInstance);
}
var onLoadFailure = function(err){
// optional module failed to load.
var failedId = err.requireModules && err.requireModules[0];
console.warn("Could not load optional module: " + failedId);
// Undefine the module to cleanup internal stuff in requireJS
requirejs.undef(failedId);
// Now define the module instance as a simple empty object
// (NOTE: you can return any other value you want here)
define(failedId, [], function(){return {};});
// Now require the module make sure that requireJS thinks
// that is it loaded. Since we've just defined it, requirejs
// will not attempt to download any more script files and
// will just call the onLoadSuccess handler immediately
parentRequire([failedId], onLoadSuccess);
}
parentRequire([moduleName], onLoadSuccess, onLoadFailure);
}
});
You can then require a module optionally using simply
require(['optional!jquery'], function(jquery){...});
knowing that if the jquery module could not be loaded, the parameter passed to your callback function will be an empty object.
You cannot really set it optional, but you can catch the error and unload the module using undef
:
require(['jquery'], function ($) {
//Do something with $ here
}, function (err) {
//The errback, error callback
//The error has a list of modules that failed
var failedId = err.requireModules && err.requireModules[0];
if (failedId === 'jquery') {
//undef is function only on the global requirejs object.
//Use it to clear internal knowledge of jQuery. Any modules
//that were dependent on jQuery and in the middle of loading
//will not be loaded yet, they will wait until a valid jQuery
//does load.
requirejs.undef(failedId);
...
}
});
Full example here.
A plugin is not needed for this.
This can be done without plugins for RequireJS. In addition, you can do this for UMD modules as well.
Using jQuery only when it's already loaded
It's actually pretty simple, using require.defined
, which lets you test whether a module has already been loaded or not. If so, you require
jQuery and use it, otherwise you just skip the optional part:
define(['require'], function(require){
if (require.defined('jquery') {
var $ = require('jquery');
$.fn.something = function(){};
}
});
Notice how we add 'require'
as a dependency, so we get a local require function that has the defined
method on it.
Also note that this code will only find jQuery if it has been loaded prior to this module. If some module loads jQuery after this module has already loaded then it will not pickup jQuery afterwards.
This will not attempt to load jQuery and hence will not cause an error message in the logs.
Bonus: UMD support
If you want your library to support AMD loaders (RequireJS), CommonJS (Node) and regular script tags, and have an optional dependency on jQuery, here is what you can do:
(function(u, m, d) {
if ((typeof define == 'object') && (define.amd)) {
// handle AMD loaders such as RequireJS
define(m, ['require'], d);
}
else if (typeof exports === 'object') {
// handle CommonJS here... Does not really make sense for jQuery but
// generally, you can check whether a dependency is already loaded
// just like in RequireJS:
var $ = null;
try {
require.resolve('jquery'));
// the dependency is already loaded, so we can safely require it
$ = require('jquery');
} catch(noJquery) {}
module.exports = d(null, $);
}
else {
// regular script tags. $ will be available globally if it's loaded
var $ = typeof jQuery == 'function' ? jQuery : null;
u[m] = d(null, $);
}
})(this, 'mymodule', function(require, $) {
// if `$` is set, we have jQuery... if not, but `require` is set, we may
// still get it here for AMD loaders
if (!$ && require && require.defined && require.defined('jquery')) {
$ = require('jquery');
}
// At this point, `$` either points to jQuery, or is null.
});
Further reading
Node.js - check if module is installed without actually requiring it
https://github.com/jrburke/requirejs/issues/856