Minify JavaScript and Attach Version Number using VS2010

3.9k views Asked by At

I have a few goals I'd like to achieve but I'm unsure of how to get there:

  1. Create a single-click deployment for my web project that includes a minified javascript file
  2. Version my minified JavaScript file to prevent browser caching of static content whenever a new build is pushed
  3. Reference the versioned JavaScript file on RELEASE builds, and the non-minified version of the JavaScript files with DEBUG builds

From this article:

http://encosia.com/automatically-minify-and-combine-javascript-in-visual-studio/

I've added JSMin to minify my files using a command like this:

"$(SolutionDir)Tools\jsmin.exe" < "$(ProjectDir)Scripts\myfile.js" > "$(ProjectDir)Scripts\myfile.min.js"

I'll also be adding this to my web pages for preserving non-minified files during debug mode:

    <% if (HttpContext.Current.IsDebuggingEnabled) { %>
             <script type="text/javascript" src="scripts\myfile.js"></script>
    <% } else { %>
             <script type="text/javascript" src="scripts/myfile.min.js"></script>
    <% } %>

So really I'm left with trying to figure out how to prevent myfile.min.js being seen as static content by a web browser when it is updated. If my goal wasn't single-click deployment I could just add a version number manually, but that doesn't seem like a solid approach. Thoughts?

3

There are 3 answers

0
FMM On BEST ANSWER

Rather than littering your source code with if/else all over the place, you might consider implementing an IHttpFilter that does the minification on-the-fly. Something like what this article does should work nicely.

Your javascript should be cacheable, and the overhead of minification on the webserver will be minimal. Implementing it as an IHttpFilter also means you don't have to add routes or change existing URLs, and you can add the IHttpFilter conditionally only in your release configuration so you can still debug your javascript during development.

As for versioning, an HTML helper (here's a link with some more info) would solve the problem nicely by tacking on the version number as a query string parameter. You could even go so far as to have your debug version of the IHttpFilter throw an exception if the version parameter doesn't exist, so you have something to remind you to use the HTTP helper whenever you add a new javascript file.

0
Visualize On

This answer is a little late, but I think it's a fairly solid approach to making sure the caching behavior is in sync with the file, based on a checksum of the file itself.

<script type="text/javascript" src="/Scripts/Scripts.min.js?v=<%= Utils.GetFileHash("/Scripts/Scripts.min.js") %>"></script>

And in your Utils class, you have the method that generates the checksum of the file

public static string GetFileHash(string path)
    {
        string hash = (string)HttpContext.Current.Cache["_hash_" + path];

        if (hash == null)
        {
            // Get the physical path of the file
            string file = HttpContext.Current.Server.MapPath(path);

            // Code for MD5 hashing omitted for brevity
            hash = Utils.GetMD5(file); 

            // Insert the hash into the Cache, with a dependency on the underlying file
            HttpContext.Current.Cache.Insert("_hash_" + path, hash, new System.Web.Caching.CacheDependency(file));
        }

        return hash;
    }

This caches the hash of the file, so it only needs to calculate it whenever the file changes.

Also, the CacheDependency ensures that if you were to change your .js file, it would make sure the hash gets regenerated.

Hope this helps, it's what I'm using in one of my production websites.

0
Blake Blackwell On

I think I have a relatively suitable answer, but feedback would be much appreciated:

  1. Automatically update version number in Assembly Info.
  2. Reflect the Assembly Version Number when including the JavaScript file. Since I'm using MVC I had to use the advice from this SO post and so something like this:

script type="text/javascript" src='@Url.Content("~/Scripts/myfile.min.js?v=" + typeof(MyProject.Web.Controllers.MyController).Assembly.GetName().Version.ToString())'

Thoughts on this approach?