
You probably heard about the JavaScript Google Closure Tools which were released as open-source (Apache License 2.0) by Google more then an year ago. It is a set of tools which are in use for development of Gmail, Google Maps, Reader, Docs and other popular Google products.
In this blog post I would like to summarize what cool features the Closure Tools offer to developers of large open-source JavaScript projects, and I would like to suggest (re)writing OpenLayers 3.0 the way, that it is fully usable with Closure Tools. We can all profit from what is already out there - and write smaller and faster web and mobile applications easier with the future versions of OpenLayers, if we decide to go this way.
The most important tool in the Closure set is the Compiler. All other tools are optional and are build around the features of the compiler. The compiler is usable from command line or as a web application. You can also HTTP POST your source code and get back the compiled code. The compiler can be utilized by the existing building scripts of OpenLayers easily.
The compiler has several cool features:
- It compiles readable JavaScript into compressed machine-readable JavaScript.
- Documentation of the code with JSDoc Tags is important: the compiler reads it and you get warnings during compilation for typos in documentation, wrong use of a @constructor, wrong type of a variable, misuse of a field annotated with @private and @protected, etc.
- If you write a reusable JavaScript library, such as OpenLayers, you formally export your public API - and the compiler optimizes your internal code.
- The end applications can be compiled together with the library - and then the unused parts of the library are removed from the produced code. Dependencies are solved automatically by the compiler.
- Compiler accepts constants to remove unwanted functionality - this allows compilation only for particular browser such as Mobile WebKit, for only one of Quirks mode or Strict mode, compilation without support of IE6, etc.
- Debugging with FireBug is possible even for the compiled version of the source code.
- Compiler supports generation of dynamically loadable modules, which can significantly speed up loading of the end application, because the code for advanced functionality can be loaded only when it is required.
1. It compiles readable JavaScript into compressed machine-readable JavaScript.
The compiler loads the whole source code of the supplied JavaScript application into memory, makes detailed analyze of the code, prepares internal graph of dependency and while knowing the syntax and partly also semantic of JavaScript language, it makes the compilation.
If you use the default "SIMPLE_OPTIMIZATION" then Closure Compiler behaves just as another JavaScript minimizer: whitespace and comment removal, renaming local variables and function parameters to shorter names (only symbols that are local to functions). It can be directly used with the OpenLayers Build Profiles as they are available now - and you get around 20% smaller OpenLayers.js file on the output, compared to the default jsmin-based minimization of the code. Check also latest #2871 and #2873 if you want to have smaller results for OL. BTW official releases of jQuery are now minimized with Closure compiler and SIMPLE_OPTIMIZATION.
The truly powerful is the compiler when it is used with the "ADVANCED_OPTIMIZATION". Then it uses several tricks to shorten the code further, including the dead code removal and function inlining. But the JavaScript code must be ready for the compilation and it can't contain buggy code patterns. It means that the programmers are pushed to write their JavaScript code in a more readable and maintainable way. Typical problem is with using of "this" outside of constructors and prototype methods. Programmers can't use some of the techniques which the JavaScript language offers, but this restrictions also help to keep the code readable and reusable by other people later on. In general, programmers should follow the rules described in the JavaScript Style Guide. There is also a tool for checking and automatic corrections of the JavaScript code called Linter which can help. OpenLayers 2.x is not following these rules right now, but I think for OpenLayers 3.0 it would make sense to write the code with these rules in mind.
2. Documentation of the code with JSDoc Tags is important: the compiler reads it
OpenLayers 2.x, same as other big JavaScript projects, are using JSDoc comments for inline documentation of the source code. Google Closure compiler is parsing such JSDoc tags, and then using it to enhance the code optimization process and to print warnings during compilation for potentially buggy code patterns or mistakes, if used with "--warning_level=VERBOSE".
In fact through JSDoc comments the JavaScript language is enhanced with complete type system for variables and with visibility of objects.
The JSDoc set of annotations and type expressions which Google Closure compiler understands is described at http://code.google.com/closure/compiler/docs/js-for-compiler.html.
The introduced type system is powerful. OpenLayers can profit from possibility to define your own object types, for example the "OpenLayers.Location" object can be a new type which always must contain ".x" and ".y" with type float. Compiler can enforce this: whenever a function expects "OpenLayers.Location" as a parameter you receive warning during compilation if you use an object which does not comply.
3. Public API is formalized - the compiler needs it
It is important to have a formal definition of the public API which you are exporting from the library such as OpenLayers - compilation of a publicly usable JavaScript library is not possible without that. It means you are pushed to keep the API clean - as well as documented correctly with JSDoc.
4. The end applications can be compiled together with the library
The end-user applications can be programmed traditionally: So the OpenLayers library is used as a standalone OpenLayers.js script, which is included in the website in the HTML header. The application itself has a separate code base, and uses the public OpenLayers API. This approach is common with OpenLayers 2.x.
Alternative approach is that the code of the application is merged with the code of the library and then the result is compiled together - this way the unused parts of the library are removed automatically from the final code. Dependencies in the code are solved by the compiler.
Only the JavaScript functionality, which is really in use by the end application is then part of the final compiled .js file, which is used for deployment and production use.
5. Constants defined during compilation to remove unwanted functionality
Variables annotated with the @define JSDoc tag can be redefined during compilation, and then the compiler can recognize blocks of code conditioned with such variables as unreachable. Because the dead code is removed automatically - such source code is completely stripped from the final result .js file.
Typical use case for this is a block of code executed only if some DEBUG variable is set to true, but there are also other use cases:
A source code of a JavaScript library can contain plenty of functions to handle differences between Quirks and Strict mode of a web browser, determined by the DOCTYPE tag in the beginning of the HTML page. The general JavaScript library must support both modes, but if you are a developer of a web application you control in which rendering mode is your application used. This means the handling of Quirks mode can be easily stripped from library which you are using.
Similarly - if you are developing a mobile web application for iPhone or Android platform, you don't really need in your application code which is specific for Internet Explorer or for older versions of Firefox or other browsers.
The Closure Library, the standard JavaScript library coming with Closure Tools, supports already this kind of conditional compilation for different browsers (via goog.userAgent.ASSUME_MOBILE_WEBKIT) and rendering modes (goog.dom.ASSUME_STANDARDS_MODE).
I saw at FOSS4G that for OpenLayers 3.x is planned closer interoperability with existing general libraries such as Prototype, jQuery or Closure Library for accessing DOM, operation on strings or other basic functionality - to not reinvent wheel inside of OpenLayers and eliminate duplicate code for the same functionality in final applications, which are already using one of these libraries anyway.
The conditional compilation with constants supported by the Closure compiler can help in this case.
6. Debugging with FireBug
The minimized JavaScript code is normally very hard to debug, because the code is obfuscated and there is no reference to the original formatting and variable names.
Closure Compiler comes with two handy approaches for debugging the code compiled in the ADVANCED_OPTIMIZATION mode:
First is the parameter "--debug=true", which causes that the renamed variables which are normally shortened to one or two letters keep meaningful names - e.g. "OpenLayers.Location.prototype.setValue" becomes $$OpenLayers$Location$$$$$setValue$$, instead of "aa" for example. This parameter is very often used with "--formatting=PRETTY_PRINT".
The second option is usage of the final compiled code together with the Inspector extension for Firefox. Compiler then generate a mapping file between original source code and the compiled code and the extension simplifies debugging with Firebug, once such mapping file is loaded.
7. Dynamically loadable modules
Google Maps API V3 as well as other Google products are compiled to load only a small core (bootstrap) of the necessary functionality and later load extensions with more code.
This approach can significantly speed up the first appearance of a web application, which is important especially on mobile devices.
The Closure compiler of course supports this form of compilation, with parameters on the command line.
The whole project can be compiled with a simple Makefile or custom merge and build scripts, but there are also several tools available for simplifying the compilation, module dependencies and handling of the code which is spread over several files - these tools are useful especially if you are using Closure Library or you need compilation into modules: Plovr, ClosureBuilder, or Closure Modules. With Closure library comes also closurebuilder.py and depswriter.py.
I am very keen to discuss with OpenLayers developers and the community the application of Closure compiler and it's advantages and disadvantages.
We have used the compiler and Closure library already at some projects which are now in production use, so we have practical experience with these tools - for example from the development of the web interface for our MapRank Search product.
If there is an interest I can publish another blog post with a code of a simple project and step-by-step guide demonstrating the use of Closure Compiler and Library.
The Closure tools are definitively worth attention for any web developer. Please write into the comments what do you think about the subject of Closure Compiler and OpenLayers V3.