Chromium Code Reviews| Index: docs/closure_compilation.md |
| diff --git a/docs/closure_compilation.md b/docs/closure_compilation.md |
| index bc1cabd3c5d673f3950c21dbd4f2e1237aeb4f76..d1f0edab5206a0a2c046802a90b9418dd7cc5ab2 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) |
|
groby-ooo-7-16
2016/05/03 05:51:15
Completely unnecessary nitpick of the week: Strict
Dan Beam
2016/05/03 21:24:06
Acknowledged.
|
| +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 eachother, the compiler can warn |
|
groby-ooo-7-16
2016/05/03 05:51:15
nit:each[space]other
Dan Beam
2016/05/03 21:24:05
Done.
|
| +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 loosely-typed and doesn't offer this safety by default. This makes |
|
groby-ooo-7-16
2016/05/03 05:51:15
weakly-typed? Since that's what you start of with?
Dan Beam
2016/05/03 21:24:06
Done.
|
| +writing JavaScript more error prone, and various type errors have resulted in |
| +real bugs seen by many users. |
| -## Background |
| +## Chrome's solution to typechecking JavaScript |
| -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. |
| +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. |
| -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. |
| +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) |
| + |
| +```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,36 +95,67 @@ 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 |
| + |
| +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`. |
| + |
| +Now you should be able to run: |
| + |
| +```shell |
| +third_party/closure_compiler/run_compiler |
| +``` |
| + |
| +and should see output like this: |
| + |
| +```shell |
| +ninja: Entering directory `out/Default/' |
| +[0/1] ACTION Compiling ui/makes_things_pretty.js |
| +``` |
| + |
| +To compile only a specific target, add an argument after the script name: |
| +```shell |
| +third_party/closure_compiler/run_compiler makes_things_pretty |
| ``` |
| -(ERROR) Error in: my_project/my_file.js |
| -## /my/home/chromium/src/my_project/my_file.js:1: ERROR - initializing variable |
| + |
| +In our example code, this error should appear: |
| + |
| +``` |
| +(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; |
| ## ^ |
| ``` |
| -Yay! We can easily find our unexpected type errors and write less error-prone |
| -code! |
| +Hooray! We can catch type errors in JavaScript! |
| -## Continuous Checking |
| +## Integrating with the continuous build |
| -To compile your code on every commit, add a line to |
| -/third_party/closure_compiler/compiled_resources.gyp |
| -like this: |
| +To compile your code on every commit, add your file to the `'dependencies'` list |
| +in `src/third_party/closure_compiler/compiled_resources2.gyp`: |
| ``` |
| { |
| @@ -142,158 +171,28 @@ like this: |
| } |
| ``` |
| -and the |
| +this file is used by the |
|
groby-ooo-7-16
2016/05/03 05:51:15
[T]his...
Dan Beam
2016/05/03 21:24:06
Done.
|
| [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 |
| - |
| -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: |
| +will automatically compile your code on every commit. |
|
groby-ooo-7-16
2016/05/03 05:51:15
And will automatically... or It will automatically
Dan Beam
2016/05/03 21:24:06
Done. (changed to "used by the closure compiler bo
|
| -``` |
| -# 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' ], |
| - }, |
| - ], |
| -} |
| -``` |
| - |
| -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: |
| - |
| -``` |
| -<?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> |
| -``` |
| - |
| -In your C++, the resource can be retrieved like this: |
| - |
| -``` |
| -base::string16 my_script = |
| - base::UTF8ToUTF16( |
| - ResourceBundle::GetSharedInstance() |
| - .GetRawDataResource(IDR_MY_FILE_GEN_JS) |
| - .as_string()); |
| -``` |
| - |
| -## Debugging Compiled JavaScript |
| +## Trying your change |
| -Along with the compiled JavaScript, a source map is created: |
| -`src/out/<Debug|Release>/gen/closure/my_project/my_file.js.map` |
| +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. |
| -Chrome DevTools has built in support for working with source maps: |
| -https://developer.chrome.com/devtools/docs/javascript-debugging#source-maps |
| +From the command line, you can issue a try job with your current diff by: |
|
groby-ooo-7-16
2016/05/03 05:51:15
s/diff/changes
Dan Beam
2016/05/03 21:24:06
Done. (From the command line, you try your change
|
| -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: |
| - |
| -``` |
| -{ |
| -"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"] |
| -} |
| -``` |
| - |
| -sources should be changed to: |
| - |
| -``` |
| -... |
| -"sources":["/tmp/test_script.js"], |
| -... |
| +```shell |
| +git cl try -b 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 |
| - |
| -`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. |
| - |
| -For example, if you would like to specify multiple sources, strict compilation, |
| -and an output wrapper, you would create a |
| +To automatically check that your code typechecks cleanly before submitting, you |
| +can add this line to your CL description: |
| ``` |
| -my_project/compiled_resources.gyp |
| +CQ_INCLUDE_TRYBOTS=tryserver.chromium.linux:closure_compilation |
| ``` |
| -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' :[ |
| - { |
| - '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'], |
| - }, |
| - ], |
| -} |
| -``` |
| +Working in common resource directories in Chrome automatically add this line for |
|
groby-ooo-7-16
2016/05/03 05:51:15
add[s]
Dan Beam
2016/05/03 21:24:06
Done.
|
| +you. |