-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
feat(why): allow specifying a version or range #6992
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
base: master
Are you sure you want to change the base?
Changes from all commits
56f6ec7
7855f00
56fcb00
aff9cfd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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,7 @@ | ||
| { | ||
| "name": "release-date", | ||
| "version": "2.0.0", | ||
| "dependencies": { | ||
| "release-date-transitive": "^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,4 @@ | ||
| { | ||
| "name": "release-date-transitive", | ||
| "version": "2.0.0" | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,6 +1,6 @@ | ||||||
| import {BaseCommand, WorkspaceRequiredError} from '@yarnpkg/cli'; | ||||||
| import {Configuration, LocatorHash, Package, formatUtils, Descriptor} from '@yarnpkg/core'; | ||||||
| import {IdentHash, Project} from '@yarnpkg/core'; | ||||||
| import {Ident, Project} from '@yarnpkg/core'; | ||||||
| import {miscUtils, structUtils, treeUtils} from '@yarnpkg/core'; | ||||||
| import {Command, Option, Usage} from 'clipanion'; | ||||||
|
|
||||||
|
|
@@ -13,13 +13,19 @@ export default class WhyCommand extends BaseCommand { | |||||
| static usage: Usage = Command.Usage({ | ||||||
| description: `display the reason why a package is needed`, | ||||||
| details: ` | ||||||
| This command prints the exact reasons why a package appears in the dependency tree. | ||||||
| This command prints the exact reasons why a package appears in the dependency tree. Specify a version or range to determine why the dependency tree contains a specific version of a package. This is particularly useful when trying to find out why your project depends on lower versions. | ||||||
|
|
||||||
| If \`-R,--recursive\` is set, the listing will go in depth and will list, for each workspaces, what are all the paths that lead to the dependency. Note that the display is somewhat optimized in that it will not print the package listing twice for a single package, so if you see a leaf named "Foo" when looking for "Bar", it means that "Foo" already got printed higher in the tree. | ||||||
| `, | ||||||
| examples: [[ | ||||||
| `Explain why lodash is used in your project`, | ||||||
| `$0 why lodash`, | ||||||
| ], [ | ||||||
| `Explain why version 3.3.1 of lodash is in your project`, | ||||||
| `$0 why [email protected]`, | ||||||
| ], [ | ||||||
| `or why version 3.X of lodash is in your project`, | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| `$0 why lodash@^3`, | ||||||
| ]], | ||||||
| }); | ||||||
|
|
||||||
|
|
@@ -46,11 +52,11 @@ export default class WhyCommand extends BaseCommand { | |||||
|
|
||||||
| await project.restoreInstallState(); | ||||||
|
|
||||||
| const identHash = structUtils.parseIdent(this.package).identHash; | ||||||
| const descriptor = structUtils.parseDescriptor(this.package, false); | ||||||
|
|
||||||
| const whyTree = this.recursive | ||||||
| ? whyRecursive(project, identHash, {configuration, peers: this.peers}) | ||||||
| : whySimple(project, identHash, {configuration, peers: this.peers}); | ||||||
| ? whyRecursive(project, descriptor, {configuration, peers: this.peers}) | ||||||
| : whySimple(project, descriptor, {configuration, peers: this.peers}); | ||||||
|
|
||||||
| treeUtils.emitTree(whyTree, { | ||||||
| configuration, | ||||||
|
|
@@ -61,7 +67,11 @@ export default class WhyCommand extends BaseCommand { | |||||
| } | ||||||
| } | ||||||
|
|
||||||
| function whySimple(project: Project, identHash: IdentHash, {configuration, peers}: {configuration: Configuration, peers: boolean}) { | ||||||
| function isSameIdent(pkg: Ident, targetPkg: Ident) { | ||||||
| return pkg.identHash === targetPkg.identHash; | ||||||
| } | ||||||
|
Comment on lines
+70
to
+72
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function already exists as |
||||||
|
|
||||||
| function whySimple(project: Project, targetPkg: Descriptor, {configuration, peers}: {configuration: Configuration, peers: boolean}) { | ||||||
| const sortedPackages = miscUtils.sortMap(project.storedPackages.values(), pkg => { | ||||||
| return structUtils.stringifyLocator(pkg); | ||||||
| }); | ||||||
|
|
@@ -85,7 +95,11 @@ function whySimple(project: Project, identHash: IdentHash, {configuration, peers | |||||
| if (!nextPkg) | ||||||
| throw new Error(`Assertion failed: The package should have been registered`); | ||||||
|
|
||||||
| if (nextPkg.identHash !== identHash) | ||||||
|
|
||||||
| if (!isSameIdent(nextPkg, targetPkg)) | ||||||
| continue; | ||||||
|
|
||||||
| if (!structUtils.isPackageInRange(nextPkg, targetPkg.range)) | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This only works for semver ranges, right? What happens if the given descriptor's range is non-semver? Either way, I think that should fail-fast above, when parsing the descriptor. |
||||||
| continue; | ||||||
|
|
||||||
| if (node === null) { | ||||||
|
|
@@ -104,7 +118,7 @@ function whySimple(project: Project, identHash: IdentHash, {configuration, peers | |||||
| return root; | ||||||
| } | ||||||
|
|
||||||
| function whyRecursive(project: Project, identHash: IdentHash, {configuration, peers}: {configuration: Configuration, peers: boolean}) { | ||||||
| function whyRecursive(project: Project, targetPkg: Descriptor, {configuration, peers}: {configuration: Configuration, peers: boolean}) { | ||||||
| const sortedWorkspaces = miscUtils.sortMap(project.workspaces, workspace => { | ||||||
| return structUtils.stringifyLocator(workspace.anchoredLocator); | ||||||
| }); | ||||||
|
|
@@ -118,16 +132,13 @@ function whyRecursive(project: Project, identHash: IdentHash, {configuration, pe | |||||
|
|
||||||
| seen.add(pkg.locatorHash); | ||||||
|
|
||||||
| if (pkg.identHash === identHash) { | ||||||
| if (isSameIdent(pkg, targetPkg)) { | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you can just can in-range-ness here and eliminate the checks below? Or is there an edge case I am not considering? |
||||||
| dependents.add(pkg.locatorHash); | ||||||
| return true; | ||||||
| } | ||||||
|
|
||||||
| let depends = false; | ||||||
|
|
||||||
| if (pkg.identHash === identHash) | ||||||
| depends = true; | ||||||
|
|
||||||
| for (const dependency of pkg.dependencies.values()) { | ||||||
| if (!peers && pkg.peerDependencies.has(dependency.identHash)) | ||||||
| continue; | ||||||
|
|
@@ -140,6 +151,10 @@ function whyRecursive(project: Project, identHash: IdentHash, {configuration, pe | |||||
| if (!nextPkg) | ||||||
| throw new Error(`Assertion failed: The package should have been registered`); | ||||||
|
|
||||||
| if (isSameIdent(nextPkg, targetPkg) && !structUtils.isPackageInRange(nextPkg, targetPkg.range)) | ||||||
| continue; | ||||||
|
|
||||||
|
|
||||||
| if (markAllDependents(nextPkg)) { | ||||||
| depends = true; | ||||||
| } | ||||||
|
|
@@ -181,6 +196,10 @@ function whyRecursive(project: Project, identHash: IdentHash, {configuration, pe | |||||
| if (dependency !== null && project.tryWorkspaceByLocator(pkg)) | ||||||
| return; | ||||||
|
|
||||||
| // We don't want to print the full path if it doesn't transitively depend on targetPkg.range | ||||||
| if (isSameIdent(pkg, targetPkg) && !structUtils.isPackageInRange(pkg, targetPkg.range)) | ||||||
| return; | ||||||
|
|
||||||
| // We don't want to reprint the children for a package that already got | ||||||
| // printed as part of another branch | ||||||
| if (printed.has(pkg.locatorHash)) | ||||||
|
|
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do not use the
release-datepackage name here. They have release time information attached to them at the package server level to test related features (e.g.npmMinimalAgeGate). Using that name here is just confusing. Reuse/create one of the generic ones likeone-fixed-dep.