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