blob: 72861267cb0ad849ff71dfd010080291fef5d137 [file] [log] [blame] [view]
andybons6eaa0c0d2015-08-26 20:12:521# Closure Compilation
2
Rebekah Potterb2f0faef2023-01-23 19:48:143**Important: Closure Compilation is only supported on ChromeOS Ash. On all
4other platforms, Closure Compiler is deprecated; TypeScript should be used
5for type checking.** See [bug](https://www.crbug.com/1316438)
6
dbeamb0b12672016-05-03 21:33:137## What is type safety?
andybons3322f762015-08-24 21:37:098
cfredric477f29af2021-10-07 23:09:199[Statically-typed languages](https://en.wikipedia.org/wiki/Type_system#Static_type_checking)
dbeamb0b12672016-05-03 21:33:1310like C++ and Java have the notion of variable types.
andybons3322f762015-08-24 21:37:0911
dbeamb0b12672016-05-03 21:33:1312This is typically baked into how you declare variables:
dbeam707cf272016-03-10 04:12:1313
dbeamb0b12672016-05-03 21:33:1314```c++
15const int32 kUniversalAnswer = 42; // type = 32-bit integer
dbeame3d03b7c2016-02-06 03:08:2416```
17
dbeamb0b12672016-05-03 21:33:1318or as [templates](https://en.wikipedia.org/wiki/Template_metaprogramming) for
19containers or generics:
dbeame3d03b7c2016-02-06 03:08:2420
dbeamb0b12672016-05-03 21:33:1321```c++
22std::vector<int64> fibonacci_numbers; // a vector of 64-bit integers
dbeam43f31dd2016-03-09 22:05:5923```
24
dbeamb0b12672016-05-03 21:33:1325When differently-typed variables interact with each other, the compiler can warn
26you if there's no sane default action to take.
dbeam707cf272016-03-10 04:12:1327
dbeamb0b12672016-05-03 21:33:1328Typing can also be manually annotated via mechanisms like `dynamic_cast` and
29`static_cast` or older C-style casts (i.e. `(Type)`).
30
cfredric477f29af2021-10-07 23:09:1931Using statically-typed languages provides _some_ level of protection against
dbeamb0b12672016-05-03 21:33:1332accidentally using variables in the wrong context.
33
cfredric477f29af2021-10-07 23:09:1934JavaScript is dynamically-typed and doesn't offer this safety by default. This
35makes writing JavaScript more error prone, and various type errors have resulted
36in real bugs seen by many users.
dbeamb0b12672016-05-03 21:33:1337
38## Chrome's solution to typechecking JavaScript
39
40Enter [Closure Compiler](https://developers.google.com/closure/compiler/), a
41tool for analyzing JavaScript and checking for syntax errors, variable
42references, and other common JavaScript pitfalls.
43
44To get the fullest type safety possible, it's often required to annotate your
45JavaScript explicitly with [Closure-flavored @jsdoc
46tags](https://developers.google.com/closure/compiler/docs/js-for-compiler)
47
48```js
49/**
50 * @param {string} version A software version number (i.e. "50.0.2661.94").
qyearsleyc0dc6f42016-12-02 22:13:3951 * @return {!Array<number>} Numbers corresponding to |version| (i.e. [50, 0, 2661, 94]).
dbeamb0b12672016-05-03 21:33:1352 */
53function versionSplit(version) {
54 return version.split('.').map(Number);
55}
dbeam43f31dd2016-03-09 22:05:5956```
57
andybons6eaa0c0d2015-08-26 20:12:5258See also:
59[the design doc](https://docs.google.com/a/chromium.org/document/d/1Ee9ggmp6U-lM-w9WmxN5cSLkK9B5YAq14939Woo-JY0/edit).
andybons3322f762015-08-24 21:37:0960
andybons6eaa0c0d2015-08-26 20:12:5261## Typechecking Your Javascript
andybons3322f762015-08-24 21:37:0962
dbeamb0b12672016-05-03 21:33:1363Given an example file structure of:
andybons3322f762015-08-24 21:37:0964
dbeamb0b12672016-05-03 21:33:1365 + lib/does_the_hard_stuff.js
66 + ui/makes_things_pretty.js
andybons3322f762015-08-24 21:37:0967
dbeamb0b12672016-05-03 21:33:1368`lib/does_the_hard_stuff.js`:
andybons3322f762015-08-24 21:37:0969
andybons6eaa0c0d2015-08-26 20:12:5270```javascript
andybons3322f762015-08-24 21:37:0971var wit = 100;
72
73// ... later on, sneakily ...
74
75wit += ' IQ'; // '100 IQ'
76```
77
dbeamb0b12672016-05-03 21:33:1378`ui/makes_things_pretty.js`:
andybons3322f762015-08-24 21:37:0979
andybons6eaa0c0d2015-08-26 20:12:5280```javascript
andybons3322f762015-08-24 21:37:0981/** @type {number} */ var mensa = wit + 50;
dbeamb0b12672016-05-03 21:33:1382
andybons3322f762015-08-24 21:37:0983alert(mensa); // '100 IQ50' instead of 150
84```
85
dbeamb0b12672016-05-03 21:33:1386Closure compiler can notify us if we're using `string`s and `number`s in
87dangerous ways.
andybons3322f762015-08-24 21:37:0988
dbeamb0b12672016-05-03 21:33:1389To do this, we can create:
andybons3322f762015-08-24 21:37:0990
Christopher Lam213440d2018-06-14 03:26:1891 + ui/BUILD.gn
dbeamb0b12672016-05-03 21:33:1392
93With these contents:
andybons3322f762015-08-24 21:37:0994
95```
Avi Drissman7b017a992022-09-07 15:50:3896# Copyright 2018 The Chromium Authors
andybons3322f762015-08-24 21:37:0997# Use of this source code is governed by a BSD-style license that can be
98# found in the LICENSE file.
dbeamb0b12672016-05-03 21:33:1399
Christopher Lam213440d2018-06-14 03:26:18100import("//third_party/closure_compiler/compile_js.gni")
dbeamb0b12672016-05-03 21:33:13101
Christopher Lam213440d2018-06-14 03:26:18102js_type_check("closure_compile") {
103 deps = [
104 ":make_things_pretty",
105 ]
106}
dbeamb0b12672016-05-03 21:33:13107
Christopher Lam213440d2018-06-14 03:26:18108js_library("make_things_pretty") {
109 deps = [
110 "../lib:does_the_hard_stuff",
111 ]
112
113 externs_list = [
114 "$externs_path/extern_name_goes_here.js"
115 ]
andybons3322f762015-08-24 21:37:09116}
117```
118
dbeamb0b12672016-05-03 21:33:13119## Running Closure compiler locally
120
121You can locally test that your code compiles on Linux or Mac. This requires
122[Java](http://www.oracle.com/technetwork/java/javase/downloads/index.html) and a
xiaoyin.l1003c0b2016-12-06 02:51:17123[Chrome checkout](https://www.chromium.org/developers/how-tos/get-the-code) (i.e.
dbeamb0b12672016-05-03 21:33:13124python, depot_tools). Note: on Ubuntu, you can probably just run `sudo apt-get
125install openjdk-7-jre`.
126
Victor Vianna2e4555c2020-10-20 06:33:11127First, add the following to your GN args:
128```
129enable_js_type_check = true
130```
131Then you should be able to run:
dbeamb0b12672016-05-03 21:33:13132
133```shell
Christopher Lam213440d2018-06-14 03:26:18134ninja -C out/Default webui_closure_compile
dbeamb0b12672016-05-03 21:33:13135```
136
137and should see output like this:
138
139```shell
140ninja: Entering directory `out/Default/'
141[0/1] ACTION Compiling ui/makes_things_pretty.js
142```
143
Christopher Lam213440d2018-06-14 03:26:18144To compile only a specific folder, add an argument after the script name:
dbeamb0b12672016-05-03 21:33:13145
146```shell
Christopher Lam213440d2018-06-14 03:26:18147ninja -C out/Default ui:closure_compile
dbeamb0b12672016-05-03 21:33:13148```
149
150In our example code, this error should appear:
andybons3322f762015-08-24 21:37:09151
152```
dbeamb0b12672016-05-03 21:33:13153(ERROR) Error in: ui/makes_things_pretty.js
154## /my/home/chromium/src/ui/makes_things_pretty.js:1: ERROR - initializing variable
andybons3322f762015-08-24 21:37:09155## found : string
156## required: number
157## /** @type {number} */ var mensa = wit + 50;
158## ^
159```
160
dbeamb0b12672016-05-03 21:33:13161Hooray! We can catch type errors in JavaScript!
andybons3322f762015-08-24 21:37:09162
Christopher Lam213440d2018-06-14 03:26:18163## Preferred BUILD.gn structure
164* Make all individual JS file targets a js\_library.
165* The top level target should be called “closure\_compile”.
166* If you have subfolders that need compiling, make “closure\_compile” a group(),
167 and any files in the current directory a js\_type\_check() called “<directory>\_resources”.
168* Otherwise, just make “closure\_compile” a js\_type\_check with all your js\_library targets as deps
169* Leave all closure targets below other kinds of targets becaure they’re less ‘important’
170
171See also:
172[Closure Compilation with GN](https://docs.google.com/a/chromium.org/document/d/1Ee9ggmp6U-lM-w9WmxN5cSLkK9B5YAq14939Woo-JY0/edit).
173
dbeamb0b12672016-05-03 21:33:13174## Trying your change
andybons3322f762015-08-24 21:37:09175
Christopher Lam213440d2018-06-14 03:26:18176Closure compilation runs in the compile step of Linux, Android and ChromeOS builds.
dbeamb0b12672016-05-03 21:33:13177
178From the command line, you try your change with:
179
180```shell
Stephen Martinis089f5f02019-02-12 02:42:24181git cl try -b linux-rel
dbeamb0b12672016-05-03 21:33:13182```
183
dbeamb0b12672016-05-03 21:33:13184## Integrating with the continuous build
185
Christopher Lam213440d2018-06-14 03:26:18186To compile your code on every commit, add your file to the
187`'webui_closure_compile'` target in `src/BUILD.gn`:
andybons3322f762015-08-24 21:37:09188
189```
Christopher Lam213440d2018-06-14 03:26:18190 group("webui_closure_compile") {
191 data_deps = [
192 # Other projects
193 "my/project:closure_compile",
194 ]
195 }
andybons3322f762015-08-24 21:37:09196```
197
michaelpgc1e2d1e2017-05-01 23:40:59198## Externs
199
200[Externs files](https://github.com/google/closure-compiler/wiki/FAQ#how-do-i-write-an-externs-file)
201define APIs external to your JavaScript. They provide the compiler with the type
202information needed to check usage of these APIs in your JavaScript, much like
203forward declarations do in C++.
204
205Third-party libraries like Polymer often provide externs. Chrome must also
206provide externs for its extension APIs. Whenever an extension API's `idl` or
207`json` schema is updated in Chrome, the corresponding externs file must be
208regenerated:
209
210```shell
211./tools/json_schema_compiler/compiler.py -g externs \
212 extensions/common/api/your_api_here.idl \
213 > third_party/closure_compiler/externs/your_api_here.js
214```