Introduction to google-closure with plovr

September 1st, 2013 by exhuma.twn

I’m about to embark on a quest to understand the development for custom google-closure components (UI widgets if you will). Reading through the relevant section in “Closure – The Definitive Guide” makes me believe, it’s not all too difficult. But there are still a bunch of concepts which I need to familiarize myself with. This article briefly outlines my aim for this “learning trail”, and starts of with a tiny HelloWorld project using plovr. This article assume a minimal knowledge of google closure (you should know what “provides” and “requires”. “exportSymbol” should also not surprise you)

Unfortunately, the example from the Book shows both “rendering” and “decorating” in one example. While it’s good to implement both in a custom component, especially if you want to publish it, it’s not necessary. If in your project, all you do is “rendering”, then you are adding a lot more code which you will never use. So in the following few posts, I will dismantle the example from the book and make two trails. One only implementing “rendering”, and another one only implementing decorating.

As said, the aim of this exercise is to familiarize myself with google-closure component concepts, get my feet wet and be able to write more complex components.

But first things first:

To me the one big thing in google closure is the compiler. Not only does it minify the code amazingly well. It also gives you extremely useful warnings and errors, especially if you add relevant annotations.

But getting the development environment properly set up is tedious. As the name “closure tools” implies, it’s not only one tool. Each of them have their own command-line syntax, not all of them are still used (f.ex.: calcdeps.py). Eventually you end up with different arcane incantations for each of your projects. Maybe you will write a Makefile, or a fabric task (or whatever rocks your boat) to simplify this. But it still is a little annoying.

Enter plovr. Plovr is a single java application combining some of the most used closure tools (including soy) bundled with a specific closure-library revision. Plovr simplifies development with closure by offering two main features: First, it combines all configuration in one simple JSON file. And second it offers a “serve” mode which will dynamically compile your JS source, injecting compile-time errors into your HTML document. This means, once this is set up, you will no longer need to run the compilation on the shell. Refreshing the browser is enough.

So let’s set up a very minimal hello-world project using plovr.

My experience with google-closure tells me to have at least two folders: One containing the JavaScript code you write yourself, and another one containing your compiled code (this could be your “static files” folder). I’ve seen someone put everything in one folder, which resulted in the error that the same “provide” line was found twice by the dependency calculation. So let’s venture on to create the file structure:

plovr-81ed862.jar
plovr-config.js
htdocs
└── hello.html
src
├── lib
│   └── example.js
└── main.js

Let’s look at those files/folders:

htdocs
The folder containing the files which are visible/reachable from the web. Including the compiled JavaScript code.
htdocs/hello.html
Our document for the browser.
plovr-81ed862.jar
Because plovr is still in strong flux, I decided to link to a specific build. This way, this article should remain valid for the future. You can till try to get the latest version of plovr. We do the mere basics here, and it should be fine. If something explodes, try with the above mentioned revision.
plovr-config.js
The plovr configuration file.
src
Our hand-written JavaScript
src/lib
This will contain our personal JavaScript library. In other words, the bulk of our code.
src/lib/example.js
This file will contain our application code.
src/main.js
This is the entry point for plovr
NOTE: plovr works best with the official Oracle JVM. I have run into strange trouble with the OpenJDK. If you get inexplicable stack-traces, first make sure that you are indeed running the official Oracle JVM/JDK!

First, let’s make sure all files exist (even if empty):

mkdir -p htdocs src/lib
touch htdocs/hello.html plovr-config.js src/lib/example.js src/main.js
wget https://plovr.googlecode.com/files/plovr-81ed862.jar

Now let’s set up our plovr environment using “plovr-config.js”:

{
  // we need a unique ID
  "id": "custom-component",

  // This is our "entry point" to the application.
  "inputs": "src/main.js",

  // Any additional module requirements "using goog.require" can be found here.
  // Note: the core library is included in plovr. This should *only* contain
  //       your own custom modules!
  "paths": "src/lib"
}

We can now try to build our code (yes, I know we still have not written anything, but bear with me):

java -jar plovr-81ed862.jar build plovr-config.js

If all went well you should not have run into any errors, and you should now see compiled JavaScript on you console. So what just happened? Both our JavaScript files are empty, and yet we get JavaScript compiled. What is this sorcery?

google-closure is different from other libraries you may have used. When developing an application with closure, you will not link a core closure JS file in your document. Instead the core library is compiled into your generated output file. So what you see here, is in fact the closure library. Essentially you see all of the “base” library being output on your console.

Just for fun and giggles, let’s try something else: Add the following value to “plovr-config.js”:

{
  ...
  "mode": "ADVANCED"
  ...
}

… and run the build command again. Lo and behold: empty output. Why? In “ADVANCED” mode, the compiler becomes sentient. And it detects that you have not written any code. So by inference, there is no dependency on the closure library. Not even the “base” functions. So it strips them away, essentially leaving you with an empty result! This is one amazing feature of closure. But I’m not going to cover this here. For now, delete the "mode": "ADVANCED" again. Without that, compilation runs faster. You may want to use ADVANCED only for production code (but trust me, you should run it from time to time during development as well, but I digress..).

With this in place, we can finally start to implement the meaty parts of the HelloWorld application. Let’s start off with src/lib/example.js:

goog.provide('example');

example.sayHello = function(content) {
  window['console']['log'](content);
};
Why not simply console.log(content);? Not all browsers have a global log function. Closure will complain about an “undeclared variable”. Writing it this way will give you a runtime error instead of compile-time error. Good enough for this tutorial. Bad for production! 😉 There is a proper logging framework included in closure. But that’s out of scope of this document!

Now… if we compile again, we still only get the base library (you won’t find the string 'console' in the output). Try it out!

We still don’t have anything in the entry point (main.je), so there’s really nothing to compile. Let’s fix that by editing src/main.js:

goog.require('example');
goog.exportSymbol('example.sayHello', example.sayHello);

The compilation this time around will yield a much larger file (without the ADVANCED mode). So let’s try out this result. First, store the output:

java -jar plovr-81ed862.jar build plovr-config.js > htdocs/compiled.js

Finally, let’s implement a simple HTML document in htdocs/hello.html:

<!doctype html>
<html>
  <head>
    <title>Example: Hello World</title>
  </head>
  <body>
    <script src="compiled.js"></script>
    <script>
      example.sayHello('Hello World!');
    </script>
  </body>
</html>

If you open this in your browser, you should see a white screen. But in your log, you should see the string “Hello World!” if you load this page.

Jobs Done!… well, not quite… There’s more:

Dynamic Compilation (server mode)

As mentioned earlier, plovr can run a development server. Very easy to set up, and very convenient for development. Let’s run, and explore it. To run simply replace “build” with “serve”:

java -jar plovr-81ed862.jar serve plovr-config.js

By default it will use port 9810. So point your browser to http://localhost:9810 and explore. I leave this as an exercise to the reader.

Now let’s modify out HTML document, by changing the line:

    <script src="compiled.js"></script>

into

    <script src="http://localhost:9810/compile?id=custom-component"></script>

Everything should still work as expected. But now for the fun part. Let’s add a logical error. Open up src/lib/example.js and add a line containing only 40 + 2; to the end of the file such that it becomes:

goog.require('goog.dom');

goog.provide('example');

example.sayHello = function(content) {
    window['console']['log'](content);
};

40 + 2;

Yes, it’s a line which does not do anything. Just reload your HTML document in the browser and observe what happens… Neat I say!

And for even more awesomeness, append the following to your URL: ?level=verbose&mode=advanced This will trigger verbose error reporting and compile in advanced mode.

Plovr inspects some query parameters (mode and verbose) being the most interesting ones in my opinion, and acts upon them. Of course, you can also put those into your plovr config file. I encourage you to have a look at the config file plovr options.

In “verbose” mode you may notice the error about closure not being able to determine the signature of example.sayHello. This is because there are no annotations in our code. Again, I leave this as an exercise to the reader.

References:

Posted in JavaScript | No Comments »

Pages

Recent Posts

Categories

Links


Archives

Meta