xref: /expo/packages/@expo/cli/README.md (revision eea2febf)
1df49d6e1SEvan Bacon<!-- Title -->
2df49d6e1SEvan Bacon
3df49d6e1SEvan Bacon<p align="center">
4df49d6e1SEvan Bacon  <a href="https://expo.dev/">
5df49d6e1SEvan Bacon    <img alt="Expo CLI" src="../../../.github/resources/cli-banner.svg">
6df49d6e1SEvan Bacon  </a>
7df49d6e1SEvan Bacon</p>
8df49d6e1SEvan Bacon
9df49d6e1SEvan Bacon<p align="center">The fastest way to build and run universal React Native apps for iOS, Android, and the web</p>
10df49d6e1SEvan Bacon
11df49d6e1SEvan Bacon<p align="center">
12df49d6e1SEvan Bacon
13df49d6e1SEvan Bacon  <a aria-label="Join the Expo Discord" href="https://discord.gg/4gtbPAdpaE" target="_blank">
14df49d6e1SEvan Bacon    <img alt="Discord" src="https://img.shields.io/discord/695411232856997968.svg?style=flat-square&labelColor=000000&color=000000&logo=discord&logoColor=FFFFFF&label=" />
15df49d6e1SEvan Bacon  </a>
16df49d6e1SEvan Bacon  <a aria-label="Browse the Expo forums" href="https://forums.expo.dev" target="_blank">
17df49d6e1SEvan Bacon    <img alt="" src="https://img.shields.io/badge/Ask%20Questions%20-000.svg?style=flat-square&logo=discourse&logoWidth=15&labelColor=000000&color=000000">
18df49d6e1SEvan Bacon  </a>
19df49d6e1SEvan Bacon
20df49d6e1SEvan Bacon</p>
21df49d6e1SEvan Bacon
22df49d6e1SEvan Bacon<p align="center">
23*eea2febfSBartosz Kaszubowski  <a aria-label="expo documentation" href="https://docs.expo.dev/more/expo-cli/">�� Read the Documentation</a>
24df49d6e1SEvan Bacon  |
25df49d6e1SEvan Bacon  <a aria-label="Contribute to Expo CLI" href="#contributing"><b>Contribute to Expo CLI</b></a>
26df49d6e1SEvan Bacon</p>
27df49d6e1SEvan Bacon
28df49d6e1SEvan Bacon<p>
29df49d6e1SEvan Bacon  <a aria-label="Follow @expo on Twitter" href="https://twitter.com/intent/follow?screen_name=expo" target="_blank">
30df49d6e1SEvan Bacon    <img  alt="Twitter: expo" src="https://img.shields.io/twitter/follow/expo.svg?style=flat-square&label=Follow%20%40expo&logo=TWITTER&logoColor=FFFFFF&labelColor=00aced&logoWidth=15&color=lightgray" target="_blank" />
31df49d6e1SEvan Bacon  </a>
32df49d6e1SEvan Bacon  <a aria-label="Follow Expo on Medium" href="https://blog.expo.dev">
33df49d6e1SEvan Bacon    <img align="right" alt="Medium: exposition" src="https://img.shields.io/badge/Learn%20more%20on%20our%20blog-lightgray.svg?style=flat-square" target="_blank" />
34df49d6e1SEvan Bacon  </a>
35df49d6e1SEvan Bacon</p>
36df49d6e1SEvan Bacon
37df49d6e1SEvan Bacon---
388d307f52SEvan Bacon
398d307f52SEvan BaconThe `@expo/cli` package is a CLI binary that should be used via `expo` like `npx expo start`.
408d307f52SEvan Bacon
418d307f52SEvan Bacon```
428d307f52SEvan Baconnpx expo
438d307f52SEvan Bacon```
448d307f52SEvan Bacon
45df49d6e1SEvan Bacon> ⭐️ Be sure to star the Expo GitHub repo if you enjoy using the project!
46df49d6e1SEvan Bacon
47a11a5f47SEvan Bacon## Design
48a11a5f47SEvan Bacon
49a11a5f47SEvan BaconThis CLI has the following purposes:
50a11a5f47SEvan Bacon
51a11a5f47SEvan Bacon- Be a minimal interface for starting a local development server that emulates a production EAS Updates server. The development server is the proxy between a native runtime (Expo Go, Dev Client) and a JS Bundler (Metro, Webpack).
52a11a5f47SEvan Bacon  - To accomplish secure manifest signing (think https/TSL/SSL for web (required for sandboxing AsyncStorage, Permissions, etc.)) we need an authenticated Expo user account. This is the only reason we include the authentication commands `login`, `logout`, `whoami`, `register`. Standard web CLIs don't have authentication commands because they either don't set up https or they use emulation via packages like `devcert`.
53df49d6e1SEvan Bacon- Orchestrating various native tools like Xcode, `Simulator.app`, Android Studio, ADB, etc. to make native builds as painless as possible. `run:ios`, `run:android` commands.
54a11a5f47SEvan Bacon- Implementing a versioned `prebuild` command that can reliably work with a project for long periods of time. Prebuild is like a bundler for native code, it generates the `ios`, `android` folders based on the project Expo config (`app.json`).
55a11a5f47SEvan Bacon  - `npx expo config` is auxiliary to `npx expo prebuild` and used for debugging/introspection.
56a11a5f47SEvan Bacon- Installing versioned libraries with `npx expo install` this is a minimal utility born out of pure necessity since versioning in React Native is hard to get right.
57a11a5f47SEvan Bacon
58a11a5f47SEvan Bacon# Contributing
598d307f52SEvan Bacon
608d307f52SEvan BaconTo develop the CLI run (defaults to watch mode):
618d307f52SEvan Bacon
628d307f52SEvan Bacon```
638d307f52SEvan Baconyarn build
648d307f52SEvan Bacon```
658d307f52SEvan Bacon
668d307f52SEvan BaconWe highly recommend setting up an alias for the Expo CLI so you can try it in projects all around your computer. Open your `.zshrc` or other config file and add:
678d307f52SEvan Bacon
688d307f52SEvan Bacon```
698d307f52SEvan Baconalias nexpo="/path/to/expo/packages/@expo/cli/build/bin/cli"
708d307f52SEvan Bacon```
718d307f52SEvan Bacon
728d307f52SEvan BaconThen use it with `nexpo` like `nexpo config`. You can also set up a debug version:
738d307f52SEvan Bacon
748d307f52SEvan Bacon```
758d307f52SEvan Baconalias expo-inspect="node --inspect /path/to/expo/packages/@expo/cli/build/bin/cli"
768d307f52SEvan Bacon```
778d307f52SEvan Bacon
78df49d6e1SEvan BaconThen you can run it and visit `chrome://inspect/#devices` in Chrome, and press "Open dedicated DevTools for Node" to get a debugger attached to your process. When debugging the CLI, you'll want to disable workers whenever possible, this will make all code run on the same thread, this is mostly applicable to the `start` command, i.e. `expo-inspect start --max-workers 0`.
79a11a5f47SEvan Bacon
80a11a5f47SEvan Bacon## Format
81a11a5f47SEvan Bacon
82a11a5f47SEvan Bacon- Be sure to update the [`CHANGELOG.md`](./CHANGELOG.md) with changes for every PR. You only need to add the message, our GitHub bot will automatically suggest adding your name and PR number to the diff.
83a11a5f47SEvan Bacon- End `async` functions with `Async` like `runAsync`. This is just how we format functions at Expo.
84a11a5f47SEvan Bacon- When throwing errors, always opt for `CommandError` instead of `Error` -- this helps with debugging and making the experience feel more coherent.
85a11a5f47SEvan Bacon- Utilize the unified `Log` module instead of `console.log`.
86a11a5f47SEvan Bacon- When logging with variables, utilize the following format `Something happened (foo: bar, baz: foz)`.
87a11a5f47SEvan Bacon  - Avoid other formats like `Something happened: bar, foz` or `Something happened: foo=bar, baz=foz`.
88a11a5f47SEvan Bacon- Main UI components like command names (`expo start`), arguments (`--port`), and `--help` messages should be modified internally, by the Expo team to ensure the developer experience is unified across Expo tooling. External contributions modifying these core aspects may be rejected.
89a11a5f47SEvan Bacon- Use the `profile` utility method with the `EXPO_PROFILE=1` environment variable to measure execution time.
90a11a5f47SEvan Bacon- Avoid globals and singletons as these make testing harder and less predictable. The only global we have (at the time of writing this) is the `isOffline` boolean.
91a11a5f47SEvan Bacon
92a11a5f47SEvan Bacon## Environment
93a11a5f47SEvan Bacon
94a11a5f47SEvan Bacon- Always be cautious of the transitive size of dependencies. [packagephobia](https://packagephobia.now.sh/) is a great resource for determining if a package is lean. Try to minimize adding dependencies to the CLI.
95a11a5f47SEvan Bacon- We build the CLI using `taskr` + `swc`, this is partially inspired by Next.js' local CLI.
96a11a5f47SEvan Bacon- The build pipeline will inline the CLI version as an environment variable that is accessible anywhere in the CLI codebase. You can access it via `process.env.__EXPO_VERSION` instead of reading the local `package.json` at runtime.
97a11a5f47SEvan Bacon- Unlike the legacy global Expo CLI, this CLI is shipped with `expo` meaning the SDK Version is always present.
98a11a5f47SEvan Bacon  - Reduce SDK specific tasks since only one SDK should be accounted for in a single version of `@expo/cli`.
99a11a5f47SEvan Bacon  - The `@expo/config` method `getConfig` does not need the `skipSDKVersionRequirement` in any case since `expo` should always be installed. Ex: `getConfig('...', { skipSDKVersionRequirement: true });` shouldn't be used.
100a11a5f47SEvan Bacon- Also unlike the global Expo CLI we can assume that node modules are always installed since this CLI should be used via a project's local `node_modules` folder.
101a11a5f47SEvan Bacon  - This means we can't perform operations that upgrade the `expo` package as these may kill the running process. Features that need this pattern (like `expo upgrade`) should live in standalone global tools.
102a11a5f47SEvan Bacon
103a11a5f47SEvan Bacon## Testing
104a11a5f47SEvan Bacon
105a11a5f47SEvan BaconThere are two testing scripts:
106a11a5f47SEvan Bacon
107a11a5f47SEvan Bacon- `yarn test`: Controlled unit and integration tests.
108df49d6e1SEvan Bacon- `yarn test:e2e`: End to end testing for CLI commands. This requires the files to be built with `yarn build`
109a11a5f47SEvan Bacon
110a11a5f47SEvan Bacon---
111a11a5f47SEvan Bacon
112a11a5f47SEvan Bacon- You can target a specific set of tests with the `--watch` flag. Example: `yarn test --watch config`.
113a11a5f47SEvan Bacon- We use backticks for `it` blocks. Example <code>it(`works`)</code>.
114df49d6e1SEvan Bacon- If a pull request is fully self-contained to the `packages/@expo/cli/` folder (i.e. no `yarn.lock` modifications, etc.) then most native CI tests will be skipped, making CI pass faster in PRs.
115a11a5f47SEvan Bacon
116a11a5f47SEvan Bacon### Unit Testing Guidelines
117a11a5f47SEvan Bacon
118a11a5f47SEvan Bacon- Use `nock` for network requests.
119a11a5f47SEvan Bacon- No top level `describe` blocks that wrap all the tests in a file.
120df49d6e1SEvan Bacon- When testing a function, pass the function to the `describe` block instead of a stringified function name:
121a11a5f47SEvan Bacon  - `describe(foobar, () => {})` instead of `describe('foobar', () => {})`
122a11a5f47SEvan Bacon- Use virtual `fs` via `memfs` whenever possible.
123a11a5f47SEvan Bacon- We have a lot of global module [**mocks**](./e2e/setup.ts) already in place, consider them when writing tests.
124a11a5f47SEvan Bacon- GitHub Copilot can make writing tests a little less tedious.
125a11a5f47SEvan Bacon
126a11a5f47SEvan Bacon### E2E Testing Guidelines
127a11a5f47SEvan Bacon
128a11a5f47SEvan Bacon- E2E tests should be resilient and reliable, be sure to give them plenty of time for network requests.
129a11a5f47SEvan Bacon- When testing locally you should attempt to reuse node modules for faster results. In the `npx expo prebuild` and `npx expo start` commands for instance, we utilize a helper method that will default to reusing a project + node_modules when run locally. This can be [toggled off](https://github.com/expo/expo/blob/11a5a4d27b7e1c8e4d6ddf0401397d789d89f52a/packages/%40expo/cli/e2e/__tests__/utils.ts#L174) to bootstrap a fresh project every time.
130a11a5f47SEvan Bacon- When bootstrapping test projects, utilize the temporary folder `os.tmpdir()` as this folder is automatically cleaned up when the computer restarts.
131a11a5f47SEvan Bacon
132a11a5f47SEvan Bacon## Coming from Expo CLI
133a11a5f47SEvan Bacon
134a11a5f47SEvan Bacon> TL;DR: `expo-cli` was 'make it work', whereas `@expo/cli` is 'make it right, make it fast'.
135a11a5f47SEvan Bacon
136a11a5f47SEvan BaconThe legacy global `expo-cli` package was deprecated in favor of this versioned `@expo/cli` package for the following reasons:
137a11a5f47SEvan Bacon
138a11a5f47SEvan Bacon- `expo-cli` was too big and took way too long to install. This made CI frustrating to set up since you needed to also target global node modules for caching.
139a11a5f47SEvan Bacon- `expo-cli` worked for almost all versions of the `expo` package, meaning it was getting more complex with every release.
140a11a5f47SEvan Bacon- `expo-cli` combined service commands (like the legacy `build`, `submit`, `publish`) with project-level commands like `expo start`. We've since divided services into `eas-cli` and project commands into `npx expo` (`@expo/cli`). This structure is more optimal/faster for developers since they can install/update commands when they need them.
141a11a5f47SEvan Bacon- This CLI utilizes more Node.js standard features like `$EDITOR` instead of the custom `$EXPO_EDITOR` environment variable. Also transitioning away from `$EXPO_DEBUG` and more towards `$DEBUG=expo:*`. These types of changes make Expo CLI play nicer with existing tooling.
142a11a5f47SEvan Bacon- The DevTools UI has been deprecated to reduce the net install size, minimize complexity, and make room for future debugging UIs (Hermes/v8 Chrome debugger).
143a11a5f47SEvan Bacon- The `expo start:web` and `expo web` commands have been rolled into `npx expo start` as we now lazily load platforms until the device requests them.
144a11a5f47SEvan Bacon- Other missing or beta features from `expo-cli` may still be getting migrated over to this new CLI. For a more comprehensive breakdown see the [start command PR](https://github.com/expo/expo/pull/16160).
145