feat: reverse pnpm why tree and improve list/why output#10615
Conversation
827cead to
8e394cd
Compare
Display a short hash (#xxxx) next to packages that have peer dependency resolutions, but only when the same name@version appears with multiple distinct peer variants in the output. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
8e394cd to
e38b935
Compare
Extract DEDUPED_LABEL to peerVariants.ts and simplify deduped rendering in pnpm list to match the why output style (no child count node). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ation Sort edges in the data layer (getTree, buildWhyTree) so that the alphabetically-first occurrence of a package is always fully expanded while later duplicates get [deduped]. Replace localeCompare with lexCompare from @pnpm/util.lex-comparator for consistency. Remove redundant ramda-based sorting from renderTree. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The readManifest callback passed to finder functions in `pnpm why` now reads the actual package.json from the content-addressable store using the integrity hash from the lockfile. This works correctly for all configurations including enableGlobalVirtualStore. Also preserves finder string messages in the why output (searchMessage field on WhyPackageResult) and removes the now-unused virtualStoreDirMaxLength parameter from buildWhyTrees/whyForPackages. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract shared CLI helpers (computeInclude, resolveFinders, determineReportAs, shorthands, help options) into common.ts. Move collectHashes to peerVariants.ts. Unify readManifest to use CAFS store-based reading in both list and why paths, with filesystem fallback for list. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…m why - Extract shared resolvePackagePath function for both list and why commands, handling global virtual store symlink resolution correctly - Add --long flag support to pnpm why (description, repository, homepage, path) reusing getPkgInfo from the list package to avoid code duplication - Use projectPaths in buildWhyTrees to respect --filter/--recursive flags Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ifest logic Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
pnpm why tree and improve list/why output
There was a problem hiding this comment.
Pull request overview
This PR significantly refactors the pnpm list and pnpm why commands to improve their output and usability.
Purpose:
Replace the archy package with a custom tree renderer (@pnpm/text.tree-renderer) and reverse the pnpm why output to show a bottom-up dependency tree (from the searched package back to workspace roots), making it easier to understand why a package is installed.
Changes:
- Introduces
@pnpm/text.tree-rendererpackage with box-drawing characters and grouped sections - Reverses
pnpm whytree to show dependants walking back to importers - Shows peer dependency hash suffixes to distinguish peer-dep variants
- Adds visual improvements: bold importers, dimmed paths, dependency grouping, package count summary
- Deduplicates shared code between
listandwhyintocommon.ts
Reviewed changes
Copilot reviewed 42 out of 43 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| text/tree-renderer/* | New package for rendering tree structures with box-drawing characters and grouped sections |
| reviewing/list/src/renderWhyTree.ts | New module for rendering reverse dependency trees for pnpm why |
| reviewing/list/src/renderTree.ts | Updated to use new tree renderer and add peer hash suffixes |
| reviewing/list/src/peerVariants.ts | New module with shared functions for peer dependency variant handling |
| reviewing/list/src/index.ts | Added whyForPackages function for the new why implementation |
| reviewing/dependencies-hierarchy/src/buildWhyTree.ts | New module that builds reverse dependency trees |
| reviewing/dependencies-hierarchy/src/{resolvePackagePath,readManifestFromCafs,peersSuffixHash}.ts | New helper modules for path resolution and manifest reading |
| reviewing/dependencies-hierarchy/src/getPkgInfo.ts | Refactored to use new helper functions and support reading manifests from CAFS |
| reviewing/dependencies-hierarchy/src/getTree.ts | Added deterministic sorting and peer hash tracking |
| reviewing/plugin-commands-listing/src/common.ts | New shared module with common functions for list and why commands |
| reviewing/plugin-commands-listing/src/{list,why}.ts | Refactored to use shared code and updated why handler |
| reviewing/plugin-commands-listing/test/* | Updated tests to match new output format |
| packages/render-peer-issues/* | Updated to use new tree renderer |
| dedupe/issues-renderer/* | Updated to use new tree renderer |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- buildDependenciesHierarchy → buildDependenciesTree - buildWhyTrees → buildDependentsTree - DependenciesHierarchy → DependenciesTree - WhyPackageResult → DependentsTree - WhyDependant → Dependent (American spelling) - dependants → dependents (field names, variables, comments) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Skip reading currentLockfile when checkWantedLockfileOnly is true - Remove defensive readManifest fallback (match buildDependenciesTree) - Hoist WalkContext creation out of the match loop Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use options objects instead of 4+ positional arguments - Fix import ordering (stdlib alphabetical, externals before third-party) - Move helper function after its callers (hoisting convention) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…dentNode Symmetric naming for tree node types: DependencyNode for the forward tree, DependentNode for the reverse tree. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 46 out of 47 changed files in this pull request and generated 6 comments.
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Replace MD5 with SHA-256 in peersSuffixHash for FIPS compatibility - Fix listSummary double-counting deduped packages - Fix unsafe non-null assertion on importerInfoMap.get() - Fix import spacing in renderDependentsTree - Fix comment describing npm-alias display format - Fix backtick escaping in tree-renderer JSDoc - Move lockfile reading out of buildDependentsTree into the caller so the lockfile is read once and passed down, eliminating the duplicate read and the lockfile sync issue between whyForPackages and buildDependentsTree Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 46 out of 47 changed files in this pull request and generated 5 comments.
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…Summary grouping - Sort trees by name, semver version, and peersSuffixHash for deterministic output - Add serialized ID tiebreaker to edge sorting in walkReverse - Fix tree-renderer: use flattened items.length for continuation chars and hasRenderableChildren() for connector detection (handles empty groups) - Rewrite whySummary to group by package name so multi-package why queries produce per-package summary lines - Add tests for tree-renderer empty group edge cases and whySummary variants Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 47 out of 48 changed files in this pull request and generated 4 comments.
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| rootLabelParts.push(result.searchMessage) | ||
| } | ||
| if (opts.long && result.path) { | ||
| const pkg = await getPkgInfo({ name: result.name, version: result.version, path: result.path, alias: undefined }) |
There was a problem hiding this comment.
The getPkgInfo function in reviewing/list/src/getPkgInfo.ts has a parameter alias that can be undefined, but this differs from the function signature in reviewing/dependencies-hierarchy/src/getPkgInfo.ts where alias is required (not optional). When calling this function from renderDependentsTree.ts at lines 20 and 120 with alias: undefined, there may be a type mismatch if TypeScript strict null checks are enabled. Consider making the parameter consistently optional across both implementations or providing a default value.
| const pkg = await getPkgInfo({ name: result.name, version: result.version, path: result.path, alias: undefined }) | |
| const pkg = await getPkgInfo({ name: result.name, version: result.version, path: result.path }) |
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- **`pnpm why` now shows a reverse dependency tree.** The searched package appears at the root with its dependants as branches, walking back to workspace roots. This replaces the previous forward-tree output which was noisy and hard to read for deeply nested dependencies. - **Replaced `archy` with a new `@pnpm/text.tree-renderer` package** that renders trees using box-drawing characters (├──, └──, │) and supports grouped sections, dim connectors, and deduplication markers. - **Show peer dependency hash suffixes** in `pnpm list` and `pnpm why` output to distinguish between different peer-dep variants of the same package. - **Improved `pnpm list` visual output:** bold importer nodes, dimmed workspace paths, dependency grouping, package count summary, and deterministic sort order. - **Added `--long` support to `pnpm why`** and the ability to read package manifests from the CAS store. - **Deduplicated shared code** between `list` and `why` commands into a common module, and reused `getPkgInfo` in the why tree builder.
- **`pnpm why` now shows a reverse dependency tree.** The searched package appears at the root with its dependants as branches, walking back to workspace roots. This replaces the previous forward-tree output which was noisy and hard to read for deeply nested dependencies. - **Replaced `archy` with a new `@pnpm/text.tree-renderer` package** that renders trees using box-drawing characters (├──, └──, │) and supports grouped sections, dim connectors, and deduplication markers. - **Show peer dependency hash suffixes** in `pnpm list` and `pnpm why` output to distinguish between different peer-dep variants of the same package. - **Improved `pnpm list` visual output:** bold importer nodes, dimmed workspace paths, dependency grouping, package count summary, and deterministic sort order. - **Added `--long` support to `pnpm why`** and the ability to read package manifests from the CAS store. - **Deduplicated shared code** between `list` and `why` commands into a common module, and reused `getPkgInfo` in the why tree builder.
`bit why` now shows a reverse dependency tree. The searched package appears at the root with its dependents as branches, walking back to workspace components. This replaces the previous forward-tree output which was noisy and hard to read for deeply nested dependencies. Also with the complete rewrite of the dependency tree builder we have fixed the out-of-memory errors that were frequent with the why command. The command also became a lot faster. ## Proposed Changes Related PRs: - pnpm/pnpm#10582 - pnpm/pnpm#10586 - pnpm/pnpm#10615 - pnpm/pnpm#10616 - pnpm/pnpm#10627 - pnpm/pnpm#10629 - pnpm/pnpm#10596 --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Summary
pnpm whynow shows a reverse dependency tree. The searched package appears at the root with its dependants as branches, walking back to workspace roots. This replaces the previous forward-tree output which was noisy and hard to read for deeply nested dependencies.archywith a new@pnpm/text.tree-rendererpackage that renders trees using box-drawing characters (├──, └──, │) and supports grouped sections, dim connectors, and deduplication markers.pnpm listandpnpm whyoutput to distinguish between different peer-dep variants of the same package.pnpm listvisual output: bold importer nodes, dimmed workspace paths, dependency grouping, package count summary, and deterministic sort order.--longsupport topnpm whyand the ability to read package manifests from the CAS store.listandwhycommands into a common module, and reusedgetPkgInfoin the why tree builder.Test plan
@pnpm/text.tree-renderer(new package)renderWhyTreepnpm listandpnpm why