requireJS optional dependency

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?

Answers:

Answer

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.

Answer

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.

Answer

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

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.