Use Ant and Closure Compiler to compress every JavaScript file in a project


There’s an updated version of this script in this blog post.

At work, we use TeamCity to automatically build and deploy to dev projects as we check in changes.

One thing that’s always been a bit of a nuisance is dealing with the compression of JavaScript files that we’ve written for the projects. Developing locally, it’s best to leave the file uncompressed to make debugging simpler. But, once it’s on the server, it’s the compressed file we want to test against.

Until now, we’ve just manually used Closure Compiler to compress. Each JavaScript file exists twice in source control – the compressed version and the uncompressed version in a subfolder (that is excluded from the deployment script).

So, the process for working on a JavaScript file is:

  • Find the JavaScript file you want to work on
  • Change the path in the referencing page to use the uncompressed file
  • Make changes and test
  • Re-compress the file
  • Change the path in the referencing page back to the compressed file
  • Check in changes

As you can imagine, this is slightly prone to error. It’s also an annoying manual step to do the compression.

Ant, man

Closure has an Ant Task. The sample they give is great if you’re happy to manually specify what files you want to compress but that’s not really useful for us. What we want is to be able to compress all JavaScript files in the project, in-place. We also want them to keep the same filename otherwise the references within the various pages would be wrong.

So, we need to get Ant to loop through all of the JavaScript files in the project and pass them to Closure. Ant doesn’t have an easy-to-use foreach loop built in so Ant Contrib is required for that.

Prerequisites

Ant Contrib

There are two ways to install Ant Contrib – it can be installed directly into the Ant directory on the machine or the Jar can be stored locally with the script that uses it. If we install it inside Ant then every build agent and developer who wants to use the script will have to go and download it separately so we’ll use the second method and include it with the script (it’ll then go into source control and be available to everyone).

Go and download the latest release of Ant Contrib and put this under a build-lib (or whatever naming you want to use) folder of your project. We’re going to use relative paths to reference this from the build script.

Google Closure

You’re also going to need Google Closure so go and grab this from Google. Unzip the Jar and drop that into your build-lib folder.

WinAnt (Optional)

If you want to run Ant scripts locally on Windows, you’ll need WinAnt.

Ant build file

Now create your build.xml Ant file in the root of your project. The lines with paths that need to be customised are highlighted.

<?xml version="1.0"?>
<project basedir="." default="run">

    <taskdef name="jscomp" classname="com.google.javascript.jscomp.ant.CompileTask" classpath="build-lib/compiler.jar"/>
    <taskdef resource="net/sf/antcontrib/antcontrib.properties">
        <classpath>
            <pathelement location="${basedir}/build-lib//ant-contrib/ant-contrib-1.0b3.jar"/>
        </classpath>
    </taskdef>

    <target name="run">
        <foreach target="compile" param="foreach.file">
            <path>
                <fileset dir="${basedir}" casesensitive="yes">
                    <include name="**/*.js"/>
                </fileset>
            </path>
        </foreach>
    </target>

    <target name="compile">
        <!-- This gets the filename without the directory path. -->
        <basename property="file.js" file="${foreach.file}"/>

        <!-- Using Javascript functions to work out the paths -->
        <script language="javascript"> <![CDATA[

            // get values
            fullPath = project.getProperty("foreach.file");
            fileOnly = project.getProperty("file.js");

            pathOnly = fullPath.substring(0, fullPath.indexOf(fileOnly));

            // store the result in a new property
            project.setProperty("directory",pathOnly);

        ]]> </script>

        <echo message="Compressing file ${file.js} in ${directory}" />

        <jscomp compilationLevel="simple" warning="verbose" debug="false" output="${foreach.file}">

            <sources dir="${directory}">
                <file name="${file.js}"/>
            </sources>

        </jscomp>

    </target>

</project>

Separating the file name from the directory in Ant isn’t all that simple, so we’re using JavaScript within the file to do that (line 32).

You can now include this as a build task within TeamCity and it’ll loop through and compress every JS file it encounters.

Remember to rename the Ant Contrib JAR filename in your build file to match whatever version you’ve downloaded.

Some sample code is available on GitHub.