;

Minifying javascript files on the fly

Posted : Tuesday, 08 September 2009 08:21:31

Recently I've been writing an online mapping project using Bing maps for Enterprise and jQuery hosted within an ASP.NET web application. The script files contain a lot of lines of code script, a lot of comments and LOADS of white space - consequently they are getting fairly large in size! For a while now I've been meaning to write a custom handler for javascript files to minify them on the fly. Not only will this result in smaller files and therefore quicker download times but an added benefit is that it will be far harder for potential competitors to get unrestricted access to your helpfully commented, well structured and hard written code! I figured this would be a good subject for my first post!

So consider the following basic javascript example which takes a supplied array of tags and displays them randomly inside a containing div:

The aim of this post is to explain how to compress the javascript files that do this. As you can see if you view the source, the javascript contains a large number of both comments and whitespace.

The first step is to create the logic to actually minify the files. Although you could implement this yourself, there are many freely available algorythms that do this. In this case I used Douglas Crockfords JavaScript Minifier which is available in a variety of languages. The c# implementation contains a method called Minify( string src, string dst ) where src is the source filepath string and dst the destination filepath, as you might guess the contents of source file are minified and then written to destination file.

As this wasn't quite what I wanted (eg I want to output the minified string to the browser rather than a file) I altered the minifying code a little. First I altered the stream types used by the minifying code from StreamReader and StreamWriter to TextReader and TextWriter respectively.

    1 namespace JavascriptMinifier

    2 {

    3     class JSMinifier

    4     {

    5         const int EOF = -1;

    6 

    7         StreamReader sr;

    8         StreamWriter sw;

becomes

    1 namespace JavascriptMinifier

    2 {

    3     class JSMinifier

    4     {

    5         const int EOF = -1;

    6 

    7         TextReader sr;

    8         TextWriter sw;

Doing this enabled me to use other implementations of TextReader/TextWriter, specifically StringWriter, without having to change the actual minifiying logic. I then added an overload of the minify method to take a stringbuilder as the second parameter.

   60         public void Minify(string src, StringBuilder dst)

   61         {

   62             using (sr = new StreamReader(src))

   63             {

   64                 using (sw = new StringWriter(dst))

   65                 {

   66                     jsmin();

   67                 }

   68             }

   69         }

So once the minifying code is playing ball, the next step is to create the handler that will actually process the request for the javascript file and return the minified content. To do this you will need to add an entry to the httpHandlers section of webconfig that will route any appropriate requests to our handler, this is acheived as follows:

<add verb="*" path="*.js.ashx" type="JavascriptMinifier.FileResolver"/>

Incidentally you can use any extension you like but because when ASP.NET is installed it automatically configures IIS to forward any requests with the .ashx to ASP.NET, I am going use .js.ashx in this example.

The second part of this step is to write the code that is going to handle the request so add a new class that implements the IHttpHandler interface.

    1 namespace JavascriptMinifier

    2 {

    3     public class FileResolver : IHttpHandler

    4     {

The method of interest here is ProcessRequest(HttpContext context), from the context object we can obtain the filepath of the file that is going to be minified.

   18         void IHttpHandler.ProcessRequest(HttpContext context)

   19         {

   20             //get the path to the file

   21             string absFilePath = context.Request.PhysicalPath.Replace(".ashx", "");

After obtaining the filepath I perform a couple of preliminary checks which are not shown here but are included in the source code available for download at the end of this post.
Create a new StringBuilder variable and pass the filepath and this StringBuilder to the overloaded Minify() method.

   35             //jsMinified is StringBuilder to which minified content is written

   36             StringBuilder jsMinified = new StringBuilder();

   37 

   38             //create an instance of the minifier

   39             JSMinifier minifier = new JSMinifier();

   40 

   41             //then invoke method

   42             minifier.Minify(absFilePath, jsMinified);

The last piece of the puzzle is to write the minified string to browser using the context.Response.Write() method:

   44             //finally write out contents to the browser

   45             context.Response.Cache.SetLastModified(DateTime.Now);

   46             context.Response.ContentType = contentType;

   47             context.Response.Write(jsMinified.ToString());

   48             context.Response.End();

Now that this mechanism is in place, all that remains necessary to minify any javascript files is to append ".ashx" to the end of the src attribute of any script tags in your pages.

<script type="text/javascript" language="javascript" src="scripts/SomeReallyCleverScript.js"></script>

becomes

<script type="text/javascript" language="javascript" src="scripts/SomeReallyCleverScript.js.ashx"></script>

You can view result of minifying the script mentioned above here

This is just a basic example to exemplify the process, in production it would be advisable to add the minified script content to the server Cache to prevent excess processing. Also, if your motivation for doing this is to protect your intellectual property, you should make sure that you prevent access to the raw .js file. This technique can be used for processing other file types such as .css files to allow the use of relative paths with the '~' character. I may provide an example of this in future posts. If you wish to use this technique within an MVC project you need to add a new "ignore route" to the RouteCollection - details of what to do can be found at Phil Haack's blog.

Please add any comments and feel free to ask any questions or suggest refinements

  • (This will not appear on the site)