blob: 4ae3688d92e3281b72612a55992255a052bbc226 [file] [log] [blame] [view]
andybons6eaa0c0d2015-08-26 20:12:521# Closure Compilation
2
dbeamb0b12672016-05-03 21:33:133## What is type safety?
andybons3322f762015-08-24 21:37:094
dbeamb0b12672016-05-03 21:33:135[Strongly-typed languages](https://en.wikipedia.org/wiki/Strong_and_weak_typing)
6like C++ and Java have the notion of variable types.
andybons3322f762015-08-24 21:37:097
dbeamb0b12672016-05-03 21:33:138This is typically baked into how you declare variables:
dbeam707cf272016-03-10 04:12:139
dbeamb0b12672016-05-03 21:33:1310```c++
11const int32 kUniversalAnswer = 42; // type = 32-bit integer
dbeame3d03b7c2016-02-06 03:08:2412```
13
dbeamb0b12672016-05-03 21:33:1314or as [templates](https://en.wikipedia.org/wiki/Template_metaprogramming) for
15containers or generics:
dbeame3d03b7c2016-02-06 03:08:2416
dbeamb0b12672016-05-03 21:33:1317```c++
18std::vector<int64> fibonacci_numbers; // a vector of 64-bit integers
dbeam43f31dd2016-03-09 22:05:5919```
20
dbeamb0b12672016-05-03 21:33:1321When differently-typed variables interact with each other, the compiler can warn
22you if there's no sane default action to take.
dbeam707cf272016-03-10 04:12:1323
dbeamb0b12672016-05-03 21:33:1324Typing can also be manually annotated via mechanisms like `dynamic_cast` and
25`static_cast` or older C-style casts (i.e. `(Type)`).
26
27Using stongly-typed languages provide _some_ level of protection against
28accidentally using variables in the wrong context.
29
30JavaScript is weakly-typed and doesn't offer this safety by default. This makes
31writing JavaScript more error prone, and various type errors have resulted in
32real bugs seen by many users.
33
34## Chrome's solution to typechecking JavaScript
35
36Enter [Closure Compiler](https://developers.google.com/closure/compiler/), a
37tool for analyzing JavaScript and checking for syntax errors, variable
38references, and other common JavaScript pitfalls.
39
40To get the fullest type safety possible, it's often required to annotate your
41JavaScript explicitly with [Closure-flavored @jsdoc
42tags](https://developers.google.com/closure/compiler/docs/js-for-compiler)
43
44```js
45/**
46 * @param {string} version A software version number (i.e. "50.0.2661.94").
qyearsleyc0dc6f42016-12-02 22:13:3947 * @return {!Array<number>} Numbers corresponding to |version| (i.e. [50, 0, 2661, 94]).
dbeamb0b12672016-05-03 21:33:1348 */
49function versionSplit(version) {
50 return version.split('.').map(Number);
51}
dbeam43f31dd2016-03-09 22:05:5952```
53
andybons6eaa0c0d2015-08-26 20:12:5254See also:
55[the design doc](https://docs.google.com/a/chromium.org/document/d/1Ee9ggmp6U-lM-w9WmxN5cSLkK9B5YAq14939Woo-JY0/edit).
andybons3322f762015-08-24 21:37:0956
andybons6eaa0c0d2015-08-26 20:12:5257## Typechecking Your Javascript
andybons3322f762015-08-24 21:37:0958
dbeamb0b12672016-05-03 21:33:1359Given an example file structure of:
andybons3322f762015-08-24 21:37:0960
dbeamb0b12672016-05-03 21:33:1361 + lib/does_the_hard_stuff.js
62 + ui/makes_things_pretty.js
andybons3322f762015-08-24 21:37:0963
dbeamb0b12672016-05-03 21:33:1364`lib/does_the_hard_stuff.js`:
andybons3322f762015-08-24 21:37:0965
andybons6eaa0c0d2015-08-26 20:12:5266```javascript
andybons3322f762015-08-24 21:37:0967var wit = 100;
68
69// ... later on, sneakily ...
70
71wit += ' IQ'; // '100 IQ'
72```
73
dbeamb0b12672016-05-03 21:33:1374`ui/makes_things_pretty.js`:
andybons3322f762015-08-24 21:37:0975
andybons6eaa0c0d2015-08-26 20:12:5276```javascript
andybons3322f762015-08-24 21:37:0977/** @type {number} */ var mensa = wit + 50;
dbeamb0b12672016-05-03 21:33:1378
andybons3322f762015-08-24 21:37:0979alert(mensa); // '100 IQ50' instead of 150
80```
81
dbeamb0b12672016-05-03 21:33:1382Closure compiler can notify us if we're using `string`s and `number`s in
83dangerous ways.
andybons3322f762015-08-24 21:37:0984
dbeamb0b12672016-05-03 21:33:1385To do this, we can create:
andybons3322f762015-08-24 21:37:0986
Christopher Lam213440d2018-06-14 03:26:1887 + ui/BUILD.gn
dbeamb0b12672016-05-03 21:33:1388
89With these contents:
andybons3322f762015-08-24 21:37:0990
91```
Christopher Lam213440d2018-06-14 03:26:1892# Copyright 2018 The Chromium Authors. All rights reserved.
andybons3322f762015-08-24 21:37:0993# Use of this source code is governed by a BSD-style license that can be
94# found in the LICENSE file.
dbeamb0b12672016-05-03 21:33:1395
Christopher Lam213440d2018-06-14 03:26:1896import("//third_party/closure_compiler/compile_js.gni")
dbeamb0b12672016-05-03 21:33:1397
Christopher Lam213440d2018-06-14 03:26:1898js_type_check("closure_compile") {
99 deps = [
100 ":make_things_pretty",
101 ]
102}
dbeamb0b12672016-05-03 21:33:13103
Christopher Lam213440d2018-06-14 03:26:18104js_library("make_things_pretty") {
105 deps = [
106 "../lib:does_the_hard_stuff",
107 ]
108
109 externs_list = [
110 "$externs_path/extern_name_goes_here.js"
111 ]
andybons3322f762015-08-24 21:37:09112}
113```
114
dbeamb0b12672016-05-03 21:33:13115## Running Closure compiler locally
116
117You can locally test that your code compiles on Linux or Mac. This requires
118[Java](http://www.oracle.com/technetwork/java/javase/downloads/index.html) and a
xiaoyin.l1003c0b2016-12-06 02:51:17119[Chrome checkout](https://www.chromium.org/developers/how-tos/get-the-code) (i.e.
dbeamb0b12672016-05-03 21:33:13120python, depot_tools). Note: on Ubuntu, you can probably just run `sudo apt-get
121install openjdk-7-jre`.
122
Christopher Lam213440d2018-06-14 03:26:18123After you set closure_compile = true in your gn args, you should be able to run:
dbeamb0b12672016-05-03 21:33:13124
125```shell
Christopher Lam213440d2018-06-14 03:26:18126ninja -C out/Default webui_closure_compile
dbeamb0b12672016-05-03 21:33:13127```
128
129and should see output like this:
130
131```shell
132ninja: Entering directory `out/Default/'
133[0/1] ACTION Compiling ui/makes_things_pretty.js
134```
135
Christopher Lam213440d2018-06-14 03:26:18136To compile only a specific folder, add an argument after the script name:
dbeamb0b12672016-05-03 21:33:13137
138```shell
Christopher Lam213440d2018-06-14 03:26:18139ninja -C out/Default ui:closure_compile
dbeamb0b12672016-05-03 21:33:13140```
141
142In our example code, this error should appear:
andybons3322f762015-08-24 21:37:09143
144```
dbeamb0b12672016-05-03 21:33:13145(ERROR) Error in: ui/makes_things_pretty.js
146## /my/home/chromium/src/ui/makes_things_pretty.js:1: ERROR - initializing variable
andybons3322f762015-08-24 21:37:09147## found : string
148## required: number
149## /** @type {number} */ var mensa = wit + 50;
150## ^
151```
152
dbeamb0b12672016-05-03 21:33:13153Hooray! We can catch type errors in JavaScript!
andybons3322f762015-08-24 21:37:09154
Christopher Lam213440d2018-06-14 03:26:18155## Preferred BUILD.gn structure
156* Make all individual JS file targets a js\_library.
157* The top level target should be called “closure\_compile”.
158* If you have subfolders that need compiling, make “closure\_compile” a group(),
159 and any files in the current directory a js\_type\_check() called “<directory>\_resources”.
160* Otherwise, just make “closure\_compile” a js\_type\_check with all your js\_library targets as deps
161* Leave all closure targets below other kinds of targets becaure they’re less ‘important’
162
163See also:
164[Closure Compilation with GN](https://docs.google.com/a/chromium.org/document/d/1Ee9ggmp6U-lM-w9WmxN5cSLkK9B5YAq14939Woo-JY0/edit).
165
dbeamb0b12672016-05-03 21:33:13166## Trying your change
andybons3322f762015-08-24 21:37:09167
Christopher Lam213440d2018-06-14 03:26:18168Closure compilation runs in the compile step of Linux, Android and ChromeOS builds.
dbeamb0b12672016-05-03 21:33:13169
170From the command line, you try your change with:
171
172```shell
Christopher Lam213440d2018-06-14 03:26:18173git cl try -b linux_chromium_rel_ng
dbeamb0b12672016-05-03 21:33:13174```
175
dbeamb0b12672016-05-03 21:33:13176## Integrating with the continuous build
177
Christopher Lam213440d2018-06-14 03:26:18178To compile your code on every commit, add your file to the
179`'webui_closure_compile'` target in `src/BUILD.gn`:
andybons3322f762015-08-24 21:37:09180
181```
Christopher Lam213440d2018-06-14 03:26:18182 group("webui_closure_compile") {
183 data_deps = [
184 # Other projects
185 "my/project:closure_compile",
186 ]
187 }
andybons3322f762015-08-24 21:37:09188```
189
michaelpgc1e2d1e2017-05-01 23:40:59190## Externs
191
192[Externs files](https://github.com/google/closure-compiler/wiki/FAQ#how-do-i-write-an-externs-file)
193define APIs external to your JavaScript. They provide the compiler with the type
194information needed to check usage of these APIs in your JavaScript, much like
195forward declarations do in C++.
196
197Third-party libraries like Polymer often provide externs. Chrome must also
198provide externs for its extension APIs. Whenever an extension API's `idl` or
199`json` schema is updated in Chrome, the corresponding externs file must be
200regenerated:
201
202```shell
203./tools/json_schema_compiler/compiler.py -g externs \
204 extensions/common/api/your_api_here.idl \
205 > third_party/closure_compiler/externs/your_api_here.js
206```