rushstack icon indicating copy to clipboard operation
rushstack copied to clipboard

[rush] How to use with React Native?

Open joerneu opened this issue 6 years ago • 21 comments

I am trying to setup a monorepo with React Native projects.

The React Native packager is not able to follow symlinks. Even with workarounds for linked projects (see reference below) React Native's packager cannot resolve React Native's own packages if "react-native" is symlinked.

To solve this I have been using the "nohoist" option of yarn's workspaces. With this option some packages are not symlinked and will be installed in the project's node_modules folder.

Is there a way to tell Rush to place some packages in the project's node_modules folder instead of the symlinked shared folder?

Any other workaround?

References:

Similiar PNPM issue with React Native and symlinks React Native's packager issue with symlinks

joerneu avatar Jan 12 '19 11:01 joerneu

I suppose we could have a setting that instructs Rush to run npm install in specific folders, rather than symlinking their node_modules folder into the common/temp/node_modules tree.

However, how would this work for locally-linked library projects? Or do you only need it for your top-level application?

BTW I am aware of people successfully using Rush with React Native, however I don't have experience with this myself. @acoates-ms did your group do anything special to get this working?

octogonz avatar Jan 20 '19 21:01 octogonz

Yes. As you pointed out, this is the oldest open issue against metro (#1). For most of our usage we use Haul instead of metro, as haul supports symlinks, unlink metro.

You can also do complex setup with your rn-cli.config.js to redirect metro for each symlink. But its a messy solution.

acoates-ms avatar Jan 22 '19 17:01 acoates-ms

@acoates-ms: Thanks for the info!

I tried using rn-cli.config.js (see Script) and it works with links to local library projects. (With yarn workspaces and "nohoist" option.)

However, I could not get it to work with React Native's tooling which seems to not find symlinked modules before rn-cli.config.js is executed.

@pgonzal: I was looking for something like the "nohoist" option of yarn's workspaces. Here is a good explanation: "Yarn Blog nohoist in Workspaces" Maybe something similiar could be implemented in Rush?

joerneu avatar Jan 23 '19 11:01 joerneu

@joerneu we would certainly accept a PR to add this sort of functionality to rush install.

(That said, I personally have spent a ton of time dealing with frustrating consequences of NPM doppelgangers and was very happy to finally eliminate them from our monorepos. Yarn/Lerna's installation strategies reintroduce these sorts of problems: "nohoist" causes doppelgangers, and if you do "hoist" it causes phantom dependencies which are also problematic. So if I was going to invest in this, I'd prefer to spend my time fixing React Native to correctly support the NodeJS module resolution standard, versus building these sorts of accommodations in Rush. But of course I recognize that this might not be the cheapest path to get React Native working, and I would of course vote for any PR that helps with that goal.)

octogonz avatar Jan 27 '19 21:01 octogonz

Hi! Our team is considering adopting Heft to manage our RN monorepo. Did someone here manage to successfully build a React Native app using Heft?

emanueleDiVizio avatar Jan 26 '21 18:01 emanueleDiVizio

I met this issue too, waiting for solutions😭

ericlee33 avatar Oct 06 '21 17:10 ericlee33

We've made some progress here. This package will add symlink support to metro: https://github.com/microsoft/rnx-kit/tree/main/packages/metro-resolver-symlinks

acoates-ms avatar Oct 06 '21 17:10 acoates-ms

@acoates-ms THANK YOU. that works wonderfully!

@zkochan can we add this ^ to the docs?

shellscape avatar Dec 08 '21 21:12 shellscape

sure, we should add some recipe page for React Native.

zkochan avatar Dec 08 '21 22:12 zkochan

Anyone have any luck getting a React Native app running with Rush + pnpm? Seeing the following error:

Error: Unable to resolve module ./index from D:\devl\<my project>\common\temp/.

Looks like metro doesn't resolve the root properly. I have tried configuring the projectRoot metro config item a couple of different ways but no luck so far.

atzawada avatar Jan 26 '22 06:01 atzawada

For our use case, Rush + pnpm + repack seems to do the trick for us. Was never able to get metro working with our package structure even with the above advice.

atzawada avatar Feb 07 '22 19:02 atzawada

Since recently pnpm also has a node-linker=hoisted option. So if nothing helps, node-linker=hoister may be used, which creates a regular node_modules structure without using symlinks.

zkochan avatar Feb 07 '22 20:02 zkochan

Hey @zkochan,

I have a couple of quick questions about the setting you mentioned, I haven't quite gotten a chance to play around with it:

  • Do you have any performance benchmarks on running hoisted vs non-hoisted? Would be curious how package install/add times differ between the two.
  • Will running in hoisted mode still allow for the workspace:<package> method of cross referencing local packages to work?
  • Without using symlinks, it seems like Typescript workflows would be broken, since the latest compiled code would not get shared between modules. Is that a correct assumption and if so is there a workaround?

Thank you for all you do to make the experience of developers working in JS/TS monorepos better. Pnpm has been a lifesaver for us!

I will create a minimal example using the tooling I described above when I get a chance.

atzawada avatar Feb 07 '22 21:02 atzawada

Do you have any performance benchmarks on running hoisted vs non-hoisted? Would be curious how package install/add times differ between the two.

I don't have benchmarks but it seems fast.

Will running in hoisted mode still allow for the workspace: method of cross referencing local packages to work?

It will work

Without using symlinks, it seems like Typescript workflows would be broken, since the latest compiled code would not get shared between modules. Is that a correct assumption and if so is there a workaround?

It will be shared. It will be hoisted to the root of the monorepo.

zkochan avatar Feb 07 '22 21:02 zkochan

For our use case, Rush + pnpm + repack seems to do the trick for us. Was never able to get metro working with our package structure even with the above advice.

Ended up ditching this config, it was really brittle when adding dependencies. Ended up getting errors out of repack that weren't of any help whatsoever.

Fell back to removing my RN app from the rush config and using yarn to manage the dependencies. Wired in all the local dependencies in using the Yarn file: URL type. Will work for now because our shared deps don't change a whole lot, but I am still going to investigate getting symlinking working again. @rnx-kit/metro-resolver-symlinks Didn't seem to work for me when linking in local modules. I'll open an issue with that project when I can create a minimal reproduction to test it out with.

I haven't tried pnp yet, but I have a feeling even if I did get the RN tooling working, other parts of the monorepo would probably explode.

atzawada avatar Feb 18 '22 17:02 atzawada

HBO is successfully using Rush+PNPM+ReactNative at my work. We're using @vjpr's patches but rolled up into a reusable adapter. Maybe we could share it, or at least add a ReactNative sample project in https://github.com/microsoft/rushstack-samples/

octogonz avatar Feb 21 '22 01:02 octogonz

HBO is successfully using Rush+PNPM+ReactNative at my work. We're using @vjpr's patches but rolled up into a reusable adapter. Maybe we could share it, or at least add a ReactNative sample project in https://github.com/microsoft/rushstack-samples/

This would be amazing

jennysharps avatar Mar 15 '22 13:03 jennysharps

Update: After testing I don't think this actually works properly, as it's just naively including modules from the first match it finds in the watchFolders regardless of if the version number matches or not.

~~Just wanted to add that I think I got metro working with both yarn and pnpm using @rnx-kit/metro-resolver-symlinks and the following metro.config.js in a test repo (that I can't currently share as it's private, sorry) with no special hoisting options:~~

const { makeMetroConfig } = require('@rnx-kit/metro-config');
const MetroSymlinksResolver = require('@rnx-kit/metro-resolver-symlinks');
const exclusionList = require('metro-config/src/defaults/exclusionList');
const path = require("path");
 
const config = makeMetroConfig({
    projectRoot: __dirname,
    resolver: {
        // ignore projects folder since it contains package.json files with module name + version that clash with the workspace package.json files
        blacklistRE: exclusionList([/temp\/projects\/.*/]),
        resolveRequest: MetroSymlinksResolver(),
    },
});
 
// Remove first element, which is always the repo root;
config.watchFolders.shift();
 
// Remove the project root, as it's already being watched by metro
config.watchFolders = config.watchFolders.filter(folder => folder !== __dirname);
 
// Add rush common/temp directory as shared node modules live here;
// many other package node_modules are symlinked to this location
config.watchFolders.push(path.resolve(__dirname, "../../common/temp"));
 
module.exports = config;

~~Just in case it's helpful to anyone. Also not sure if this actually works properly, but it doesn't throw any metro errors or warnings, and at first glance seems to be working in a very basic app.~~

jennysharps avatar Mar 24 '22 18:03 jennysharps

@octogonz please do

jcgertig avatar Apr 01 '22 03:04 jcgertig

Hi ! Is someone still have the issue with rushjs and RN or did you found a solution ? We are in 2024 and we are facing the issue 🫠

QuanticPotatoes avatar Aug 05 '24 15:08 QuanticPotatoes

If you use pnpm 9+ should work properly. I just replicate it with a minimal example using @jennysharps snippet.

sebastiantuyu avatar Oct 20 '25 16:10 sebastiantuyu