Index: docs/closure_compilation.md |
diff --git a/docs/closure_compilation.md b/docs/closure_compilation.md |
index bc1cabd3c5d673f3950c21dbd4f2e1237aeb4f76..5ba5e05e293cff7c2b97332da13f173b3fb1d34c 100644 |
--- a/docs/closure_compilation.md |
+++ b/docs/closure_compilation.md |
@@ -1,73 +1,67 @@ |
# Closure Compilation |
-## I just need to fix the compile! |
+## What is type safety? |
-### Pre-requisites |
+[Strongly-typed languages](https://en.wikipedia.org/wiki/Strong_and_weak_typing) |
+like C++ and Java have the notion of variable types. |
-You'll need Java 7 (preferably the OpenJDK version). To install on Ubuntu: |
+This is typically baked into how you declare variables: |
-```shell |
-sudo apt-get install openjdk-7-jre |
+```c++ |
+const int32 kUniversalAnswer = 42; // type = 32-bit integer |
``` |
-On Mac or Windows, visit: |
-[http://www.oracle.com/technetwork/java/javase/downloads/index.html](http://www.oracle.com/technetwork/java/javase/downloads/index.html) |
- |
-### Using ninja to compile the code |
- |
-To compile the JavaScript, run this script: |
+or as [templates](https://en.wikipedia.org/wiki/Template_metaprogramming) for |
+containers or generics: |
-```shell |
-third_party/closure_compiler/run_compiler |
+```c++ |
+std::vector<int64> fibonacci_numbers; // a vector of 64-bit integers |
``` |
-The output should look something like this: |
+When differently-typed variables interact with each other, the compiler can warn |
+you if there's no sane default action to take. |
-```shell |
-ninja: Entering directory `out/Default/' |
-[30/106] ACTION Compiling chrome/browser/resources/md_history/constants.js |
-``` |
+Typing can also be manually annotated via mechanisms like `dynamic_cast` and |
+`static_cast` or older C-style casts (i.e. `(Type)`). |
-To compile only a specific target, add an argument after the script name: |
+Using stongly-typed languages provide _some_ level of protection against |
+accidentally using variables in the wrong context. |
-```shell |
-third_party/closure_compiler/run_compiler people_page |
-``` |
+JavaScript is weakly-typed and doesn't offer this safety by default. This makes |
+writing JavaScript more error prone, and various type errors have resulted in |
+real bugs seen by many users. |
+ |
+## Chrome's solution to typechecking JavaScript |
-## Background |
+Enter [Closure Compiler](https://developers.google.com/closure/compiler/), a |
+tool for analyzing JavaScript and checking for syntax errors, variable |
+references, and other common JavaScript pitfalls. |
-In C++ and Java, compiling the code gives you _some_ level of protection against |
-misusing variables based on their type information. JavaScript is loosely typed |
-and therefore doesn't offer this safety. This makes writing JavaScript more |
-error prone as it's _one more thing_ to mess up. |
+To get the fullest type safety possible, it's often required to annotate your |
+JavaScript explicitly with [Closure-flavored @jsdoc |
+tags](https://developers.google.com/closure/compiler/docs/js-for-compiler) |
-Because having this safety is handy, Chrome now has a way to optionally |
-typecheck your JavaScript and produce compiled output with |
-[Closure Compiler](https://developers.google.com/closure/compiler/). |
-The type information is |
-[annotated in comment tags](https://developers.google.com/closure/compiler/docs/js-for-compiler) |
-that are briefly described below. |
+```js |
+/** |
+ * @param {string} version A software version number (i.e. "50.0.2661.94"). |
+ * @return {!Array<number>} Numbers corresponing to |version| (i.e. [50, 0, 2661, 94]). |
+ */ |
+function versionSplit(version) { |
+ return version.split('.').map(Number); |
+} |
+``` |
See also: |
[the design doc](https://docs.google.com/a/chromium.org/document/d/1Ee9ggmp6U-lM-w9WmxN5cSLkK9B5YAq14939Woo-JY0/edit). |
-## Assumptions |
- |
-A working Chrome checkout. See here: |
-https://www.chromium.org/developers/how-tos/get-the-code |
- |
## Typechecking Your Javascript |
-So you'd like to compile your JavaScript! |
- |
-Maybe you're working on a page that looks like this: |
+Given an example file structure of: |
-```html |
-<script src="other_file.js"></script> |
-<script src="my_product/my_file.js"></script> |
-``` |
+ + lib/does_the_hard_stuff.js |
+ + ui/makes_things_pretty.js |
-Where `other_file.js` contains: |
+`lib/does_the_hard_stuff.js`: |
```javascript |
var wit = 100; |
@@ -77,18 +71,22 @@ var wit = 100; |
wit += ' IQ'; // '100 IQ' |
``` |
-and `src/my_product/my_file.js` contains: |
+`ui/makes_things_pretty.js`: |
```javascript |
/** @type {number} */ var mensa = wit + 50; |
+ |
alert(mensa); // '100 IQ50' instead of 150 |
``` |
-In order to check that our code acts as we'd expect, we can create a |
+Closure compiler can notify us if we're using `string`s and `number`s in |
+dangerous ways. |
+ |
+To do this, we can create: |
- my_project/compiled_resources2.gyp |
+ + ui/compiled_resources2.gyp |
-with the contents: |
+With these contents: |
``` |
# Copyright 2016 The Chromium Authors. All rights reserved. |
@@ -97,203 +95,104 @@ with the contents: |
{ |
'targets': [ |
{ |
- 'target_name': 'my_file', # file name without ".js" |
- 'dependencies': [ # No need to specify empty lists. |
- '../compiled_resources2.gyp:other_file', |
- '<(EXTERNS_GYP):any_needed_externs' # e.g. chrome.send(), chrome.app.window, etc. |
+ # Target names is typically just without ".js" |
+ 'target_name': 'makes_things_pretty', |
+ |
+ 'dependencies': [ |
+ '../lib/compiled_resources2.gyp:does_the_hard_stuff', |
+ |
+ # Teaches closure about non-standard environments/APIs, e.g. |
+ # chrome.send(), chrome.app.window, etc. |
+ '<(EXTERNS_GYP):extern_name_goes_here' |
], |
- 'includes': ['../third_party/closure_compiler/compile_js2.gypi'], |
+ |
+ 'includes': ['../path/to/third_party/closure_compiler/compile_js2.gypi'], |
}, |
], |
} |
``` |
-You should get results like: |
+## Running Closure compiler locally |
-``` |
-(ERROR) Error in: my_project/my_file.js |
-## /my/home/chromium/src/my_project/my_file.js:1: ERROR - initializing variable |
-## found : string |
-## required: number |
-## /** @type {number} */ var mensa = wit + 50; |
-## ^ |
-``` |
+You can locally test that your code compiles on Linux or Mac. This requires |
+[Java](http://www.oracle.com/technetwork/java/javase/downloads/index.html) and a |
+[Chrome checkout](http://www.chromium.org/developers/how-tos/get-the-code) (i.e. |
+python, depot_tools). Note: on Ubuntu, you can probably just run `sudo apt-get |
+install openjdk-7-jre`. |
-Yay! We can easily find our unexpected type errors and write less error-prone |
-code! |
+Now you should be able to run: |
-## Continuous Checking |
- |
-To compile your code on every commit, add a line to |
-/third_party/closure_compiler/compiled_resources.gyp |
-like this: |
- |
-``` |
-{ |
- 'targets': [ |
- { |
- 'target_name': 'compile_all_resources', |
- 'dependencies': [ |
- # ... other projects ... |
-++ '../my_project/compiled_resources2.gyp:*', |
- ], |
- } |
- ] |
-} |
+```shell |
+third_party/closure_compiler/run_compiler |
``` |
-and the |
-[Closure compiler bot](http://build.chromium.org/p/chromium.fyi/builders/Closure%20Compilation%20Linux) |
-will [re-]compile your code whenever relevant .js files change. |
- |
-## Using Compiled JavaScript |
+and should see output like this: |
-Compiled JavaScript is output in |
-`src/out/<Debug|Release>/gen/closure/my_project/my_file.js` along with a source |
-map for use in debugging. In order to use the compiled JavaScript, we can create |
-a |
- |
- my_project/my_project_resources.gyp |
- |
-with the contents: |
- |
-``` |
-# Copyright 2015 The Chromium Authors. All rights reserved. |
-# Use of this source code is governed by a BSD-style license that can be |
-# found in the LICENSE file. |
- |
-{ |
- 'targets': [ |
- { |
- # GN version: //my_project/resources |
- 'target_name': 'my_project_resources', |
- 'type': 'none', |
- 'variables': { |
- 'grit_out_dir': '<(SHARED_INTERMEDIATE_DIR)/my_project', |
- 'my_file_gen_js': '<(SHARED_INTERMEDIATE_DIR)/closure/my_project/my_file.js', |
- }, |
- 'actions': [ |
- { |
- # GN version: //my_project/resources:my_project_resources |
- 'action_name': 'generate_my_project_resources', |
- 'variables': { |
- 'grit_grd_file': 'resources/my_project_resources.grd', |
- 'grit_additional_defines': [ |
- '-E', 'my_file_gen_js=<(my_file_gen_js)', |
- ], |
- }, |
- 'includes': [ '../build/grit_action.gypi' ], |
- }, |
- ], |
- 'includes': [ '../build/grit_target.gypi' ], |
- }, |
- ], |
-} |
+```shell |
+ninja: Entering directory `out/Default/' |
+[0/1] ACTION Compiling ui/makes_things_pretty.js |
``` |
-The variables can also be defined in an existing .gyp file if appropriate. The |
-variables can then be used in to create a |
- |
- my_project/my_project_resources.grd |
- |
-with the contents: |
+To compile only a specific target, add an argument after the script name: |
-``` |
-<?xml version="1.0" encoding="utf-8"?> |
-<grit-part> |
- <include name="IDR_MY_FILE_GEN_JS" file="${my_file_gen_js}" use_base_dir="false" type="BINDATA" /> |
-</grit-part> |
+```shell |
+third_party/closure_compiler/run_compiler makes_things_pretty |
``` |
-In your C++, the resource can be retrieved like this: |
+In our example code, this error should appear: |
``` |
-base::string16 my_script = |
- base::UTF8ToUTF16( |
- ResourceBundle::GetSharedInstance() |
- .GetRawDataResource(IDR_MY_FILE_GEN_JS) |
- .as_string()); |
+(ERROR) Error in: ui/makes_things_pretty.js |
+## /my/home/chromium/src/ui/makes_things_pretty.js:1: ERROR - initializing variable |
+## found : string |
+## required: number |
+## /** @type {number} */ var mensa = wit + 50; |
+## ^ |
``` |
-## Debugging Compiled JavaScript |
+Hooray! We can catch type errors in JavaScript! |
-Along with the compiled JavaScript, a source map is created: |
-`src/out/<Debug|Release>/gen/closure/my_project/my_file.js.map` |
+## Trying your change |
-Chrome DevTools has built in support for working with source maps: |
-https://developer.chrome.com/devtools/docs/javascript-debugging#source-maps |
+Closure compilation also has [try |
+bots](https://build.chromium.org/p/tryserver.chromium.linux/builders/closure_compilation) |
+which can check whether you could *would* break the build if it was committed. |
-In order to use the source map, you must first manually edit the path to the |
-'sources' in the .js.map file that was generated. For example, if the source map |
-looks like this: |
+From the command line, you try your change with: |
-``` |
-{ |
-"version":3, |
-"file":"/tmp/gen/test_script.js", |
-"lineCount":1, |
-"mappings":"A,aAAA,IAAIA,OAASA,QAAQ,EAAG,CACtBC,KAAA,CAAM,OAAN,CADsB;", |
-"sources":["/tmp/tmp70_QUi"], |
-"names":["fooBar","alert"] |
-} |
+```shell |
+git cl try -b closure_compilation |
``` |
-sources should be changed to: |
+To automatically check that your code typechecks cleanly before submitting, you |
+can add this line to your CL description: |
``` |
-... |
-"sources":["/tmp/test_script.js"], |
-... |
+CQ_INCLUDE_TRYBOTS=tryserver.chromium.linux:closure_compilation |
``` |
-In your browser, the source map can be loaded through the Chrome DevTools |
-context menu that appears when you right click in the compiled JavaScript source |
-body. A dialog will pop up prompting you for the path to the source map file. |
-Once the source map is loaded, the uncompiled version of the JavaScript will |
-appear in the Sources panel on the left. You can set break points in the |
-uncompiled version to help debug; behind the scenes Chrome will still be running |
-the compiled version of the JavaScript. |
- |
-## Additional Arguments |
+Working in common resource directories in Chrome automatically adds this line |
+for you. |
-`compile_js.gypi` accepts an optional `script_args` variable, which passes |
-additional arguments to `compile.py`, as well as an optional `closure_args` |
-variable, which passes additional arguments to the closure compiler. You may |
-also override the `disabled_closure_args` for more strict compilation. |
+## Integrating with the continuous build |
-For example, if you would like to specify multiple sources, strict compilation, |
-and an output wrapper, you would create a |
+To compile your code on every commit, add your file to the `'dependencies'` list |
+in `src/third_party/closure_compiler/compiled_resources2.gyp`: |
``` |
-my_project/compiled_resources.gyp |
-``` |
- |
-with contents similar to this: |
- |
-``` |
-# Copyright 2015 The Chromium Authors. All rights reserved. |
-# Use of this source code is governed by a BSD-style license that can be |
-# found in the LICENSE file. |
{ |
- 'targets' :[ |
+ 'targets': [ |
{ |
- 'target_name': 'my_file', |
- 'variables': { |
- 'source_files': [ |
- 'my_file.js', |
- 'my_file2.js', |
- ], |
- 'script_args': ['--no-single-file'], # required to process multiple files at once |
- 'closure_args': [ |
- 'output_wrapper=\'(function(){%output%})();\'', |
- 'jscomp_error=reportUnknownTypes', # the following three provide more strict compilation |
- 'jscomp_error=duplicate', |
- 'jscomp_error=misplacedTypeAnnotation', |
- ], |
- 'disabled_closure_args': [], # remove the disabled closure args for more strict compilation |
- }, |
- 'includes': ['../third_party/closure_compiler/compile_js.gypi'], |
- }, |
- ], |
+ 'target_name': 'compile_all_resources', |
+ 'dependencies': [ |
+ # ... other projects ... |
+++ '../my_project/compiled_resources2.gyp:*', |
+ ], |
+ } |
+ ] |
} |
``` |
+ |
+This file is used by the |
+[Closure compiler bot](http://build.chromium.org/p/chromium.fyi/builders/Closure%20Compilation%20Linux) |
+to automatically compile your code on every commit. |