Closure Compiler for JS fun and profit

The Closure Compiler — also named JSCompiler, internally at Google — underpins most of Google’s web properties by optimizing, typechecking, transpiling and organizing JavaScript code. It makes Google more productive and allows us to build JavaScript using software engineering practices, rather than as a random collection of source files strung together.

You can use it too, although it can be tricky to get right. ⚠️

the Closure Compiler logo

Tricky, you say? For the benefits Closure brings you, the challenges can be worth it. This post will cover packing, typing, and how you can use the compiler in your projects.

Let’s see a demo, then

Ok! Load up the Closure Compiler webservice. Don’t be fooled by the ageing UI — it runs the whole compiler under the hood.

sample code inside the Closure Compiler webservice

The link above will load some simple ES6 code. Hit ‘Compile’ and see the minified ES5 result — usable in most browsers — on the right. Pretty cool!

Now, choose the Advanced optimization mode and hit ‘Compile’ again. The result is even smaller than with Simple, actually removing redundant paths (here’s a smaller example) — but it will also eat symbol names, such as the Foo object — which is now a single-letter variable, because we didn’t tell Closure we care.

Congratulations — you’ve hit the one of the main simultaneous benefit/pain point for folks using the compiler.

The typing challenge

Closure has Simple and Advanced. It also has Whitespace Only (although it’s not exciting and not worth covering).

Nearly all code, when piped through Simple, will output transpiled, minified code. Closure will complain if you have syntax errors, or if you have type errors it knows about. It’ll also consume and generate source maps so that in-browser debugging is easy (source maps help your browser map from a compiled version back to its original source).

Simple mode will give you behavior similar or better than most other JavaScript compilers out there today. You should use it! 👍

the Custom Elements polyfill compiled with Simple

But, as you saw before, it won’t statically analyze your code, trimming branches (e.g., conditionals, unused function calls) or performing other optimizations. Only Advanced will really reduce the complexity of your code.

What’s the catch?

To use Advanced, or to get the best benefit out of Simple, your code will need to be (somewhat) annotated. These annotations are a type of JSDoc, and fall into three categories:

  • Type annotations on your code, e.g.:
function returnTwiceThisNumber(/** number */ x) { return x + x; }

This example tells the compiler that you should only expect a number here. Passing anything else will cause an error. Why can’t I leave this off, you ask — won’t the compiler work it out for me?

Well, note that adding two strings together is also valid, even though that’s not what your function should be used for — and without hints, the type will likely be unknown, which means the compiler can’t optimize it.

  • Externs files, e.g.:
/** @return {string} */
jQuery.deferred.prototype.state = function() {};

This is a single line from jQuery’s externs. If you’re using code that you don’t compile as part of your bundle, you need to tell the compiler about it. This method takes no arguments and returns a string. If you use state() on a jQuery deferred object, Closure will now know that it’ll get back a string.

Standard library methods — e.g., Math.pow — have externs too, but they’re built-in to the compiler.

  • Export definitions, e.g.:
/** @export */
class EntryPointToMyApp { constructor() { ... } }

This line tells Closure to leave EntryPointToMyApp well-enough alone. It won’t rename it in your output code.

Exports are probably the least-well understood part of Closure: many applications don’t use them (as applications ‘run’ themselves), and you only need them if you’re writing a library that is in turn consumed by others (e.g., via your own externs files in Closure, or directly in HTML).

This sounds confusing

The biggest caveat in Closure is typing. If your typing is not correct — or nonexistent, if you’re thinking about migrating an existing project — Advanced mode may yield you unusable code.

On the plus side, it’ll also tell you very loudly that there are problems. 💥

Lack of typing information will prevent the compiler from doing its job — the string vs number example, before (solved with type annotations)— but it might also eat your variable names — the Foo example.

This is complicated. If you’re starting a new project, Closure in Advanced mode is great and can make you more productive; for old projects, Simple is easy to enable, will shrink your code and won’t harm you. For some more information on this whole area, check out the following —

Tell me more!

Great! There’s a few different ways to integrate Closure into your projects For a sample project that compiles in Advanced mode, look no further.

But if you’re reading on, you should first know that there’s a number of flags you can configure when you run Closure. Here’s a short list of suggestions:

  • warningLevel: VERBOSE, to let you know if you make a mistake — normally, Closure will try to succeed at all costs
  • processCommonJsModules: true, to support require statements
  • rewritePolyfills: true, will add libraries that do not exist in your target language (typically, and by default, ES5)
  • compilationLevel: ADVANCED (or SIMPLE, but it’s the default)

Compile through the webservice

The webservice — seen before, and available here — can be used manually, but also has an API. There’s Node plugins which could call it as part of a build process. It’s fast, as Google eats the compilation cost for you, but it may not be for everyone.

Personally, I use a helper script, left in my $PATH, to invoke Closure from the command-line: this is useful for small applications.

using js-compile to check types

Compile via Node module

As of August 2016, Google has just released a pure-JS version of the Closure Compiler. There’s also the Java version — a bit faster — but you may not like the Java dependency.

Both JS and Java versions have Gulp, Webpack and other build system plugins available. You should be able to drop these into your existing projects fairly easily. For a real-world example, Google uses the Gulp plugin to compile Santa Tracker 🎄 every year.

Compile on the command-line

If you’ve download the Closure Compiler Java application, you can invoke it on the command-line and specify files directly. It has even more flags and options; you can use this application as part of a larger build process.

java -jar compiler.jar --js hello.js --js_output_file out.js

Cute Anecdote

At Google, we’ve taken type-safe JavaScript for granted for a long time. It makes refactoring easy, and we can actually have fewer, simple tests because the compiler is checking that e.g., this type is what we expect at any moment.

When I joined the company, back in 2009, I asked during my interviews: “Just how does Gmail not constantly crash with JS errors?” — and while the JS community has innovated with lots of other tools since then, Closure was and is one of the reasons Google is able to produce high-quality web applications.

I hope it helps you do the same!

💻 🛠️