Video icon 64
Learn to Code. Start your free trial today.
Advertisement

Using Ant to Build a JavaScript Library

by
Student iconAre you a student? Get a yearly Tuts+ subscription for $45 →

Applications all seem to have a build version number, don't they? Anywhere you look for advice about managing a large software project, you'll find that an automated build process is virtually a requirement. I have found it essential to automate a build process that concatenates and minifies the files needed for a given page. This tutorial will de-mystify the Ant build tool and show you how to create your own, flexible, build files for a JavaScript library.


Software Requirements

For this tutorial, you will need NetBeans with Ant installed. I often use:

  • NetBeans 7.0.1 for PHP projects - Ant version 1.12.1
  • NetBeans 7.1 for JSP projects - Ant version 1.14.1

The Ant tool in the PHP version is a slightly limited version, but is ideal for our purposes, because the PHP projects are not complicated with an automatically generated build file. So for this tutorial, I will demonstrate with the PHP version. However, Ant is of course available at Apache and is widely used in IDEs, like Eclipse. For the .Net C# community, there is a tool called Nant, which I use in my .NET projects - it's quite similar.


Rationale: Why Build?

In my first serious attempt at creating a one-page Ajax application, I ended up with a list of nearly 40 script tags that produced a start-up time of over a minute. To make the project manageable, I needed to contain the code in multiple modules, not to mention all of the YUI modules that were required. After reading blogs written by the YUI team, I realized how important it is for performance to reduce the number of script tags to as small a number as possible. Hence, my interest in concatenating and minifying JavaScript files.

Combining multiple files reduces the extra bytes from HTTP headers as well as potential transfer latency caused by TCP slow starts, packet losses, etc.

YUI Blog: Performance Research, Part 6

Why Ant?

Lajos Moczar's excellent Tomcat 5 Unleashed had a huge influence on my attitude to developing a complete web-based application. It's much more than a book about Tomcat. It gave me the motivation, guidance and courage to start using Ant to build my JSP projects. Ant is built into NetBeans, my favourite IDE for JSP, and I got used to using the automatically generated build file with little need for manual editing when building a Java package of classes. However, as my understanding of JavaScript grew, I found that I needed a build process and was forced to write my own build configuration files manually just for the JavaScript part of the project. Moczar's build.xml for a Tomcat application gave me a great starting point.

Having a good development environment is absolutely critical to the success of your development efforts. You need a structured environment that allows you to execute your build processes in an efficient and repeatable fashion.
- Lajos Moczar, Tomcat 5 Unleashed

Editor's Note: If you decide against using Ant, Grunt is a fantastic build tool for your JavaScript applications. Learn more about it here on Nettuts+.


Step 1: Setting Up Your Environment

Open a new project in NetBeans. I've called mine NetTutsBuildJs, and have created it inside my Nettuts+ folder here: C:\NetTuts\BuildJs. Obviously, JavaScript doesn't need to be compiled into an exe file; we have different concerns. There are at least three things that we need for a large JavaScript project:

  • Develop Source: Create a bunch of modules in separate files. This is our source code.
  • Concatenate: Collect all the source files you need for a particular page and concatenate them into one file.
  • Minify: Minify files using a well-know minifying tool to make them as small as possible. I prefer the YUI Compressor tool.

As you can see in the screenshot, I have created a folder, called js for my JavaScript, and then added the folders, src, concat and min.


Step 2: Ignore World

I am a bit bored with saying "Hello World" at the beginning of every new IT tutorial, aren't you? So I thought it would be nice to ignore the world this time. After all, it might be just a figment of my imagination!

I'm a solipsist. Why aren't there more of us?

  • Right-click the context menu on the Source Files folder and add a new XML document, called build.xml.
  • Remove all the automatic template text and type in this text:
<project name="NetTutBuildJs"  basedir=".">
</project>

You might not notice anything now, but if you restart the IDE, you will see that build.xml now has a special icon with a yellow triangle associated with Ant files. If you select it, you will see the navigator panel now displays Ant Targets in its header.

Each set of tasks in an Ant build file is called a target, so we need to create a simple message target
nested inside the project tag, like this:

<target name="ignore-world-message">
    <echo message="World. You may be a figment of my imagination."></echo>
</target>

Now, expand the build.xml file in the Project panel and you will see the new target in the Navigator panel. Right click on ignore-world-message and you should see the message in the Output panel, like this:


Step 3: Sort Out Paths

Right. The world may not exist and we have ignored it, but at least Ant seems to be working! Joking apart, we now have to get the most crucial thing in Ant right: paths.

I may be a bit slow, but I always had trouble with this, so let's tread carefully. Add a property to the top of the file, just below the project tag. Call the property root and set the location to a zero-length string.

<property name="root" location="" />

Add a new target to display this location so that we can ensure that we've got our paths straight. Notice the convoluted syntax to refer to the root property? You need to enclose the property name inside double quotes, but, in addition, you have to wrap it with a dollar sign and a curly brace on the left, then close it with a curly brace on the right. What a fuss!

<target name="show-root-path">
<echo message="${root}"/>
</target>

You could put that after the ignore-world target. Now when you right-click the show-root-path target to show the context menu and then click "Run Target," you should see the correct path to the root of your project. In my case: C:\NetTuts\BuildJs.


Step 4: Add Other Paths

Lovely. We've got our environment and we've got a root path pointing to the right location on our hard drive. Now we can add the other paths.

<property name="js" location="${root}/js" />
<property name="src" location="${js}/src" />
<property name="concat" location="${js}/concat" />
<property name="min" location="${js}/min" />

Step 5: Concatenating the Files

At last, some real work. We add a new target, which includes a concat tag, like this:

<target name="concat">
<concat destfile="${concat}/tree-concat.js" encoding="UTF-8" >
<filelist dir="${src}"
files= "tree_data.js,
tree.js"
>
</filelist>
</concat>  
</target>

This is just a trivial example, but for quickness so that you can follow along, I've created two simple JavaScript files: tree_data.js and tree.js, which depend on the YUI files, yahoo-dom-event.js and treeview-min.js. tree_data.js has the following rather meaningless contents:

var treeData = [{
    "label": "Britain",
        "children":[
            "London",
            "Edinburgh"
        ]
    },{
    "label": "France",
        "children":[
            "Paris",
            "Lyon"
        ]
    },{
    "label": "Japan",
        "children":[
            "Tokyo",
            "Kyoto"
        ]
    },{
    "label": "Thailand",
        "children":[
            "Bangkok",
            "Pattaya"
        ]
}]

And tree.js simply renders a TreeView with that data.

YAHOO.util.Event.onDOMReady(function(){
    var  tree = new YAHOO.widget.TreeView("tree", treeData);
    tree.render();
});

Notice that the filelist tag is exactly what we need here. In JavaScript, order matters, so we probably want the data first and then the rendering file second. If we used a tag that relied on the natural order of the files in the operating system, we might get them in the wrong order. So, we laboriously type out the list manually in a filelist tag to ensure the order we want.

For you JavaScript purists out there: I know my treeData variable is a global variable and I should do it a different way. This is just a quick example to explain how to use Ant. I'm quite sure the people following the tutorial are also following current best practices for their JavaScript library.

Now run the concat target. Lo and behold, a file called tree-concat.js magically appears in the concat directory, and, when you open it up, you can see the data defined at the top and the rendering function at the bottom.

To try this out, I've created two simple html files: tree_src.html and tree_concat.html. In the header, they both have the same links to the CSS files needed to create the Sam skin for a TreeView.

<link rel="stylesheet" href="js/yui/fonts-min.css">
<link rel="stylesheet" href="js/yui/treeview.css">
<link rel="stylesheet" href="js/yui/treeview-skin.css">

Just before the end of the body in tree_src.html, I've added

<script src="js/yui/yahoo-dom-event.js"></script>
<script src="js/yui/treeview-min.js"></script>
<script src="js/src/tree_data.js"></script>
<script src="js/src/tree.js"></script>

To test the concatenated file. I've changed the script tags in tree_concat.html to:

<script src="js/yui/yahoo-dom-event.js"></script>
<script src="js/yui/treeview-min.js"></script>
<script src="js/concat/tree-concat.js"></script>

Step 6: Final Stage: Minifying

Our tree library seems to be working, and, when we concatenate the files, we seem to have gotten the right order. Excellent! It's now finally time to minify everything and reduce the number of script tags down to one. This is a bit more complicated.

    <target name="min">
        <apply executable="java" parallel="false" dest="${min}" taskname="yui">
            <fileset dir="${concat}">
                <patternset>
                    <include name="tree-concat.js"/>
                </patternset>
            </fileset>
            <arg line="-jar"></arg>
            <arg path="${compressor}"></arg>
            <arg line="--charset UTF-8"/>
            <arg line="-v"></arg>
            <srcfile/>
            <arg line="-o"></arg>
            <mapper type="glob" from="*-concat.js" to="*-min.js"></mapper>
            <targetfile/>
       </apply>        
    </target>

Notice the property compressor. To get this all to run, I copied the YUI compressor jar file to the yui_compressor folder in my project and created a property in the build file:

    <property name="compressor"  location="${root}/yui_compressor/yuicompressor-2.4.2.jar"/>

When we run the min target, you should now see this output and a new file, called tree-min.js in the min folder. If you open it, you'll see a long continuous stream of JavaScript with no whitespace, all on one line.

There's just one more target needed: concatenate the two YUI files with our new minified file.

    <target name="all">
        <concat destfile="${min}/all-tree-min.js" encoding="UTF-8" >
            <filelist dir="${yui}"
                files= "yahoo-dom-event.js,
                treeview-min.js"
                >
            </filelist>
            <filelist dir="${min}"
                files= "tree-min.js"
                >
            </filelist>
        </concat>  
    </target>

In the test file, tree_min.html, I now only need one script tag:

<script src="js/min/tree-min.js"></script>

Step 7: One-Click Build

The final step is to add a target that calls all the necessary targets and runs them in the correct order. The convention is to call this target, build. It's also useful to have a clean target to delete the concat and min directories, and an init target to set up those directories.

    <target name="clean">
        <delete dir="${concat}"/>
        <delete dir="${min}"/>        
    </target>
    <target name="init">
        <mkdir dir="${concat}"/>
        <mkdir dir="${min}"/>        
    </target>

The build target should now run:

  1. clean
  2. init
  3. concat
  4. min
  5. all

The way to combine all these is to simply add them to the depends attribute, like this.

<target name="build" depends="clean, init, concat, min, all">    
</target>

Conclusion

We walked through the steps required to create a configuration file for Ant to build a JavaScript library.

In this tutorial, we walked through the steps required to create a configuration file for Ant to build a JavaScript library. Starting from the source code, we concatenated all the files in the library into one file, ensuring that each of the source files is added in the correct order. We tested the resulting concatenated file to ensure that nothing was missing or out of place. We then minified that file and concatenated it with the YUI files it depended on.

The final result was that we had a web page with only one script tag, containing all the complex JavaScript needed to run the page. I think you can see how easy it would be to adapt this example to a very large complex JavaScript library. With this basic example as a starting point, you should be able to explore the Ant documentation and develop a fully working build file to automate every part of your build process.

I also use Ant for SQL to build the local clone of my database.

Additionally, I use this kind of build for CSS files as well. They can become almost as convoluted as the JavaScript files and it really helps to concatenate and minify them too. I also use Ant for SQL to build the local clone of my database. I find that when I want to start afresh with a project, cleaning out all the experimental code and starting from scratch, it's really useful to bring along a nice fresh new database. The Ant tool makes it easy to quickly build the tables, functions and procedures, then populate the thing with some sample data.

Advertisement