Using the Google Closure Compiler in Java

I recently had a chance to try out Google's closure compiler. The closure compiler is similar to the YUI compressor except that along with minimizing it may rewrite the JavaScript. If you want to understand more about what it does start at the overview documentation and then go from there.

What I needed was a way to use the closure compiler in an Ant task. The Ant task that comes with the library is good but there wasn't a way for me to integrate it into an existing system that wasn't going to change. After looking around for some example code and not finding any I went into the library's Ant task and figured out how to wire it all up myself.

It isn't that hard to use the compiler library in your own Java code but without a simple example it takes some work to figure out what is needed and what isn't. The following code is just about as bare bones as you can get. It takes a number of JavaScript files and compile them using the medium setting, more about the choice of settings after the code:

import com.google.javascript.jscomp.*;

import java.io.FileWriter;
import java.util.List;
import java.util.ArrayList;
import java.util.logging.Level;

public class Test
{
  public static void main(String[] args) throws Exception
  {
    // These are external JavaScript files you reference but don't want changed
    String externalJavascriptResources[] = {
        "jquery.js",
        "jqueryui.js"
    };
    // These are the files you want optimized
    String primaryJavascriptToCompile[] = { 
        "somejavascript.js",
        "otherjavascript.js"
    };
    // This is where the optimized code will end up
    String outputFilename = "combined.min.js";

    com.google.javascript.jscomp.Compiler.setLoggingLevel(Level.INFO);
    com.google.javascript.jscomp.Compiler compiler = new com.google.javascript.jscomp.Compiler();

    CompilerOptions options = new CompilerOptions();
    CompilationLevel.SIMPLE_OPTIMIZATIONS.setOptionsForCompilationLevel(options);

    WarningLevel.VERBOSE.setOptionsForWarningLevel(options);

    List<JSSourceFile> externalJavascriptFiles = new ArrayList<JSSourceFile>();
    for (String filename : externalJavascriptResources)
    {
      externalJavascriptFiles.add(JSSourceFile.fromFile(filename));
    }

    List<JSSourceFile> primaryJavascriptFiles = new ArrayList<JSSourceFile>();
    for (String filename : primaryJavascriptToCompile)
    {
      primaryJavascriptFiles.add(JSSourceFile.fromFile(filename));
    }

    compiler.compile(externalJavascriptFiles, primaryJavascriptFiles, options);

    for (JSError message : compiler.getWarnings())
    {
      System.err.println("Warning message: " + message.toString());
    }

    for (JSError message : compiler.getErrors())
    {
      System.err.println("Error message: " + message.toString());
    }

    FileWriter outputFile = new FileWriter(outputFilename);
    outputFile.write(compiler.toSource());
    outputFile.close();
  }
}

The above code is doing a number of things:

  • It takes both external resources you don't want optimized as well as resources you do want optimized that refer to those external resources. You may not need to list external resources depending on what level of optimization you use.
  • It combines all the input files that are not external into one output file.
  • It compiles the javascript code given to it using the SIMPLE_OPTIMIZATIONS setting.
  • It lists any warnings or errors it found while compiling the code.

One of the nice side effects of using the closure compiler is that because it compiles the code it can alert you to errors or potential issues using warnings.

What is listed above is using the middle of the road optimizations. There is a level above SIMPLE_OPTIMIZATIONS that you can set by changing that line in the above code to:

  CompilationLevel.ADVANCED_OPTIMIZATIONS.setOptionsForCompilationLevel(options);

Be aware that the advanced optimizations will do things, renaming your variables and functions to name two, that can break your code. The compiler gives you a way of alerting it to things you don't want changed using externs and exports. Before using the advanced option you should read the advanced options tutorial.

If you do not like seeing all the steps the compiler takes you can set the logging level to QUIET by changing the WarningLevel line to the following:

  WarningLevel.QUIET.setOptionsForWarningLevel(options);

One last note about the compiler is that the closure inspector is a Firebug plugin that will let you see what the original line of code looked like before it was compiled. This can help a lot when something goes wrong.

Leave a Reply

Your email address will not be published. Required fields are marked *