Yarn 2 (berry) + React Native / Expo has never been so easy
⚠️ This repository uses expo for illustration purposes, but it works equally well with “vanilla” React Native
Latest tested Expo SDK: 44 Latest tested yarn version: 3.2.0 PRs welcome to upgrade Expo SDK and yarn versions.
The goal of this project is to have the workspace expo-client
depend on workspace common
.
yarn install
yarn workspace expo-client start
You need yarn version 2.3+
yarn set version berry
Add this line to your .yarnrc.yml
, see explanation here.
nodeLinker: node-modules
In the workspace using React Native or Expo, add this to package.json
file:
{
//...
"installConfig": {
"hoistingLimits": "workspaces"
}
}
This is a central piece: it prevents react-native
and other packages to be hoisted to the root node_modules
folder.
It replaces the outdated nohoist
config from yarn 1.
metro.config.js
This latest step will help metro identify how to resolve workspace dependencies (those specified with the workspace protocol). To address the issue of workspaces being symlinked by yarn, we use the Proxy trick:
const path = require("path");
const fs = require("fs");
const { getDefaultConfig } = require("expo/metro-config");
const workspaces = fs.readdirSync(path.resolve(__dirname, "../"));
const currentWorkspace = path.basename(__dirname);
module.exports = (async () => {
const expoMetroConfig = await getDefaultConfig(__dirname);
return {
...expoMetroConfig,
projectRoot: __dirname,
watchFolders: workspaces
.filter((f) => f !== currentWorkspace)
.map((f) => path.join(__dirname, "../", f)),
resolver: {
extraNodeModules: new Proxy(
{},
{
get: (target, name) => path.join(__dirname, `node_modules/${name}`),
}
),
},
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: true,
},
}),
},
};
})();
You will need to replace commonModulePath
with any workspace path you'd like to import.
If your project follows a pattern where all workspaces are under the same folder, you can automate the process easily.
See how it is implemented in this project.
Remark: Extending
expo/metro-config
is required since Expo SDK 41 but must not be used in a vanilla React Native projects or Expo SDK 40 and lower versions.
EAS builds will almost work out of the box with one hurdle: the pre-build step. Because
EAS will tamper with the package.json
file to add required dependencies for the build step,
and engage with a yarn install
. The latest will fail since yarn berry sets the immutable
flag when the CI
environment is present.
Fortunately, you can force the immutable flag off by setting the YARN_ENABLE_IMMUTABLE_INSTALLS
env to false
.
Just patch your eas.json
file:
diff --git a/packages/expo-client/eas.json b/packages/expo-client/eas.json
index 15b318c..d142455 100644
--- a/packages/expo-client/eas.json
+++ b/packages/expo-client/eas.json
@@ -7,7 +7,11 @@
"preview": {
"distribution": "internal"
},
- "production": {}
+ "production": {
+ "env": {
+ "YARN_ENABLE_IMMUTABLE_INSTALLS": "false"
+ }
+ }
},
"submit": {
"production": {}
If you have mismatched React versions between your expo-client package and workspace dependencies, you will run into failures. Make sure all React versions match. You can audit this with the below command:
yarn why react
If only one react version is resolved, you will be fine.
You can also force the resolution to a specific version via the resolutions
field of your root package.json
. This can be done via the following command:
yarn set resolution -s react@npm 17.0.1