Minifying CSS and JS with Yahoo YUI Compressor

In my previous blog post I explained how to use Ant to concatenate javascript and css files in a way to minimize the number of http requests.
Another recommendation by yahoo for building high performance websites is to minify JS and CSS files. The reasoning is that the smaller the size of the javascript/css, the less time it takes for them to be interpreted.

Yahoo provides a great utility – yahoo yui compressor that minifies both javascript and css.

We can therefore improve our build script and add the following task:

<!-- this compresses all the css and js in the static folder -->
        <target name="js.minify">
                <property
                    name="yui-compressor.jar"
                    location="lib/yuicompressor-2.4.2.jar" />
                <property
                    name="yui-compressor-ant-task.jar"
                    location="lib/yui-compressor-ant-task-0.3.jar" />

                <path id="task.classpath">
                  <pathelement location="${yui-compressor.jar}" />
                  <pathelement location="${yui-compressor-ant-task.jar}" />
                </path>

                <taskdef
                    name="yui-compressor"
                    classname="net.noha.tools.ant.yuicompressor.tasks.YuiCompressorTask">

                  <classpath refid="task.classpath"/>
                </taskdef>

           <yui-compressor warn="false" charset="UTF-8" jsSuffix="-min.js" cssSuffix="-min.css" fromdir="${build.dir}/static" todir="${build.dir}/static">
           <include name="**/*.js" />
           <include name="**/*.css" />
       </yui-compressor>


                  <move todir="${build.dir}/static" includeemptydirs="false">
                    <fileset dir="${build.dir}/static">
                                <include name="**/*-min.css"/>
                    </fileset>
                    <mapper type="glob" from="*-min.css" to="*.css"/>
                  </move>
                  <move todir="${build.dir}/static" includeemptydirs="false">
                    <fileset dir="${build.dir}/static">
                                <include name="**/*-min.js"/>
                    </fileset>
                    <mapper type="glob" from="*-min.js" to="*.js"/>
                  </move>

        </target>

First we copy all the javascript and css files to the build/static folder and then we use yahoo compressor to minify all the files in this directory.


Click here to download an example.

Minimizing number of http requests by concatenating all the CSS and Javascript files

If you have installed yslow in your browser you will know all about yahoo’s high performance rules for a website. One of the most important rules is to minimize the number of http requests. There are several ways of doing that. I will cover one of them in this post – minimizing the number of http requests by concatenating all the javascript files and css files into two big files.

Apache Ant makes the task of concatenating files together extremely easy by using concat.

<concat destfile="${build.dir}/concatenated.js">
        <filelist dir="${src.dir}/js"
           files="one.js, two.js"/>
</concat>

This is a simple solution but I chose not to use it for the following reasons:

1. I don’t always want every single javascript/css to be included for all pages.
You could create multiple concatenated files with different combinations but that would be too messy.

2. We are creating a dependency between the jsps and the ant build script. Do you really want to do that?

3. Javascript, css can be notoriously difficult to debug, so for development purposes
it is useful not to do any concatenation.

Ant has a neat feature that allows to do string replacement on any file by using a filterset.

<copy file="${build.dir}/version.txt" toFile="${dist.dir}/version.txt">
 <filterset>
   <filter token="DATE" value="whatever"/>
 </filterset>
</copy>

You can specify the string that you want to be replaced at build time by surrounding it with ampersands. So in the template jsps instead of including javascripts/css in the normal way, I did the following:

@IncludeCSS css/file1.css css/file2.css ...@
...
@IncludeJS js/jquery.js js/thickbox.js js/vmSlider.js js/blockUI.js@

Then I created the following ant task:

<ExtendedFileTransform concatenate="true"  propertiesfile="${build.dir}/static.mapping.properties" >
  <jspfileset refid="jspFileset">
  </jspfileset>
  <staticfileset dir="${static_content.dir}">
 <include name="**/*.js" />
 <include name="**/*.css" />
 <include name="**/*.png" />
 <include name="**/*.jpg" />
 <include name="**/*.jpeg" />
 <include name="**/*.gif" />
 <include name="**/*.PNG" />
 <include name="**/*.JPG" />
 <include name="**/*.GIF" />
  </staticfileset>
</ExtendedFileTransform>

ExtendedFileTransform is a custom task I created, based on Julien Lecombte’s File Transform.
This custom task inspects all the jsps and searches for javascript(IncludeJS) and css includes(IncludeCSS). If concatenated=”true” we concatenate the files specified within the IncludeXXX directory and we replace it with a valid html javascript/css include to the generated files. If concatenated=”false”, i.e. for development, there is no concatenation and the IncludeXXX directory is replaced with an include for each javascript/css file.

The full ant target that you could use for production looks like this:

<target name="-copy.static.files-prod">
        <taskdef name="ExtendedFileTransform" classname="uk.co.simpletech.ant.ExtendedFileTransform" classpath="${build.dir}/classes" />

        <mkdir dir="${build.dir}/staticpre" />

        <!-- we copy all jsp files to a temporary location -->
        <copy todir="${build.dir}/jsppre">
            <fileset dir="jsp" includes="**/*" />
        </copy>

        <fileset dir="${build.dir}/jsppre" id="jspFileset">
            <include name="**/*.jsp" />
        </fileset>
        <!-- we concatenate the files together -->

        <ExtendedFileTransform concatenate="true">
        <jspfileset refid="jspFileset">
            </jspfileset>
            <staticfileset dir="${build.dir}/static">
                <include name="**/*.js" />
                <include name="**/*.css" />
            </staticfileset>
        </ExtendedFileTransform>

       

        <delete dir="${build.dir}/jsppre" />=
    </target>

Here is an example project where you can see the css/js concatenation in action.