Closed superguineapig closed 2 months ago
:warning: | Newer Version of React Native is Available! |
---|---|
:information_source: | You are on a supported minor version, but it looks like there's a newer patch available - 0.74.5. Please upgrade to the highest patch for your minor or latest and verify if the issue persists (alternatively, create a new project and repro the issue in it). If it does not repro, please let us know so we can close out this issue. This helps us ensure we are looking at issues that still exist in the most recent releases. |
:warning: | Newer Version of React Native is Available! |
---|---|
:information_source: | You are on a supported minor version, but it looks like there's a newer patch available - undefined. Please upgrade to the highest patch for your minor or latest and verify if the issue persists (alternatively, create a new project and repro the issue in it). If it does not repro, please let us know so we can close out this issue. This helps us ensure we are looking at issues that still exist in the most recent releases. |
Tested at latest 0.75.2. Issue persists.
Tagging @dmytrorykun for visibility. Looks like you've been around these parts before ;-)
(my apologies for the spam if not)
@superguineapig thank you for this great issue report! Could you please try this PR https://github.com/facebook/react-native/pull/46229 and see if it fixes the problem.
@dmytrorykun Thank you for looking at this! The changes in the PR do appear to fix the issue.
Just for reference, I added some console logging to the findExternalLibraries
function, and it appears that the explicit addition of the {paths: [projectRoot]}
configuration is appropriately overriding the default module resolution for each call to resolve
.
...
[Codegen] Searching for codegen-enabled libraries in the project dependencies.
// Default resolution; note would never find app-level deps
[DEBUG] module.paths:
[
'/Users/X/rntest/node_modules/react-native/scripts/codegen/node_modules',
'/Users/X/rntest/node_modules/react-native/scripts/node_modules',
'/Users/X/rntest/node_modules/react-native/node_modules',
'/Users/X/rntest/node_modules',
'/Users/X/node_modules',
'/Users/node_modules',
'/node_modules'
]
// Found an app-relative dep!
[DEBUG] found path: '/Users/X/rntest/packages/App2/node_modules/@react-native-community/geolocation/package.json'
[Codegen] Found @react-native-community/geolocation
// And it can still resolve workspace-root deps
[DEBUG] found path: '/Users/X/rntest/node_modules/react/package.json'
[DEBUG] found path: '/Users/X/rntest/node_modules/react-native/package.json'
[Codegen] Found react-native
...
...
This appears to fix the issue as described. Thank you again for your attention.
@superguineapig
[DEBUG] module.paths:
[
'/Users/X/rntest/node_modules/react-native/scripts/codegen/node_modules',
'/Users/X/rntest/node_modules/react-native/scripts/node_modules',
'/Users/X/rntest/node_modules/react-native/node_modules',
'/Users/X/rntest/node_modules',
'/Users/X/node_modules',
'/Users/node_modules',
'/node_modules'
]
This explains everything. require.resolve
was trying to resolve the dependency relative to the codegen root. Not finding anything, it was making its way up to the monorepo root.
The correct behaviour is to start at the project workspace directory.
Description
Abstract
It appears that codegen is improperly resolving (node_module) dependencies when the app's source is part of a multi-app monorepo, and there are multiple versions of the dep specified across apps. This is possibly due to the use of node's
require.resolve
mechanism.Background
I have recently migrated a pair of react-native apps into a (yarn workspaces) monorepo setup. While testing one of the apps, I noticed some new warning messages in the terminal during a routine
pod install
. After checking the dependencies, I verified that the app in question had specified the latest version of the library being flagged. I verified that the dep existed and was updated by the author to fix this exact warning. Poking around more, I noticed that the other app in my repo was still using the older version of the library.The differences between the two apps was simply the version of the dependency (as specified in their respective
package.json
) and each dep's (node_modules) location within the monorepo. The older version lived in node_modules at the root, the newer version lived in node_modules adjacent to the app being pod install'ed.The presence of the codegen deprecation warning indicated that the wrong version of the dependency was being included/discovered during the process for this app (which specifies the appropriate version in its package.json)
TL;DR
Codegen is finding the wrong path to dependencies in this particular monorepo setup
Setup
The repository is set up as follows (note: node deps installed automatically via yarn):
A
pod install
from/packages/App2/ios
will output a warning message indicating the deprecation of the dependency in question. See below for output.See below for additional details and a reproducer repo.
Findings
I wasn't super familiar with the chain of events that occurs during a pod install, so it took a while to trace down, but surprisingly, it appears that most of the machinery involved does find the appropriate (paths to) the package dependencies within this monorepo structure (my private repo with lots of stuff going on). Codegen seems to be the outlier.
I traced things down to findExternalLibraries in
generate-artifacts-executor.js
This appears to be the code that is responsible for iterating over the apps dependencies and resolving paths to their respective
package.json
files. It is currently using Node'srequire.resolve
machinery to locate the nearest matching package. However,require
will change its search paths based on the how the module itself was imported (module.paths
) regardless of theprocess.cwd()
. This makes it non-deterministic when being invoked through a complicated process chain as exhibited by the cocoapods setup.In a nutshell, the pods executable launches a process which then executes some ruby code, which then requires additional ruby files, eventually executing the Podfile, which then requires more ruby scripts, which may themselves launch additional processes (e.g.
node
) which then may launch additional processes, and requires, etc, etc, etc. Through that whole chain, it is possible to change the search order forrequire.resolve
(via the spawned processes) even if the process is executing in the expected place.(Aside: the use of the "command" pattern in the js scripts may be what's messing with the node resolver; not a bug, per se, but just a guess as to the unreliability of resolve in this instance...)
It appears that in this particular repository structure, due to the require search paths, the app local node_modules will not be found. Instead, the search moves down from the location of the
generate-artifacts-executor.js
script itself, thereby finding the (root-level)node-modules
first (where thereact-native
framework code lives) (in the context of the codegen process; other code (js/rb) seems to resolve things fine)I came to that conclusion by tracing out the whole chain starting in the Podfile with logging to see what was being found by the various scripts (js/rb) and their functions in the stack; injecting logs of node's module and process state, paths found for each dep, etc
A Potential Fix
Through looking at other react-native js code, I discovered the use of the find-up package in some places. I believe that this can also be used in this instance to invert the search process outward from a deterministic starting point (the project root as opposed to the framework root).
And with very little change required.
The main change would be in
findExternalLibraries
at line 241FROM:
TO:
Some misc changes were also needed to support this:
Around Line 514 or so:
Add
projectRoot
parameter tofindExternalLibraries
declaration (Line 228):I made this change locally, and it fixed the issue I was experiencing on both my repo and the fresh reproducer repo, and on 2 different macs as well (Intel & M1 Pro).
However I am not at all familiar with react-native framework/tools development, so I am unable to verify that other stuff isn't impacted by this/run proper unit tests, etc. But... the code does appear to be isolated to this very specific case in the codegen dependency discovery process.
Other Environment Notes
Thanks for reading!
I'm even more in awe over React Native after having gone through this internal process step by step. Been using this stuff for over 7 years now, and wow have things evolved. Thanks to everyone involved. It's like magic!
Steps to reproduce
yarn
at the repo rootcd packages/App2/ios
pod install
@react-native-community/geolocation
NOTE This issue is NOT about that library. It just happens that it is useful to trigger the codegen deprecation warning that indicates dependency resolution is not working properly.
The deprecation warning is a reliable indicator that codegen has found the wrong version (the root node_modules vs the App2/node_modules) in this case.
3.2.1
should cause the warning3.3.0
should not - App2 specifies 3.3.0 in its package.jsonMORE NOTES
I initially tested with RN 74.2. The reproducer was init'ed fresh as a minimal demonstration using RN latest (75.2) and exhibits the same issue/fix behavior.
React Native Version
0.74.2, 0.75.2
Affected Platforms
Build - MacOS
Output of
npx react-native info
Stacktrace or Logs
NOTE: I replaced the name of the dep below to avoid any confusion that this issue is related to the external lib vs codegen. The deprecation warning itself is appropriate for the code codegen is finding, however, codegen is finding the wrong code <-- (that's the issue). The warning is a symptom of the underlying problem in this case and what tipped me off to the issue.