1# Expo JavaScript Style Guide 2 3- [Modern JavaScript](#modern-javascript) 4- [ESLint and Prettier](#eslint-and-prettier) 5 - [Editor Integration](#editor-integration) 6- [Formatting](#formatting) 7 - [Prettier](#prettier) 8 - [Comments](#comments) 9 - [Imports](#imports) 10- [Naming](#naming) 11 - [Classes, functions, and variables](#classes--functions--and-variables) 12 - [Async functions](#async-functions) 13 - [Private variables](#private-variables) 14 - [Boolean names](#boolean-names) 15- [Declarations](#declarations) 16 - [let and const](#let-and-const) 17- [Examples](#examples) 18- [Babel](#babel) 19 20This guide explains style guidelines for writing JavaScript for Expo. It prioritizes readability for the team and also is meant to simplify small decisions when writing code. Most of this guide applies widely across the Expo repository but sometimes writing JavaScript differs between React, the web, and Node. 21 22# Modern JavaScript 23 24We generally use modern JavaScript on Expo, which means stable versions of the ES20xx specification with a few extensions, like JSX. We stay near the leading edge but away from the bleeding edge. 25 26# ESLint and Prettier 27 28ESLint reports errors and warnings for several style guidelines. Generally, the Expo ESLint configuration will report an error when it detects something that will prevent the code from working and a warning when it detects a style or formatting nit. The Expo configuration is written leniently and you should almost never have to use `/* eslint-disable */` comments. If you find yourself wanting to disable it, tell @ide so we can adjust the ESLint configuration to always be on. 29 30ESLint also uses Prettier, a code formatter, to check code formatting and to reformat code automatically; with Expo’s configuration, running ESLint runs Prettier too. 31 32ESLint has a `--fix` flag that tells it to fix errors and warnings when it can. Not all errors and warnings are automatically fixable but several are, including those reported by Prettier. 33 34## Editor Integration 35 36Many popular editors have ESLint plugins. Since the Expo ESLint configuration uses Prettier, if you configure your editor to use ESLint, it will use Prettier as well. These are some popular plugins: 37 38- **VS Code:** https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint 39- **Atom and Nuclide:** https://atom.io/packages/linter-eslint 40- **Sublime Text:** https://github.com/roadhump/SublimeLinter-eslint 41- **Emacs:** Flycheck with javascript-eslint 42 - Configure it to use the nearest available copy of ESLint by searching up `node_modules`: https://github.com/codesuki/add-node-modules-path 43- **Vim:** Syntastic: https://github.com/vim-syntastic/syntastic/blob/master/syntax_checkers/javascript/eslint.vim 44 - Configure it to use the nearest available copy of ESLint by searching up `node_modules` 45 46# Formatting 47 48## Prettier 49 50We use Prettier with Expo-specific settings for most of our code formatting. These settings are in `.prettierrc` in the Expo repository. Most small decisions about how to format code disappear with Prettier so we think less about formatting when writing and reviewing code. 51 52Sometimes Prettier makes code hard enough to read that we don’t want Prettier to format it. Add a `// prettier-ignore` comment above the expression whose formatting you want to preserve and let Prettier format the rest of the file. 53 54```js 55// prettier-ignore 56let matrix = [ 57 -c, 1, 1, 58 1, -c, 1, 59 1, 1, -c, 60]; 61``` 62 63If you would like Prettier to ignore the entire file rather than only a portion of it, add the file path to the `.prettierignore` file in the Expo repository. 64 65Since Prettier formats entire files (except ignored lines), we need to keep our files “pretty” so that the next person who runs Prettier on a file reformats only the lines they’re changing in their commit. We’ll talk more about Prettier later in this document. 66 67## Comments 68 69Use `// line` comments in most places. Use `/** block */` comments above classes, methods, and other structures and use `/* inline block */` comments in the middle of lines: 70 71```js 72// CORRECT 73/** 74 * Gets the latest version of Android that's been released. This is a version 75 * string like 7.1 instead of the code name Nougat. 76 */ 77function getLatestAndroidVersion() { 78 // Keep this logic in sync with Google's versioning scheme 79 return maxBy(getAndroidVersions(/* includePrereleases */ false), linearizeSemver); 80} 81``` 82 83Remove commented-out code before pushing it to GitHub. 84 85## Imports 86 87(Note: we don’t programmatically sort nor check the order of imports since there currently isn’t a linter plugin for these choices. This section is meant to be read as light guidance and not for code reviewers to spend much attention on.) 88 89Group and sort `import` statements and `require()` calls in this order: 90 911. `import` statements before `require()` calls 921. JavaScript hoists `import` statements; write the code to reflect that 931. Unassigned imports (`import 'side-effect'`) before assigned imports (`import React from 'react'`) 941. Unassigned imports almost always have side effects, which we usually want to apply earlier in the program’s lifetime. 951. External modules and Node.js built-in modules (`path`, `react`) before aliased internal modules (`www/module`) before relative modules (`../a/b`, `./c`) 96 97```js 98// CORRECT 99import 'side-effect'; 100 101import invariant from 'invariant'; 102import Expo, { Audio } from 'expo'; 103import path from 'path'; 104 105import HomeScreen from '../screens/HomeScreen'; 106import Colors from '../style/Colors'; 107import calculateViewport from '../style/calculateViewport'; 108import LoginButton './LoginButton'; 109 110const assert = require('assert'); 111``` 112 113Within each group, sort the statements by the names of the imported modules, not their assigned variables. Use ASCII order: uppercase before lowercase before scoped modules. 114 115```js 116// CORRECT 117import Z from 'Z'; 118import b from 'x'; 119import a from 'y'; 120import p from '@scope/package'; 121``` 122 123Write default imports before namespace imports before named imports: 124 125```js 126// CORRECT 127import a, * as b, { c } from 'module'; 128``` 129 130## React and JSX 131 132When writing React components, place your declarations and static methods near the top, followed by the constructor and lifecycle methods, followed by the render method and methods it calls, and other methods. 133 134Use Prettier to format JSX. 135 136```jsx 137// CORRECT 138type Props = { 139 title: string, 140 onPress?: event => void, 141}; 142 143type State = { 144 isPressed: boolean, 145}; 146 147class Button extends React.Component { 148 props: Props; 149 state: State = { 150 isPressed: true, 151 }; 152 153 constructor(props, context) { 154 super(props, context); 155 this.state = { 156 ...this.state, 157 bounce: new Animated.Value(1), 158 }; 159 } 160 161 componentWillUnmount() { 162 if (this.state.animation) { 163 this.state.animation.stop(); 164 } 165 } 166 167 render() { 168 return ( 169 <Animated.View 170 onPress={this._handlePress} 171 style={{ transform: [{ scale: this.state.bounce }] }}> 172 <Text> 173 {this.props.title} 174 </Text> 175 </Animated.View> 176 ); 177 } 178 179 _handlePress = event => { 180 this._bounce(); 181 if (this.props.onPress) { 182 this.props.onPress(event); 183 } 184 }; 185 186 _bounce() { 187 this.setState(state => { 188 state.bounce.setValue(0); 189 let animation = Animated.spring(state.bounce, { toValue: 1 }); 190 animation.start(({ finished }) => { 191 if (finished) { 192 this.setState(() => ({ animation: null })); 193 } 194 }); 195 return { animation }; 196 }); 197 } 198} 199```` 200 201# Naming 202 203Prioritize the reader when naming things. Choosing a greppable name tends to have a lot of benefits since it’s easier to find how the thing with the name is used, easier to rename and refactor, and is less context-sensitive. 204 205```js 206class TestPipeline { 207 // PREFERRED 208 runTests() { ... } 209 210 // DISFAVORED 211 run() { ... } 212} 213 214// "runTests" is a lot easier to grep for than "run". It also plainly communicates 215// more about what it does without being too wordy. 216``` 217 218## Classes, functions, and variables 219 220Use camel case for all names. Capitalize the names of classes and constructor functions. Start other names with lowercase. 221 222```js 223// CORRECT 224class Aquarium { 225 filterWater() {...} 226} 227 228function Fish() {...} 229Object.assign(Fish.prototype, ...); 230 231function populateAquarium(aquarium, school) {...} 232``` 233 234```js 235// INCORRECT 236class house { 237 CloseWindows() {...} 238} 239 240function EstimatePrice(house) {...} 241``` 242 243## Async functions 244 245Name async functions and other functions that return promises with “Async” at the end if they may complete asynchronously. This communicates that the function does work (often I/O) asynchronously and we need to await its result. 246 247```js 248// CORRECT 249async function fetchAccountAsync(accountId: ID): Promise<Account> { ... } 250``` 251 252It doesn’t matter how the function creates a promise for its asynchronous work. If the function isn’t defined with the `async` keyword but still looks like an async function from its call site, use the same naming convention. 253 254```js 255// CORRECT 256function readSettingsFileAsync(): Promise<string> { 257 return Promise((resolve, reject) => { 258 fs.readFile('settings.txt', 'utf8', ...); 259 }); 260} 261``` 262 263However, if a function does synchronous work but still returns a promise, it might make sense to omit the “Async” suffix. 264 265```jsx 266// OK 267function multiplexPromises(promises: Promise<*>[]): Promise<Array<* | Error>> { 268 // Given an array of promises, returns a promise that resolves to an array of 269 // promise results or errors. Semantically, this function doesn't do asynchronous 270 // work itself and the reader sees it operates on promises that do the actual work. 271} 272``` 273 274## Private variables 275 276Use an underscore to prefix instance variables that are intended to be private. This strikes a nice balance between communicating that the variable stores private data while keeping it accessible in a simple way for debugging, tests, and (sparingly) patches. 277 278```js 279// CORRECT 280class Counter { 281 _currentNumber = 0; 282 getNextNumber() { ... } 283} 284``` 285 286If it helps, use this same convention on variables that are internal to a module to make it clearer to readers which variables are defined and used only within the current module. 287 288```js 289// CORRECT 290export default function prettyPrintAll(values) { 291 for (let value of values) { 292 _prettyPrint(value); 293 } 294} 295 296function _prettyPrint(value) { ... } 297``` 298 299## Boolean names 300 301If it helps, consider naming Boolean variables with “is” or a similar verb at the beginning. Sometimes the names of Boolean variables can ambiguously describe an object (or program state) or reference an object, and using verbs like “is”, “was”, and “did” help communicate the variable’s purpose. 302 303```js 304// AMBIGUOUS 305console.log(history.deleted); 306 307// CLEAR 308console.log(history.isDeleted); 309``` 310 311# Declarations 312 313## `let` and `const` 314 315Write `const` where possible and `let` when you need to reassign a variable. This is simple to explain to developers working on Expo and easy to enforce with a linter. We are optimizing for a holistic combination of code quality and the attention we spend writing and reviewing code. 316 317When optimizing for code quality alone, it demonstrates clearer thinking to use `const` to communicate when a variable stores a constant, rather than when a variable just happens not to be reassigned at this point in the code's lifetime, and `let` otherwise. 318 319However, when optimizing for writing and reviewing code, using `const` when possible is easy to enforce and auto-fix with a linter. This guidance is also very easy to explain to developers and streamlines code reviews since the author does not need to carefully choose between `let` and `const` depending on semantic correctness. 320 321So, overall, we're trading an acceptable amount of code quality in exchange for reducing our attention cost by writing `const` by default and `let` when needed. 322 323# Examples 324 325```js 326import Expo from 'expo'; 327import PropTypes from 'prop-types'; 328import React from 'react'; 329import { StyleSheet, Text } from 'react-native'; 330 331import Log from '../log/Log'; 332import Colors from '../style/Colors'; 333 334export default class GreetingText extends React.PureComponent { 335 static propTypes = { 336 greeting: PropTypes.string.isRequired, 337 ...Text.propTypes, 338 }; 339 340 componentDidUpdate() { 341 Log.info('The greeting was re-rendered'); 342 } 343 344 render() { 345 let { greeting, style, ...props } = this.props; 346 return ( 347 <Text {...props} onPress={this._handlePress} style={[styles.greeting, style]}> 348 {greeting} 349 </Text> 350 ); 351 } 352 353 _handlePress = event => { 354 alert('Congratulations!'); 355 }; 356} 357 358const styles = StyleSheet.create({ 359 greeting: { 360 color: Colors.energetic, 361 fontSize: 30, 362 }, 363}); 364``` 365 366# Babel 367 368We use Babel to enable some of the newer JavaScript features that are sufficiently stable for us. This mostly includes transforms for features that are in a finalized version of the JavaScript standard. 369 370We use `babel-eslint`, which allows ESLint to use the Babel parser. In practice, with newer syntax extensions, Babel produces AST nodes that ESLint can’t consume; stable linter compatibility is another feature we look for in Babel plugins. 371