mirror of
https://github.com/pnpm/pnpm.git
synced 2026-07-26 07:27:20 -04:00
fix(reviewing): fix memory error in pnpm why (#7122)
* fix(reviewing): fix memory error in `pnpm why` * fix: update comments * fix: add support for `--depth` * fix: move implementation to `@pnpm/list` * fix: revert changes in Dependencies Hierarchy * fix: add pnpm to patch list * refactor: rename functions to descriptive names * refactor: rename `findEndLeafs` to `findEndLeaves`
This commit is contained in:
6
.changeset/happy-lemons-smile.md
Normal file
6
.changeset/happy-lemons-smile.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@pnpm/list": patch
|
||||
"pnpm": patch
|
||||
---
|
||||
|
||||
Fix memory error in `pnpm why` when the dependencies tree is too big, the command will now prune the tree to just 10 end leafs and now supports `--depth` argument.
|
||||
@@ -6,6 +6,7 @@ import { renderJson } from './renderJson'
|
||||
import { renderParseable } from './renderParseable'
|
||||
import { renderTree } from './renderTree'
|
||||
import { type PackageDependencyHierarchy } from './types'
|
||||
import { pruneDependenciesTrees } from './pruneTree'
|
||||
|
||||
export type { PackageNode } from '@pnpm/reviewing.dependencies-hierarchy'
|
||||
export { renderJson, renderParseable, renderTree }
|
||||
@@ -105,8 +106,10 @@ export async function listForPackages (
|
||||
|
||||
const pkgs = await searchForPackages(packages, projectPaths, opts)
|
||||
|
||||
const prunedPkgs = pruneDependenciesTrees(pkgs ?? null, 10)
|
||||
|
||||
const print = getPrinter(opts.reportAs)
|
||||
return print(pkgs, {
|
||||
return print(prunedPkgs, {
|
||||
alwaysPrintRootPackage: opts.alwaysPrintRootPackage,
|
||||
depth: opts.depth,
|
||||
long: opts.long,
|
||||
|
||||
78
reviewing/list/src/pruneTree.ts
Normal file
78
reviewing/list/src/pruneTree.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { type DependenciesHierarchy, type PackageNode } from '@pnpm/reviewing.dependencies-hierarchy'
|
||||
import { type PackageDependencyHierarchy } from './types'
|
||||
import { createHash } from 'crypto'
|
||||
|
||||
export function pruneDependenciesTrees (trees: PackageDependencyHierarchy[] | null, limit: number): PackageDependencyHierarchy[] {
|
||||
if (trees === null) {
|
||||
return []
|
||||
}
|
||||
|
||||
return trees.map((tree) => {
|
||||
const endLeafPaths: PackageNode[][] = []
|
||||
const visitedNodes = new Set<string>()
|
||||
|
||||
function findEndLeaves (node: PackageNode, path: PackageNode[]): void {
|
||||
if (node.circular) {
|
||||
return
|
||||
}
|
||||
|
||||
const nodeId = `${node.name}@${node.version}`
|
||||
if (visitedNodes.has(nodeId)) {
|
||||
return
|
||||
}
|
||||
|
||||
visitedNodes.add(nodeId)
|
||||
const newPath = [...path, node]
|
||||
|
||||
if (!node.dependencies || node.dependencies.length === 0) {
|
||||
endLeafPaths.push(newPath)
|
||||
if (endLeafPaths.length >= limit) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for (const child of node.dependencies ?? []) {
|
||||
findEndLeaves(child, newPath)
|
||||
if (endLeafPaths.length >= limit) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
visitedNodes.delete(nodeId)
|
||||
}
|
||||
|
||||
if (tree.dependencies) {
|
||||
for (const node of tree.dependencies) {
|
||||
findEndLeaves(node, [])
|
||||
}
|
||||
}
|
||||
|
||||
const firstNPaths = endLeafPaths.slice(0, limit)
|
||||
const map = new Map<string, PackageNode>()
|
||||
const newTree: DependenciesHierarchy = { dependencies: [] }
|
||||
|
||||
for (const path of firstNPaths) {
|
||||
let currentDependencies: PackageNode[] = newTree.dependencies!
|
||||
let pathSoFar = ''
|
||||
|
||||
for (const node of path) {
|
||||
pathSoFar += `${node.name}@${node.version},`
|
||||
const id = createHash('sha256').update(pathSoFar).digest('hex')
|
||||
let existingNode = map.get(id)
|
||||
|
||||
if (!existingNode) {
|
||||
existingNode = { ...node, dependencies: [] }
|
||||
currentDependencies.push(existingNode)
|
||||
map.set(id, existingNode)
|
||||
}
|
||||
|
||||
currentDependencies = existingNode.dependencies!
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...tree,
|
||||
dependencies: newTree.dependencies,
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import { handler as list, type ListCommandOptions } from './list'
|
||||
|
||||
export function rcOptionsTypes () {
|
||||
return pick([
|
||||
'depth',
|
||||
'dev',
|
||||
'global-dir',
|
||||
'global',
|
||||
@@ -79,6 +80,10 @@ For options that may be used with `-r`, see "pnpm help recursive"',
|
||||
description: "Don't display packages from `optionalDependencies`",
|
||||
name: '--no-optional',
|
||||
},
|
||||
{
|
||||
name: '--depth <number>',
|
||||
description: 'Max display depth of the dependency graph',
|
||||
},
|
||||
OPTIONS.globalDir,
|
||||
...UNIVERSAL_OPTIONS,
|
||||
],
|
||||
@@ -103,7 +108,7 @@ export async function handler (
|
||||
...opts,
|
||||
cliOptions: {
|
||||
...(opts.cliOptions ?? {}),
|
||||
depth: Infinity,
|
||||
depth: opts.depth ?? Infinity,
|
||||
},
|
||||
}, params)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user