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- [Examples](#examples) 16- [Babel](#babel) 17 18This 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. 19 20# Modern JavaScript 21 22We 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. 23 24# ESLint and Prettier 25 26ESLint 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. 27 28ESLint also uses Prettier, a code formatter, to check code formatting and to reformat code automatically; with Expo’s configuration, running ESLint runs Prettier too. 29 30ESLint 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. 31 32## Editor Integration 33 34Many 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: 35 36- **VS Code:** https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint 37- **Atom and Nuclide:** https://atom.io/packages/linter-eslint 38- **Sublime Text:** https://github.com/roadhump/SublimeLinter-eslint 39- **Emacs:** Flycheck with javascript-eslint 40 - Configure it to use the nearest available copy of ESLint by searching up `node_modules`: https://github.com/codesuki/add-node-modules-path 41- **Vim:** Syntastic: https://github.com/vim-syntastic/syntastic/blob/master/syntax_checkers/javascript/eslint.vim 42 - Configure it to use the nearest available copy of ESLint by searching up `node_modules` 43 44# Formatting 45 46## Prettier 47 48We 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. 49 50Sometimes 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. 51 52```js 53// prettier-ignore 54let matrix = [ 55 -c, 1, 1, 56 1, -c, 1, 57 1, 1, -c, 58]; 59``` 60 61If 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. 62 63Since 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. 64 65## Comments 66 67Use `// line` comments in most places. Use `/** block */` comments above classes, methods, and other structures and use `/* inline block */` comments in the middle of lines: 68 69```js 70// CORRECT 71/** 72 * Gets the latest version of Android that's been released. This is a version 73 * string like 7.1 instead of the code name Nougat. 74 */ 75function getLatestAndroidVersion() { 76 // Keep this logic in sync with Google's versioning scheme 77 return maxBy(getAndroidVersions(/* includePrereleases */ false), linearizeSemver); 78} 79``` 80 81Remove commented-out code before pushing it to GitHub. 82 83## Imports 84 85(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.) 86 87Group and sort `import` statements and `require()` calls in this order: 88 891. `import` statements before `require()` calls 901. JavaScript hoists `import` statements; write the code to reflect that 911. Unassigned imports (`import 'side-effect'`) before assigned imports (`import React from 'react'`) 921. Unassigned imports almost always have side effects, which we usually want to apply earlier in the program’s lifetime. 931. External modules and Node.js built-in modules (`path`, `react`) before aliased internal modules (`www/module`) before relative modules (`../a/b`, `./c`) 94 95```js 96// CORRECT 97import 'side-effect'; 98 99import invariant from 'invariant'; 100import Expo, { Audio } from 'expo'; 101import path from 'path'; 102 103import HomeScreen from '../screens/HomeScreen'; 104import Colors from '../style/Colors'; 105import calculateViewport from '../style/calculateViewport'; 106import LoginButton './LoginButton'; 107 108const assert = require('assert'); 109``` 110 111Within 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. 112 113```js 114// CORRECT 115import Z from 'Z'; 116import b from 'x'; 117import a from 'y'; 118import p from '@scope/package'; 119``` 120 121Write default imports before namespace imports before named imports: 122 123````js 124// CORRECT 125import a, * as b, { c } from 'module'; 126``` 127 128## React and JSX 129 130When 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. 131 132Use Prettier to format JSX. 133 134```jsx 135// CORRECT 136type Props = { 137 title: string, 138 onPress?: event => void, 139}; 140 141type State = { 142 isPressed: boolean, 143}; 144 145class Button extends React.Component { 146 props: Props; 147 state: State = { 148 isPressed: true, 149 }; 150 151 constructor(props, context) { 152 super(props, context); 153 this.state = { 154 ...this.state, 155 bounce: new Animated.Value(1), 156 }; 157 } 158 159 componentWillUnmount() { 160 if (this.state.animation) { 161 this.state.animation.stop(); 162 } 163 } 164 165 render() { 166 return ( 167 <Animated.View 168 onPress={this._handlePress} 169 style={{ transform: [{ scale: this.state.bounce }] }}> 170 <Text> 171 {this.props.title} 172 </Text> 173 </Animated.View> 174 ); 175 } 176 177 _handlePress = event => { 178 this._bounce(); 179 if (this.props.onPress) { 180 this.props.onPress(event); 181 } 182 }; 183 184 _bounce() { 185 this.setState(state => { 186 state.bounce.setValue(0); 187 let animation = Animated.spring(state.bounce, { toValue: 1 }); 188 animation.start(({ finished }) => { 189 if (finished) { 190 this.setState(() => ({ animation: null })); 191 } 192 }); 193 return { animation }; 194 }); 195 } 196} 197```` 198 199# Naming 200 201Prioritize 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. 202 203```js 204class TestPipeline { 205 // PREFERRED 206 runTests() { ... } 207 208 // DISFAVORED 209 run() { ... } 210} 211 212// "runTests" is a lot easier to grep for than "run". It also plainly communicates 213// more about what it does without being too wordy. 214``` 215 216## Classes, functions, and variables 217 218Use camel case for all names. Capitalize the names of classes and constructor functions. Start other names with lowercase. 219 220```js 221// CORRECT 222class Aquarium { 223 filterWater() {...} 224} 225 226function Fish() {...} 227Object.assign(Fish.prototype, ...); 228 229function populateAquarium(aquarium, school) {...} 230``` 231 232```js 233// INCORRECT 234class house { 235 CloseWindows() {...} 236} 237 238function EstimatePrice(house) {...} 239``` 240 241## Async functions 242 243Name 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. 244 245```js 246// CORRECT 247async function fetchAccountAsync(accountId: ID): Promise<Account> { ... } 248``` 249 250It 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. 251 252```js 253// CORRECT 254function readSettingsFileAsync(): Promise<string> { 255 return Promise((resolve, reject) => { 256 fs.readFile('settings.txt', 'utf8', ...); 257 }); 258} 259``` 260 261However, if a function does synchronous work but still returns a promise, it might make sense to omit the “Async” suffix. 262 263```jsx 264// OK 265function multiplexPromises(promises: Promise<*>[]): Promise<Array<* | Error>> { 266 // Given an array of promises, returns a promise that resolves to an array of 267 // promise results or errors. Semantically, this function doesn't do asynchronous 268 // work itself and the reader sees it operates on promises that do the actual work. 269} 270``` 271 272## Private variables 273 274Use 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. 275 276```js 277// CORRECT 278class Counter { 279 _currentNumber = 0; 280 getNextNumber() { ... } 281} 282``` 283 284If 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. 285 286```js 287// CORRECT 288export default function prettyPrintAll(values) { 289 for (let value of values) { 290 _prettyPrint(value); 291 } 292} 293 294function _prettyPrint(value) { ... } 295``` 296 297## Boolean names 298 299If 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. 300 301```js 302// AMBIGUOUS 303console.log(history.deleted); 304 305// CLEAR 306console.log(history.isDeleted); 307``` 308 309# Examples 310 311```js 312import Expo from 'expo'; 313import PropTypes from 'prop-types'; 314import React from 'react'; 315import { StyleSheet, Text } from 'react-native'; 316 317import Log from '../log/Log'; 318import Colors from '../style/Colors'; 319 320export default class GreetingText extends React.PureComponent { 321 static propTypes = { 322 greeting: PropTypes.string.isRequired, 323 ...Text.propTypes, 324 }; 325 326 componentDidUpdate() { 327 Log.info('The greeting was re-rendered'); 328 } 329 330 render() { 331 let { greeting, style, ...props } = this.props; 332 return ( 333 <Text {...props} onPress={this._handlePress} style={[styles.greeting, style]}> 334 {greeting} 335 </Text> 336 ); 337 } 338 339 _handlePress = event => { 340 alert('Congratulations!'); 341 }; 342} 343 344const styles = StyleSheet.create({ 345 greeting: { 346 color: Colors.energetic, 347 fontSize: 30, 348 }, 349}); 350``` 351 352# Babel 353 354We 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. 355 356We 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. 357