Script Bundling doesn’t work for SignalR Release Candidates

With SignalR now available as a Release Candidate from Microsoft, we found that our script bundling no longer worked.  Our BundleConfig contained a statement to register the bundle and include the version-based file.

bundles.Add(new ScriptBundle(“~/bundles/signalr”).Include(
            “~/Scripts/jquery.signalR-{version}.js”));

Once upgrading from 0.5.3 to 1.0.0-rc2 via NuGet, the bundle request returned an empty response.  Downgrading back to 0.5.3 fixed the issue and our bundle request returned as expected.

Hardcoding the entry by replacing “{version}” with “1.0.0-rc2” also resolved the issue, so the {version} parsing was not working for some reason.  Presumably, {version} was supposed to be replaced with the installed version of the product during bundling.  A check of packages.config showed that the version for SignalR was correctly set to “1.0.0-rc2”.

It turns out that {version}, rather than being replaced with the installed version number, is simply a regex matching pattern for version numbers in a format similar to #.#.#.  It does not look for a particular version number, but rather anything that looks like a version number.  In this case, it is getting tripped up on the “-rc2” in the script name.  You can work around this without breaking future (non-Prerelease) upgrades by adding the following to ScriptBundle configuration.

bundles.Add(new ScriptBundle(“~/bundles/signalr”).Include(
            “~/Scripts/jquery.signalR-{version}.js”,
            “~/Scripts/jquery.signalR-1.0.0-rc2.js”));

It’s not the most dynamic solution, but it does allow bundling to immediately work with any upgrades.  The only caveat would be what if upgrading to a different Prerelease version (rc3, for example), an update to the config would be required.  While there is wildcard (*) support, Microsoft advises against using it in most cases, preferring that scripts be hardcoded.  So for now, this is probably the least painful solution.

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.