How to Minify JavaScript with C#, a Google PageSpeed suggestion

Google sais “compacting JavaScript code can save many bytes of data and speed up downloading, parsing, and execution time” and that is obviously true (you can read about it here. And although there are many tools to minify javascript, like JSMin and YUI Compressor, it’s quite time consuming (and boring) to have to run these tools for each change your Web Designer decides to do for every JS file in your project. Except that, non of these tools compacts JS in one file to minimize browser requests.

The solution I came up goes like this (in my project this code runs only once in every application boot by using HttpContext.Current.Cache)

Step 1
Add a new Web Form to your project and name it js.aspx


Step 2
Clear all html and controls from js.aspx, leaving only this:

   <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="js.aspx.cs" Inherits="js" />

Step 3
Add the following to OnInit

    base.OnInit( e );            
    
    Assembly assembly = Assembly.GetExecutingAssembly();
    var fileInfo = new FileInfo( assembly.Location );
    DateTime lastModified = fileInfo.LastWriteTime;    Response.Clear();

    Response.AddHeader( "Content-Type", "application/javascript; charset=utf-8" );
    Response.AddHeader( "Cache-Control", "public" );
    Response.AddHeader( "ETag", "\"" + lastModified.ToString( "s" ) + "\"" );
    Response.AddHeader( "Last-Modified", lastModified.ToString( "ddd, dd MMM yyyy hh:mm:ss" ) + " GMT" );

Step 4
Add the following method

private void WriteJsNewFile( string f ) {
    var s = "";
    s = File.ReadAllText( f );
    s = common.Libraries.Regexp.Replace( s, @"//(.*?)$", " " );
    s = common.Libraries.Regexp.Replace( s, @"\s+", " " );
    s = common.Libraries.Regexp.Replace( s, @"/\*(.*?)\*/", " " );
    s = common.Libraries.Regexp.Replace( s, @"\s*([{}()\[\]=;:,+!?|<>*]){1}\s*", "$1" );
    Response.Write( s + ";"  );
}

Step 5
And last, add on Page_Load the following code

    var ll = Directory.GetFiles( "PATH_TO_YOUR_JS_FILES", "*.js", SearchOption.TopDirectoryOnly );
    foreach(var js in ll){
        WriteJsNewFile( js );
    }

And that’s it! Just add the call to your MasterPage or wherever your project requires like this.

<script type="text/javascript" src="/js/js.aspx?201311121440550000"></script>

* The DateTime after js.aspx? is dynamically added based on build time and is just a way of forcing the browser to make the call and don’t use a local version of the file.

I use the exact same way to combined CSS files also.
And just for the reference, here is the code above combined:

using System;
using System.Web;
using System.IO;
using System.Reflection;

namespace YOUR_NAMESPACE {
    public partial class js : Page{
        protected void Page_Load( object sender, EventArgs e ) {
            var ll = Directory.GetFiles( "PATH_TO_YOUR_JS_FILES", "*.js", SearchOption.TopDirectoryOnly );
            foreach (var js in ll) {
                WriteJsNewFile( js );
            }
            Response.End();
        }

        private void WriteJsNewFile( string f ) {
            var s = File.ReadAllText( f );
            s = common.Libraries.Regexp.Replace( s, @"//(.*?)$", " " );
            s = common.Libraries.Regexp.Replace( s, @"\s+", " " );
            s = common.Libraries.Regexp.Replace( s, @"(\r\n)+", " " );
            s = common.Libraries.Regexp.Replace( s, @"/\*(.*?)\*/", " " );
            Response.Write( s + ";"  );
        }

        protected override void OnInit( EventArgs e ) {
            base.OnInit( e );    

            Assembly assembly = Assembly.GetExecutingAssembly();
            var fileInfo = new FileInfo( assembly.Location );
            DateTime lastModified = fileInfo.LastWriteTime;

            Response.Clear();
            Response.AddHeader( "Content-Type", "application/javascript; charset=utf-8" );
            Response.AddHeader( "Cache-Control", "public" );
            Response.AddHeader( "ETag", "\"" + lastModified.ToString( "s" ) + "\"" );
            Response.AddHeader( "Last-Modified", lastModified.ToString( "ddd, dd MMM yyyy hh:mm:ss" ) + " GMT" );
        }
    }
}
Advertisements

2 thoughts on “How to Minify JavaScript with C#, a Google PageSpeed suggestion

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s