microsoft / rushstack

Monorepo for tools developed by the Rush Stack community
https://rushstack.io/
Other
5.82k stars 592 forks source link

[rush] How to use with React Native? #1025

Open joerneu opened 5 years ago

joerneu commented 5 years ago

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

octogonz commented 5 years ago

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?

acoates-ms commented 5 years ago

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.

joerneu commented 5 years ago

@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?

octogonz commented 5 years ago

@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.)

emanueleDiVizio commented 3 years ago

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?

ericlee33 commented 2 years ago

I met this issue too, waiting for solutions😭

acoates-ms commented 2 years ago

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

shellscape commented 2 years ago

@acoates-ms THANK YOU. that works wonderfully!

@zkochan can we add this ^ to the docs?

zkochan commented 2 years ago

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

atzawada commented 2 years ago

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 commented 2 years ago

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.

zkochan commented 2 years ago

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.

atzawada commented 2 years ago

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:

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.

zkochan commented 2 years ago

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.

atzawada commented 2 years ago

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.

octogonz commented 2 years ago

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/

jennysharps commented 2 years ago

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 commented 2 years ago

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.

jcgertig commented 2 years ago

@octogonz please do

QuanticPotatoes commented 1 month ago

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 🫠