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