Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[workspaces / lerna] Unexpected version of a package installed inside a dependency #5978

Open
SebastianBogado opened this issue Jun 12, 2018 · 10 comments
Assignees
Labels

Comments

@SebastianBogado
Copy link

SebastianBogado commented Jun 12, 2018

Do you want to request a feature or report a bug?

Bug maybe.

What is the current behavior?
In the context of a lerna monorepo with yarn workspaces, I'm getting an unexpected version of a dev dependency installed inside a dev dependency itself. Here's the folders structure (simplified):

<ROOT>
├── applications/my-app/frontend
│   └── node_modules
│       ├── jest-styled-components@5.0.1
│       │   ├── node_modules
│       │   │   └── styled-components@2.2.4
│       └── styled-components@3.3.2
└── node_modules 
    ├── jest-styled-components@4.10.0
    └── styled-components@2.2.4

Note: there's a lot of stuff in node_modules that I excluded and also inside applications folder there are other modules with many publishable packages.

My app has a direct dependency on styled-components and jest-styled-component as a dev dependency. Here's a part of our package.json:

{
  // ...
  "dependencies": {
    // ...
    "styled-components": "^3.3.0"
  },
  "devDependencies": {
    // ...
    "jest-styled-components": "^5.0.1"
  }
  // ..
}

So far so good.

The unexpected installed module is inside jest-styled-components. As you can see, it gets installed its own version of styled-components. That's OK, I can handle that. I'm not running with the production flag anyways. But if you check its package.json:

  "devDependencies": {
    // ...
    "styled-components": "^3.2.0"
  },
  "dependencies": {
    "css": "^2.2.1"
  },
  "peerDependencies": {
    "styled-components": "^2.0.0 || ^3.0.2"
  },

jest-styled-components expects styled-components@^3.2.0 as a dev dependency.

So why the v2.2.4 gets installed inside jest-styled-components?

If the current behavior is a bug, please provide the steps to reproduce.
Here's a repo with the same issue: https://github.com/SebastianBogado/yarn-peer-dependencies
Difference is that it reproduces with react-redux and React instead of jest-styled-components and styled-components

What is the expected behavior?
I would expect yarn not to install a dev dependency of a dev dependency. And if needed, then it should install the version specified in the package.json.

Please mention your node.js, yarn and operating system version.
Yarn: v1.6.0 (--> tried with both v1.7.0 and v1.8.0 and ran into two different issues)
Node: v8.5.0
OS: macOS Sierra v10.12.6

Thanks for your help in advance!

PS:
Workaround
If someone stumbles upon something similar, you can use a postinstall script in your package.json. In my case:

  "scripts": {
    "postinstall": "rm -rf node_modules/jest-styled-components/node_modules/styled-components"
  },

It's a bit ugly but you can properly arrange any weird nested dependency issue with it.

Might be related to issues: #5705, #5744

@ghost ghost assigned rally25rs Jun 12, 2018
@ghost ghost added the triaged label Jun 12, 2018
@rally25rs
Copy link
Contributor

Would it be possible to make a minimal reproduction project that demonstrates the issue? I'm guessing something includes styled-components@2.2.4 somewhere.
I tried just using the package.json chunk you included above but that did not cause the same issue.

@SebastianBogado
Copy link
Author

I'll try to come up with a repo this weekend :)

@SebastianBogado
Copy link
Author

I just came into a similar situation.

I have a module with this dependencies and forcing nohoist on a particular package:

{
  // ...
  "dependencies": {
    // ...
    "react": "^16.4.0",
    "react-transition-group": "^2.3.0"
  },
  "workspaces": {
    "nohoist": [
      "react-transition-group",
      "react-transition-group/**"
    ]
  },
  // ..
}

And I got and old version of React installed inside react-transition-group:

<ROOT>
└── applications/my-second-app/frontend
    └── node_modules
        ├── react@16.4.1
        └── react-transition-group@2.3.1
            └── node_modules
                └── react@15.6.2

Here are the dependencies of react-transition-group:

{
  // ...
  "peerDependencies": {
    "react": ">=15.0.0",
    "react-dom": ">=15.0.0"
  },
  "dependencies": {
    "dom-helpers": "^3.3.1",
    "loose-envify": "^1.3.1",
    "prop-types": "^15.6.1"
  },
  // ..
}

As you can see, no devDependencies this time. Does yarn look into peerDependencies when deciding what to install?

@rally25rs
Copy link
Contributor

rally25rs commented Jun 22, 2018

what gets reported if you run yarn why react?

if you delete node_modules and yarn.lock and rerun install, does it still happen? Maybe react was installed once with an old version and an existing yarn.lock is preserving it?

@SebastianBogado
Copy link
Author

$ yarn why react
yarn why v1.6.0
[1/4] 🤔  Why do we have the module "react"...?
[2/4] 🚚  Initialising dependency graph...
[3/4] 🔍  Finding dependency...
[4/4] 🚡  Calculating file sizes...
=> Found "react@15.6.2"
info Has been hoisted to "react"
info Reasons this module exists
   - "workspace-aggregator-0faa7920-3a55-4ce0-be11-ee81decb34dc" depends on it
   - Hoisted from "_project_#@m#local-package-A#react"
   - Hoisted from "_project_#@m#local-package-B#react"
   - Hoisted from "_project_#@m#local-package-C#react"
   - Hoisted from "_project_#@m#local-package-D#react"
   - Hoisted from "_project_#@m#local-package-E#react"
   - Hoisted from "_project_#@m#local-package-F#react"
   - Hoisted from "_project_#@m#local-package-G#react"
   - Hoisted from "_project_#@m#local-package-H#react"
   - Hoisted from "_project_#@m#local-package-I#react"
   - Hoisted from "_project_#@m#local-package-H#@m#external-private-package-A#react"
info Disk size without dependencies: "708KB"
info Disk size with unique dependencies: "6.3MB"
info Disk size with transitive dependencies: "14.77MB"
info Number of shared dependencies: 14
=> Found "grammkit#react@0.12.2"
info This module exists because "_project_#grammkit" depends on it.
info Disk size without dependencies: "2.86MB"
info Disk size with unique dependencies: "3.23MB"
info Disk size with transitive dependencies: "3.51MB"
info Number of shared dependencies: 3
=> Found "@m/my-second-app#react@16.4.0"
info This module exists because "_project_#@m#my-second-app" depends on it.
info Disk size without dependencies: "164KB"
info Disk size with unique dependencies: "5.66MB"
info Disk size with transitive dependencies: "14.13MB"
info Number of shared dependencies: 13
=> Found "@m/local-package-J#react@16.4.0"
info This module exists because "_project_#@m#local-package-J depends on it.
info Disk size without dependencies: "164KB"
info Disk size with unique dependencies: "5.66MB"
info Disk size with transitive dependencies: "14.13MB"
info Number of shared dependencies: 13
=> Found "@m/my-app#react@16.4.0"
info This module exists because "_project_#@m#my-app" depends on it.
info Disk size without dependencies: "164KB"
info Disk size with unique dependencies: "5.66MB"
info Disk size with transitive dependencies: "14.13MB"
info Number of shared dependencies: 13
=> Found "@m/local-package-K#react@0.12.2"
info Reasons this module exists
   - "_project_#@m#local-package-K#grammkit" depends on it
   - Hoisted from "_project_#@m#local-package-K#grammkit#react"
info Disk size without dependencies: "2.86MB"
info Disk size with unique dependencies: "3.23MB"
info Disk size with transitive dependencies: "3.51MB"
info Number of shared dependencies: 3
=> Found "@m/local-package-L#react@0.12.2"
info Reasons this module exists
   - "_project_#@m#local-package-L#grammkit" depends on it
   - Hoisted from "_project_#@m#local-package-L#grammkit#react"
info Disk size without dependencies: "2.86MB"
info Disk size with unique dependencies: "3.23MB"
info Disk size with transitive dependencies: "3.51MB"
info Number of shared dependencies: 3
=> Found "@m/local-package-M#react@0.12.2"
info Reasons this module exists
   - "_project_#@m#local-package-M#grammkit" depends on it
   - Hoisted from "_project_#@m#local-package-M#grammkit#react"
info Disk size without dependencies: "2.91MB"
info Disk size with unique dependencies: "3.28MB"
info Disk size with transitive dependencies: "3.55MB"
info Number of shared dependencies: 3
=> Found "@m/external-private-package-B#react@15.4.2"
info This module exists because "_project_#@m#local-package-E#@m#external-private-package-B" depends on it.
info Disk size without dependencies: "712KB"
info Disk size with unique dependencies: "6.08MB"
info Disk size with transitive dependencies: "14.55MB"
info Number of shared dependencies: 12
✨  Done in 15.33s.

I've been doing clean installs all the time, deleting root node_modules and each package node_modules :/. About yarn.lock, we're not using it (yet).

@SebastianBogado
Copy link
Author

I've noted the same behavior with react-redux, which I'm nohoisting in my app.

The pattern I'm seeing is that the version hoisted at the root (react@15.6.2, react-dom@15.6.2, styled-components@2.2.4) gets installed inside a local (i.e., not hoisted) dependency (react-redux, react-transition-group, jest-styled-components) due to peerDependencies being compatible with that hoisted version:

<ROOT>
├── applications/my-app/frontend
│   └── node_modules
│       ├── jest-styled-components@5.0.1
│       │   └── node_modules
│       │       └── styled-components@2.2.4
│       ├── react@16.4.0
│       ├── react-redux@5.0.7
│       │   └── node_modules
│       │       └── react@15.6.2
│       ├── react-transition-group@2.3.1
│       │   └── node_modules
│       │       ├── react@15.6.2
│       │       └── react-dom@15.6.2
│       └── styled-components@3.3.2
└── node_modules 
    ├── jest-styled-components@4.10.0
    ├── react@15.6.2
    ├── react-dom@15.6.2
    └── styled-components@2.2.4

And to recap, peer dependencies:

But I think yarn shouldn't copy the hoisted version of certain package just because it's compatible with its peer dependencies. At least not without checking if another compatible version is installed in an upper level.

Thanks for looking into this!

PS: This may be happening with other packages, but only these I've found to be problematic when you have two versions loaded.

@SebastianBogado SebastianBogado changed the title [workspaces / lerna] Unexpected version of a package installed inside a dev dependency [workspaces / lerna] Unexpected version of a package installed inside a dependency Jun 26, 2018
@SebastianBogado
Copy link
Author

@rally25rs finally came up with a repo to reproduce the issue with React and react-redux: https://github.com/SebastianBogado/yarn-peer-dependencies

@surbina
Copy link

surbina commented Jul 17, 2018

Hi @rally25rs, have you had the chance to take a look at this? My team has also stumbled upon this.

@Fnxxxxo
Copy link

Fnxxxxo commented Mar 9, 2019

#7088 I meet this. For Nuxt v2 (webpack v4) project and any other project built on webpack v3, the html-webpack-plugin will resolve a wrong webpack version.

@jackyef
Copy link

jackyef commented Sep 24, 2019

Hi, do we have any updates for this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants