Thursday, February 25, 2010

Event Applied: When to minify js and css files in your grails projects

High Performance Web Sites is a great book discussing how to accelerate your web sites. The rule 10 in this book is "Minify JavaScript", which I think can be applied in my grails projects easily.
There are some tools can be used to minify your js and css files, such as JSMIN, Dojo compressor, Edwards' Packer and YUI Compressor. Since I was using YUI these days, I decide to choose YUI Compressor to do this job.
The first solution comes into my mind is to create a Grails Script to process js and css files by "grails create-script". On second thought, I think this is not an appropriate one:
  • If it changes the name of js or css files, you have to change the corresponding files name in your frontend source code. This is annoying and error-prone.
  • If it doesn't change the files' name, it will modify the content of js and css source files. This can also make you unhappy: facing a minified js and css files, you can even hardly change the name of a variable. It is a big problem!
So the solution what I need is: a) which can minify js and css files, b) which can not change the name of files and the content of source code. By controlling the grails application build process, I can achieve my goal. Now, I can hear what you said: Event. That's it! We just need to create an event handler to do this job, and the event we should focus on is : CreateWarStart,which will be fired before grails war your grails application.
Since we know how to do it, let's do it:
  1. Downloading YUI Compressor,which usage is simple:"java -jar yuicompressor-x.x.x.jar inputfile -o outputfile".
  2. Unzipping it and copying YUI Compressor binary to lib directory in your grails project.
  3. Creating a "_Event.groovy" in your grails project's script directory:
       eventCreateWarStart = { warLocation, stagingDir ->
    stagingDir.eachFileRecurse{
    if(it.name.endsWith('.js')||it.name.endsWith('.css')){
    def f= it.absolutePath

    println "Minizing ${f}......................"
    ant.java(jar:"${basedir}/lib/yuicompressor-2.4.2.jar",
    fork: true){
    arg(value: "${f}")
    arg(value: "-o")
    arg(value: "${f}")
    }
    println "Minizing ${f}.................done!"
    }
    }
    }
Only 3 steps, you can minify all the js and css files in your grails projects. Typing "grails war" to test it. Of course, it is not perfect: it always does minification and doesn't care if the js or css files have been minified. If there are many minified files in your projects, which could come from the js lib(such as yui/dojo/jquery) you include, it will waste your cpu time. The way to fix it is simple: you can define an excluding directory list, all directories in it will not be processed.
Another interesting thing is: if there are some errors in your javascript source, YUI Compressor won't minify it(this won't break the build process). So you get an additional checking javascript function ;). Hope you enjoy it!