blob: 4f403cdea60bf295a9aa492c6d32dc56a456b869 [file] [log] [blame] [view]
andybons6eaa0c0d2015-08-26 20:12:521# Closure Compilation
2
3## I just need to fix the compile!
andybons3322f762015-08-24 21:37:094
5To locally run closure compiler like the bots, do this:
6
andybons6eaa0c0d2015-08-26 20:12:527```shell
andybons3322f762015-08-24 21:37:098cd $CHROMIUM_SRC
9# sudo apt-get install openjdk-7-jre # may be required
andybons6eaa0c0d2015-08-26 20:12:5210GYP_GENERATORS=ninja tools/gyp/gyp --depth . \
11third_party/closure_compiler/compiled_resources.gyp
andybons3322f762015-08-24 21:37:0912ninja -C out/Default
13```
14
andybons6eaa0c0d2015-08-26 20:12:5215## Background
andybons3322f762015-08-24 21:37:0916
andybons6eaa0c0d2015-08-26 20:12:5217In C++ and Java, compiling the code gives you _some_ level of protection against
18misusing variables based on their type information. JavaScript is loosely typed
19and therefore doesn't offer this safety. This makes writing JavaScript more
20error prone as it's _one more thing_ to mess up.
andybons3322f762015-08-24 21:37:0921
andybons6eaa0c0d2015-08-26 20:12:5222Because having this safety is handy, Chrome now has a way to optionally
23typecheck your JavaScript and produce compiled output with
24[Closure Compiler](https://developers.google.com/closure/compiler/).
dschuyler630436142015-09-22 05:10:1625The type information is
26[annotated in comment tags](https://developers.google.com/closure/compiler/docs/js-for-compiler)
27that are briefly described below.
andybons3322f762015-08-24 21:37:0928
andybons6eaa0c0d2015-08-26 20:12:5229See also:
30[the design doc](https://docs.google.com/a/chromium.org/document/d/1Ee9ggmp6U-lM-w9WmxN5cSLkK9B5YAq14939Woo-JY0/edit).
andybons3322f762015-08-24 21:37:0931
andybons6eaa0c0d2015-08-26 20:12:5232## Assumptions
andybons3322f762015-08-24 21:37:0933
andybons6eaa0c0d2015-08-26 20:12:5234A working Chrome checkout. See here:
35http://www.chromium.org/developers/how-tos/get-the-code
andybons3322f762015-08-24 21:37:0936
andybons6eaa0c0d2015-08-26 20:12:5237## Typechecking Your Javascript
andybons3322f762015-08-24 21:37:0938
39So you'd like to compile your JavaScript!
40
41Maybe you're working on a page that looks like this:
42
andybons6eaa0c0d2015-08-26 20:12:5243```html
andybons3322f762015-08-24 21:37:0944<script src="other_file.js"></script>
45<script src="my_product/my_file.js"></script>
46```
47
48Where `other_file.js` contains:
49
andybons6eaa0c0d2015-08-26 20:12:5250```javascript
andybons3322f762015-08-24 21:37:0951var wit = 100;
52
53// ... later on, sneakily ...
54
55wit += ' IQ'; // '100 IQ'
56```
57
58and `src/my_product/my_file.js` contains:
59
andybons6eaa0c0d2015-08-26 20:12:5260```javascript
andybons3322f762015-08-24 21:37:0961/** @type {number} */ var mensa = wit + 50;
62alert(mensa); // '100 IQ50' instead of 150
63```
64
65In order to check that our code acts as we'd expect, we can create a
66
andybons6eaa0c0d2015-08-26 20:12:5267 my_project/compiled_resources.gyp
andybons3322f762015-08-24 21:37:0968
69with the contents:
70
71```
72# Copyright 2015 The Chromium Authors. All rights reserved.
73# Use of this source code is governed by a BSD-style license that can be
74# found in the LICENSE file.
75{
76 'targets': [
77 {
78 'target_name': 'my_file', # file name without ".js"
79
80 'variables': { # Only use if necessary (no need to specify empty lists).
81 'depends': [
82 'other_file.js', # or 'other_project/compiled_resources.gyp:target',
83 ],
84 'externs': [
85 '<(CLOSURE_DIR)/externs/any_needed_externs.js' # e.g. chrome.send(), chrome.app.window, etc.
86 ],
87 },
88
89 'includes': ['../third_party/closure_compiler/compile_js.gypi'],
90 },
91 ],
92}
93```
94
95You should get results like:
96
97```
98(ERROR) Error in: my_project/my_file.js
99## /my/home/chromium/src/my_project/my_file.js:1: ERROR - initializing variable
100## found : string
101## required: number
102## /** @type {number} */ var mensa = wit + 50;
103## ^
104```
105
andybons6eaa0c0d2015-08-26 20:12:52106Yay! We can easily find our unexpected type errors and write less error-prone
107code!
andybons3322f762015-08-24 21:37:09108
andybons6eaa0c0d2015-08-26 20:12:52109## Continuous Checking
andybons3322f762015-08-24 21:37:09110
andybons6eaa0c0d2015-08-26 20:12:52111To compile your code on every commit, add a line to
112/third_party/closure_compiler/compiled_resources.gyp
113like this:
andybons3322f762015-08-24 21:37:09114
115```
116{
117 'targets': [
118 {
119 'target_name': 'compile_all_resources',
120 'dependencies': [
121 # ... other projects ...
122++ '../my_project/compiled_resources.gyp:*',
123 ],
124 }
125 ]
126}
127```
128
andybons6eaa0c0d2015-08-26 20:12:52129and the
130[Closure compiler bot](http://build.chromium.org/p/chromium.fyi/builders/Closure%20Compilation%20Linux)
131will [re-]compile your code whenever relevant .js files change.
andybons3322f762015-08-24 21:37:09132
andybons6eaa0c0d2015-08-26 20:12:52133## Using Compiled JavaScript
andybons3322f762015-08-24 21:37:09134
andybons6eaa0c0d2015-08-26 20:12:52135Compiled JavaScript is output in
136`src/out/<Debug|Release>/gen/closure/my_project/my_file.js` along with a source
137map for use in debugging. In order to use the compiled JavaScript, we can create
138a
andybons3322f762015-08-24 21:37:09139
andybons6eaa0c0d2015-08-26 20:12:52140 my_project/my_project_resources.gpy
andybons3322f762015-08-24 21:37:09141
142with the contents:
143
144```
145# Copyright 2015 The Chromium Authors. All rights reserved.
146# Use of this source code is governed by a BSD-style license that can be
147# found in the LICENSE file.
148
149{
150 'targets': [
151 {
152 # GN version: //my_project/resources
153 'target_name': 'my_project_resources',
154 'type': 'none',
155 'variables': {
156 'grit_out_dir': '<(SHARED_INTERMEDIATE_DIR)/my_project',
157 'my_file_gen_js': '<(SHARED_INTERMEDIATE_DIR)/closure/my_project/my_file.js',
158 },
159 'actions': [
160 {
161 # GN version: //my_project/resources:my_project_resources
162 'action_name': 'generate_my_project_resources',
163 'variables': {
164 'grit_grd_file': 'resources/my_project_resources.grd',
165 'grit_additional_defines': [
166 '-E', 'my_file_gen_js=<(my_file_gen_js)',
167 ],
168 },
169 'includes': [ '../build/grit_action.gypi' ],
170 },
171 ],
172 'includes': [ '../build/grit_target.gypi' ],
173 },
174 ],
175}
176```
177
andybons6eaa0c0d2015-08-26 20:12:52178The variables can also be defined in an existing .gyp file if appropriate. The
179variables can then be used in to create a
andybons3322f762015-08-24 21:37:09180
andybons6eaa0c0d2015-08-26 20:12:52181 my_project/my_project_resources.grd
andybons3322f762015-08-24 21:37:09182
183with the contents:
184
185```
186<?xml version="1.0" encoding="utf-8"?>
187<grit-part>
188 <include name="IDR_MY_FILE_GEN_JS" file="${my_file_gen_js}" use_base_dir="false" type="BINDATA" />
189</grit-part>
190```
191
192In your C++, the resource can be retrieved like this:
193```
194base::string16 my_script =
195 base::UTF8ToUTF16(
196 ResourceBundle::GetSharedInstance()
197 .GetRawDataResource(IDR_MY_FILE_GEN_JS)
198 .as_string());
199```
200
andybons6eaa0c0d2015-08-26 20:12:52201## Debugging Compiled JavaScript
andybons3322f762015-08-24 21:37:09202
andybons6eaa0c0d2015-08-26 20:12:52203Along with the compiled JavaScript, a source map is created:
204`src/out/<Debug|Release>/gen/closure/my_project/my_file.js.map`
andybons3322f762015-08-24 21:37:09205
andybons6eaa0c0d2015-08-26 20:12:52206Chrome DevTools has built in support for working with source maps:
207https://developer.chrome.com/devtools/docs/javascript-debugging#source-maps
andybons3322f762015-08-24 21:37:09208
andybons6eaa0c0d2015-08-26 20:12:52209In order to use the source map, you must first manually edit the path to the
210'sources' in the .js.map file that was generated. For example, if the source map
211looks like this:
212
andybons3322f762015-08-24 21:37:09213```
214{
215"version":3,
216"file":"/tmp/gen/test_script.js",
217"lineCount":1,
218"mappings":"A,aAAA,IAAIA,OAASA,QAAQ,EAAG,CACtBC,KAAA,CAAM,OAAN,CADsB;",
219"sources":["/tmp/tmp70_QUi"],
220"names":["fooBar","alert"]
221}
222```
223
224sources should be changed to:
andybons6eaa0c0d2015-08-26 20:12:52225
andybons3322f762015-08-24 21:37:09226```
227...
228"sources":["/tmp/test_script.js"],
229...
230```
231
andybons6eaa0c0d2015-08-26 20:12:52232In your browser, the source map can be loaded through the Chrome DevTools
233context menu that appears when you right click in the compiled JavaScript source
234body. A dialog will pop up prompting you for the path to the source map file.
235Once the source map is loaded, the uncompiled version of the JavaScript will
236appear in the Sources panel on the left. You can set break points in the
237uncompiled version to help debug; behind the scenes Chrome will still be running
238the compiled version of the JavaScript.
andybons3322f762015-08-24 21:37:09239
andybons6eaa0c0d2015-08-26 20:12:52240## Additional Arguments
andybons3322f762015-08-24 21:37:09241
andybons6eaa0c0d2015-08-26 20:12:52242`compile_js.gypi` accepts an optional `script_args` variable, which passes
243additional arguments to `compile.py`, as well as an optional `closure_args`
244variable, which passes additional arguments to the closure compiler. You may
245also override the `disabled_closure_args` for more strict compilation.
andybons3322f762015-08-24 21:37:09246
andybons6eaa0c0d2015-08-26 20:12:52247For example, if you would like to specify multiple sources, strict compilation,
248and an output wrapper, you would create a
andybons3322f762015-08-24 21:37:09249
250```
251my_project/compiled_resources.gyp
252```
253
254with contents similar to this:
255```
256# Copyright 2015 The Chromium Authors. All rights reserved.
257# Use of this source code is governed by a BSD-style license that can be
258# found in the LICENSE file.
259{
260 'targets' :[
261 {
262 'target_name': 'my_file',
263 'variables': {
264 'source_files': [
265 'my_file.js',
266 'my_file2.js',
267 ],
268 'script_args': ['--no-single-file'], # required to process multiple files at once
269 'closure_args': [
270 'output_wrapper=\'(function(){%output%})();\'',
271 'jscomp_error=reportUnknownTypes', # the following three provide more strict compilation
272 'jscomp_error=duplicate',
273 'jscomp_error=misplacedTypeAnnotation',
274 ],
275 'disabled_closure_args': [], # remove the disabled closure args for more strict compilation
276 },
277 'includes': ['../third_party/closure_compiler/compile_js.gypi'],
278 },
279 ],
280}
andybons6eaa0c0d2015-08-26 20:12:52281```