I am trying to upgrade from [email protected] and [email protected] to [email protected] and [email protected]. I'm on macOS and using [email protected] with its "workspaces" feature. I did not initially have a rn-cli.config.js.
After upgrading, whenever I try to get Metro to generate a bundle, I get the following error:
error: bundling failed: Error: Unable to resolve module `./../node_modules/react-transform-hmr/lib/index.js` from `/Users/ashoat/Dropbox/src/Sites/squadcal/native/app.react.js`: The module `./../node_modules/react-transform-hmr/lib/index.js` could not be found from `/Users/ashoat/Dropbox/src/Sites/squadcal/native/app.react.js`. Indeed, none of these files exist:
* `/Users/ashoat/Dropbox/src/Sites/squadcal/node_modules/react-transform-hmr/lib/index.js(.native||.ios.js|.native.js|.js|.ios.json|.native.json|.json)`
* `/Users/ashoat/Dropbox/src/Sites/squadcal/node_modules/react-transform-hmr/lib/index.js/index(.native||.ios.js|.native.js|.js|.ios.json|.native.json|.json)`
In fact, /Users/ashoat/Dropbox/src/Sites/squadcal/node_modules/react-transform-hmr/lib/index.js does exist. However, it is outside the list of watched directories printed when Metro is started. react-transform-hmr is the third module/file that Metro tries to resolve when bundling my application, after my app entry point, and react-native.
Before upgrading, this was the list of watched directories. The list included all four of my workspaces, as well as the root folder of the monorepo. The root folder of the monorepo is the one that contains react-transform-hmr, as it is a "secondary" dependency (ie. dependency of a dependency), and yarn workspaces put those in the root folder. I presume that since the root folder was in the list of watched directories, the previous version of metro was able to resolve things correctly.
After upgrading, this is the list of watched directories. Note that it is much shorter, does not include all the workspaces, and notably does not include the root folder of the monorepo.
My initial read of the situation was that I should create a rn-cli.config.js with a getProjectRoots that includes the root folder of the monorepo. That would presumably enable Metro to resolve it.
However, when I tried that, I got an error regarding ambiguous resolution of react-native: https://gist.github.com/Ashoat/7c4b41c063391cd1c3c59d38bc23d71a
I get the ambiguous resolution error even when I set up my getProjectRoots to return only two folders, the project folder (native) and the root folder.
I've also tried using the metro-bundler-config-yarn-workspaces NPM package, which provides a custom rn-cli.config.js that attempts to (a) include the right folders in getProjectRoots, (b) blacklist the duplicate react-native package. However, the custom blacklist doesn't seem to prevent the ambiguous resolution error. I've also tried to write my own custom blacklist to no avail.
Here is my current rn-cli.config.js:
const blacklist = require('metro/src/blacklist');
module.exports = {
getBlacklistRE() {
return blacklist([
/native\/node_modules\/react-native\//,
]);
},
getProjectRoots() {
return [
"/Users/ashoat/Dropbox/src/Sites/squadcal/native",
"/Users/ashoat/Dropbox/src/Sites/squadcal",
];
},
}
I've been banging on my head on this problem for two days now. Would appreciate any help I can get, even if it's just an explanation of what may have changed from [email protected] to [email protected] that could've caused this.
Thanks for reading.
I was finally able to resolve this issue by following the instructions here: https://github.com/viewstools/yarn-workspaces-cra-crna#native
For anybody who stumbles on this issue, and is using a vanilla React Native config without Expo, the steps you need to follow start with yarn add --dev metro-bundler-config-yarn-workspaces crna-make-symlinks-for-yarn-workspaces and end with "prestart": "node link-workspaces.js".
My only remaining questions are:
Anyways, thanks team for all your hard work helping to support the open source community. Feel free to close this issue. (I'm leaving it open in hopes of getting answers to the above questions.)
@Ashoat could you please reopen this issue because it's not solved :)
There is something cheesy going on in React-Native or/and Metro and worth to be investigated and fixed. I have electron-based app that works just fine with workspaces with zero tweaks, I wonder why react-native and/or metro are so buggy.
Yarn workspaces use symlinks, which are not currently supported. See this comment.
It's very much still doable with a custom metro.config.js, though #257 would certainly be an improvement.
I'm thinking of patching this issue by writing an NPM script that watches for changes and automatically copies my intra-workspace builds to the node_modules folder, overwriting if they already exist.
For anyone who lands here like I did, here's how to get workspaces working with importing code from another workspace:
// package.json
"devDependencies": {
...
"expo-yarn-workspaces": "1.2.1"
},
"workspaces": {
"nohoist": [
"react-native",
"react-native-*",
"react-native/**",
"expo-yarn-workspaces"
]
}
// metro.config.js
const { createMetroConfiguration } = require("expo-yarn-workspaces")
const config = createMetroConfiguration(__dirname)
// Make react-native import from files in other workspace resolve to node_modules in this dir
config.resolver.extraNodeModules["react-native"] = `${__dirname}/node_modules/react-native`
// Default metro config
config.transformer.getTransformOptions = async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: false,
},
})
module.exports = config
Hey @flybayer was that package.json the one at your workspace root or in your RN package? Did you do any of the other steps at https://github.com/expo/expo/tree/master/packages/expo-yarn-workspaces in addition to your metro config?
@Maushundb in the RN package.
However, I ran into blocking issues with that approach and am now hoisting everything.
// <root>/native/metro.config.js
const path = require("path")
module.exports = {
projectRoot: path.resolve(__dirname, "../"),
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: false,
},
}),
},
}
and change your Podfile to be like this
// <root>/native/ios/Podfile
...
pod 'React', :path => '../../node_modules/react-native/'
pod 'React-Core', :path => '../../node_modules/react-native/'
...
Most helpful comment
It's very much still doable with a custom
metro.config.js, though #257 would certainly be an improvement.