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