pnpm-dedup 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +218 -0
- package/dist/applyClusterFixes.js +252 -0
- package/dist/applyClusterFixes.js.map +1 -0
- package/dist/applyClusterFixes.test.js +474 -0
- package/dist/applyClusterFixes.test.js.map +1 -0
- package/dist/bin/pnpm-dedupe.js +35 -0
- package/dist/bin/pnpm-dedupe.js.map +1 -0
- package/dist/bin/pnpm-why-duplicate.js +23 -0
- package/dist/bin/pnpm-why-duplicate.js.map +1 -0
- package/dist/bin/pnpm-why-duplicate.test.js +57 -0
- package/dist/bin/pnpm-why-duplicate.test.js.map +1 -0
- package/dist/dedupe.js +86 -0
- package/dist/dedupe.js.map +1 -0
- package/dist/dedupeCheckConsistency.test.js +149 -0
- package/dist/dedupeCheckConsistency.test.js.map +1 -0
- package/dist/dedupeEndToEnd.test.js +121 -0
- package/dist/dedupeEndToEnd.test.js.map +1 -0
- package/dist/dedupePeers.test.js +105 -0
- package/dist/dedupePeers.test.js.map +1 -0
- package/dist/definitions/applyClusterFixes.d.ts +24 -0
- package/dist/definitions/applyClusterFixes.d.ts.map +1 -0
- package/dist/definitions/applyClusterFixes.test.d.ts +2 -0
- package/dist/definitions/applyClusterFixes.test.d.ts.map +1 -0
- package/dist/definitions/bin/pnpm-dedupe.d.ts +3 -0
- package/dist/definitions/bin/pnpm-dedupe.d.ts.map +1 -0
- package/dist/definitions/bin/pnpm-why-duplicate.d.ts +3 -0
- package/dist/definitions/bin/pnpm-why-duplicate.d.ts.map +1 -0
- package/dist/definitions/bin/pnpm-why-duplicate.test.d.ts +2 -0
- package/dist/definitions/bin/pnpm-why-duplicate.test.d.ts.map +1 -0
- package/dist/definitions/dedupe.d.ts +15 -0
- package/dist/definitions/dedupe.d.ts.map +1 -0
- package/dist/definitions/dedupeCheckConsistency.test.d.ts +2 -0
- package/dist/definitions/dedupeCheckConsistency.test.d.ts.map +1 -0
- package/dist/definitions/dedupeEndToEnd.test.d.ts +2 -0
- package/dist/definitions/dedupeEndToEnd.test.d.ts.map +1 -0
- package/dist/definitions/dedupePeers.test.d.ts +2 -0
- package/dist/definitions/dedupePeers.test.d.ts.map +1 -0
- package/dist/definitions/displayMany.d.ts +16 -0
- package/dist/definitions/displayMany.d.ts.map +1 -0
- package/dist/definitions/displayMany.test.d.ts +2 -0
- package/dist/definitions/displayMany.test.d.ts.map +1 -0
- package/dist/definitions/helpers/buildIdentifiedFixesMap.test.d.ts +2 -0
- package/dist/definitions/helpers/buildIdentifiedFixesMap.test.d.ts.map +1 -0
- package/dist/definitions/helpers/buildPnpmPackagesMap.d.ts +13 -0
- package/dist/definitions/helpers/buildPnpmPackagesMap.d.ts.map +1 -0
- package/dist/definitions/helpers/buildPnpmPackagesMap.test.d.ts +2 -0
- package/dist/definitions/helpers/buildPnpmPackagesMap.test.d.ts.map +1 -0
- package/dist/definitions/helpers/collectDependentRanges.d.ts +22 -0
- package/dist/definitions/helpers/collectDependentRanges.d.ts.map +1 -0
- package/dist/definitions/helpers/collectDependentRanges.test.d.ts +2 -0
- package/dist/definitions/helpers/collectDependentRanges.test.d.ts.map +1 -0
- package/dist/definitions/helpers/collectPnpmDependents.d.ts +8 -0
- package/dist/definitions/helpers/collectPnpmDependents.d.ts.map +1 -0
- package/dist/definitions/helpers/collectPnpmDependents.test.d.ts +2 -0
- package/dist/definitions/helpers/collectPnpmDependents.test.d.ts.map +1 -0
- package/dist/definitions/helpers/duplicateSnapshot.d.ts +3 -0
- package/dist/definitions/helpers/duplicateSnapshot.d.ts.map +1 -0
- package/dist/definitions/helpers/parsePnpmLockPackages.d.ts +44 -0
- package/dist/definitions/helpers/parsePnpmLockPackages.d.ts.map +1 -0
- package/dist/definitions/helpers/parsePnpmLockPackages.test.d.ts +2 -0
- package/dist/definitions/helpers/parsePnpmLockPackages.test.d.ts.map +1 -0
- package/dist/definitions/helpers/pnpmProtocol.d.ts +2 -0
- package/dist/definitions/helpers/pnpmProtocol.d.ts.map +1 -0
- package/dist/definitions/helpers/pnpmVersion.d.ts +11 -0
- package/dist/definitions/helpers/pnpmVersion.d.ts.map +1 -0
- package/dist/definitions/helpers/pnpmWorkspaceYaml.d.ts +23 -0
- package/dist/definitions/helpers/pnpmWorkspaceYaml.d.ts.map +1 -0
- package/dist/definitions/helpers/pnpmWorkspaceYaml.test.d.ts +2 -0
- package/dist/definitions/helpers/pnpmWorkspaceYaml.test.d.ts.map +1 -0
- package/dist/definitions/helpers/projectDir.d.ts +3 -0
- package/dist/definitions/helpers/projectDir.d.ts.map +1 -0
- package/dist/definitions/helpers/readInstalledManifest.d.ts +32 -0
- package/dist/definitions/helpers/readInstalledManifest.d.ts.map +1 -0
- package/dist/definitions/helpers/readInstalledManifest.test.d.ts +2 -0
- package/dist/definitions/helpers/readInstalledManifest.test.d.ts.map +1 -0
- package/dist/definitions/helpers/runPnpm.d.ts +11 -0
- package/dist/definitions/helpers/runPnpm.d.ts.map +1 -0
- package/dist/definitions/helpers/tempProjects.d.ts +8 -0
- package/dist/definitions/helpers/tempProjects.d.ts.map +1 -0
- package/dist/definitions/helpers/toLockstepGraph.d.ts +12 -0
- package/dist/definitions/helpers/toLockstepGraph.d.ts.map +1 -0
- package/dist/definitions/helpers/toLockstepGraph.test.d.ts +2 -0
- package/dist/definitions/helpers/toLockstepGraph.test.d.ts.map +1 -0
- package/dist/definitions/helpers/versionsSnapshot.d.ts +3 -0
- package/dist/definitions/helpers/versionsSnapshot.d.ts.map +1 -0
- package/dist/definitions/identifyClusterFixes.d.ts +7 -0
- package/dist/definitions/identifyClusterFixes.d.ts.map +1 -0
- package/dist/definitions/identifyClusterFixes.test.d.ts +2 -0
- package/dist/definitions/identifyClusterFixes.test.d.ts.map +1 -0
- package/dist/definitions/identifyResolutionFixes.test.d.ts +2 -0
- package/dist/definitions/identifyResolutionFixes.test.d.ts.map +1 -0
- package/dist/definitions/index.d.ts +25 -0
- package/dist/definitions/index.d.ts.map +1 -0
- package/dist/definitions/pnpmLockTypes.d.ts +42 -0
- package/dist/definitions/pnpmLockTypes.d.ts.map +1 -0
- package/dist/definitions/readPnpmLock.d.ts +3 -0
- package/dist/definitions/readPnpmLock.d.ts.map +1 -0
- package/dist/displayMany.js +56 -0
- package/dist/displayMany.js.map +1 -0
- package/dist/displayMany.test.js +146 -0
- package/dist/displayMany.test.js.map +1 -0
- package/dist/helpers/buildIdentifiedFixesMap.test.js +27 -0
- package/dist/helpers/buildIdentifiedFixesMap.test.js.map +1 -0
- package/dist/helpers/buildPnpmPackagesMap.js +23 -0
- package/dist/helpers/buildPnpmPackagesMap.js.map +1 -0
- package/dist/helpers/buildPnpmPackagesMap.test.js +68 -0
- package/dist/helpers/buildPnpmPackagesMap.test.js.map +1 -0
- package/dist/helpers/collectDependentRanges.js +118 -0
- package/dist/helpers/collectDependentRanges.js.map +1 -0
- package/dist/helpers/collectDependentRanges.test.js +211 -0
- package/dist/helpers/collectDependentRanges.test.js.map +1 -0
- package/dist/helpers/collectPnpmDependents.js +55 -0
- package/dist/helpers/collectPnpmDependents.js.map +1 -0
- package/dist/helpers/collectPnpmDependents.test.js +78 -0
- package/dist/helpers/collectPnpmDependents.test.js.map +1 -0
- package/dist/helpers/duplicateSnapshot.js +5 -0
- package/dist/helpers/duplicateSnapshot.js.map +1 -0
- package/dist/helpers/parsePnpmLockPackages.js +62 -0
- package/dist/helpers/parsePnpmLockPackages.js.map +1 -0
- package/dist/helpers/parsePnpmLockPackages.test.js +62 -0
- package/dist/helpers/parsePnpmLockPackages.test.js.map +1 -0
- package/dist/helpers/pnpmProtocol.js +2 -0
- package/dist/helpers/pnpmProtocol.js.map +1 -0
- package/dist/helpers/pnpmVersion.js +23 -0
- package/dist/helpers/pnpmVersion.js.map +1 -0
- package/dist/helpers/pnpmWorkspaceYaml.js +48 -0
- package/dist/helpers/pnpmWorkspaceYaml.js.map +1 -0
- package/dist/helpers/pnpmWorkspaceYaml.test.js +91 -0
- package/dist/helpers/pnpmWorkspaceYaml.test.js.map +1 -0
- package/dist/helpers/projectDir.js +10 -0
- package/dist/helpers/projectDir.js.map +1 -0
- package/dist/helpers/readInstalledManifest.js +139 -0
- package/dist/helpers/readInstalledManifest.js.map +1 -0
- package/dist/helpers/readInstalledManifest.test.js +83 -0
- package/dist/helpers/readInstalledManifest.test.js.map +1 -0
- package/dist/helpers/runPnpm.js +16 -0
- package/dist/helpers/runPnpm.js.map +1 -0
- package/dist/helpers/tempProjects.js +85 -0
- package/dist/helpers/tempProjects.js.map +1 -0
- package/dist/helpers/toLockstepGraph.js +34 -0
- package/dist/helpers/toLockstepGraph.js.map +1 -0
- package/dist/helpers/toLockstepGraph.test.js +81 -0
- package/dist/helpers/toLockstepGraph.test.js.map +1 -0
- package/dist/helpers/versionsSnapshot.js +5 -0
- package/dist/helpers/versionsSnapshot.js.map +1 -0
- package/dist/identifyClusterFixes.js +53 -0
- package/dist/identifyClusterFixes.js.map +1 -0
- package/dist/identifyClusterFixes.test.js +307 -0
- package/dist/identifyClusterFixes.test.js.map +1 -0
- package/dist/identifyResolutionFixes.test.js +49 -0
- package/dist/identifyResolutionFixes.test.js.map +1 -0
- package/dist/index.js +86 -0
- package/dist/index.js.map +1 -0
- package/dist/pnpmLockTypes.js +7 -0
- package/dist/pnpmLockTypes.js.map +1 -0
- package/dist/readPnpmLock.js +6 -0
- package/dist/readPnpmLock.js.map +1 -0
- package/package.json +71 -0
- package/src/applyClusterFixes.test.ts +560 -0
- package/src/applyClusterFixes.ts +466 -0
- package/src/bin/pnpm-dedupe.ts +44 -0
- package/src/bin/pnpm-why-duplicate.test.ts +79 -0
- package/src/bin/pnpm-why-duplicate.ts +34 -0
- package/src/dedupe.ts +124 -0
- package/src/dedupeCheckConsistency.test.ts +218 -0
- package/src/dedupeEndToEnd.test.ts +168 -0
- package/src/dedupePeers.test.ts +175 -0
- package/src/displayMany.test.ts +208 -0
- package/src/displayMany.ts +104 -0
- package/src/helpers/buildIdentifiedFixesMap.test.ts +33 -0
- package/src/helpers/buildPnpmPackagesMap.test.ts +119 -0
- package/src/helpers/buildPnpmPackagesMap.ts +53 -0
- package/src/helpers/collectDependentRanges.test.ts +311 -0
- package/src/helpers/collectDependentRanges.ts +185 -0
- package/src/helpers/collectPnpmDependents.test.ts +112 -0
- package/src/helpers/collectPnpmDependents.ts +79 -0
- package/src/helpers/duplicateSnapshot.ts +18 -0
- package/src/helpers/parsePnpmLockPackages.test.ts +93 -0
- package/src/helpers/parsePnpmLockPackages.ts +99 -0
- package/src/helpers/pnpmProtocol.ts +12 -0
- package/src/helpers/pnpmVersion.ts +28 -0
- package/src/helpers/pnpmWorkspaceYaml.test.ts +140 -0
- package/src/helpers/pnpmWorkspaceYaml.ts +77 -0
- package/src/helpers/projectDir.ts +14 -0
- package/src/helpers/readInstalledManifest.test.ts +124 -0
- package/src/helpers/readInstalledManifest.ts +202 -0
- package/src/helpers/runPnpm.ts +34 -0
- package/src/helpers/tempProjects.ts +94 -0
- package/src/helpers/toLockstepGraph.test.ts +98 -0
- package/src/helpers/toLockstepGraph.ts +52 -0
- package/src/helpers/versionsSnapshot.ts +9 -0
- package/src/identifyClusterFixes.test.ts +430 -0
- package/src/identifyClusterFixes.ts +114 -0
- package/src/identifyResolutionFixes.test.ts +82 -0
- package/src/index.ts +159 -0
- package/src/pnpmLockTypes.ts +44 -0
- package/src/readPnpmLock.ts +7 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Christophe Hurpeau <christophe@hurpeau.com> (https://christophe.hurpeau.com)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
<h1 align="center">
|
|
2
|
+
pnpm-dedup
|
|
3
|
+
</h1>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
List duplicates and dedupe pnpm lock file
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="https://npmjs.org/package/pnpm-dedup"><img src="https://img.shields.io/npm/v/pnpm-dedup.svg?style=flat-square" alt="npm version"></a>
|
|
11
|
+
<a href="https://npmjs.org/package/pnpm-dedup"><img src="https://img.shields.io/npm/dw/pnpm-dedup.svg?style=flat-square" alt="npm downloads"></a>
|
|
12
|
+
<a href="https://npmjs.org/package/pnpm-dedup"><img src="https://img.shields.io/node/v/pnpm-dedup.svg?style=flat-square" alt="node version"></a>
|
|
13
|
+
<a href="https://npmjs.org/package/pnpm-dedup"><img src="https://img.shields.io/npm/types/pnpm-dedup.svg?style=flat-square" alt="types"></a>
|
|
14
|
+
</p>
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
Nothing needs to be installed: `pnpm dlx` fetches the package, and
|
|
19
|
+
`--package pnpm-dedup` is what tells it which one, since `pnpm dlx` otherwise
|
|
20
|
+
fetches the package whose name matches the command. The bins are
|
|
21
|
+
`pnpm-why-duplicate` and `pnpm-dedupe`, plus `pnpm-dedup` as an alias of the
|
|
22
|
+
latter — that one does match the package name, so `pnpm dlx pnpm-dedup` is a
|
|
23
|
+
shorter way to run the dedupe.
|
|
24
|
+
|
|
25
|
+
Installing it (`pnpm add -D pnpm-dedup`) is only worth it to pin a version — for
|
|
26
|
+
a CI gate, say. `pnpm exec <bin>` then runs the local one.
|
|
27
|
+
|
|
28
|
+
Both bins may be run from any subdirectory of the project: they walk up to the
|
|
29
|
+
nearest `pnpm-lock.yaml` and operate on that project, naming the lockfile they
|
|
30
|
+
found on stderr when it is not the working directory.
|
|
31
|
+
|
|
32
|
+
### List duplicates, or explain one
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
pnpm dlx --package pnpm-dedup pnpm-why-duplicate # one line per duplicated package
|
|
36
|
+
pnpm dlx --package pnpm-dedup pnpm-why-duplicate --details # every dependent, and the lockstep clusters
|
|
37
|
+
pnpm dlx --package pnpm-dedup pnpm-why-duplicate <dependencyName> # explains the matching packages, detailed
|
|
38
|
+
pnpm dlx --package pnpm-dedup pnpm-why-duplicate <dependencyName> --all
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`<dependencyName>` is a glob, so `@babel/*` works. `--all` (`-a`) keeps packages
|
|
42
|
+
that are not duplicated.
|
|
43
|
+
|
|
44
|
+
A named package that is not duplicated still gets its dependents listed —
|
|
45
|
+
"nothing found" is not an answer to "why is this duplicated". `--all` is only
|
|
46
|
+
needed to keep single-version matches next to duplicated ones.
|
|
47
|
+
|
|
48
|
+
By default the report is one line per duplicated package — the versions it
|
|
49
|
+
resolved to, and what it can be deduped to:
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
- @eslint/plugin-kit resolved to 2 versions (0.7.1, 0.6.1)
|
|
53
|
+
- picomatch resolved to 3 versions (4.0.4, 4.0.3, 2.3.1), can be deduped to 2 versions
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`can be deduped to N versions` always means N remain, never N merged away.
|
|
57
|
+
|
|
58
|
+
`--details` (`-d`), implied when a package is named, files every dependent under
|
|
59
|
+
the version it resolved to, so the range that keeps each copy alive is read off
|
|
60
|
+
the version it sits under:
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
@eslint/plugin-kit — 2 versions
|
|
64
|
+
0.7.1
|
|
65
|
+
- check-package-dependencies requires "^0.7.1"
|
|
66
|
+
- eslint requires "^0.7.1"
|
|
67
|
+
0.6.1
|
|
68
|
+
- @eslint/json requires "^0.6.1"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
After the packages come the lockstep clusters — families published together,
|
|
72
|
+
whose duplicates only go away when the whole family moves. A package a cluster
|
|
73
|
+
converges carries the family's target and a `(cluster N)` reference.
|
|
74
|
+
|
|
75
|
+
The dedupable count is a floor: merging a package collapses its own dependency
|
|
76
|
+
edges too, so a run can remove copies no per-package fix predicted.
|
|
77
|
+
|
|
78
|
+
### Dedupe
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
pnpm dlx --package pnpm-dedup pnpm-dedupe # apply
|
|
82
|
+
pnpm dlx --package pnpm-dedup pnpm-dedupe --dry-run # print the plan, change nothing, exit 0
|
|
83
|
+
pnpm dlx --package pnpm-dedup pnpm-dedupe --check # print the plan, change nothing, exit 1 if anything would change
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`--check` is the CI gate; `--dry-run` (`-n`) is the same output without the
|
|
87
|
+
failing exit code. `--check` also exits 1 when `pnpm dedupe --check` itself
|
|
88
|
+
cannot confirm the lockfile is settled, so a probe that fails for environmental
|
|
89
|
+
reasons fails the gate rather than passing silently.
|
|
90
|
+
|
|
91
|
+
`--no-convergence-overrides` writes plain overrides instead of convergence ones,
|
|
92
|
+
which pnpm applies to every requester whatever range it declares. Convergence
|
|
93
|
+
overrides need pnpm >= 11.13.0; without them the cluster pass is skipped.
|
|
94
|
+
|
|
95
|
+
A run that changes something ends with what it merged, one line per package:
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
Deduped 2 packages, 2 copies merged:
|
|
99
|
+
barcode-detector: 2 versions (3.0.3, 3.2.2) -> 1 version (3.0.3)
|
|
100
|
+
zxing-wasm: 2 versions (2.2.4, 3.1.3) -> 1 version (2.2.4)
|
|
101
|
+
No duplicate left.
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
The cluster pass and `pnpm dedupe` are covered by that one summary. Both sides
|
|
105
|
+
of a line are counted and named, so a family converging onto an older release
|
|
106
|
+
reads as two copies becoming one rather than as a downgrade; a package only
|
|
107
|
+
partly collapsed lists the versions it still has. The last line counts the
|
|
108
|
+
duplicates left, as `pnpm-why-duplicate` counts them.
|
|
109
|
+
|
|
110
|
+
### Progressive deduplication
|
|
111
|
+
|
|
112
|
+
Deduping the whole lockfile is the default: a run told nothing takes everything
|
|
113
|
+
it can. In case you only need to dedupe part of it, these flags bound a run to
|
|
114
|
+
the packages you name, and work on both commands:
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
pnpm dlx --package pnpm-dedup pnpm-dedupe --dry-run --scopes @babel # one scope
|
|
118
|
+
pnpm dlx --package pnpm-dedup pnpm-dedupe --dry-run --packages 'lodash,@babel/**' # names or globs
|
|
119
|
+
pnpm dlx --package pnpm-dedup pnpm-dedupe --dry-run --exclude lodash # everything but these
|
|
120
|
+
pnpm dlx --package pnpm-dedup pnpm-dedupe --dry-run --exclude-scopes @types
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Each flag takes a comma list or is repeated (`--packages a --packages b`).
|
|
124
|
+
Selecting nothing selects everything. `--packages`/`--scopes` say what may be
|
|
125
|
+
touched; `--exclude`/`--exclude-scopes` win over them, so a scope can be taken
|
|
126
|
+
minus a few of its packages. Patterns are globs whose `*` stops at `/`, so
|
|
127
|
+
`@babel/*` matches `@babel/core` and a bare `*` matches only unscoped names.
|
|
128
|
+
|
|
129
|
+
A lockstep cluster moves as one, so a fix is kept only when the filter selects
|
|
130
|
+
every member of its family — otherwise it is skipped and the plan names the
|
|
131
|
+
members that blocked it. Widen the filter to take the family, or leave it for
|
|
132
|
+
another run.
|
|
133
|
+
|
|
134
|
+
A filtered run does not end with `pnpm dedupe`, which knows nothing of the
|
|
135
|
+
filter and would merge everything it can. It says so instead, so you can run it
|
|
136
|
+
yourself when you are ready. Applying still needs a real resolution — an
|
|
137
|
+
override only takes effect through one — so a filtered run that changes
|
|
138
|
+
something has already let pnpm merge what it could reach on its own; a
|
|
139
|
+
`--dry-run` or `--check` runs nothing at all.
|
|
140
|
+
|
|
141
|
+
Colors follow the terminal: `NO_COLOR` and a piped stdout turn them off,
|
|
142
|
+
`FORCE_COLOR` turns them on.
|
|
143
|
+
|
|
144
|
+
## Two passes, one of them pnpm's
|
|
145
|
+
|
|
146
|
+
`pnpm dedupe` merges what it can resolve to a single version on its own, but it
|
|
147
|
+
never widens a workspace range and never repoints an edge that resolved past a
|
|
148
|
+
version the family already carries. That gap is what the cluster pass covers, so
|
|
149
|
+
it runs first and `pnpm dedupe` finishes the residuals. Unlike the bun and yarn
|
|
150
|
+
tools, there is no lockfile-rewriting pass here: `pnpm-lock.yaml` is only ever
|
|
151
|
+
written by pnpm itself.
|
|
152
|
+
|
|
153
|
+
The cluster pass edits workspace `package.json` ranges and adds overrides to
|
|
154
|
+
`pnpm-workspace.yaml`, then re-resolves without them: overrides are scaffolding,
|
|
155
|
+
and a fix that holds on its own has them removed again.
|
|
156
|
+
|
|
157
|
+
When a fix does _not_ hold — `pnpm dedupe` resolves the duplicate straight back
|
|
158
|
+
once the override is gone — the override stays, with a comment above it saying
|
|
159
|
+
so and pointing at the issue tracker, and the run reports how many were left.
|
|
160
|
+
That is the one case where this tool leaves something standing in your
|
|
161
|
+
configuration; it is a workaround, and the cluster is worth reporting.
|
|
162
|
+
|
|
163
|
+
Convergence overrides (`"pkg@": "1.2.3"`, an empty range selector) are what make
|
|
164
|
+
this safe: they rewrite an edge only when the declared range accepts that exact
|
|
165
|
+
version, so a third-party range that legitimately pins elsewhere is left alone.
|
|
166
|
+
`--no-convergence-overrides` falls back to plain overrides, which force every
|
|
167
|
+
requester regardless.
|
|
168
|
+
|
|
169
|
+
## Peer dependencies
|
|
170
|
+
|
|
171
|
+
pnpm resolves peers before writing the lockfile, folds each into the snapshot's
|
|
172
|
+
`dependencies` and encodes the context in the peer-suffixed key. So the _edge_
|
|
173
|
+
survives — and an edge that exists means the peer really was provided — but the
|
|
174
|
+
_range_ does not: the snapshot stores the resolved version.
|
|
175
|
+
|
|
176
|
+
Left at that, 17 eslint plugins declaring `eslint: "^8.57.0 || ^9.0.0"` would all
|
|
177
|
+
read as exact pins of whatever got installed, and nothing could be merged. The
|
|
178
|
+
range is therefore recovered from the installed manifest first, then from the
|
|
179
|
+
lockfile's own `packages:` entry, so it survives with no `node_modules` present.
|
|
180
|
+
Such a dependent is marked with a dim `(peer)` after its range in the report.
|
|
181
|
+
|
|
182
|
+
A peer range never explains why a second version exists — pnpm resolves peers
|
|
183
|
+
against what is in the tree and warns instead of backtracking, and the several
|
|
184
|
+
peer-suffixed keys of one version are several _installations_, folded back into a
|
|
185
|
+
single resolution here. It is an extra constraint on a package duplicated for
|
|
186
|
+
other reasons, which is why recovering it changes the fixes.
|
|
187
|
+
|
|
188
|
+
## Notes
|
|
189
|
+
|
|
190
|
+
- Requires pnpm on `PATH`. Cluster fixes additionally need pnpm >= 11.13.0; below
|
|
191
|
+
that the pass is skipped with a message, and `pnpm dedupe` still runs.
|
|
192
|
+
- `--check` shells out to `pnpm dedupe --check`, so it fails the gate when that
|
|
193
|
+
probe cannot confirm the lockfile is settled — an environmental failure fails
|
|
194
|
+
loudly rather than passing silently.
|
|
195
|
+
- Non-npm resolutions (`link:`, git, tarball…) are parsed and reported but never
|
|
196
|
+
merged: only semver-comparable npm versions are.
|
|
197
|
+
- Node only (`>=22.18.0`), no bun runtime dependency, despite the monorepo being
|
|
198
|
+
developed with bun.
|
|
199
|
+
|
|
200
|
+
## API
|
|
201
|
+
|
|
202
|
+
```js
|
|
203
|
+
import { dedupe, listDuplicates, whyDuplicate } from "pnpm-dedup/src/index";
|
|
204
|
+
|
|
205
|
+
listDuplicates({ details: true });
|
|
206
|
+
whyDuplicate({ filter: { include: ["@babel/*"] }, all: true, details: true });
|
|
207
|
+
dedupe({
|
|
208
|
+
mode: "dry-run",
|
|
209
|
+
convergenceOverrides: true,
|
|
210
|
+
filter: { includeScopes: ["@babel"] },
|
|
211
|
+
});
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
`mode` is `"apply"`, `"dry-run"` or `"check"` — the same three the CLI exposes.
|
|
215
|
+
The lockfile parsing and graph building steps are exported too (`readPnpmLock`,
|
|
216
|
+
`parsePnpmLockPackages`, `buildPnpmPackagesMap`, `collectPnpmDependents`,
|
|
217
|
+
`collectDependentRanges`, `toLockstepGraph`), for building something else on
|
|
218
|
+
top; the algorithm itself lives in [pm-dedup-core](../pm-dedup-core).
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join, relative } from "node:path";
|
|
3
|
+
import { applyWorkspaceRangeEdit, captureFiles, createPackageFilter, describeSkippedClusterFix, diffDuplicates, partitionUnconditionalOverrides, planClusterApply, renderApplyPlan, restoreFiles, selectClusterFixes, shouldColorize, } from "pm-dedup-core";
|
|
4
|
+
import { buildPnpmPackagesMap } from "./helpers/buildPnpmPackagesMap.js";
|
|
5
|
+
import { readDuplicateSnapshot } from "./helpers/duplicateSnapshot.js";
|
|
6
|
+
import { parsePnpmLockPackages } from "./helpers/parsePnpmLockPackages.js";
|
|
7
|
+
import { convergenceOverridesMinVersion, readPnpmVersion, supportsConvergenceOverrides, } from "./helpers/pnpmVersion.js";
|
|
8
|
+
import { addOverrides, overrideKey } from "./helpers/pnpmWorkspaceYaml.js";
|
|
9
|
+
import { lockPathOf } from "./helpers/projectDir.js";
|
|
10
|
+
import { createManifestReader } from "./helpers/readInstalledManifest.js";
|
|
11
|
+
import { runPnpm } from "./helpers/runPnpm.js";
|
|
12
|
+
import { identifyClusterFixes } from "./identifyClusterFixes.js";
|
|
13
|
+
import { readPnpmLock } from "./readPnpmLock.js";
|
|
14
|
+
const issuesUrl = "https://github.com/christophehurpeau/pm-tools/issues";
|
|
15
|
+
const defaultReadFixes = (projectDir) => {
|
|
16
|
+
const lock = readPnpmLock(lockPathOf(projectDir));
|
|
17
|
+
return identifyClusterFixes(lock, buildPnpmPackagesMap(parsePnpmLockPackages(lock)), createManifestReader(projectDir));
|
|
18
|
+
};
|
|
19
|
+
const manifestPathOf = (projectDir, importerPath) => join(projectDir, importerPath === "." ? "" : importerPath, "package.json");
|
|
20
|
+
const describeOverride = (override, convergence) => `"${overrideKey(override.packageName, convergence)}": "${override.version}"`;
|
|
21
|
+
/**
|
|
22
|
+
* Why the overrides below them had to stay. Used verbatim as the comment left in
|
|
23
|
+
* `pnpm-workspace.yaml` and as the console explanation, so the file and the run
|
|
24
|
+
* say the same thing. The packages are the entries that follow, so they are not
|
|
25
|
+
* repeated here.
|
|
26
|
+
*/
|
|
27
|
+
const stickyOverrideReason = [
|
|
28
|
+
"Added by pnpm-dedup.",
|
|
29
|
+
"Removing these makes `pnpm dedupe` resolve them back to a duplicate, so they",
|
|
30
|
+
"are what holds the deduplicated result. Keeping them is a workaround: no fix",
|
|
31
|
+
"was found that survives on its own. Please report this cluster so it can be",
|
|
32
|
+
`handled without a standing override: ${issuesUrl}`,
|
|
33
|
+
];
|
|
34
|
+
const reuseKeys = (fixes) => new Set(fixes.flatMap((fix) => fix.reuseFixes.map((reuse) => `${reuse.requesterName}>${reuse.packageName}@${reuse.to}`)));
|
|
35
|
+
export const applyClusterFixes = ({ projectDir, dryRun = false, log = console.log,
|
|
36
|
+
// `pnpm dedupe`, not `pnpm install`: an override is applied by a read-package
|
|
37
|
+
// hook that only runs during a real resolution, and an incremental install
|
|
38
|
+
// reports "Already up to date" without ever re-reading a manifest. `dedupe`
|
|
39
|
+
// re-resolves, and is also what runs right after this, so it is the only
|
|
40
|
+
// honest thing to verify against.
|
|
41
|
+
resolve = () => runPnpm(["dedupe"], { cwd: projectDir }).status, readFixes = defaultReadFixes, readDuplicates = readDuplicateSnapshot, pnpmVersion = () => readPnpmVersion(), convergenceOverrides = true, filter, packageManagerResiduals, color = shouldColorize(), }) => {
|
|
42
|
+
const packageFilter = createPackageFilter(filter);
|
|
43
|
+
const readSelectedFixes = (dir) => selectClusterFixes(readFixes(dir), packageFilter);
|
|
44
|
+
const lockPath = lockPathOf(projectDir);
|
|
45
|
+
const workspaceYamlPath = join(projectDir, "pnpm-workspace.yaml");
|
|
46
|
+
const before = readDuplicates(lockPath);
|
|
47
|
+
const unchanged = (status, plannedChangeCount = 0) => ({
|
|
48
|
+
status,
|
|
49
|
+
before,
|
|
50
|
+
after: before,
|
|
51
|
+
stickyOverrides: [],
|
|
52
|
+
plannedChangeCount,
|
|
53
|
+
});
|
|
54
|
+
const { selected: fixes, skipped: filteredOut } = readSelectedFixes(projectDir);
|
|
55
|
+
const plan = planClusterApply(fixes);
|
|
56
|
+
const skipped = [
|
|
57
|
+
...filteredOut.map(describeSkippedClusterFix),
|
|
58
|
+
...plan.unresolvableChanges.map((unresolvable) => `${unresolvable}: no workspace file recorded for it`),
|
|
59
|
+
...plan.conflicts.map((conflict) => `${conflict.packageName}: keeping ${conflict.kept}, ignoring ${conflict.dropped} asked by another cluster`),
|
|
60
|
+
];
|
|
61
|
+
// a plain override applies to every requester of the package, so one a
|
|
62
|
+
// third-party range rejects would force that dependent too
|
|
63
|
+
const plannedOverrides = convergenceOverrides
|
|
64
|
+
? plan.overrides
|
|
65
|
+
: (() => {
|
|
66
|
+
const { safe, rejected } = partitionUnconditionalOverrides(fixes, plan.overrides);
|
|
67
|
+
for (const { override, rejectedBy } of rejected) {
|
|
68
|
+
for (const constraint of rejectedBy) {
|
|
69
|
+
skipped.push(`override ${describeOverride(override, false)}: ${constraint.requesterName ?? "workspace"} requires "${constraint.range}", and a plain override would force it too`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return safe;
|
|
73
|
+
})();
|
|
74
|
+
const changeCount = plan.manifestEdits.length + plannedOverrides.length;
|
|
75
|
+
const relativeManifestPath = (importerPath) => relative(projectDir, manifestPathOf(projectDir, importerPath));
|
|
76
|
+
const planFileChanges = () => [
|
|
77
|
+
...[
|
|
78
|
+
...new Set(plan.manifestEdits.map((edit) => relativeManifestPath(edit.importerPath))),
|
|
79
|
+
].map((path) => ({
|
|
80
|
+
path,
|
|
81
|
+
changes: plan.manifestEdits
|
|
82
|
+
.filter((edit) => relativeManifestPath(edit.importerPath) === path)
|
|
83
|
+
.map((edit) => `"${edit.packageName}": "${edit.range}" -> "${edit.to}" (${edit.depType})`),
|
|
84
|
+
})),
|
|
85
|
+
{
|
|
86
|
+
path: relative(projectDir, workspaceYamlPath),
|
|
87
|
+
transient: "removed once the result is verified",
|
|
88
|
+
changes: plannedOverrides.map((override) => `${describeOverride(override, convergenceOverrides)} (${override.reason})`),
|
|
89
|
+
},
|
|
90
|
+
];
|
|
91
|
+
// A dry run prints the plan through the shared renderer and stops. Anything
|
|
92
|
+
// that rules the plan out has to be decided before that, or the report would
|
|
93
|
+
// promise edits pnpm cannot make.
|
|
94
|
+
const reportPlan = (applicable) => {
|
|
95
|
+
renderApplyPlan({
|
|
96
|
+
fileChanges: applicable ? planFileChanges() : [],
|
|
97
|
+
skipped,
|
|
98
|
+
packageManagerResiduals,
|
|
99
|
+
dedupeCommand: "pnpm-dedupe",
|
|
100
|
+
log,
|
|
101
|
+
color,
|
|
102
|
+
});
|
|
103
|
+
return unchanged("dry-run", applicable ? changeCount : 0);
|
|
104
|
+
};
|
|
105
|
+
if (!dryRun) {
|
|
106
|
+
for (const entry of skipped) {
|
|
107
|
+
log(` Skipped ${entry}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (changeCount === 0) {
|
|
111
|
+
return dryRun ? reportPlan(false) : unchanged("nothing-to-do");
|
|
112
|
+
}
|
|
113
|
+
if (convergenceOverrides) {
|
|
114
|
+
const version = pnpmVersion();
|
|
115
|
+
if (version === null || !supportsConvergenceOverrides(version)) {
|
|
116
|
+
log(`Cluster fixes need pnpm >= ${convergenceOverridesMinVersion} (convergence overrides); found ${version ?? "no pnpm"}. Skipping them.`);
|
|
117
|
+
// nothing can be applied, so a `--check` run has nothing to fail on
|
|
118
|
+
return dryRun ? reportPlan(false) : unchanged("not-supported");
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (dryRun) {
|
|
122
|
+
return reportPlan(true);
|
|
123
|
+
}
|
|
124
|
+
const touchedPaths = [
|
|
125
|
+
...new Set(plan.manifestEdits.map((edit) => manifestPathOf(projectDir, edit.importerPath))),
|
|
126
|
+
workspaceYamlPath,
|
|
127
|
+
lockPath,
|
|
128
|
+
];
|
|
129
|
+
const originalSnapshot = captureFiles(touchedPaths);
|
|
130
|
+
const revertTo = (snapshot) => {
|
|
131
|
+
restoreFiles(snapshot);
|
|
132
|
+
resolve();
|
|
133
|
+
};
|
|
134
|
+
const editManifests = (edits) => edits.every((edit) => {
|
|
135
|
+
const path = manifestPathOf(projectDir, edit.importerPath);
|
|
136
|
+
const updated = applyWorkspaceRangeEdit(readFileSync(path, "utf8"), edit);
|
|
137
|
+
if (updated === undefined) {
|
|
138
|
+
log(` ${path} no longer declares "${edit.packageName}": "${edit.range}" — the lockfile is out of date`);
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
log(` ${path}: "${edit.packageName}" ${edit.range} -> ${edit.to} in ${edit.depType}`);
|
|
142
|
+
writeFileSync(path, updated);
|
|
143
|
+
return true;
|
|
144
|
+
});
|
|
145
|
+
// A reuse fix removes no duplicate — both copies keep their dependents — so
|
|
146
|
+
// the duplicate set cannot tell whether it took. The detector can: the fix is
|
|
147
|
+
// gone from its output once the edge points at the anchored version.
|
|
148
|
+
const readState = () => ({
|
|
149
|
+
duplicates: readDuplicates(lockPath),
|
|
150
|
+
reuses: reuseKeys(readSelectedFixes(projectDir).selected),
|
|
151
|
+
});
|
|
152
|
+
// A step keeps its edits as long as it broke nothing: a widened range often
|
|
153
|
+
// deduplicates nothing on its own and only makes the overrides applicable.
|
|
154
|
+
const resolveAndCheck = (label) => {
|
|
155
|
+
if (resolve() !== 0) {
|
|
156
|
+
log(` \`pnpm dedupe\` failed after ${label} — reverting`);
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
const state = readState();
|
|
160
|
+
const { added } = diffDuplicates(before, state.duplicates);
|
|
161
|
+
if (added.length > 0) {
|
|
162
|
+
log(` ${label} introduced ${added.length} new duplicate(s) — reverting`);
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
return state;
|
|
166
|
+
};
|
|
167
|
+
if (plan.manifestEdits.length > 0) {
|
|
168
|
+
log("Editing workspace ranges:");
|
|
169
|
+
if (!editManifests(plan.manifestEdits)) {
|
|
170
|
+
revertTo(originalSnapshot);
|
|
171
|
+
return unchanged("reverted", changeCount);
|
|
172
|
+
}
|
|
173
|
+
if (resolveAndCheck("the workspace range edits") === null) {
|
|
174
|
+
revertTo(originalSnapshot);
|
|
175
|
+
return unchanged("reverted", changeCount);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
const withoutOverridesSnapshot = captureFiles(touchedPaths);
|
|
179
|
+
const workspaceYamlBefore = withoutOverridesSnapshot.find((snapshot) => snapshot.path === workspaceYamlPath);
|
|
180
|
+
const remaining = readState();
|
|
181
|
+
// An override is still worth writing while its package is duplicated, or
|
|
182
|
+
// while the edge it repoints is still resolving elsewhere.
|
|
183
|
+
const outstanding = plannedOverrides.filter((override) => [...remaining.duplicates].some((resolution) => resolution.startsWith(`${override.packageName}@`)) ||
|
|
184
|
+
[...remaining.reuses].some((key) => key.endsWith(`>${override.packageName}@${override.version}`)));
|
|
185
|
+
if (outstanding.length === 0) {
|
|
186
|
+
return {
|
|
187
|
+
status: "applied",
|
|
188
|
+
before,
|
|
189
|
+
after: remaining.duplicates,
|
|
190
|
+
stickyOverrides: [],
|
|
191
|
+
plannedChangeCount: changeCount,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
log(`Adding ${convergenceOverrides ? "convergence" : "plain"} overrides to pnpm-workspace.yaml:`);
|
|
195
|
+
for (const override of outstanding) {
|
|
196
|
+
log(` ${describeOverride(override, convergenceOverrides)} (${override.reason})`);
|
|
197
|
+
}
|
|
198
|
+
writeFileSync(workspaceYamlPath, addOverrides(workspaceYamlBefore.content, new Map(outstanding.map((override) => [override.packageName, override.version])), { convergence: convergenceOverrides }));
|
|
199
|
+
const withOverrides = resolveAndCheck("the overrides");
|
|
200
|
+
if (withOverrides === null) {
|
|
201
|
+
revertTo(withoutOverridesSnapshot);
|
|
202
|
+
return unchanged("reverted", changeCount);
|
|
203
|
+
}
|
|
204
|
+
// Overrides are scaffolding first: if pnpm holds the result without them they
|
|
205
|
+
// are removed, and if it does not they go back with the reason recorded.
|
|
206
|
+
log("Removing the overrides and re-resolving to check the result holds:");
|
|
207
|
+
restoreFiles([workspaceYamlBefore]);
|
|
208
|
+
if (resolve() !== 0) {
|
|
209
|
+
log(" `pnpm dedupe` failed without the overrides — reverting");
|
|
210
|
+
revertTo(withoutOverridesSnapshot);
|
|
211
|
+
return unchanged("reverted", changeCount);
|
|
212
|
+
}
|
|
213
|
+
const after = readState();
|
|
214
|
+
const returnedDuplicates = diffDuplicates(withOverrides.duplicates, after.duplicates).added;
|
|
215
|
+
const returnedReuses = [...after.reuses].filter((key) => !withOverrides.reuses.has(key));
|
|
216
|
+
if (returnedDuplicates.length > 0 || returnedReuses.length > 0) {
|
|
217
|
+
writeFileSync(workspaceYamlPath, addOverrides(workspaceYamlBefore.content, new Map(outstanding.map((override) => [
|
|
218
|
+
override.packageName,
|
|
219
|
+
override.version,
|
|
220
|
+
])), {
|
|
221
|
+
convergence: convergenceOverrides,
|
|
222
|
+
comment: stickyOverrideReason.join("\n"),
|
|
223
|
+
}));
|
|
224
|
+
if (resolve() !== 0) {
|
|
225
|
+
log(" `pnpm dedupe` failed with the overrides back — reverting");
|
|
226
|
+
revertTo(withoutOverridesSnapshot);
|
|
227
|
+
return unchanged("reverted", changeCount);
|
|
228
|
+
}
|
|
229
|
+
log(` Kept in ${workspaceYamlPath}, with the same explanation as a comment:`);
|
|
230
|
+
for (const override of outstanding) {
|
|
231
|
+
log(` ${describeOverride(override, convergenceOverrides)}`);
|
|
232
|
+
}
|
|
233
|
+
for (const line of stickyOverrideReason) {
|
|
234
|
+
log(` ${line}`);
|
|
235
|
+
}
|
|
236
|
+
return {
|
|
237
|
+
status: "kept-overrides",
|
|
238
|
+
before,
|
|
239
|
+
after: readDuplicates(lockPath),
|
|
240
|
+
stickyOverrides: outstanding,
|
|
241
|
+
plannedChangeCount: changeCount,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
return {
|
|
245
|
+
status: "applied",
|
|
246
|
+
before,
|
|
247
|
+
after: after.duplicates,
|
|
248
|
+
stickyOverrides: [],
|
|
249
|
+
plannedChangeCount: changeCount,
|
|
250
|
+
};
|
|
251
|
+
};
|
|
252
|
+
//# sourceMappingURL=applyClusterFixes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"applyClusterFixes.js","sourceRoot":"","sources":["../src/applyClusterFixes.ts"],"names":[],"mappings":"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"}
|