Getting Started with the Closure Library illustrates how to start working with Closure Library in your project. This article outlines how to use ClosureBuilder and Closure Compiler with your project to shrink your files into one, small, minimized JavaScript file.
Why closurebuilder.py?
If you did the Hello World exercise
in Getting Started with the Closure
Library, you may have noticed that the browser made several HTTP
requests during page load. For each Closure Library file required by
goog.require()
, a new script tag is created to
load the file. Each Closure Library file is loaded as a separate HTTP request.
This works well for development, since one can see the uncompiled source and use existing debugging tools. But it's not good for serving to users. Loading all these files individually is unnecessary and slow.
For a live application serving to real users, you're going to want to serve a "compiled" version of your app. Closure Compiler takes your code and the required Closure Library files, strip comments, does some optimizations, and creates one larger file as output. Your server can then just serve that one file rather than many individual files.
The closurebuilder.py
tool, located in
closure/bin/build
, is a tool to help build compiled JavaScript files.
It scans your files and the Closure Library files and can calculate orderings
of script files in dependency order. It can output either the scripts'
filenames or their contents in dependency order, or can pass the files along to
the Closure Compiler to produce a compiled version.
Resolving Closure Library Dependencies with closurebuilder.py
This example uses closurebuilder.py
to generate a single script
file containing everything the script needs.
If you haven't already done so, follow the instructions
in Getting Started with the
Closure Library to download and unpack the Closure Library.
In the directory in which you saved closure-library
,
create a directory myproject
for your new project.
In the myproject
directory, make a start.js
file
containing the following code.
goog.provide('myproject.start'); goog.require('goog.dom'); goog.require('goog.dom.TagName'); myproject.start = function() { var newDiv = goog.dom.createDom(goog.dom.TagName.H1, {'style': 'background-color:#EEE'}, 'Hello world!'); goog.dom.appendChild(document.body, newDiv); }; // Ensures the symbol will be visible after compiler renaming. goog.exportSymbol('myproject.start', myproject.start);
In the same directory, make a a simple HTML file named
myproject.html
to run this script.
<!doctype html> <html> <head> <script src="../closure-library/closure/goog/base.js"></script> <script src="start.js"></script> </head> <body> <script> myproject.start(); </script> </body> </html>
When you load this page in a browser, you should see the message "Hello world!"
Calculating dependencies
Execute the following command from the directory containing both the
closure-library
and myproject
directories.
closure-library/closure/bin/build/closurebuilder.py \ --root=closure-library/ \ --root=myproject/ \ --namespace="myproject.start"
Note: these tools are executable Python scripts and are set to be
interpreted with /usr/bin/env
on UNIX systems. If you're using
Windows, see Python's
documentation on running Python programs on Windows.
The output should look like this (this is combined stdout and stderr):
closure-library/closure/bin/build/closurebuilder.py: Scanning paths... closure-library/closure/bin/build/closurebuilder.py: 596 sources scanned. closure-library/closure/bin/build/closurebuilder.py: Building dependency tree.. closure-library/closure/goog/base.js closure-library/closure/goog/debug/error.js closure-library/closure/goog/string/string.js closure-library/closure/goog/asserts/asserts.js closure-library/closure/goog/array/array.js closure-library/closure/goog/dom/classes.js closure-library/closure/goog/object/object.js closure-library/closure/goog/dom/tagname.js closure-library/closure/goog/useragent/useragent.js closure-library/closure/goog/math/size.js closure-library/closure/goog/math/coordinate.js closure-library/closure/goog/dom/dom.js myproject/start.js
What is this doing?
Each --root
flag tells ClosureBuilder to scan that directory for
.js
files. Each file is scanned for goog.provide
and goog.require
statements, which indicate that the file
provides a namespace or requires a namespace provided in
another file.
Scanning all files allows ClosureBuilder to build an in-memory dependency tree
of all namespaces. The --namespace
flag tells ClosureBuilder to
start with the myproject.start
namespace in that tree and get
all of its dependencies. By default, it outputs the results as a list of files,
in dependency order. For every file in the list, every required
namespace is provided by another file that appears earlier in the list.
Shrinking Code with ClosureBuilder and the Closure Compiler
To get a compiled and minified version of your code, use
the --output_mode
Closure Compiler documentation.
To get compiled output, you'll need the Closure Compiler executable jar
file and you'll need to tell ClosureBuilder where it is.
Download the jar from the repository and put it in the same directory
that contains your closure-library
directory.
Re-run closurebuilder.py
with two additional flags to request
compiled output and to indicate the location of compiler.jar
.
closure-library/closure/bin/build/closurebuilder.py \ --root=closure-library/ \ --root=myproject/ \ --namespace="myproject.start" \ --output_mode=compiled \ --compiler_jar=compiler.jar \ > myproject/start-compiled.js
ClosureBuilder writes the compiler's output to standard out. Here,
we're piping it into a file named start-compiled.js
.
Alternatively, you can specify a file to write to using the
--output_file
flag (this may be easier if you're unfamiliar
with Unix-style piping).
Put start-compiled.js
next to myproject.html
and
modify the HTML file to instead use the compiled version:
<!doctype html> <html> <head> <script src="start-compiled.js"></script> </head> <body> <script> myproject.start(); </script> </body> </html>
This file behaves the same as your original code, but it can be downloaded as one small JavaScript file.
Using ClosureBuilder in web servers and build systems
Generally, developers using Closure Library implement the ability to serve in debug and compiled modes.
- In debug mode, the server serves the uncompiled files. In the
HTML, the
head
tag includesscript
tags that point at Closure Library'sbase.js
and the project.js
files, as in the example above. There may also be additional dependency files as described in the DepsWriter documentation. - In compiled mode, the server does not serve the uncompiled files.
In the
head
tag, there is just onescript
tag that points at the compiled JavaScript file, as in the example above.
The served web page should have the appropriate <script>
tags for each mode.
It's much easier to work with uncompiled code when developing. You can edit and save your files and reload to see changes. Errors give correct line numbers and you can use in-browser debuggers with uncompiled, unobfuscated source.
But when serving your application, the compiled version is preferable. Compiled output loads quickly and speed is very important when you serve to users. The compiler optimizes the size of the JavaScript and puts it in one small file rather than many uncompiled files.
Use in a build system
ClosureBuilder is provided as a standalone command-line tool so that it can be incorporated into different build systems. You can set up ClosureBuilder's output to be a step in your build system.
Additional flags to Closure Compiler
Closure Compiler takes its own additional flags for particular features, so
ClosureBuilder has a way of specifying flags to pass along to the compiler.
This can be done using the --compiler_flags
flag.
For example, we can tell the compiler to use its advanced optimization mode and
to specify an additional file, deps.js
, to include in the
compilation.
closure-library/closure/bin/build/closurebuilder.py \ --root=closure-library/ \ --root=myproject/ \ --namespace="myproject.start" \ --output_mode=compiled \ --compiler_jar=compiler.jar \ --compiler_flags="--js=closure-library/closure/goog/deps.js" \ --compiler_flags="--compilation_level=ADVANCED_OPTIMIZATIONS" \ > start-compiled.js
See Advanced
Compilation and Externs in the Closure Compiler documentation
for more information about
using ADVANCED_OPTIMIZATIONS
.
See Using DepsWriter for more
information on deps.js
. In general though, it's a good idea to
pass Closure's deps.js
file directly to the compiler. This is a
way to forward declare types that may not be directly
goog.require()
'd in some Closure files, and will help avoid
confusing errors if attempting to compile with
type checking enabled.
Similarly, if you are writing your own JavaScript library based on Closure, you
may also need to generate a deps file for your library and then specifically
include it as well.
ClosureBuilder option reference
There are other options for ClosureBuilder that aren't documented here. You
can always get a full list by using the --help
flag.
closure-library/closure/bin/build/closurebuilder.py --help
For convenience, the output is copied here:
$ closure/bin/build/closurebuilder.py --help Usage: Utility for Closure Library dependency calculation. ClosureBuilder scans source files to build dependency info. From the dependencies, the script can produce a deps.js file, a manifest in dependency order, a concatenated script, or compiled output from the Closure Compiler. Paths to files can be expressed as individual arguments to the tool (intended for use with find and xargs). As a convenience, --root can be used to specify all JS files below a directory. usage: closurebuilder.py [options] [file1.js file2.js ...] Options: -h, --help show this help message and exit -i INPUTS, --input=INPUTS One or more input files to calculate dependencies for. The namespaces in this file will be combined with those given with the -n flag to form the set of namespaces to find dependencies for. -n NAMESPACES, --namespace=NAMESPACES One or more namespaces to calculate dependencies for. These namespaces will be combined with those given with the -i flag to form the set of namespaces to find dependencies for. A Closure namespace is a dot- delimited path expression declared with a call to goog.provide() (e.g. "goog.array" or "foo.bar"). --root=ROOTS The paths that should be traversed to build the dependencies. -o OUTPUT_MODE, --output_mode=OUTPUT_MODE The type of output to generate from this script. Options are "list" for a list of filenames, "script" for a single script containing the contents of all the files, or "compiled" to produce compiled output with the Closure Compiler. Default is "list". -c COMPILER_JAR, --compiler_jar=COMPILER_JAR The location of the Closure compiler .jar file. -f COMPILER_FLAGS, --compiler_flags=COMPILER_FLAGS Additional flags to pass to the Closure compiler. --output_file=OUTPUT_FILE If specified, write output to this path instead of writing to standard output.