Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
56f6ec7
feat(why): allow specifying a version or range
remypar5 Nov 26, 2025
7855f00
chore: bump versions
remypar5 Nov 26, 2025
56fcb00
Merge branch 'master' into feat/6859-allow-version-range-for-yarn-why
remypar5 Nov 27, 2025
aff9cfd
Merge branch 'master' into feat/6859-allow-version-range-for-yarn-why
remypar5 Dec 9, 2025
60e4cb0
Merge branch 'master' of github.com:yarnpkg/berry into feat/6859-allo…
remypar5 Jan 6, 2026
ff997bc
test: use targeted dependencies instead of specific
remypar5 Jan 6, 2026
a1ae2eb
refactor: use existing global utility over a local one
remypar5 Jan 6, 2026
6a78d2f
test: add failure for invalid range
remypar5 Jan 6, 2026
1235171
refactor: simplify and fail fast
remypar5 Jan 6, 2026
9770333
chore: remove unused import
remypar5 Jan 6, 2026
0e4ec7d
chore: retry after flaky test fail
remypar5 Jan 7, 2026
4298abb
chore: remove obsolete code
remypar5 Feb 11, 2026
81c879c
test: fix false positive and add new scenario
remypar5 Feb 19, 2026
0406f58
fix: make `why` fail-fast on non-semver range
clemyan Feb 22, 2026
392fcb6
Merge branch 'master' into feat/6859-allow-version-range-for-yarn-why
remypar5 Mar 3, 2026
3831ea5
chore: re-run pipelines after failing
remypar5 Mar 4, 2026
929f1dc
Change default Node.js version to 25.6
arcanis Mar 10, 2026
5c61684
Add Re-release workflow to republish artifacts from a specific commit…
Copilot Mar 19, 2026
1fc0d2a
Modify build command to exclude git hash
arcanis Mar 19, 2026
14d2850
chore: retry after flaky test fail
remypar5 Jan 7, 2026
da68f90
chore: re-run pipelines after failing
remypar5 Mar 4, 2026
276aea1
Merge branch 'master' into feat/6859-allow-version-range-for-yarn-why
remypar5 Mar 30, 2026
08352b6
Merge branch 'master' into feat/6859-allow-version-range-for-yarn-why
remypar5 Mar 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .yarn/versions/b4b98142.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
releases:
"@yarnpkg/cli": minor
"@yarnpkg/core": minor
"@yarnpkg/plugin-essentials": minor

declined:
- "@yarnpkg/plugin-catalog"
- "@yarnpkg/plugin-compat"
- "@yarnpkg/plugin-constraints"
- "@yarnpkg/plugin-dlx"
- "@yarnpkg/plugin-exec"
- "@yarnpkg/plugin-file"
- "@yarnpkg/plugin-git"
- "@yarnpkg/plugin-github"
- "@yarnpkg/plugin-http"
- "@yarnpkg/plugin-init"
- "@yarnpkg/plugin-interactive-tools"
- "@yarnpkg/plugin-jsr"
- "@yarnpkg/plugin-link"
- "@yarnpkg/plugin-nm"
- "@yarnpkg/plugin-npm"
- "@yarnpkg/plugin-npm-cli"
- "@yarnpkg/plugin-pack"
- "@yarnpkg/plugin-patch"
- "@yarnpkg/plugin-pnp"
- "@yarnpkg/plugin-pnpm"
- "@yarnpkg/plugin-stage"
- "@yarnpkg/plugin-typescript"
- "@yarnpkg/plugin-version"
- "@yarnpkg/plugin-workspace-tools"
- "@yarnpkg/builder"
- "@yarnpkg/doctor"
- "@yarnpkg/extensions"
- "@yarnpkg/nm"
- "@yarnpkg/pnpify"
- "@yarnpkg/sdks"
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = require(`./package.json`);

for (const key of [`dependencies`, `devDependencies`, `peerDependencies`]) {
for (const dep of Object.keys(module.exports[key] || {})) {
module.exports[key][dep] = require(dep);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"name": "why-no-deps",
"version": "1.0.0"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = require(`./package.json`);

for (const key of [`dependencies`, `devDependencies`, `peerDependencies`]) {
for (const dep of Object.keys(module.exports[key] || {})) {
module.exports[key][dep] = require(dep);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"name": "why-no-deps",
"version": "1.1.0"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = require(`./package.json`);

for (const key of [`dependencies`, `devDependencies`, `peerDependencies`]) {
for (const dep of Object.keys(module.exports[key] || {})) {
module.exports[key][dep] = require(dep);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"name": "why-no-deps",
"version": "1.1.1"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = require(`./package.json`);

for (const key of [`dependencies`, `devDependencies`, `peerDependencies`]) {
for (const dep of Object.keys(module.exports[key] || {})) {
module.exports[key][dep] = require(dep);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"name": "why-no-deps",
"version": "2.0.0"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = require(`./package.json`);

for (const key of [`dependencies`, `devDependencies`, `peerDependencies`]) {
for (const dep of Object.keys(module.exports[key] || {})) {
module.exports[key][dep] = require(dep);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "why-range-dep",
"version": "1.0.0",
"dependencies": {
"why-no-deps": "^1.0.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = require(`./package.json`);

for (const key of [`dependencies`, `devDependencies`, `peerDependencies`]) {
for (const dep of Object.keys(module.exports[key] || {})) {
module.exports[key][dep] = require(dep);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "why-range-dep",
"version": "1.0.1",
"dependencies": {
"why-no-deps": "^1.0.1"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = require(`./package.json`);

for (const key of [`dependencies`, `devDependencies`, `peerDependencies`]) {
for (const dep of Object.keys(module.exports[key] || {})) {
module.exports[key][dep] = require(dep);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "why-range-dep",
"version": "1.1.0",
"dependencies": {
"why-no-deps": "^1.1.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = require(`./package.json`);

for (const key of [`dependencies`, `devDependencies`, `peerDependencies`]) {
for (const dep of Object.keys(module.exports[key] || {})) {
module.exports[key][dep] = require(dep);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "why-range-dep",
"version": "1.1.1",
"dependencies": {
"why-no-deps": "^1.1.1"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = require(`./package.json`);

for (const key of [`dependencies`, `devDependencies`, `peerDependencies`]) {
for (const dep of Object.keys(module.exports[key] || {})) {
module.exports[key][dep] = require(dep);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "why-range-dep",
"version": "2.0.0",
"dependencies": {
"why-no-deps": "^2.0.0"
}
}
190 changes: 190 additions & 0 deletions packages/acceptance-tests/pkg-tests-specs/sources/commands/why.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,5 +207,195 @@ describe(`Commands`, () => {
}]);
}),
);

describe(`with a specified version`, () => {
test(
`it should list workspaces using a specific version range`,
makeTemporaryEnv({
workspaces: [`packages/*`],
}, async ({path, run, source}) => {
await fs.writeJson(ppath.join(path, `packages/a/package.json`), {
name: `a`,
dependencies: {
[`b`]: `workspace:^`,
[`c`]: `workspace:^`,
[`why-no-deps`]: `1.0.0`,
},
});

await fs.writeJson(ppath.join(path, `packages/b/package.json`), {
name: `b`,
dependencies: {
[`why-no-deps`]: `1.1.0`,
},
});

await fs.writeJson(ppath.join(path, `packages/c/package.json`), {
name: `c`,
dependencies: {
[`why-no-deps`]: `2.0.0`,
},
});

await run(`install`);

const {stdout} = await run(`why`, `why-no-deps@^1.1.0`, `--json`);

// Don't list v1.0.0 (package A) nor v2.0.0 (package C)
expect(misc.parseJsonStream(stdout)).toEqual([{
value: `b@workspace:packages/b`,
children: {
[`why-no-deps@npm:1.1.0`]: {
descriptor: `why-no-deps@npm:1.1.0`,
locator: `why-no-deps@npm:1.1.0`,
},
},
}]);
}));

test(
`it should list workspaces transitively using a specific version range`,
makeTemporaryEnv({
workspaces: [`packages/*`],
}, async ({path, run, source}) => {
await fs.writeJson(ppath.join(path, `packages/a/package.json`), {
name: `a`,
dependencies: {
[`b`]: `workspace:^`,
[`why-range-dep`]: `1.0.0`,
},
});

await fs.writeJson(ppath.join(path, `packages/b/package.json`), {
name: `b`,
dependencies: {
[`c`]: `workspace:^`,
[`why-range-dep`]: `1.1.0`,
},
});

await fs.writeJson(ppath.join(path, `packages/c/package.json`), {
name: `c`,
dependencies: {
[`d`]: `workspace:^`,
[`why-range-dep`]: `1.1.1`,
},
});

await fs.writeJson(ppath.join(path, `packages/d/package.json`), {
name: `d`,
dependencies: {
[`why-range-dep`]: `2.0.0`,
},
});

await run(`install`);

const {stdout} = await run(`why`, `-R`, `why-no-deps@^1.1.0`, `--json`);

// Don't list v1.0.0 (package A) nor v2.0.0 (package D)
expect(stdout).not.toContain(`why-no-deps@npm:1.0.0`);
expect(stdout).not.toContain(`why-no-deps@npm:1.0.1`);
expect(stdout).not.toContain(`why-no-deps@npm:2.0.0`);

expect(misc.parseJsonStream(stdout)).toEqual([{
value: `a@workspace:packages/a`,
children: {
[`b@workspace:packages/b`]: {
children: {},
value: {
descriptor: `b@workspace:^`,
locator: `b@workspace:packages/b`,
},
},
[`why-range-dep@npm:1.0.0`]: {
value: {
descriptor: `why-range-dep@npm:1.0.0`,
locator: `why-range-dep@npm:1.0.0`,
},
children: {
[`why-no-deps@npm:1.1.1`]: {
children: {},
value: {
descriptor: `why-no-deps@npm:^1.0.0`,
locator: `why-no-deps@npm:1.1.1`,
},
},
},
},
},
}, {
value: `b@workspace:packages/b`,
children: {
[`c@workspace:packages/c`]: {
children: {},
value: {
descriptor: `c@workspace:^`,
locator: `c@workspace:packages/c`,
},
},
"why-range-dep@npm:1.1.0": {
children: {
"why-no-deps@npm:1.1.1": {
children: {},
value: {
descriptor: `why-no-deps@npm:^1.1.0`,
locator: `why-no-deps@npm:1.1.1`,
},
},
},
value: {
descriptor: `why-range-dep@npm:1.1.0`,
locator: `why-range-dep@npm:1.1.0`,
},
},
},
}, {
value: `c@workspace:packages/c`,
children: {
[`why-range-dep@npm:1.1.1`]: {
value: {
descriptor: `why-range-dep@npm:1.1.1`,
locator: `why-range-dep@npm:1.1.1`,
},
children: {
[`why-no-deps@npm:1.1.1`]: {
children: {},
value: {
descriptor: `why-no-deps@npm:^1.1.1`,
locator: `why-no-deps@npm:1.1.1`,
},
},
},
},
},
}]);
}));

test(
`it should not list the packages with an out-of-range range specified`,
makeTemporaryEnv({
workspaces: [`packages/*`],
}, async ({path, run, source}) => {
await fs.writeJson(ppath.join(path, `package.json`), {
name: `a`,
dependencies: {
[`why-no-deps`]: `1.0.0`,
},
});
await run(`install`);

const {stdout} = await run(`why`, `why-no-deps@2.0.0`, `--json`);
return expect(stdout).toEqual(``);
}));

test(`it should fail with non-semver range specified`, makeTemporaryEnv({
workspaces: [`packages/*`],
}, async ({path, run, source}) => {
await run(`install`);
await expect(run(`why`, `why-no-deps@1.0.0`)).resolves.not.toThrow();
await expect(run(`why`, `irrelevant-dependency@invalid.range`)).rejects.toThrow();
}));
});
});
});
Loading
Loading