Using Ant to Consolidate CSS and JavaScript

Given the increasing complexity of the JavaScript and CSS code used to drive websites, it is ever more important to optimize for performance. A great tool to help with site optimizations is YSlow, and one of its recommendations is to consolidate CSS and JavaScript files into the fewest number of files as possible. This is because all browsers have limitations on the number of requests they can make to the server to retrieve files (such as JavaScript, CSS, Images, etc.). The worst browsers can only make 2 simultaneous requests, so fetching data from the server quickly becomes a bottleneck. Todays article will explain how to consolidate CSS and JavaScript files using YUI Compressor and Ant.

YUI compressor is my favorite JavaScript compressor/obfuscater, because it is simple to use and effective at removing white-spaces and shortening long variable names. Apache Ant converts XML into command line statements, to help simplify any build process. Together we can use these technologies to consolidate our JavaScript and CSS files, then compress them into minified versions.

Here is a zip file containing the Ant "build.xml", the latest YUI Compressor, and the core JavaScript and CSS files I use for my projects. A lot of this was inspired by work originally done by Jason Yiin at Mint.com. These files will be used as todays example:

Example 1: Core.zip

./build.xml
./bin/yuicompressor-2.4.2.jar
./assets/css/reset.css
./assets/css/base.css
./assets/js/yahoo-2.6/animation.js
./assets/js/yahoo-2.6/connection.js
./assets/js/yahoo-2.6/dom.js
./assets/js/yahoo-2.6/event.js
./assets/js/yahoo-2.6/json.js
./assets/js/yahoo-2.6/yahoo.js
./assets/js/yahoo-ext/constant.js
./assets/js/yahoo-ext/dom.js
./assets/js/yahoo-ext/event.js
./assets/js/yahoo-ext/form.js
./assets/js/yahoo-ext/formElement.js
./assets/js/yahoo-ext/formSerializer.js
./assets/js/yahoo-ext/lang.js
./assets/js/native-ext/boolean.js
./assets/js/native-ext/date.js
./assets/js/native-ext/number.js
./assets/js/native-ext/object.js
./assets/js/native-ext/regexp.js
./assets/js/native-ext/string.js
./assets/js/mvc/lib/core.js
./assets/js/mvc/util/eventDispatcher.js

The included CSS is only a reset file to normalize browser variants, then a base file to correct type-face and set a few defaults. The JavaScript files are broken into 4 sections: yahoo-X.X, where the current YUI version goes; yahoo-ext, where my extensions to the YUI objects go; native-ext, where my extensions to the native Javascript objects go; and mvc, where the base JavaScript MVC infrastructure goes (this is still under development). The build.xml will be in the root directory and the current version of YUICompressor (2.4.2 at the time of this article) will be put into the bin directory.

If you already have Ant installed (if not, follow instructions here), then simply unpack this file anywhere and run ant. The Ant task creates a build directory, where it will put a consolidated and minified version of each folder, and a consolidated and minified version of all files. Subsequent builds will check to see if the files have changed before attempting to reconsolidate them.

Example 2: Setting Up Properties

<!-- the location of the YUI compressor -->
<property name="yuicompressor" value="${basedir}/bin/yuicompressor-2.4.2.jar"/>
<!-- the location of the files -->
<property name="jsdir" value="${basedir}/assets/js/"/>
<property name="cssdir" value="${basedir}/assets/css/"/>
<property name="builddir" value="${basedir}/build/"/>

The first part of "build.xml" sets up a few parameters that can be used as shortcuts throughout the file. All properties can be references inside of quotes using the following syntax:

Example 3: Referencing Properties

<antTask taskAttribute="${propertyName}" />>

The basedir (defined in the project definition) is already referenced this way in Example 2. The next step in building an Ant XML file is to define targets. Targets will be executable from the command line by typing "ant targetName1 targetName2 targetNameX…" or they can be referenced from other targets in the file. The included "build.xml" has 4 types of targets: clean - removes all the files possibly generated by "build.xml", setup - creates the build directory, check - tests if the currently generated files are up-to-date with source files, consolidate - consolidates and compresses files. We wont cover the clean and setup targets, because you can probably understand them by looking at them in "build.xml". Using the "yahoo-x.x" target as an example, we will look at the update and consolidate targets:

Example 4: Update Target

<target name="reconsolidate.yahoo-2.6.js.check">
	<condition property="yahoo-2.6.js.uptodate">
		<uptodate targetfile="${builddir}js/yahoo-2.6.js">
			<srcfiles dir="${jsdir}yahoo-2.6" includes="yahoo.js"/>
			<srcfiles dir="${jsdir}yahoo-2.6" includes="dom.js"/>
			<srcfiles dir="${jsdir}yahoo-2.6" includes="event.js"/>
			<srcfiles dir="${jsdir}yahoo-2.6" includes="json.js"/>
			<srcfiles dir="${jsdir}yahoo-2.6" includes="animation.js"/>
			<srcfiles dir="${jsdir}yahoo-2.6" includes="connection.js"/>
		</uptodate>
	</condition>
</target>

This target is named, "reconsolidate.yahoo-2.6.js.check", and returns a true or false value as the property, yahoo-2.6.js.uptodate, depending on whether or not the source files are up-to-date (have they changed since the last time this script ran). The srcfilesdir will look in the jsdir, defined above, then in the "yahoo-2.6" directory for each of the files in the includes section. This target doesnt ever need to be called from the command line, but is referenced by the consolidate target.

Example 5: Consolidate Target

<target name="consolidate.yahoo-2.6.js" depends="reconsolidate.yahoo-2.6.js.check" unless="yahoo-2.6.js.uptodate">
	<echo message="processing yahoo-2.6.js"/>
	
	<concat destfile="${builddir}js/yahoo-2.6.js">
		<filelist dir="${jsdir}yahoo-2.6">
			<file name="yahoo.js"/>
			<file name="dom.js"/>
			<file name="event.js"/>
			<file name="json.js"/>
			<file name="animation.js"/>
			<file name="connection.js"/>
		</filelist>
	</concat>

	<java jar="${yuicompressor}" fork="true" failonerror="true" output="${builddir}js/yahoo-2.6-min.js">
		<arg value="--charset" />
		<arg value="UTF-8" />
		<arg value="--line-break" />
		<arg value="0" />
		<arg value="${builddir}js/yahoo-2.6.js" />
	</java>
</target>

The target definition in Example 5 depends on the update target, which ensures that the update target always runs, then the unless attribute is checked against the boolean created by the update target and the consolidate&rsquot; target will be skipped anytime that boolean is true. Otherwise, the target echos that it is beginning and consolidates all the "yahoo-2.6" files into "${builddir}js/yahoo-2.6.js". Then YUI Compressor is called creating a minified version from the consolidated file. The "line-break" argument intelligently leaves in a few line breaks, so that your production code is debug-able. If this is removed all white-spaces will be stripped out, and debugging becomes nearly impossible.

Each folder is thus consolidated and minified, then all of them are combined into a "library.js" and "library-min.js".

Ant is a great tool for consolidating your JavaScript and CSS files, and I hope these examples help you leverage it on your own sites. I will make all the files in "core.zip" available individually later this week.