yarnpkg / yarn

The 1.x line is frozen - features and bugfixes now happen on https://github.com/yarnpkg/berry
https://classic.yarnpkg.com
Other
41.4k stars 2.73k forks source link

Random transitive dependency ends up in .bin instead of hoisted version #8890

Open dmnd opened 2 years ago

dmnd commented 2 years ago

My monorepo has a new version of typescript hoisted to the root:

% yarn why typescript
yarn why v1.22.19
=> Found "typescript@4.8.3"
info Has been hoisted to "typescript"
info Reasons this module exists
   - "workspace-aggregator-e4a51017-084b-4caa-962d-9b6c1efc223e" depends on it
   - Hoisted from <snipped like 15 projects />
=> Found "@vercel/node#typescript@4.3.4"
info This module exists because "_project_#@gosynthschool#av-admin#vercel#@vercel#node" depends on it.

But I'm surprised to find that the version of tsc inside node_modules/.bin is an older version from a random transitive dependency:

% yarn run tsc --version
yarn run v1.22.19
$ /Users/dmnd/github/gosynthschool/monorepo/node_modules/.bin/tsc --version
Version 4.3.4

% ./node_modules/.bin/tsc --version
Version 4.3.4

% ./node_modules/typescript/bin/tsc --version
Version 4.8.3

% ls -l ./node_modules/.bin/tsc
lrwxr-xr-x  1 dmnd  staff  47 Sep 26 11:03 ./node_modules/.bin/tsc -> ../@vercel/node/node_modules/typescript/bin/tsc

Instead, I expect that node_modules/bin/tsc points to node_modules/typescript/bin/tsc. This seems buggy to me, is this expected behaviour?

dmnd commented 2 years ago

For now I've worked around this with a resolution, so that random transitive dependency doesn't install a different tsc.

rally25rs commented 1 year ago

I vaguely remember working on some of that linker behavior years ago: https://github.com/yarnpkg/yarn/blob/master/src/package-linker.js#L578-L593

IIRC, I had set it up so that direct dependencies would overwrite transitive ones (the simplest thing to fix #4937 ), but the behavior for bin links of transitive deps is a bit "undefined". I think it would just be whichever transitive dep is linked last.

In general, I don't think you should be depending on bin links for transitive deps. If you plan to exec their bins, then making a direct dependency is the safest thing to do.

dmnd commented 1 year ago

Good point @rally25rs. We're using workspaces, so here the problem was that workspace direct dependencies get overwritten by transitive dependencies. But since I wrote up this issue, we ended up adding the dependency at root for a different reason (VS Code).

jonscheiding commented 1 year ago

I've experienced this as well, a dependency that is directly declared at the root being superseded in .bin by a transitive dependency.

After running yarn install, the version that ends up in .bin is what I expect it to be. But after yarn adding any dependency in any workspace, the version in .bin is the transitive dependency.

# 1
❯ yarn install
yarn install v1.22.19
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 🔨  Building fresh packages...
✨  Done in 1.55s.

# 2
❯ yarn why prettier
yarn why v1.22.19
[1/4] 🤔  Why do we have the module "prettier"...?
[2/4] 🚚  Initialising dependency graph...
[3/4] 🔍  Finding dependency...
[4/4] 🚡  Calculating file sizes...
=> Found "prettier@2.8.4"
info Has been hoisted to "prettier"
info Reasons this module exists
   - "workspace-aggregator-a42f4464-5bf4-43b0-83e3-95dcf9900c47" depends on it
   - Specified in "devDependencies"
   - Hoisted from "_project_#prettier"
info Disk size without dependencies: "11.21MB"
info Disk size with unique dependencies: "11.21MB"
info Disk size with transitive dependencies: "11.21MB"
info Number of shared dependencies: 0
=> Found "@storybook/mdx1-csf#prettier@2.3.0"
info This module exists because "_project_#@ywsvb#package-with-transitive-prettier-dep#@storybook#addon-essentials#@storybook#addon-docs#@storybook#mdx1-csf" depends on it.
info Disk size without dependencies: "19.01MB"
info Disk size with unique dependencies: "19.01MB"
info Disk size with transitive dependencies: "19.01MB"
info Number of shared dependencies: 0
=> Found "@storybook/source-loader#prettier@2.3.0"
info This module exists because "_project_#@ywsvb#package-with-transitive-prettier-dep#@storybook#addon-essentials#@storybook#addon-docs#@storybook#source-loader" depends on it.
info Disk size without dependencies: "19.01MB"
info Disk size with unique dependencies: "19.01MB"
info Disk size with transitive dependencies: "19.01MB"
info Number of shared dependencies: 0
✨  Done in 0.26s.

# 3
❯ yarn prettier -v
yarn run v1.22.19
$ /Users/jonscheiding/Code/Personal/yarn-workspace-bin-version-issue/node_modules/.bin/prettier -v
2.8.4
✨  Done in 0.32s.

# 4
❯ yarn workspace @ywsbv/unrelated-package add uuid
yarn workspace v1.22.19
yarn add v1.22.19
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 🔨  Building fresh packages...
success Saved lockfile.
success Saved 1 new dependency.
info Direct dependencies
info All dependencies
└─ uuid@9.0.0
✨  Done in 1.89s.
✨  Done in 2.05s.

# 5
❯ yarn why prettier
yarn why v1.22.19
[1/4] 🤔  Why do we have the module "prettier"...?
[2/4] 🚚  Initialising dependency graph...
[3/4] 🔍  Finding dependency...
[4/4] 🚡  Calculating file sizes...
=> Found "prettier@2.8.4"
info Has been hoisted to "prettier"
info Reasons this module exists
   - "workspace-aggregator-3bec8ec4-773b-4509-8319-994d89d84355" depends on it
   - Specified in "devDependencies"
   - Hoisted from "_project_#prettier"
info Disk size without dependencies: "19.01MB"
info Disk size with unique dependencies: "19.01MB"
info Disk size with transitive dependencies: "19.01MB"
info Number of shared dependencies: 0
=> Found "@storybook/mdx1-csf#prettier@2.3.0"
info This module exists because "_project_#@ywsvb#package-with-transitive-prettier-dep#@storybook#addon-essentials#@storybook#addon-docs#@storybook#mdx1-csf" depends on it.
=> Found "@storybook/source-loader#prettier@2.3.0"
info This module exists because "_project_#@ywsvb#package-with-transitive-prettier-dep#@storybook#addon-essentials#@storybook#addon-docs#@storybook#source-loader" depends on it.
✨  Done in 0.26s.

# 6
❯ yarn prettier -v
yarn run v1.22.19
$ /Users/jonscheiding/Code/Personal/yarn-workspace-bin-version-issue/node_modules/.bin/prettier -v
2.3.0
✨  Done in 0.26s.

# 7
❯ yarn install
yarn install v1.22.19
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 🔨  Building fresh packages...
✨  Done in 1.57s.

# 8
❯ yarn prettier -v
yarn run v1.22.19
$ /Users/jonscheiding/Code/Personal/yarn-workspace-bin-version-issue/node_modules/.bin/prettier -v
2.8.4
✨  Done in 0.26s.

Here's a repro: https://github.com/jonscheiding/yarn-workspace-bin-version-issue

jonscheiding commented 1 year ago

This doesn't seem to affect just the .bin directory ... I created the following script in my repo:

console.log(require("prettier").version)

Running this at steps 3, 6 and 8 above produces the same version number that yarn prettier -v did.