What is @expo/cli?
@expo/cli is a command-line tool that helps developers build, develop, and manage React Native projects using the Expo framework. It simplifies the process of creating, running, and deploying mobile applications.
What are @expo/cli's main functionalities?
Creating a new Expo project
This command initializes a new Expo project with a default template. You can choose from various templates like blank, tabs, or minimal.
expo init my-new-project
Running the project
This command starts the development server and opens the Expo DevTools in the browser. It allows you to run your app on an emulator, simulator, or physical device.
expo start
Building the project
This command builds the project for Android. You can also build for iOS using `expo build:ios`. It generates the APK or IPA files needed for distribution.
expo build:android
Publishing the project
This command publishes your project to Expo's hosting service, making it available to anyone with the Expo Go app. It uploads your app's JavaScript bundle and assets.
expo publish
Ejecting from Expo
This command ejects your project from the managed workflow to the bare workflow, giving you full control over the native code. This is useful if you need to add custom native modules.
expo eject
Other packages similar to @expo/cli
react-native-cli
The React Native CLI is a command-line tool for managing React Native projects. It offers similar functionalities to @expo/cli, such as initializing projects, running them on emulators or devices, and building for production. However, it does not provide the same level of managed services and ease of use as Expo.
ignite-cli
Ignite CLI is a command-line tool for creating and managing React Native projects with a focus on best practices and scalability. It provides a set of boilerplates and plugins to kickstart development. Compared to @expo/cli, Ignite offers more opinionated setups and additional tools for state management and navigation.
create-react-native-app
Create React Native App (CRNA) is a tool to create a React Native project with no build configuration. It is similar to `expo init` but focuses solely on the initial setup. CRNA has been integrated into Expo CLI, making @expo/cli a more comprehensive tool.
The fastest way to build and run universal React Native apps for Android, iOS, and the web
📚 Read the Documentation
|
Contribute to Expo CLI
The @expo/cli
package is a CLI binary that should be used via the expo
package, like npx expo start
(or npx expo
for short).
npx expo
⭐️ Be sure to star the Expo GitHub repo if you enjoy using the project!
Design
This CLI has the following purposes:
- 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).
- 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
.
- 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. - 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
).
npx expo config
is auxiliary to npx expo prebuild
and used for debugging/introspection.
- 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.
Contributing
To develop the CLI run (defaults to watch mode):
yarn build
We 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:
alias nexpo="/path/to/expo/packages/@expo/cli/build/bin/cli"
Then use it with nexpo
like nexpo config
. You can also set up a debug version:
alias expo-inspect="node --inspect /path/to/expo/packages/@expo/cli/build/bin/cli"
Then 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
.
Format
- Be sure to update the
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. - End
async
functions with Async
like runAsync
. This is just how we format functions at Expo. - When throwing errors, always opt for
CommandError
instead of Error
-- this helps with debugging and making the experience feel more coherent. - Utilize the unified
Log
module instead of console.log
. - When logging with variables, utilize the following format
Something happened (foo: bar, baz: foz)
.
- Avoid other formats like
Something happened: bar, foz
or Something happened: foo=bar, baz=foz
.
- 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. - Use the
profile
utility method with the EXPO_PROFILE=1
environment variable to measure execution time. - 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.
Environment
- Always be cautious of the transitive size of dependencies. packagephobia is a great resource for determining if a package is lean. Try to minimize adding dependencies to the CLI.
- We build the CLI using
taskr
+ swc
, this is partially inspired by Next.js' local CLI. - 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. - Unlike the legacy global Expo CLI, this CLI is shipped with
expo
meaning the SDK Version is always present.
- Reduce SDK specific tasks since only one SDK should be accounted for in a single version of
@expo/cli
. - 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.
- 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.
- 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.
Testing
There are two testing scripts:
yarn test
: Controlled unit and integration tests.yarn test:e2e
: End to end testing for CLI commands. This requires the files to be built with yarn build
- You can target a specific set of tests with the
--watch
flag. Example: yarn test --watch config
. - We use backticks for
it
blocks. Example it(works
)
. - 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.
Unit Testing Guidelines
- Use
nock
for network requests. - No top level
describe
blocks that wrap all the tests in a file. - When testing a function, pass the function to the
describe
block instead of a stringified function name:
describe(foobar, () => {})
instead of describe('foobar', () => {})
- Use virtual
fs
via memfs
whenever possible. - We have a lot of global module mocks already in place, consider them when writing tests.
- GitHub Copilot can make writing tests a little less tedious.
E2E Testing Guidelines
- E2E tests should be resilient and reliable, be sure to give them plenty of time for network requests.
- 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 to bootstrap a fresh project every time. - When bootstrapping test projects, utilize the temporary folder
os.tmpdir()
as this folder is automatically cleaned up when the computer restarts.
Coming from Expo CLI
TL;DR: expo-cli
was 'make it work', whereas @expo/cli
is 'make it right, make it fast'.
The legacy global expo-cli
package was deprecated in favor of this versioned @expo/cli
package for the following reasons:
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.expo-cli
worked for almost all versions of the expo
package, meaning it was getting more complex with every release.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.- 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. - 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).
- 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. - 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.