sullivan | 1355e18 | 2016-03-05 02:34:56 | [diff] [blame] | 1 | # Catapult Style guide |
| 2 | |
| 3 | ## Base style guide |
| 4 | |
| 5 | Unless stated below, we follow the conventions listed in the [Chromium style |
| 6 | guide](https://www.chromium.org/developers/coding-style) and [Google JavaScript |
| 7 | style guide](http://google.github.io/styleguide/javascriptguide.xml). |
| 8 | |
eakuefner | a8a844e | 2017-01-06 18:24:50 | [diff] [blame] | 9 | ## JavaScript |
| 10 | |
| 11 | ### Files |
sullivan | 1355e18 | 2016-03-05 02:34:56 | [diff] [blame] | 12 | File names `should_look_like_this.html`. |
| 13 | |
| 14 | Keep to one concept per file, always. In practice, this usually means one |
| 15 | component or class per file, but can lead to multiple if they’re small and |
| 16 | closely related. If you can, group utility functions into a static class to |
| 17 | clarify their relationship, e.g. `base/statistics.html`. |
| 18 | |
benjhayden | b11ee72 | 2016-09-21 17:08:46 | [diff] [blame] | 19 | ```html |
sullivan | 1355e18 | 2016-03-05 02:34:56 | [diff] [blame] | 20 | <!-- tracing/model/point.html --> |
| 21 | <script> |
| 22 | ‘use strict’; |
| 23 | |
| 24 | tr.exportTo(‘tr.model’, function() { |
| 25 | function Point() {} |
| 26 | |
| 27 | return { |
| 28 | Point: Point |
| 29 | }; |
| 30 | }); |
| 31 | </script> |
| 32 | ``` |
| 33 | |
| 34 | The exception to this rule is when there are multiple small, related classes or |
| 35 | methods. In this case, a file may export multiple symbols: |
| 36 | |
benjhayden | b11ee72 | 2016-09-21 17:08:46 | [diff] [blame] | 37 | ```html |
sullivan | 1355e18 | 2016-03-05 02:34:56 | [diff] [blame] | 38 | <!-- tracing/base/dom_helpers.html --> |
| 39 | <script> |
| 40 | ‘use strict’; |
| 41 | |
| 42 | tr.exportTo(‘tr.ui.b’, function() { |
| 43 | function createSpan() { // … } |
| 44 | function createDiv() { // … } |
| 45 | function isElementAttached(element) { // … } |
| 46 | |
| 47 | return { |
| 48 | createSpan: createSpan, |
| 49 | createDiv: createDiv, |
| 50 | isElementAttached: isElementAttached |
| 51 | }; |
| 52 | }); |
| 53 | </script> |
| 54 | ``` |
| 55 | |
| 56 | Any tests for a file should be in a file with the same name as the |
| 57 | implementation file, but with a trailing `_test`. |
| 58 | |
benjhayden | b11ee72 | 2016-09-21 17:08:46 | [diff] [blame] | 59 | ```sh |
sullivan | 1355e18 | 2016-03-05 02:34:56 | [diff] [blame] | 60 | touch tracing/model/access_point.html |
| 61 | touch tracing/model/access_point_test.html |
| 62 | ``` |
benjhayden | b11ee72 | 2016-09-21 17:08:46 | [diff] [blame] | 63 | |
eakuefner | a8a844e | 2017-01-06 18:24:50 | [diff] [blame] | 64 | ### Namespacing and element names |
sullivan | 1355e18 | 2016-03-05 02:34:56 | [diff] [blame] | 65 | |
| 66 | All symbols that exist in the global namespace should be exported using the |
| 67 | `exportTo` method. |
| 68 | |
| 69 | Exported package names show the file’s location relative to the root `tracing/` |
| 70 | directory. These package names are abbreviated, usually with a 1 or 2 letter |
| 71 | abbreviation - just enough to resolve naming conflicts. All files in the same |
| 72 | directory should share the same package. |
| 73 | |
benjhayden | b11ee72 | 2016-09-21 17:08:46 | [diff] [blame] | 74 | ```html |
benjhayden | 50b8d5f | 2017-01-26 19:32:43 | [diff] [blame] | 75 | <!-- tracing/extras/chrome/cc/input_latency_async_slice.html → |
| 76 | tr.exportTo(‘tr.e.cc’, function() { |
sullivan | 1355e18 | 2016-03-05 02:34:56 | [diff] [blame] | 77 | // ... |
| 78 | }); |
| 79 | ``` |
| 80 | |
| 81 | Polymer element names should use the convention |
| 82 | `hyphenated-package-name-element-name`. |
| 83 | |
benjhayden | b11ee72 | 2016-09-21 17:08:46 | [diff] [blame] | 84 | ```html |
sullivan | 1355e18 | 2016-03-05 02:34:56 | [diff] [blame] | 85 | <!-- tracing/ui/analysis/counter_sample_sub_view.html --> |
benjhayden | b11ee72 | 2016-09-21 17:08:46 | [diff] [blame] | 86 | <dom-module id='tr-ui-a-counter-sample-sub-view'> |
sullivan | 1355e18 | 2016-03-05 02:34:56 | [diff] [blame] | 87 | ... |
benjhayden | b11ee72 | 2016-09-21 17:08:46 | [diff] [blame] | 88 | </dom-module> |
sullivan | 1355e18 | 2016-03-05 02:34:56 | [diff] [blame] | 89 | ``` |
| 90 | |
eakuefner | a8a844e | 2017-01-06 18:24:50 | [diff] [blame] | 91 | ### Classes and objects |
sullivan | 1355e18 | 2016-03-05 02:34:56 | [diff] [blame] | 92 | |
| 93 | Classes should expose public fields only if those fields represent a part of the |
| 94 | class’s public interface. |
| 95 | |
| 96 | All fields should be initialized in the constructor. Fields with no reasonable |
| 97 | default value should be initialized to undefined. |
| 98 | |
| 99 | Do not set defaults via the prototype chain. |
| 100 | |
benjhayden | b11ee72 | 2016-09-21 17:08:46 | [diff] [blame] | 101 | ```javascript |
sullivan | 1355e18 | 2016-03-05 02:34:56 | [diff] [blame] | 102 | function Line() { |
| 103 | // Good |
| 104 | this.yIntercept_ = undefined; |
| 105 | } |
| 106 | |
| 107 | Line.prototype = { |
| 108 | // Bad |
| 109 | xIntercept_: undefined, |
| 110 | |
| 111 | |
| 112 | set slope(slope) { |
| 113 | // Bad: this.slope_ wasn’t initialized in the constructor. |
| 114 | this.slope_ = slope; |
| 115 | }, |
| 116 | |
| 117 | set yIntercept() { |
| 118 | // Good |
| 119 | return this.yIntercept_; |
| 120 | } |
| 121 | }; |
| 122 | ``` |
| 123 | |
eakuefner | a8a844e | 2017-01-06 18:24:50 | [diff] [blame] | 124 | ### Blocks |
benjhayden | b11ee72 | 2016-09-21 17:08:46 | [diff] [blame] | 125 | |
benjhayden | 0fccc6c | 2016-09-26 17:35:19 | [diff] [blame] | 126 | From the [Blocks section of the airbnb style |
| 127 | guide](https://github.com/airbnb/javascript#blocks): |
| 128 | Use braces with all multi-line blocks. |
| 129 | |
| 130 | ```javascript |
| 131 | // bad |
| 132 | if (test) |
| 133 | return false; |
| 134 | |
| 135 | // good |
| 136 | if (test) return false; |
| 137 | |
| 138 | // good |
| 139 | if (test) { |
| 140 | return false; |
| 141 | } |
| 142 | |
| 143 | // bad |
| 144 | function foo() { return false; } |
| 145 | |
| 146 | // good |
| 147 | function bar() { |
| 148 | return false; |
| 149 | } |
| 150 | ``` |
| 151 | |
| 152 | If you're using multi-line blocks with `if` and `else`, put `else` on the same |
| 153 | line as your `if` block's closing brace. |
| 154 | |
| 155 | ```javascript |
| 156 | // bad |
| 157 | if (test) { |
| 158 | thing1(); |
| 159 | thing2(); |
| 160 | } |
| 161 | else { |
| 162 | thing3(); |
| 163 | } |
| 164 | |
| 165 | // good |
| 166 | if (test) { |
| 167 | thing1(); |
| 168 | thing2(); |
| 169 | } else { |
| 170 | thing3(); |
| 171 | } |
| 172 | ``` |
benjhayden | b11ee72 | 2016-09-21 17:08:46 | [diff] [blame] | 173 | |
benjhayden | 0d592b9 | 2017-03-24 04:42:13 | [diff] [blame] | 174 | ### Variables |
| 175 | |
| 176 | Use `const` and `let` instead of `var` in all new files and functions. Prefer `const` over `let` when a variable can only refer to a single value throughout its lifetime. |
| 177 | |
| 178 | ```javascript |
| 179 | // bad |
| 180 | function() { |
| 181 | let hello = ' hello '; |
| 182 | return hello.trim(); |
| 183 | } |
| 184 | |
| 185 | // good |
| 186 | function() { |
| 187 | const hello = ' hello '; |
| 188 | return hello.trim(); |
| 189 | } |
| 190 | ``` |
| 191 | |
eakuefner | a8a844e | 2017-01-06 18:24:50 | [diff] [blame] | 192 | ### Polymer elements |
sullivan | 1355e18 | 2016-03-05 02:34:56 | [diff] [blame] | 193 | The `<script>` block for the Polymer element can go either inside or outside of |
| 194 | the element’s definition. Generally, the block outside is placed outside when |
| 195 | the script is sufficiently complex that having 2 fewer spaces of indentation |
| 196 | would make it more readable. |
| 197 | |
benjhayden | b11ee72 | 2016-09-21 17:08:46 | [diff] [blame] | 198 | ```html |
| 199 | <dom-module id="tr-bar"> |
sullivan | 1355e18 | 2016-03-05 02:34:56 | [diff] [blame] | 200 | <template><div></div></template> |
| 201 | <script> |
| 202 | // Can go here... |
| 203 | </script> |
benjhayden | b11ee72 | 2016-09-21 17:08:46 | [diff] [blame] | 204 | </dom-module> |
sullivan | 1355e18 | 2016-03-05 02:34:56 | [diff] [blame] | 205 | |
| 206 | <script> |
| 207 | 'use strict'; |
| 208 | (function(){ // Use this if you need to define constants scoped to that element. |
hjd | a1e9f79 | 2017-04-10 16:23:10 | [diff] [blame] | 209 | Polymer('tr-bar', { |
sullivan | 1355e18 | 2016-03-05 02:34:56 | [diff] [blame] | 210 | // ... or here. |
| 211 | }); |
| 212 | })(); |
| 213 | </script> |
| 214 | ``` |
| 215 | |
| 216 | Style sheets should be inline rather than in external .css files. |
| 217 | |
benjhayden | b11ee72 | 2016-09-21 17:08:46 | [diff] [blame] | 218 | ```html |
| 219 | <dom-module id="tr-bar"> |
sullivan | 1355e18 | 2016-03-05 02:34:56 | [diff] [blame] | 220 | <style> |
| 221 | #content { |
| 222 | display: flex; |
| 223 | } |
| 224 | </style> |
| 225 | <template><div id=”content”></div></template> |
benjhayden | b11ee72 | 2016-09-21 17:08:46 | [diff] [blame] | 226 | </dom-module> |
sullivan | 1355e18 | 2016-03-05 02:34:56 | [diff] [blame] | 227 | ``` |
| 228 | |
eakuefner | a8a844e | 2017-01-06 18:24:50 | [diff] [blame] | 229 | ### `undefined` and `null` |
charliea | 1082ddd | 2016-03-23 17:22:40 | [diff] [blame] | 230 | Prefer use of `undefined` over `null`. |
| 231 | |
benjhayden | b11ee72 | 2016-09-21 17:08:46 | [diff] [blame] | 232 | ```javascript |
charliea | 1082ddd | 2016-03-23 17:22:40 | [diff] [blame] | 233 | function Line() { |
| 234 | // Good |
| 235 | this.yIntercept_ = undefined; |
| 236 | // Bad |
| 237 | this.slope = null; |
| 238 | } |
| 239 | ``` |
| 240 | |
eakuefner | a8a844e | 2017-01-06 18:24:50 | [diff] [blame] | 241 | ### Tests |
sullivan | 1355e18 | 2016-03-05 02:34:56 | [diff] [blame] | 242 | UI element tests that make sure that an element is instantiable should have |
| 243 | names that start with “`instantiate`”. These tests should, as a general rule, |
| 244 | should not make assertions. |
| 245 | |
benjhayden | 8114199 | 2017-05-25 19:15:09 | [diff] [blame] | 246 | Assertions should specify the actual value before the expected value. |
| 247 | |
| 248 | ```javascript |
| 249 | assert.strictEqual(value.get(), 42); |
| 250 | assert.isBelow(value.get(), 42); |
| 251 | assert.isAbove(value.get(), 42); |
| 252 | assert.lengthOf(value.get(), 42); |
| 253 | ``` |
| 254 | |
| 255 | ```python |
| 256 | self.assertEqual(value.Get(), 42) |
| 257 | self.assertLess(value.Get(), 42) |
| 258 | ``` |
| 259 | |
eakuefner | a8a844e | 2017-01-06 18:24:50 | [diff] [blame] | 260 | ### ECMAScript 2015 (ES6) features |
sullivan | 1355e18 | 2016-03-05 02:34:56 | [diff] [blame] | 261 | |
petrcermak | 1ae8e1d | 2016-04-12 10:22:36 | [diff] [blame] | 262 | **Use of ES6 features is prohibited unless explicitly approved in the table below.** However, we're currently working to allow them. |
sullivan | 1355e18 | 2016-03-05 02:34:56 | [diff] [blame] | 263 | |
charliea | b0f205e | 2016-03-21 20:58:50 | [diff] [blame] | 264 | | Feature | Status | |
| 265 | |---------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------| |
benjhayden | 2f63c68 | 2016-09-09 01:05:40 | [diff] [blame] | 266 | | [Arrows](https://github.com/lukehoban/es6features#arrows) | [Approved](https://github.com/catapult-project/catapult/issues/2165) | |
| 267 | | [Classes](https://github.com/lukehoban/es6features#classes) | [Approved](https://github.com/catapult-project/catapult/issues/2176) | |
benjhayden | 63e5a71 | 2016-11-09 19:06:51 | [diff] [blame] | 268 | | [Enhanced object literals](https://github.com/lukehoban/es6features#enhanced-object-literals) | Approved | |
| 269 | | [Template strings](https://github.com/lukehoban/es6features#template-strings) | Approved | |
benjhayden | 2f63c68 | 2016-09-09 01:05:40 | [diff] [blame] | 270 | | [Destructuring](https://github.com/lukehoban/es6features#destructuring) | Approved | |
benshayden | 237fb2a | 2018-10-17 17:17:45 | [diff] [blame] | 271 | | [Default, rest, and spread](https://github.com/lukehoban/es6features#default--rest--spread) | Approved | |
| 272 | | [`let` and `const`](https://github.com/lukehoban/es6features#let--const) | Approved and required for new code | |
petrcermak | 1ae8e1d | 2016-04-12 10:22:36 | [diff] [blame] | 273 | | [Iterators and `for...of`](https://github.com/lukehoban/es6features#iterators--forof) | Approved | |
charliea | 37ee8c7 | 2016-06-08 18:45:57 | [diff] [blame] | 274 | | [Generators](https://github.com/lukehoban/es6features#generators) | Approved | |
charliea | b0f205e | 2016-03-21 20:58:50 | [diff] [blame] | 275 | | [Unicode](https://github.com/lukehoban/es6features#unicode) | To be discussed | |
| 276 | | [Modules](https://github.com/lukehoban/es6features#modules) | To be discussed | |
| 277 | | [Module loaders](https://github.com/lukehoban/es6features#module-loaders) | To be discussed | |
petrcermak | 1ae8e1d | 2016-04-12 10:22:36 | [diff] [blame] | 278 | | [`Map`, `Set`, `WeakMap`, and `WeakSet`](https://github.com/lukehoban/es6features#map--set--weakmap--weakset) | Approved | |
charliea | b0f205e | 2016-03-21 20:58:50 | [diff] [blame] | 279 | | [Proxies](https://github.com/lukehoban/es6features#proxies) | To be discussed | |
| 280 | | [Symbols](https://github.com/lukehoban/es6features#symbols) | To be discussed | |
benjhayden | 2f63c68 | 2016-09-09 01:05:40 | [diff] [blame] | 281 | | [Subclassable Built-ins](https://github.com/lukehoban/es6features#subclassable-built-ins) | Approved | |
petrcermak | 1ae8e1d | 2016-04-12 10:22:36 | [diff] [blame] | 282 | | [Promises](https://github.com/lukehoban/es6features#promises) | Approved | |
charliea | b0f205e | 2016-03-21 20:58:50 | [diff] [blame] | 283 | | [`Math`, `Number`, `String`, `Array`, and `Object` APIs](https://github.com/lukehoban/es6features#math--number--string--array--object-apis) | To be discussed | |
| 284 | | [Binary and octal literals](https://github.com/lukehoban/es6features#binary-and-octal-literals) | To be discussed | |
| 285 | | [Reflect API](https://github.com/lukehoban/es6features#reflect-api) | To be discussed | |
sullivan | 1355e18 | 2016-03-05 02:34:56 | [diff] [blame] | 286 | |
eakuefner | a8a844e | 2017-01-06 18:24:50 | [diff] [blame] | 287 | ### ECMAScript 2016 (ES7) features |
charliea | e88f2af | 2016-11-16 00:46:59 | [diff] [blame] | 288 | |
| 289 | **Use of ES7 features is prohibited unless explicitly approved in the table below.** However, we're currently working to allow them. |
| 290 | |
| 291 | | Feature | Status | |
| 292 | |--------------------------|-----------------| |
charliea | 489ef35 | 2017-03-22 16:10:02 | [diff] [blame] | 293 | | [Array.prototype.includes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes) | [Approved](https://github.com/catapult-project/catapult/issues/3424) | |
charliea | e88f2af | 2016-11-16 00:46:59 | [diff] [blame] | 294 | | [Exponentiation operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Exponentiation_(**)) | To be discussed | |
| 295 | |
eakuefner | a8a844e | 2017-01-06 18:24:50 | [diff] [blame] | 296 | ### ECMAScript 2017 (ES8) features |
charliea | e88f2af | 2016-11-16 00:46:59 | [diff] [blame] | 297 | |
| 298 | **Use of ES8 features is prohibited unless explicitly approved in the table below.** Generally, ES8 features are still experimental and liable to change and therefore not fit for use in Catapult. However, in a few rare cases, features may be stable enough for use. |
| 299 | |
| 300 | | Feature | Status | |
| 301 | |--------------------------|-----------------| |
charliea | b38578d | 2016-11-30 14:52:00 | [diff] [blame] | 302 | | [Object.entries](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries) and [Object.values](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/values) | Approved | |
benjhayden | eef0bb1 | 2017-03-21 16:53:54 | [diff] [blame] | 303 | | [async/await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) | Approved | |
charliea | e88f2af | 2016-11-16 00:46:59 | [diff] [blame] | 304 | |
charliea | b0f205e | 2016-03-21 20:58:50 | [diff] [blame] | 305 | ### Possible feature statuses |
| 306 | - **Approved**: this feature is approved for general use. |
| 307 | - **Testing in progress**: there's agreement that we should use this feature, but we still need to make sure that it's safe. "Testing in progress" statuses should link to a Catapult bug thread tracking the testing. |
| 308 | - **Discussion in progress**: there's not yet agreement that we should use this feature. "Discussion in progress" statuses should link to a Catapult bug thread about whether the feature should be used. |
| 309 | - **To be discussed**: this feature hasn't been discussed yet. |
sullivan | 1355e18 | 2016-03-05 02:34:56 | [diff] [blame] | 310 | |
petrcermak | 1ae8e1d | 2016-04-12 10:22:36 | [diff] [blame] | 311 | Use of an ES6 features shouldn't be considered until that feature is [supported](https://kangax.github.io/compat-table/es6/) in both Chrome stable and [our current version of D8](/third_party/vinn/third_party/v8/README.chromium). |
charliea | b0f205e | 2016-03-21 20:58:50 | [diff] [blame] | 312 | |
| 313 | If you see that Catapult’s version of D8 is behind Chrome stable's, use |
sullivan | 1355e18 | 2016-03-05 02:34:56 | [diff] [blame] | 314 | [this script](/third_party/vinn/bin/update_v8) to update it. |
| 315 | |
charliea | 9096796 | 2016-03-31 16:26:43 | [diff] [blame] | 316 | ## Avoid defensive programming (and document it when you can't) |
sullivan | 1355e18 | 2016-03-05 02:34:56 | [diff] [blame] | 317 | |
charliea | 9096796 | 2016-03-31 16:26:43 | [diff] [blame] | 318 | Don't silently handle unexpected conditions. When such conditions occur, you |
| 319 | should: |
sullivan | 1355e18 | 2016-03-05 02:34:56 | [diff] [blame] | 320 | |
charliea | 9096796 | 2016-03-31 16:26:43 | [diff] [blame] | 321 | 1. Emit a clear warning and continue if the error is non-catastrophic |
| 322 | 2. Fail loudly if the error is catastrophic |
| 323 | |
| 324 | If fixing the problem is hard but a simple workaround is possible, then using |
| 325 | the workaround is OK so long as: |
| 326 | |
charliea | dcd2de1 | 2016-11-03 18:59:54 | [diff] [blame] | 327 | 1. An issue is created to track the problem. |
| 328 | 2. The defensive patch is wrapped in a `// TODO` linking to that issue. |
| 329 | 3. The todo and defensive patch are removed after the problem is fixed. |