Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I control which version Yarn chooses for a dependency's peerDependency when using Yarn workspaces/nohoist?

I have a Yarn workspace with two packages, watermelon-web and watermelon-native, which use the latest version of react-redux but different versions of react. The issue is that I am not able to control which version of react Yarn chooses for the peerDependency of react-redux.

Workspace package.json:

{
    "private": true,
    "workspaces": {
        "packages": [
            "watermelon-web",
            "watermelon-native"
        ],
        "nohoist": [            
            "**/watermelon-native/react-redux"
        ]
    }
}

(the nohoist is required to prevent a runtime error)

watermelon-web/package.json:

{
  "name": "watermelon-web",
  "dependencies": {
    "react": "^16.12.0",
    "react-redux": "^7.1.3"
  }
}

watermelon-native/package.json:

{
  "name": "watermelon-native",
  "dependencies": {
    "react": "16.8.3",
    "react-redux": "^7.1.3"
  }
}

Meanwhile, react-redux has a peerDependency "react": "^16.8.3".

What I want to happen: After Yarn install, watermelon-native/node_modules/react-redux/node_modules does NOT contain react. This way, when, react-redux tries to import react at runtime, it would get [email protected] from watermelon-native/node_modules.

What actually happens: Yarn installs [email protected] in watermelon-native/node_modules/react-redux/node_modules. When I run watermelon-native, React reports an "Invalid hook call" because watermelon-native is using [email protected] but react-redux is using [email protected]. (Both packages must be using the exact same instance of React for React hooks to work.)

How do I get Yarn to work the way I want it to?

I have tried using Yarn selective dependency resolutions, AKA the "resolutions" element in package.json, in almost every way possible, but there was no observable change in Yarn's behavior. For example, I tried adding

"resolutions": {
    "**/watermelon-native/react-redux/react": "16.8.3"
}

to the workspace package.json.

Two easy "solutions" would be to use the same React version in all of my packages (would require downgrading watermelon-web to 16.8.3) or to forgo using Yarn workspaces. Each of these has drawbacks that I'd like to avoid if possible.

(Note: My code examples come from React Native development, but the question itself applies only to Yarn and has nothing to do with React. react and react-redux could be replaced by any other packages that have sufficiently similar dependencies.)

like image 461
Sam Magura Avatar asked Nov 07 '22 11:11

Sam Magura


1 Answers

I think the answer to the question is just "Yarn doesn't work like that", for the time being at least.

In the meantime, I found a simple workaround. Since deleting the react folder from watermelon-native/node_modules/react-redux/node_modules/react fixes the issue, I added a postinstall script that deletes that react folder after every install.

My PowerShell script (use whatever shell you like)

function TryRemove-Path($path) {
    if(Test-Path $path) {
        Remove-Item -Recurse -Force $path
    }
}


TryRemove-Path("$PSScriptRoot/node_modules/react-redux/node_modules/react")

This is pretty hacky, but even the Expo project relies on postinstall magic to get their React Native apps to work with yarn workspaces. So maybe this workaround is not so bad.

like image 135
Sam Magura Avatar answered Nov 15 '22 13:11

Sam Magura