Closed maxkorp closed 3 years ago
Update, added a single-native-app monorepo that breaks when I include a native module (react-native-add-calendar-event, in this case)
https://github.com/maxkorp/my-new-monorepo
This works (mostly) with iOS. Just yarn && cd packages/mobileProfessional/ios && pod install && cd ../../.. && yarn workspace mobileProfessional ios
. However I get some error package android.support.annotation does not exist
(referenced inside /Users/maxkorp/Development/work/my-new-repo/node_modules/react-native-add-calendar-event/android/src/main/java/com/vonovak/Utils.java
). I get somewhat similar errors with other native modules.
I modifed the root level native_modules.gradle (at ../../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle
) to have println(reactNativeConfigOutput)
right before it parses the output, and everything looks to be in the correct location (output below)
Also of note that metro starts in the wrong directory (for both ios and android), but if I also run yarn workspace mobileProfessional start
then it works properly.
Aha! So it turns out the jetifier runs automatically, but is not finding the modules at the root directory;
if I run cd packages/mobileProfessional && npx jetifier
it finds nothing, but if I just run npx jetifier
at the root it works. Still does not fix the metro pathing.
Hey,
Thanks for deep explanation of your issue. Please run react-native config
from within your React Native mobile project and paste the output here or email me and we will go from there.
As a principle, you should always run the CLI from the root of your React Native project (especially, when you have multiple React Native projects).
Are you using Yarn Workspaces? If not, you should, and each package that references React Native should have it as its dependency. I suspect that React Native lives at the root of your monorepo, which is good.
CLI shouldn't have issues finding and linking things in such scenario, so I guess we have a small configuration issue to resolve.
(PS. Updating paths a bit is a normal thing to do, so don't worry. Ideally, I'd like to replace paths with React Native CLI calls so you dont have to update that).
Thanks for getting back to me!
So we do use yarn workspaces sortof , but due to how we have our bucklescript setup, we can't really do the dependency thing where moduleA uses moduleB so it has moduleB in it's dependencies and it gets linked in (the ocaml compiler doesn't work with the symlinks, everything is compiled in place basically eg packages/packageA/myfile.re
becomes packages/packageA/myfile.bs.js
which would just require('../../packageB/myOtherFile.bs.js)
).
I have a bunch of new notes on this as we've found stuff. Currently we have almost working with some hackery, except for loading images out of hoisted node_modules on ios only during production builds. I'll try to write everything up today and update the example repo
So there are currently a few issues. A lot of this is metro related I suspect more than anything else, but I don't know how much of it is something to resolve here in the meantime. At least one or two issues are with scripts from react-native
itself, but
I don't know how much of that actually belongs here or is influenced by this project etc.
If you would like me to make individual issues for any or all of these, or file issues against other projects (like react-native or metro), just let me know and I am happy to do so! Or if you need any help digging into this all, I can make myself available at any time of day (IIRC You are in Europe, I can sync up with you during your workday). I'm also happy to crank on the tooling and submit PRs if you know which direction you want to go on things.
As a principle, you should always run the CLI from the root of your React Native project (especially, when you have multiple React Native projects).
Do you mean in this case the root of the monorepo, or the specific workspace? We've been always doing it from the workspace (e.g. monorepo/packages/myApp
). I get results indicating I've not nothing initialized when done at the monorepo root, which I'd kind of expect.
I've attached the output of npx react-native config
from my actual repo (not the example repo). I couldn't find anything that needed redaction and I don't have the example app all up to date with my changes.
Here are all of the issues i've found trying to get things set up, that aren't as simple as changing some paths.
This is the actual cause of the issue described here: https://github.com/react-native-community/cli/issues/826#issuecomment-547189551. It seems like it runs jetify inside the rn project workspace, so no node_modules get found (since they're all hoisted to the monorepo root),
Once I run npx jetify
in the root of my monorepo, it is good. I just have this in the beggining of my scripts that run or build android.
In the "start packager" step in xcode (I forget where it happens for android, somewhere in react-native
in a gradle file IIRC), it kicks off the metro server with open somepath/run-metro.command
. That opens metro relative to where it is installed, instead of in the actual RN project.
I've solved this for now by duplicating that file into my project, and adding a CD to the project directory first.
SCRIPT_DIR="$( cd "$( dirname "$0" )" && pwd )"
cd "$SCRIPT_DIR"
Not ideal, but it works. perhaps run-metro.command
could take an arg or an environment variable to cd to the project dir? I don't know if open
supports that or not.
I found an issue about this in metro (I can't find it now), it seems okhttp resolves relative paths away before making requests (e.g. assets/../shared-mobile/someFile gets loaded as shared-mobile/someFile
which is not in the search directories). It seems they intend to move this to the query string rather than the request path, which would resolve this particular issue (although the relative pathing still causes issues with prod I'll outline below)
I fixed this in a bit of a hack by adding the root of my monorepo as well as the packages folder to the metro config search directories.
assets
is not in the request pathWith the hack I mentioned above, I could get everything to load with the exception of platform specific files e.g. requesting
assets/../../node_modules/some-module/myfile.png?platform=androidWILL find
project/../../node_modules/some-module/myfile.android.png, but
../node_modules/some-module/myfile.png?platform=android` will NOT resolve that file.
To fix this (please forgive my sins, time was of the essence for my team), I actually went full Hackstradamus and threw up a proxy on 8081 that proxies to the metro server (which I now start on 8082) that on a 404 response, will see if the requested platform is android, if we have a file extension, and the file extension is not .android.something, and if so, tries requesting the .android
file explicitly and proxies that back. This whole problem might just drop away by resolving the previous issue.
../
in it's path)So this one is kind of interesting, relating to how filepaths are copied over directly when bundling. On android, the paths seem to get flattened into for example drawable-hdpi/__node_modules_reactnavigationstack_lib_module_views_assets_backicon.png
, so any relative path issues are resolved. On ios when called via react-native/scripts/react-native-xcode.sh
the image files are resolved compared to the assets
folder inside the myApp.app
. So, for things with only a single updirectory in the relative path, this is ok (e.g. assets/../shared/something/myimage.png
ends at someXcodeBuildFolder/myApp.app/shared/something/myimage.png
), but anything farther than that it breaks because stuff is outside of the .app
(e.g. assets/../../node_modules/some_module/someImage.png
ends up at someXcodeBuildFolder/node_modules/some_module/someImage.png
, not inside someXcodeBuildFolder/myApp.app
). JS files are properly bundled into the main.jsbundle file, so it's just images (and possibly other types of asset files? I couldn't find any others). This is not an issue when running locally in the simulator, since it can properly resolve that path in my project structure.
I thought of moving everything that is resolved up from assets
inside of assets and just replacing assets\/(\.\.\/)*
with assets/
in the main.jsbundle, but that is messy since there is lots of weird symlinking that happens at build time and I don't know how and when all of it happens and stuff gets moved in the process. I might change the assetsdir when packaging to something more deeply nested, and then move it out and change the main.jsbundle.
For now, we've just copied those images out of node_modules and into our codebase, but this isn't a good long term solution, as we have no guarantee someone wont start relying on an image in node_modules later, and not see it since it works everywhere in dev and on android in prod.
This isn't directly monorepo relevant, but I found it setting up to be able to run both apps at once by running metro on 2 ports. When you start android with run-android
and give it a different packager port (e.g. --port 8082
), it starts metro on that port, and then it makes requests to localhost at that port e.g. http://localhost:8082/somepath
. On iOS it starts metro on that port, but it still requests http://localhost:8081/somepath
.
I didn't really find a solution for this, other than running android on a different port, and ios on the default.
Thank you for sending this! I will look into it this week. There's some valid points here and I'd like to take some time to understand it and see if there's anything we can do to improve your DX. Appreciate time put into describing this.
Sure thing! Please let me know if you have any questions or need me to try anything!
check this thread
https://twitter.com/sseraphini/status/1202217314638282752
the example repo is here
@sibelius Does your repo structure have the issue mentioned above: When packaging for release, iOS builds lose images from node_modules (or anything with more than one ../ in it's path)
? This can be seen if you use react-navigation
with a StackNavigator and you don't see the back arrow icon
everything is working fine for us
@Minishlink were you able to solve? Experiencing the same issue with assets.
I think we got some problems with assets in both android and iOS
IOS works well in dev mode, but android and iOS file to show images
@sibelius In DEV mod is OK. The problem is on release mode the assets got messed up as @Minishlink said
Just to support what I am saying
Here's a probable workaround, make sure you have the same cli and RN version. I don't give any warranty on this. If you use this, test it thoroughly in debug/release mode on Android/iOS. Also make sure to understand how patch-package work. https://gist.github.com/Minishlink/14fde025b4352d0802e25e098e357046
Here is an example repo I set up that shows what we've done to get things working
https://github.com/maxkorp/codepush-monorepo-example. There are some commits on master that are related to similar issues for codepush, but you can go back to the second commit to see how I got things working.
I have a problem with my monorepo, it seems that when I run the install it does not install my modules in the global package.
@Wellers0n autolinking only works in the direct RN app dependencies - you'll need native modules to be nohoist-ed dependencies of the app directly as well as the global package.
assets are still broken on production mode
is there a workaround for this?
for instance, if you use react-navigation the hamburger menu will be missing in the prod version
it works on rn 63
Ask your Question
Sorry for the impending wall of text, and thank you in advance for any help.
I have a monorepo (using yarn workspaces) that has several non-rn apps (a graphql server, 2 different websites, others) and as of now a single react-native ios/android app. This works by just nohoisting everything RN related, and some metro config to make sure it can find the js in the other packages. Everything related I've found (mostly issues in this repo, honestly) is more about getting a single RN app to work in a monorepo.
I'm looking for help with configuring everything to make this work. This is rapidly approaching being a blocker for some of my team, so I can definitely dedicate some decent time to working on cli if needed. My native-fu is somewhat weak, but I'm happy to jump in if I can get some some guidance. I am flexible.
Here is my desired project structure:
Currently, it's basically the same as that except that there is only one mobile app, and the shared-mobile folder doesn't reference any native code.
We're trying to add a second RN android/ios app to the mix. Currently we have a single shared folder for stuff accessible everywhere. I'm first trying to get the single app to work with the native code hoisted, since that seems prerequisite to the shared code being able to be hoisted. Assuming that works, I'd be able to get stuff working by not having any shared native code, but this involves a ton of code duplication with tedious reference changes.
Ideally, we'd like to add the majority of the native stuff to our shared-mobile folder. I was able to get IOS to work with a bit of a hack, but I've been unable to get android working. I've been trying both with what ships with RN 0.61.2, as well as upgrading the cli to the latest 3 alpha (7?).
For the ioS stuff, to get it working, I simply updated the xcode project and podfile to look for stuff at
../../../node_modules
instead of../node_modules
, and then I duplicated the.command
file that gets opened in a new window and added a step to CD into thepackages/mobile-professional
folder (otherwise it runs in the wrong directory and metro doesn't pick up the config that I've got inside that folder).For the android stuff, I first adjusted the build.grade, app/build.gradle and settings.graddle to look up 2 directories more than before, which gets me farther but then i get
A problem occurred evaluating settings ... Text must not be null or empty
. I've tried various react-native.config.js values in both the root and my folder (and pointing the android dir within both to./android
and./packages/mobile-professional/android
) but that does not work with the old cli or the new. If I modify the native_modules.gradle file that gets installed as part of cli, (or the android subcomponent), so that it runsreact-native config
in the mobile folder rather than the root, I finds what it wants, but then I get some build errors about not being able to findandroid.support.annotations.nullable
in one of the native components, saying more specifically thatandroid.support.annotations
doesn't exist, which feels like a linking issue.Just to make sure it's not a hangup, re: react-native.config.js, i've also moved the limited rnpm stuff (just assets) in there as well out of the package.json
I'm trying to set up an example repo, as ours is closed and stripping things out has proven time consuming (we have a lot of process going on, everything is built in reasonml and compiled with bucklescript down to JS). In the meantime I'm posting original versions of my build and config files, and new versions.
app-build.gradle-modified.txt app-build.gradle-original.txt build.gradle-modified.txt build.gradle-original.txt metro.config.js.txt mobile-professional-package.json.txt rn-cli.config.js.txt settings.gradle-modified.txt settings.gradle-original.txt
Sorry again for the huge wall of text, but I figured it was better to provide everything relevant rather than too little.