Chapter 15. Validation

Because JavaScript isn’t compiled before being deployed, web developers don’t have the extra compilation step to identify errors. JavaScript code validators partly fill this void by performing static analysis on your JavaScript code. You were introduced to JSLint and JSHint earlier in this book. In this chapter, you’ll learn how to incorporate JSHint into your build system to automatically analyze and verify your JavaScript code.

Note

JSHint is used in this chapter, because it comes with a prebuilt command-line file suitable for running with Rhino. JSLint, as of the time of this writing, doesn’t have a prebuilt command-line file, though some third parties have written utilities including command-line controls for JSLint.

Finding Files

The first step in validating files is to locate the files. There are two different tasks for this purpose: <fileset> and <filelist>. The <fileset> task is used when you want to include a large number of files based on a pattern. Specify the dir attribute as the directory to look in and then includes with a filename pattern, such as:

<fileset dir="./src" includes="**/*.js" />

This fileset includes all JavaScript files contained in the src directory. You can optionally specify some file patterns to exclude as well:

<fileset dir="./src" includes="**/*.js" excludes="**/*-test.js />

This fileset includes all JavaScript files except the ones that end with -test.js. This is a common practice for excluding unit test files that are contained in the same directories as the source files.

The <filelist> task works similarly except that you must explicitly list the files to include. This task is best used when you want to retrieve references to a specific set of files. The <fileset> element also expects a dir for the directory. The files attribute contains a comma-separated list of files. For example:

<filelist dir="./src" files="core/core.js" />

In practice, you’ll end up using <fileset> more frequently than <filelist>, as it’s far more likely that you’ll be dealing with large groups of files rather than specific, named files.

The Task

The <apply> task is used to execute command-line utilities on a collection of files from within an Ant target. Because JSHint is written in JavaScript, you’ll need to use the Rhino command-line JavaScript engine to execute it. Download the latest Rhino release from http://www.mozilla.org/rhino and place the js.jar file in your dependencies folder (lib.dir).

To run JSHint on the command line, type the following:

java -jar js.jar jshint.js [options] [list of files]

For example:

java -jar js.jar jshint.js curly=true,noempty-true core/core.js

The <apply> task allows you to recreate command-line entires using the <arg> element. There are two ways to use <arg>: by specifying the path attribute for file or directory references, or by specifying the line attribute for plain text. You can break down the command-line format into the following pieces:

java

The program to execute, specified by the executable attribute of <apply>

-jar

An option for java, represented by the line attribute of <arg>

jshint.js

The main JSHint file, represented by the path attribute of <arg>

curly=true,noempty-true

The options, represented by the line attribute of <arg>

core/core.js

The file to validate, represented by the path attribute of <arg>

Given that, you can quickly create an Ant skeleton for this utility:

<target name="validate">       
  <apply executable="java">
    <arg line="-jar"/>
    <arg path="js.jar"/>
    <arg path="jshint.js" />        
    <arg 
     line="curly=true,forin=true,latedef=true,noempty=true,undef=true,rhino=false"
    />
    <arg path="core/core.js"/>
  </apply>        
</target>

Although this approach works, you should really run JSHint on a collection of JavaScript files rather than on a single one. The <apply> task makes this step easy by allowing you to specify a <fileset> and then include it in a particular spot on the command line by using the <srcfile> element. For example, the following target validates all JavaScript files in the source directory:

<target name="validate">       
  <apply executable="java">
    <fileset dir="${src.dir}" includes="**/*.js" />
    <arg line="-jar"/>
    <arg path="js.jar"/>
    <arg path="jshint.js" />        
    <arg 
     line="curly=true,forin=true,latedef=true,noempty=true,undef=true,rhino=false"
    />
    <srcfile/>
  </apply>
</target>

This Ant target now executes JSHint on every file specified by the <fileset> element. You can run the target via:

ant validate

Improving the Target

Although the validate target works well, there are some improvements that can be made. First, the current version runs JSHint once on each file. So if there are three JavaScript files, it’s the equivalent of running this:

java -jar js.jar jshint.js curly=true,noempty-true first.js
java -jar js.jar jshint.js curly=true,noempty-true second.js
java -jar js.jar jshint.js curly=true,noempty-true third.js

There is some overhead when running java, specifically the creation and destruction of the Java Virtual Machine (JVM). This task adds a significant amount of time to the target.

The JSHint command-line script actually accepts multiple files, so it’s perfectly capable of using one JVM to check every file. Fortunately, the <apply> task makes it easy to pass in all of the filenames. You just need to set the parallel attribute to "true":

<target name="validate">       
  <apply executable="java" parallel="true">
    <fileset dir="${src.dir}" includes="**/*.js" />
    <arg line="-jar"/>
    <arg path="js.jar"/>
    <arg path="jshint.js" />        
    <arg 
     line="curly=true,forin=true,latedef=true,noempty=true,undef=true,rhino=false"
    />
    <srcfile/>
  </apply>
</target>

By adding one attribute, the validate target now passes all of the files onto the command line at once (separated by spaces). Doing so allows JSHint to read and validate all files with just one JVM and dramatically improves the target speed.

The last addition to the validate target is to have the build fail if validation fails. It’s usually a good idea to add this step, because it ensures that developers are aware of the problem. The current version of the validate target will output validation failures to the command line, but any other task that follows will continue to execute, potentially causing the failure messages to scroll off screen.

You can force <apply> to fail the build by setting the failonerror property to "true":

<target name="validate">       
  <apply executable="java" failonerror="true" parallel="true">
    <fileset dir="${src.dir}" includes="**/*.js" />
    <arg line="-jar"/>
    <arg path="js.jar"/>
    <arg path="jshint.js" />        
    <arg 
     line="curly=true,forin=true,latedef=true,noempty=true,undef=true,rhino=false" 
    />
    <srcfile/>
  </apply>
</target>

This version of the validate target will fail the build when an error occurs during execution of <apply>. An error is any nonzero exit code returned from the executable. Because JSHint returns 1 when a validation error occurs, it will cause the build to fail.

Other Improvements

The last step in improving the validate task is to externalize three pieces of data:

  • The location of js.jar

  • The location of jshint.js

  • The command-line options

As these may change in the future, it’s best to represent them as properties and include them in your properties file or at the top of your build.xml file. Here’s an example properties file:

src.dir = ./src
lib.dir = ./lib

rhino = ${lib.dir}/js.jar
jshint = ${lib.dir}/jshint.js

jshint.options = curly=true,forin=true,latedef=true,noempty=true,undef=true\
,rhino=false

You can then reference the properties from the validate target:

<target name="validate">       
    <apply executable="java" failonerror="true" parallel="true">
        <fileset dir="${src.dir}" includes="**/*.js" />
        <arg line="-jar"/>
        <arg path="${rhino}"/>
        <arg path="${jshint}" />        
        <arg line="${jshint.options}" />
        <srcfile/>
    </apply>
</target>

With this change, you’re easily able to update the location of files and JSHint options without needing to go back into the target.

Buildr Task

Buildr has a <jshint> task that abstracts away a lot of the configuration necessary to run JSHint. After importing the buildr.xml file as mentioned in the previous chapter, use the <jshint> task by passing in any number of <fileset> elements:

<target name="validate">
    <jshint>
        <fileset dir="${src.dir}" includes="**/*.js" />
    </jshint>
</target>

You can also change the default options by using the options attribute:

<target name="validate">
    <jshint options="${jshint.options}">
        <fileset dir="${src.dir}" includes="**/*.js" />
    </jshint>
</target>

The end result is exactly the same as using the target from the previous section.