Changing the ordering for single bundles in Asp.Net 4

The new bundling support for Asp.Net MVC and WebForms is superb, and as it’s all built into the new framework, there’s no real reason not to use it if you’re not doing anything of this kind already. In a nutshell, it allows you to package up all your related scripts and css files and serve them us as one request, and even minify them in the process. You can even get it to transform LESS files and all sorts of other cool stuff.

And that’s what I like about it; it’s so configurable, and it even does some smart things for you, like automatically favouring minified javascript files over non-minified (based on popular filename convention) when running with optimisations turned on. It will even put known framework javascript files first in the bundle automatically, such as jQuery or Prototype scripts, to make sure they run before your own code which uses their types gets executed.

But this last one can be stick, like in the case I had today. I was using the popular Plupload javascript file uploader, which requires a number of Javascript files to be included on the page. It also has a jQuery-UI extension library, which must be executed after the primary Plupload library, otherwise it complains about missing types and methods. So I created a bundle to handle all this stuff, which looks like this:

string pluploadBase = "/scripts/jquery/plupload/1.5.4/";  
var pluploadBundle = new ScriptBundle("~/bundles/js/plupload").Include(pluploadBase + "plupload.full.js",  
  pluploadBase + "plupload.browserplus.js", 
  pluploadBase + "plupload.flash.js", 
  pluploadBase + "plupload.gears.js", 
  pluploadBase + "plupload.html4.js", 
  pluploadBase + "plupload.html5.js", 
  pluploadBase + "plupload.silverlight.js", 
  pluploadBase + "jquery.ui.plupload/jquery.ui.plupload.js");

bundles.Add(pluploadBundle);  

This looks fine, except what will actually happen is the jQuery-UI library will be rendered first when the bundle is actually used on the page. This is how the bundle is written out without optimisations turned on, when the bundle is configured exactly as above:

Luckily, in this case I want to just write out the scripts as they appear in my bundle without Asp.Net doing anything fancy to it. I can write a custom BundleOrderer by implementing the IBundleOrderer interface:

class PassthruBundleOrderer: IBundleOrderer {  
 public IEnumerable < BundleFile > OrderFiles(BundleContext context, IEnumerable < BundleFile > files) {
  return files;
 }
}

Simple: it just returns the same file list back to the caller without doing any ordering on it whatsoever. We can now apply this orderer to this one specific bundle that needs it, by setting the Orderer property, and our code becomes:

string pluploadBase = "/scripts/jquery/plupload/1.5.4/";  
var pluploadBundle = new ScriptBundle("~/bundles/js/plupload").Include( pluploadBase + "plupload.full.js",  
  pluploadBase + "plupload.browserplus.js", 
  pluploadBase + "plupload.flash.js", 
  pluploadBase + "plupload.gears.js", 
  pluploadBase + "plupload.html4.js", 
  pluploadBase + "plupload.html5.js", 
  pluploadBase + "plupload.silverlight.js", 
  pluploadBase + "jquery.ui.plupload/jquery.ui.plupload.js"); 

pluploadBundle.Orderer = new PassthruBundleOrderer(); 

bundles.Add(pluploadBundle);  

And the order is now correct in the browser:

You can of course apply any ordering you like here, but at least it’s one way to break convention for one specific instance, should you need it!