blob: 2067c48866783966e7fac8220eaeeb087985cd55 [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
cfredric477f29af2021-10-07 23:09:195[Statically-typed languages](https://en.wikipedia.org/wiki/Type_system#Static_type_checking)
dbeamb0b12672016-05-03 21:33:136like 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
cfredric477f29af2021-10-07 23:09:1927Using statically-typed languages provides _some_ level of protection against
dbeamb0b12672016-05-03 21:33:1328accidentally using variables in the wrong context.
29
cfredric477f29af2021-10-07 23:09:1930JavaScript is dynamically-typed and doesn't offer this safety by default. This
31makes writing JavaScript more error prone, and various type errors have resulted
32in real bugs seen by many users.
dbeamb0b12672016-05-03 21:33:1333
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
Victor Vianna2e4555c2020-10-20 06:33:11123First, add the following to your GN args:
124```
125enable_js_type_check = true
126```
127Then you should be able to run:
dbeamb0b12672016-05-03 21:33:13128
129```shell
Christopher Lam213440d2018-06-14 03:26:18130ninja -C out/Default webui_closure_compile
dbeamb0b12672016-05-03 21:33:13131```
132
133and should see output like this:
134
135```shell
136ninja: Entering directory `out/Default/'
137[0/1] ACTION Compiling ui/makes_things_pretty.js
138```
139
Christopher Lam213440d2018-06-14 03:26:18140To compile only a specific folder, add an argument after the script name:
dbeamb0b12672016-05-03 21:33:13141
142```shell
Christopher Lam213440d2018-06-14 03:26:18143ninja -C out/Default ui:closure_compile
dbeamb0b12672016-05-03 21:33:13144```
145
146In our example code, this error should appear:
andybons3322f762015-08-24 21:37:09147
148```
dbeamb0b12672016-05-03 21:33:13149(ERROR) Error in: ui/makes_things_pretty.js
150## /my/home/chromium/src/ui/makes_things_pretty.js:1: ERROR - initializing variable
andybons3322f762015-08-24 21:37:09151## found : string
152## required: number
153## /** @type {number} */ var mensa = wit + 50;
154## ^
155```
156
dbeamb0b12672016-05-03 21:33:13157Hooray! We can catch type errors in JavaScript!
andybons3322f762015-08-24 21:37:09158
Christopher Lam213440d2018-06-14 03:26:18159## Preferred BUILD.gn structure
160* Make all individual JS file targets a js\_library.
161* The top level target should be called “closure\_compile”.
162* If you have subfolders that need compiling, make “closure\_compile” a group(),
163 and any files in the current directory a js\_type\_check() called “<directory>\_resources”.
164* Otherwise, just make “closure\_compile” a js\_type\_check with all your js\_library targets as deps
165* Leave all closure targets below other kinds of targets becaure they’re less ‘important’
166
167See also:
168[Closure Compilation with GN](https://docs.google.com/a/chromium.org/document/d/1Ee9ggmp6U-lM-w9WmxN5cSLkK9B5YAq14939Woo-JY0/edit).
169
dbeamb0b12672016-05-03 21:33:13170## Trying your change
andybons3322f762015-08-24 21:37:09171
Christopher Lam213440d2018-06-14 03:26:18172Closure compilation runs in the compile step of Linux, Android and ChromeOS builds.
dbeamb0b12672016-05-03 21:33:13173
174From the command line, you try your change with:
175
176```shell
Stephen Martinis089f5f02019-02-12 02:42:24177git cl try -b linux-rel
dbeamb0b12672016-05-03 21:33:13178```
179
dbeamb0b12672016-05-03 21:33:13180## Integrating with the continuous build
181
Christopher Lam213440d2018-06-14 03:26:18182To compile your code on every commit, add your file to the
183`'webui_closure_compile'` target in `src/BUILD.gn`:
andybons3322f762015-08-24 21:37:09184
185```
Christopher Lam213440d2018-06-14 03:26:18186 group("webui_closure_compile") {
187 data_deps = [
188 # Other projects
189 "my/project:closure_compile",
190 ]
191 }
andybons3322f762015-08-24 21:37:09192```
193
michaelpgc1e2d1e2017-05-01 23:40:59194## Externs
195
196[Externs files](https://github.com/google/closure-compiler/wiki/FAQ#how-do-i-write-an-externs-file)
197define APIs external to your JavaScript. They provide the compiler with the type
198information needed to check usage of these APIs in your JavaScript, much like
199forward declarations do in C++.
200
201Third-party libraries like Polymer often provide externs. Chrome must also
202provide externs for its extension APIs. Whenever an extension API's `idl` or
203`json` schema is updated in Chrome, the corresponding externs file must be
204regenerated:
205
206```shell
207./tools/json_schema_compiler/compiler.py -g externs \
208 extensions/common/api/your_api_here.idl \
209 > third_party/closure_compiler/externs/your_api_here.js
210```
close