-
Notifications
You must be signed in to change notification settings - Fork 24
Getting Started
We assume you have an existing Angular application. If not, you can use the Angular CLI to create one.
Your application needs to work with Angular's Ahead-of-time compiler, which is a requirement for using Closure Compiler.
You'll need recent versions of the dependencies:
- Angular 4.1
- Zone.js 0.8.10
- rxjs 5.3.1
- google-closure-compiler 20170409 (20170423 is broken)
You'll need to have Java installed on your computer. Zulu's OpenJDK is one option. We hope to remove the need for this step in the future.
Add the google-closure-compiler package as a devDependency
in your project, using your package tool (eg. yarn or npm).
Note that the google-closure-compiler-js project does not work due to a number of open issues.
We'll store command-line flags in the closure.conf
file. These flags are documented in various places on https://github.com/google/closure-compiler/wiki and can also be seen by running Closure Compiler with the --help
option.
$ java -jar node_modules/google-closure-compiler/compiler.jar --help
The full set of required options is in the example closure.conf. Below is a description of the options required:
-
--compilation_level=ADVANCED_OPTIMIZATIONS
: Enable property renaming and other optimizations. Using any other value is missing the point: you won't get a smaller bundle than with other tools. -
--language_out=ES5
: We want ES2015-to-ES5 transpilation (like Babel) -
--js_output_file=dist/bundle.js
: where to write the bundle. Make sure your development server serves this so that the<script>
tag in the HTML can load it.
-
--entry_point=./built/bootstrap
: points to the location of the bootstrap .js file. -
--dependency_mode=STRICT
: turn on dead-file elimination. Any file that's not in the transitive imports of your entry point will be dropped, even if it has some side-effects.
Unlike most tools, Closure Compiler does not locate input files on disk (eg. in the node_modules
folder) which are not specified. So we exhaustively list the inputs, using a glob syntax
Internally, Closure compiler converts our ES2015 modules (which are file-based) into an internal module system (goog.module
). Later when it encounters an import
statement, the module name must match. This requires trimming a prefix off the file paths, so that a file at prefix/path
is imported by import 'path`` rather than
import 'prefix$path'`.
node_modules/@angular/core/@angular/core.js
--js_module_root=node_modules/@angular/core
node_modules/@angular/common/@angular/common.js
--js_module_root=node_modules/@angular/common
node_modules/@angular/platform-browser/@angular/platform-browser.js
--js_module_root=node_modules/@angular/platform-browser
Also point to the .js files that were written by the build:
--js built/**.js
Finally, we need to avoid Closure Compiler renaming anything that will be loaded externally, since such renaming would not be applied to all usage sites. This includes scripts we load separately (like zone.js) and interactions we'll have with the page at runtime (testing with protractor).
Closure Compiler uses "externs" files to specify these external, non-renamable identifiers. We can just pass the bare file paths as flags:
node_modules/zone.js/dist/zone_externs.js
node_modules/@angular/core/src/testability/testability.externs.js
The following options produce some extra files to help you understand which inputs were retained, how the properties were renamed, and enable sourcemap for easier debugging.
--output_manifest=dist/manifest.MF
--variable_renaming_report=dist/variable_renaming_report
--property_renaming_report=dist/property_renaming_report
--create_source_map=%outname%.map
Finally, we turn off the warnings produced by the compiler. It expects to type-check your code, but TypeScript has already done that.
--warning_level=QUIET
Closure compiler has some restrictions on the input JS files, so we tell Angular to transform all the TypeScript files to conform. For example, we add JSDoc comments to transport some type information. Add this to your tsconfig.json
file at the top-level (a sibling of the "compilerOptions"
property):
"angularCompilerOptions": {
"annotateForClosureCompiler": true
}
First, we must run the Angular and TypeScript compilers to convert .ts
and .html
inputs into JavaScript. We can run the ngc
command.
$ ./node_modules/.bin/ngc -p path/to/tsconfig.json
Now we can run Closure Compiler, passing the file of flags:
$ java -jar node_modules/google-closure-compiler/compiler.jar --flagfile closure.conf
For convenience, you can wrap these in a package.json
script:
"scripts": {
"build": "ngc -p src && yarn run closure",
"closure": "java -jar node_modules/google-closure-compiler/compiler.jar --flagfile closure.conf",
},