This post is one in a series where I slowly assemble a Maven workflow for large javascript projects. If you came here via google, I recommend starting at the beginning: Designing a Javascript Maven Plugin. Alternatively, you can just go directly to the js-maven project on github.
Since there’s no sense in rebuilding something that already exists, I decided to start with google’s excellent closure compiler as a baseline, and simply write a maven wrapper around it. There’s nothing inherently wrong with the YUI Compressor; It’s quite a nice compressor. In the light of supporting more use cases, however, I feel that a compiler with more optimization options would be a better choice, especially since all of those options are individually configurable.
However before I wrote the plugin, there are a few things I had to take into consideration:
- Developers should be able to debug while developing. This means that the compiled code needs to be human readable.
- Optimization may impact code function, so the compiled code should be optimized.
- Once packaged, the code should be minified.
In practice, this means two compiler passes over the source code. The first is to perform code optimizations, yet provide a human readable source file for debugging (in jetty, for instance). The second is to minify, but not optimize, shortly before the asset is packaged. This suggests the following steps for my Javascript Library lifecycle.
- compile
- net.krotscheck:js-maven-plugin:optimize-js-lib
- prepare-package
- net.krotscheck:js-maven-plugin:minify-js-lib
Given that with Google Closure two steps are practically identical, most of the functionality will be abstracted. This means I’ll effectively be running the compiler twice, not a problem unless a browser decides to throw errors on whitespace usage.
The Compiler Plugin
There are three steps to compiling using Closure. First we need to gather all of our source files, then we need to configure the compiler, and then we’ll run the compiler and output our source. There is a fourth step- gathering dependencies, which I’ll cover in a subsequent post. None of these steps are especially difficult. Here’s step one, finding all of our source code.
Full source available on github: JSLibraryCompilerMojo.java
/**
* Location of the source directory.
*
* @parameter expression="${project.build.sourceDirectory}"
* @required
*/
protected File sourceDirectory;
/**
* This method scans the configured source directory of the Mojo and returns all
* javascript documents, filtering by extension.
*/
protected List<JSSourceFile> generateJavascriptSourceList() {
ArrayList<JSSourceFile> sourceList = new ArrayList<JSSourceFile>();
String[] extensions = { "js" };
getLog().debug("Discovering Source Files");
Collection<File> sourceFiles = FileUtils.listFiles(sourceDirectory, extensions, true);
for (Iterator<File> iterator = sourceFiles.iterator(); iterator.hasNext();) {
File file = (File) iterator.next();
sourceList.add(JSSourceFile.fromFile(file));
getLog().debug(" + " + file.getAbsolutePath());
}
return sourceList;
}
Here’s step two, writing our compiler configuration. Note that I consciously made the choice to reduce the complexity of what my users can alter for the sake of illustration; This is primarily done