Search code examples
mavenrequirejsunderscore.jswebjars

Using UnderscoreJS with RequireJS


I'm trying to use/load UnderscoreJS 1.7.0 with RequireJS 2.1.14-3. At my application startup, UnderscoreJS is well loaded, but it is "undefined". See details below:

main.js

define(function() {
    // Configuration of RequireJS
    requirejs.config({
        enforceDefine : true,

        map : {
            '*': { 
                ...
                'underscore' : 'webjars/underscorejs/1.7.0/underscore'
            },
        },

        // The base URL is just the top-level directory where the files are stored
        baseUrl : './',

        // Kick-start the application by loading these files
        deps : [ 'MyPanel' ],
    });
});

The module using it :

define(['ractive',
    'underscore',
    ...], 
    function(Ractive,
            _,
            ...){
var Foo = Ractive.extend({
    ...

    oninit: function(){
        var anArray = [1, 2, 3]
        _.each(anArray, function(item){
               ...
        })
    }
}

And the result in the browser console : Browser result

The underscoreJS file is loaded by the browser:

Module loaded

This must be a detail, but I managed my Javascript dependencies with mavenand webjars

So why is my _ undefined ?


Solution

  • If you look at the source of Underscore 1.7.0, you see it registers itself like this:

    if (typeof define === 'function' && define.amd) {
        define('underscore', [], function() {
            return _;
        });
    }
    

    Note the first argument to define. This hardcodes the name of the module as 'underscore'.

    The problem is that you are using a map configuration which is not compatible with this hardcoded name. What you do is tell RequireJS "in all modules ("*"), when the module requires a module with the name 'underscore', then please return instead the module with the name 'webjars/underscorejs/1.7.0/underscore'". So when you require 'underscore':

    1. RequireJS looks for the module named 'webjars/underscorejs/1.7.0/underscore' instead.

    2. It uses the default path for such a module name and finds a file at that location. It loads the file and executes it.

    3. However, the file contains a define calls that defines 'underscore', not 'webjars/underscorejs/1.7.0/underscore'. So RequireJS is not able to honor the request.

    Instead of map, you should be using a paths configuration for Underscore. Something like:

    paths : {
        'underscore' : 'webjars/underscorejs/1.7.0/underscore'
    }
    

    This tells RequireJS something like "you'll find the module named 'underscore' at the location 'webjars/underscorejs/1.7.0/underscore'". When you use this, the name of the module requested and the name of the module defined match.