• Stars
    star
    661
  • Rank 68,192 (Top 2 %)
  • Language
    TypeScript
  • License
    MIT License
  • Created almost 4 years ago
  • Updated 4 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Fast pnpm monorepo for cross-platform apps built with Expo / React Native and React.

Expo monorepo

Fast pnpm monorepo for cross-platform apps built with Expo and React

Test workflows Preview workflows Build workflows

Why is it fast?  —  How to use it  —  Structure  —  Workflows  —  Caveats & Issues


⚡ Why is it fast?

This repository uses both pnpm and Turborepo to speed things up, by a lot. With pnpm, we leverage the installation performance using the global store cache. Turborepo helps us to run certain tasks, and cache the result if we rerun tasks with the same input or code. In the workflows we cache the pnpm store and Turborepo cache using GitHub Actions built-in cache, resulting in the best performance possible.

What about Metro?

In apps/mobile we leverage the Metro cache to speed up building and publishing. We use Turborepo to restore or invalidate this cache, working around potential environment variable issues. To populate this Metro cache, the apps/mobile has a $ pnpm build script that exports React Native bundles. The resulting Metro cache is then reused when publishing previews.

ℹī¸ Should I use it?

This repository demonstrates a working stack using Expo in a fast monorepo, while sharing most of the codebase with web. The primary goal of this repository is to showcase what is possible with Expo while keeping the code as "vanilla" as possible. Feel free to use this repository however you prefer, but when starting a project from scratch, consider a template with more assumptions. Those assumptions should help you develop your project faster than this repository can.

🚀 How to use it

You can use and modify this repository however you want. If you want to use EAS to build your app, you'll need to create an Expo access token and set it as EXPO_TOKEN GitHub actions secret.

To run the repository locally, run these two commands:

  • $ pnpm install - This installs all required Node libraries using pnpm.
  • $ pnpm dev - Starts the development servers for all apps.

Commands

Because this monorepo uses Turborepo, you don't need to run additional commands to set things up. Whenever you run $ pnpm build, it will build all packages if they aren't built yet. In this monorepo we use a few commands or pipelines:

  • $ pnpm dev - Build and watch all apps and packages for development.
  • $ pnpm lint - Analyze the source code of all apps and packages using ESLint.
  • $ pnpm test - Run all tests for packages with Jest tests.
  • $ pnpm build - Build all apps and packages for production or to publish them on npm.

When developing or deploying a single app, you might not need the development server for all apps. For example, if you need to make a fix in the mobile app, you don't need the web development server. Or when deploying a single app to production, you only need to build that single app with all dependencies.

This monorepo uses a simple npm script convention of dev:<app-name> and build:<app-name> to keep this process simple. Under the hood, it uses Turborepo's workspace filtering, defined as an npm script in the root package.json.

  • $ pnpm dev:mobile - Build and watch app/mobile and all packages used in mobile, for development.
  • $ pnpm dev:web - Build and watch app/web and all packages used in web, for development.
  • $ pnpm build:mobile - Build apps/mobile and all packages used in mobile, for production deployments
  • $ pnpm build:web - Build apps/web and all packages used in web, for production deployments

Switching to yarn or npm

You can use yarn or npm with this monorepo as well. If you want to use one of these package managers, instead of pnpm, all you have to do is:

  • Remove .npmrc, pnpm-lock.yaml, and pnpm-workspace.yaml.
  • Remove the pnpm property from the root package.json file.
  • Add the workspaces property to the root package.json file.
  • Update the workflows to use yarn or npm instead.

📁 Structure

  • apps - Apps that only use packages and aren't aware of other apps.
  • packages - Packages that may use external and/or other monorepo packages.

Apps

  • apps/mobile - Expo app using eslint-config and feature-home packages.
  • apps/web - Next.js app using eslint-config and feature-home packages.

Packages

  • packages/eslint-config - Preconfigured ESLint configuration for each app or package.
  • packages/feature-home - Shared React Native domain-logic for apps, using both ui and eslint-config packages.
  • packages/ui - Shared React Native UI components for apps, using the eslint-config package.

👷 Workflows

  • build - Starts the EAS builds for apps/mobile using the given profile.
  • preview - Publishes apps to a PR-specific release channel and adds a QR code to that PR.
  • test - Ensures that the apps and packages are healthy on multiple OSs.

Composite workflows

  • setup-monorepo - Reusable composite workflow to setup the monorepo in GitHub Actions.

⚠ī¸ Caveats

Using environment variables in React Native

Reusing Metro caches can be dangerous if you use Babel plugins like transform-inline-environment-variables. When using Babel plugins to swap out environment variables for their actual value, you are creating a dependency on environment variables. Because Metro is unaware of dependencies on environment variables, Metro might reuse an incorrect cached environment variable.

Since Turborepo handles the cache in this repository, we can leverage caching based on environment variables. This invalidates the Metro cache whenever certain environment variables are changed and avoid reusing incorrect cached code.

pnpm workarounds

In the current React Native ecosystem, there are a lot of implicit dependencies. These can be from the native code that is shipped within packages, or even implicit dependencies through installing a specific version of Expo or React Native. In the newer package managers like pnpm, you will run into issues due to these implicit dependencies. Besides that there are other issues like Metro not following symlinks.

To workaround these issues, we have to change some config:

  1. Let pnpm generate a flat node_modules folder, without symlinks. You can do that by creating a root .npmrc file containing (node-linker=hoisted). This works around two things; no Metro symlink support, and having a simple way to determine where the modules are installed (see point 3).

  2. Either disable strict-peer-dependencies or add peerDependencyRules.ignoreMissing rules in the package.json. This disables some of the expected implicit peer dependencies issues. Without these changes, pnpm will fail on install asking you to install various peer dependencies.

  3. Update the metro.config.js configuration for usage in monorepos. Full explanation per configuration option can be found in the Expo docs. The only addition in this repository is the config.cacheStores. This change moves the Metro cache to a place which is accessible by Turborepo, our main cache handler (see Why is it fast?).

Precompile packages

EAS only sends the files which are committed to the repository. That means the packages/*/build folders need to be generated before building our apps. To tell EAS how to compile our packages, we can use the postinstall hook.

Running EAS from apps directories

As of writing, the eas build command needs to be executed from the package folder itself. EAS will still create a tarball with all files from your monorepo, but runs the build commands from this local folder. You can see this happening in the build workflow.

Using local credentials in CI

If you want to maintain the keystore or certificates yourself, you have to configure EAS with local credentials. When your CI provider doesn't allow you to add "secret files", you can encode these files to base64 strings and decode whenever you need it.

It's highly recommended to keep keystores and certificates out of your repository to avoid security issues.

❌ Common issues

No ongoing issues, we are actively monitoring and fixing potential issues


with ❤ī¸Â Â byCedric

More Repositories

1

use-expo

Complementary hooks for Expo
TypeScript
314
star
2

semantic-release-expo

An Expo implementation for semantic release, so you don't have to bother.
TypeScript
86
star
3

office-marathon

Example app for background location tracking in Expo
TypeScript
61
star
4

eas-gha

Running EAS locally in GitHub actions
TypeScript
36
star
5

custom-prebuild-example

Demonstration of out-of-tree platforms for Expo Prebuild and Config Plugins
JavaScript
28
star
6

expo-vscode-debug

JavaScript
9
star
7

snack-custom-runtime

TypeScript
9
star
8

nice-little-present-for-brent

TypeScript
8
star
9

swaglint

Yet another swagger linter
TypeScript
8
star
10

Allay

Build scalable and restful API's, in minutes.
PHP
6
star
11

Delegator

A nice API, for Laravel, helper to keep your responses rested.
PHP
6
star
12

expo-guide-ci

Example code for the CI/CD guide in the Expo documentation
JavaScript
5
star
13

Docker

A set of docker images, used by me
Dockerfile
5
star
14

GitHub-Website-legacy

Let your work speak for itself, have a simple site that shows your GitHub feed!
TypeScript
4
star
15

expo-cli-images

Prebuilt docker images for Expo on Docker-based environments
Dockerfile
4
star
16

expo-yarn-winspaces

`expo-yarn-workspaces` with better Windows support
JavaScript
4
star
17

Laravel-Now-Example

An example of Laravel running on Now
PHP
3
star
18

semantic-release-git-branches

A fork of @semantic-release/git that uses a more gitflow approach for releases
JavaScript
3
star
19

expo-monorepo-benchmark

Java
3
star
20

expo-issue-14248

JavaScript
3
star
21

test-rn-languages

TypeScript
2
star
22

everywhere-app

Template app to create apps that run everywhere
JavaScript
2
star
23

test-github-actions

JavaScript
2
star
24

Inquiry

A Laravel URI syntax parser, designed for REST apis.
PHP
2
star
25

bycedric.com-legacy

The actual website bycedric.com
JavaScript
2
star
26

commit-ast-experiment

Conventional commits parser using Nearley grammar
TypeScript
2
star
27

benchmark-nitro-expo-monorepo

Java
2
star
28

Hue-P

Play patterns on your Hue Lamps
TypeScript
2
star
29

expo-pnpm-tests

JavaScript
2
star
30

sometimes-you-just-have-to-practice-what-you-preach-instead-of-only-preaching

JavaScript
2
star
31

expo-text-encoding-example

TypeScript
2
star
32

test-bundle-inspect

TypeScript
2
star
33

expo-issue-android-permissions

JavaScript
1
star
34

expo-issue-9793

TypeScript
1
star
35

dogfood

TypeScript
1
star
36

i-cant-believe-this-works

TypeScript
1
star
37

expo-monorepo-archived

Helper to configure monorepo support for Expo
TypeScript
1
star
38

expo-background-location-issue

TypeScript
1
star
39

Nibbler

TBD
TypeScript
1
star
40

react-native-expo-modules

Java
1
star
41

expo-monorepo-assets

TypeScript
1
star
42

expo-bare-ota

Java
1
star
43

Commitlint-Formats

Extra formatters for commitlint
JavaScript
1
star
44

expo-dom-components

TypeScript
1
star
45

expo-area51

1
star
46

Haste

No time to waste
JavaScript
1
star
47

expo-eject-ios-windows

Java
1
star
48

react-promiser

A react component to help manage promises
TypeScript
1
star
49

expo-web-device-motion-issue

TypeScript
1
star
50

lint-staged-cz-customizable-commitlint

An example for https://github.com/conventional-changelog/commitlint/issues/766
JavaScript
1
star
51

expo-system

TypeScript
1
star
52

expo-pull-27212

Test bench to validate big changes
1
star
53

expo-issue-router-base-url

TypeScript
1
star
54

expo-template

This is a test for `npm create expo`
TypeScript
1
star
55

expo-50-pnpm

TypeScript
1
star
56

expo-test-android-archs

TypeScript
1
star
57

npm-npx-bin-collision

JavaScript
1
star