Friday, January 25, 2013

RequireJS 2.0 dependencies seemingly ignored

After struggling for months with RequireJS 2.0 and why it seemed to ignore our dependencies for ordered script loading, we finally found the issue today and it was a facepalmableteachable moment.  When setting up a module to use something like SignalR, jQuery must be loaded first.  Searches quickly led to StackOverflow posts that suggested configuring dependency shims in RequireJS with something like:

require.config({
  shims: {
    'signalr': ['jquery'],
    'backbone': ['jquery'],
  }
}

In theory, when later requiring 'signalr', RequireJS will see that it needs to load 'jquery' first and all is well:

define(['signalr'], function() { 
  return {
    initialize: function() { $.connection.doSomething; }
  };
});

In practice, what we (and many others, based on the abundance of posts) saw was that SignalR complained about needing jQuery loaded first.  In fact, Developer Tools showed that not only was the request for jQuery not being prioritized before SignalR, but it wasn't being requested at all.

Maybe Dependencies Don't Autoload?

A quick check of the RequireJS API confirms that this should be working.  Some more searches lead to StackOverflow posts where others are inexplicably having similar problems with dependencies.  My first logical guess was that RequireJS doesn't automatically load specified dependencies for you, but rather it just orders them correctly while still requiring you to specify every dependency.  This seemed to defeat the purpose of RequireJS, but what the heck.  I tried adding 'jquery' in to our define:


define(['jquery', 'signalr'], function() { 
  return {
    initialize: function() { $.connection.doSomething; }
  };
});

This seemed to resolve the issue, though we'd still occasionally see transient "jQuery must be loaded first" errors that went away whenever we tried to debug them.  Eventually, we discovered that dependencies were still not working and that Require was not waiting for jQuery to finish loading before trying to load SignalR.   Whenever the browser cache was cleared and the page reloaded, the same "jQuery must be loaded first" error would appear.  However, subsequent refreshes would fetch the jQuery page from the browser cache fast enough that it was finished loaded by the time RequireJS had moved on to SignalR.  At this point, it became obvious that something was fundamentally wrong with how we were using RequireJS.

Face, meet palm

After looking through the RequireJS API more closely, it became painfully clear why our shims were being ignored.  The RequireJS API config option for dependencies is *shim*, not *shims*.  The extra "s" in our config was causing our dependency chains to be completely ignored.  Searching online again, it looks like many of the examples on StackOverflow make the same "shims" typo, thus adding to the confusion.  Removing the "s" allowed RequireJS to suddenly start working as expected.

require.config({
  shim: {

    'signalr': ['jquery'],
    'backbone': ['jquery'],

  }
}

Developer Tools now shows jQuery always being loaded to completion before SignalR's is even requested.  Our define went back to just containing 'signalr', with 'jquery' no longer being necessary to explicitly specify as it is being automatically pulled in.  Modules suddenly make sense again.

So if you see issues where your RequireJS dependencies don't load as expected, make sure you're not making the same mistake we did.  Configure a shim, not shims.  One little character makes all the difference.

No comments: