HowTo: EAS managed build in a monorepo on SDK 41

I know EAS isn’t available yet for managed projects, but wanted to share some notes since I was able to get things working with yarn, a monorepo, and a managed project.

  • SDK 41
  • expo-cli version: 4.4.1
  • eas version: eas-cli/0.10.0 wsl-x64 node-v14.8.0
  • yarn >=2.3.0
  • expo-yarn-workspaces no longer required
  1. Create a standard eas.json with the workflow set to managed
  2. In your package.json, add the installConfig option of "hoistingLimits": "workspaces" (see example below)
  3. In your .yarnrc.yml, enable the default node-modules linker: nodeLinker: node-modules
  4. eas build

There were two really awkward issues that come out of using a monorepo. First, older versions of yarn supported workspaces, but didn’t have first class support for managing your hoisting. While expo-yarn-workspaces worked locally, it seemed to confuse the build service during the managed + eject step. As of yarn 2.3, the team added nohoisting options you can place in your app’s package.json, making it much easier to simply turn off the hoisting in one part of your monorepo. The team even refers to React Native as a major motivator for this support. Yarn 2.3 🍦✨ Info Command, Detailed Options, Nohoist, ... - DEV Community

Second, you’ll want the node-modules linker. I did some brief testing with yarn 2’s PNP / zero-install, but just found the builds to be more consistent with the node-modules linker.

If you were using expo 40 and eas, you’ll want to also clear your cache by setting your cache values in the eas.json, specifically for your ios build which might be caching old pod files.

I hope someone else finds this useful. It’s amazing to see EAS working, as there’s nothing quite like watching expo manage the whole release for you.

package.json section for limiting hoisting
In your app, add the following configuration option. It will tell yarn “for this workspace, don’t hoist anything”. It’s a yarn-official alternative to the workarounds we did with the older expo-yarn-workspaces module.

{
  // ...
  "installConfig": {
    "hoistingLimits": "workspaces"
  },
5 Likes

Thanks. This worked for me. The key was blocking yarn from hoisting.

But one snag. I can import modules from my monorepo that are simple JS modules, but when I try to import one of my react components, I get this:

Unable to resolve module @babel/runtime/helpers/interopRequireDefault from /Users/jim/development/.../modules/loading-overlay/index.js: @babel/runtime/helpers/interopRequireDefault could not be found within the project.

The error message in the console points to the first line of my react component:

iOS Bundling failed 3378ms
Unable to resolve module @babel/runtime/helpers/interopRequireDefault from /Users/jim/development/.../modules/loading-overlay/index.js: @babel/runtime/helpers/interopRequireDefault could not be found within the project.

If you are sure the module exists, try these steps:
 1. Clear watchman watches: watchman watch-del-all
 2. Delete node_modules and run yarn install
 3. Reset Metro's cache: yarn start --reset-cache
 4. Remove the cache: rm -rf /tmp/metro-*
> 1 | import React, { useState, useEffect, useRef } from 'react';
  2 | import PropTypes from 'prop-types';
  3 | import { CSSTransition } from 'react-transition-group';
  4 | import { css, cx } from 'emotion';

Several hours of whack-a-mole trying things from the Internet hasn’t helped.

Any ideas?

I’ve narrowed this down a bit. The import of React is what triggers is.

The dependencies in my non-hoisted expo app are (and include react and babel):

  "dependencies": {
    "@babel/runtime": "^7.15.4",
    "@react-navigation/native": "^6.0.2",
    "@react-navigation/native-stack": "^6.1.0",
    "expo": "~42.0.1",
    "expo-status-bar": "~1.0.4",
    "react": "16.13.1",
    "react-dom": "16.13.1",
    "react-native": "https://github.com/expo/react-native/archive/sdk-42.0.0.tar.gz",
    "react-native-safe-area-context": "3.2.0",
    "react-native-screens": "~3.4.0",
    "react-native-web": "~0.13.12"
  },
  "devDependencies": {
    "@babel/core": "^7.9.0",
    "eslint": "^7.32.0"
  },

Would you mind sharing how you were able to get things working at all with PnP enabled?

yarn pnp does not support react-native. you have to use the node_modules linker

Would love to see a refresh of this. My current solution is only good for development, not for build/deploy.

I read SDK43 might fix this. Any ETA on that?

yarn pnp support in react-native isn’t something we’re working on, and i don’t think it’s something that the yarn or react-native teams are working on either. but monorepo support for yarn workspaces with the node_modules linker does indeed improve in sdk 43

Expo SDK 43 Beta was released a few days ago, so the final release is expected any day now: