Minify JavaScript and Attach Version Number using VS2010
Asked Answered
S

3

13

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?

Strengthen answered 7/10, 2011 at 13:46 Comment(8)
You can add a query parameter for versioning: scripts/myfile.min.js?v=1.0 It'd be best to interpolate the src string with the version number.Dianoetic
That's not a bad approach. How would you recommend updating ?v=1.0 on each deployment without having to do it manually?Strengthen
Use semantic versioning. Whenever you have a major, minor, or patch update, increment the corresponding version number in the program itself.Georgeannageorgeanne
My team does versioning manually through app.config. I don't agree with that approach 100%, since you also have AssemblyInfo and you can get version info. But, it's apparently easier for everyone to remember to update app.config than AssemblyInfo.Dianoetic
Well the issue I have with semantic versioning and the app.config approach is that it is one more thing someone is likely to forget. The idea behind one-click deployment, from my understanding, is that it shouldn't require someone to know about all the "tricks" to deploy the app correctly.Strengthen
Just add the current time somewhere after the question mark.Mariann
The current time is generated each time the page is executed. That would mean retrieving the static content each time a user loads the page. Not when the content has changed.Strengthen
If you increment your version number every time your automated build runs (you do have automated builds, I assume :) ), and make sure that the Javascript URLs are constructed based on the version of the app, your versioning issue should be solved.Nutter
N
4

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.

Nutter answered 7/10, 2011 at 18:10 Comment(0)
K
6

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.

Knight answered 19/2, 2012 at 5:56 Comment(0)
S
4

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?

Strengthen answered 7/10, 2011 at 15:19 Comment(0)
N
4

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.

Nutter answered 7/10, 2011 at 18:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.