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.
Files changed (198) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +218 -0
  3. package/dist/applyClusterFixes.js +252 -0
  4. package/dist/applyClusterFixes.js.map +1 -0
  5. package/dist/applyClusterFixes.test.js +474 -0
  6. package/dist/applyClusterFixes.test.js.map +1 -0
  7. package/dist/bin/pnpm-dedupe.js +35 -0
  8. package/dist/bin/pnpm-dedupe.js.map +1 -0
  9. package/dist/bin/pnpm-why-duplicate.js +23 -0
  10. package/dist/bin/pnpm-why-duplicate.js.map +1 -0
  11. package/dist/bin/pnpm-why-duplicate.test.js +57 -0
  12. package/dist/bin/pnpm-why-duplicate.test.js.map +1 -0
  13. package/dist/dedupe.js +86 -0
  14. package/dist/dedupe.js.map +1 -0
  15. package/dist/dedupeCheckConsistency.test.js +149 -0
  16. package/dist/dedupeCheckConsistency.test.js.map +1 -0
  17. package/dist/dedupeEndToEnd.test.js +121 -0
  18. package/dist/dedupeEndToEnd.test.js.map +1 -0
  19. package/dist/dedupePeers.test.js +105 -0
  20. package/dist/dedupePeers.test.js.map +1 -0
  21. package/dist/definitions/applyClusterFixes.d.ts +24 -0
  22. package/dist/definitions/applyClusterFixes.d.ts.map +1 -0
  23. package/dist/definitions/applyClusterFixes.test.d.ts +2 -0
  24. package/dist/definitions/applyClusterFixes.test.d.ts.map +1 -0
  25. package/dist/definitions/bin/pnpm-dedupe.d.ts +3 -0
  26. package/dist/definitions/bin/pnpm-dedupe.d.ts.map +1 -0
  27. package/dist/definitions/bin/pnpm-why-duplicate.d.ts +3 -0
  28. package/dist/definitions/bin/pnpm-why-duplicate.d.ts.map +1 -0
  29. package/dist/definitions/bin/pnpm-why-duplicate.test.d.ts +2 -0
  30. package/dist/definitions/bin/pnpm-why-duplicate.test.d.ts.map +1 -0
  31. package/dist/definitions/dedupe.d.ts +15 -0
  32. package/dist/definitions/dedupe.d.ts.map +1 -0
  33. package/dist/definitions/dedupeCheckConsistency.test.d.ts +2 -0
  34. package/dist/definitions/dedupeCheckConsistency.test.d.ts.map +1 -0
  35. package/dist/definitions/dedupeEndToEnd.test.d.ts +2 -0
  36. package/dist/definitions/dedupeEndToEnd.test.d.ts.map +1 -0
  37. package/dist/definitions/dedupePeers.test.d.ts +2 -0
  38. package/dist/definitions/dedupePeers.test.d.ts.map +1 -0
  39. package/dist/definitions/displayMany.d.ts +16 -0
  40. package/dist/definitions/displayMany.d.ts.map +1 -0
  41. package/dist/definitions/displayMany.test.d.ts +2 -0
  42. package/dist/definitions/displayMany.test.d.ts.map +1 -0
  43. package/dist/definitions/helpers/buildIdentifiedFixesMap.test.d.ts +2 -0
  44. package/dist/definitions/helpers/buildIdentifiedFixesMap.test.d.ts.map +1 -0
  45. package/dist/definitions/helpers/buildPnpmPackagesMap.d.ts +13 -0
  46. package/dist/definitions/helpers/buildPnpmPackagesMap.d.ts.map +1 -0
  47. package/dist/definitions/helpers/buildPnpmPackagesMap.test.d.ts +2 -0
  48. package/dist/definitions/helpers/buildPnpmPackagesMap.test.d.ts.map +1 -0
  49. package/dist/definitions/helpers/collectDependentRanges.d.ts +22 -0
  50. package/dist/definitions/helpers/collectDependentRanges.d.ts.map +1 -0
  51. package/dist/definitions/helpers/collectDependentRanges.test.d.ts +2 -0
  52. package/dist/definitions/helpers/collectDependentRanges.test.d.ts.map +1 -0
  53. package/dist/definitions/helpers/collectPnpmDependents.d.ts +8 -0
  54. package/dist/definitions/helpers/collectPnpmDependents.d.ts.map +1 -0
  55. package/dist/definitions/helpers/collectPnpmDependents.test.d.ts +2 -0
  56. package/dist/definitions/helpers/collectPnpmDependents.test.d.ts.map +1 -0
  57. package/dist/definitions/helpers/duplicateSnapshot.d.ts +3 -0
  58. package/dist/definitions/helpers/duplicateSnapshot.d.ts.map +1 -0
  59. package/dist/definitions/helpers/parsePnpmLockPackages.d.ts +44 -0
  60. package/dist/definitions/helpers/parsePnpmLockPackages.d.ts.map +1 -0
  61. package/dist/definitions/helpers/parsePnpmLockPackages.test.d.ts +2 -0
  62. package/dist/definitions/helpers/parsePnpmLockPackages.test.d.ts.map +1 -0
  63. package/dist/definitions/helpers/pnpmProtocol.d.ts +2 -0
  64. package/dist/definitions/helpers/pnpmProtocol.d.ts.map +1 -0
  65. package/dist/definitions/helpers/pnpmVersion.d.ts +11 -0
  66. package/dist/definitions/helpers/pnpmVersion.d.ts.map +1 -0
  67. package/dist/definitions/helpers/pnpmWorkspaceYaml.d.ts +23 -0
  68. package/dist/definitions/helpers/pnpmWorkspaceYaml.d.ts.map +1 -0
  69. package/dist/definitions/helpers/pnpmWorkspaceYaml.test.d.ts +2 -0
  70. package/dist/definitions/helpers/pnpmWorkspaceYaml.test.d.ts.map +1 -0
  71. package/dist/definitions/helpers/projectDir.d.ts +3 -0
  72. package/dist/definitions/helpers/projectDir.d.ts.map +1 -0
  73. package/dist/definitions/helpers/readInstalledManifest.d.ts +32 -0
  74. package/dist/definitions/helpers/readInstalledManifest.d.ts.map +1 -0
  75. package/dist/definitions/helpers/readInstalledManifest.test.d.ts +2 -0
  76. package/dist/definitions/helpers/readInstalledManifest.test.d.ts.map +1 -0
  77. package/dist/definitions/helpers/runPnpm.d.ts +11 -0
  78. package/dist/definitions/helpers/runPnpm.d.ts.map +1 -0
  79. package/dist/definitions/helpers/tempProjects.d.ts +8 -0
  80. package/dist/definitions/helpers/tempProjects.d.ts.map +1 -0
  81. package/dist/definitions/helpers/toLockstepGraph.d.ts +12 -0
  82. package/dist/definitions/helpers/toLockstepGraph.d.ts.map +1 -0
  83. package/dist/definitions/helpers/toLockstepGraph.test.d.ts +2 -0
  84. package/dist/definitions/helpers/toLockstepGraph.test.d.ts.map +1 -0
  85. package/dist/definitions/helpers/versionsSnapshot.d.ts +3 -0
  86. package/dist/definitions/helpers/versionsSnapshot.d.ts.map +1 -0
  87. package/dist/definitions/identifyClusterFixes.d.ts +7 -0
  88. package/dist/definitions/identifyClusterFixes.d.ts.map +1 -0
  89. package/dist/definitions/identifyClusterFixes.test.d.ts +2 -0
  90. package/dist/definitions/identifyClusterFixes.test.d.ts.map +1 -0
  91. package/dist/definitions/identifyResolutionFixes.test.d.ts +2 -0
  92. package/dist/definitions/identifyResolutionFixes.test.d.ts.map +1 -0
  93. package/dist/definitions/index.d.ts +25 -0
  94. package/dist/definitions/index.d.ts.map +1 -0
  95. package/dist/definitions/pnpmLockTypes.d.ts +42 -0
  96. package/dist/definitions/pnpmLockTypes.d.ts.map +1 -0
  97. package/dist/definitions/readPnpmLock.d.ts +3 -0
  98. package/dist/definitions/readPnpmLock.d.ts.map +1 -0
  99. package/dist/displayMany.js +56 -0
  100. package/dist/displayMany.js.map +1 -0
  101. package/dist/displayMany.test.js +146 -0
  102. package/dist/displayMany.test.js.map +1 -0
  103. package/dist/helpers/buildIdentifiedFixesMap.test.js +27 -0
  104. package/dist/helpers/buildIdentifiedFixesMap.test.js.map +1 -0
  105. package/dist/helpers/buildPnpmPackagesMap.js +23 -0
  106. package/dist/helpers/buildPnpmPackagesMap.js.map +1 -0
  107. package/dist/helpers/buildPnpmPackagesMap.test.js +68 -0
  108. package/dist/helpers/buildPnpmPackagesMap.test.js.map +1 -0
  109. package/dist/helpers/collectDependentRanges.js +118 -0
  110. package/dist/helpers/collectDependentRanges.js.map +1 -0
  111. package/dist/helpers/collectDependentRanges.test.js +211 -0
  112. package/dist/helpers/collectDependentRanges.test.js.map +1 -0
  113. package/dist/helpers/collectPnpmDependents.js +55 -0
  114. package/dist/helpers/collectPnpmDependents.js.map +1 -0
  115. package/dist/helpers/collectPnpmDependents.test.js +78 -0
  116. package/dist/helpers/collectPnpmDependents.test.js.map +1 -0
  117. package/dist/helpers/duplicateSnapshot.js +5 -0
  118. package/dist/helpers/duplicateSnapshot.js.map +1 -0
  119. package/dist/helpers/parsePnpmLockPackages.js +62 -0
  120. package/dist/helpers/parsePnpmLockPackages.js.map +1 -0
  121. package/dist/helpers/parsePnpmLockPackages.test.js +62 -0
  122. package/dist/helpers/parsePnpmLockPackages.test.js.map +1 -0
  123. package/dist/helpers/pnpmProtocol.js +2 -0
  124. package/dist/helpers/pnpmProtocol.js.map +1 -0
  125. package/dist/helpers/pnpmVersion.js +23 -0
  126. package/dist/helpers/pnpmVersion.js.map +1 -0
  127. package/dist/helpers/pnpmWorkspaceYaml.js +48 -0
  128. package/dist/helpers/pnpmWorkspaceYaml.js.map +1 -0
  129. package/dist/helpers/pnpmWorkspaceYaml.test.js +91 -0
  130. package/dist/helpers/pnpmWorkspaceYaml.test.js.map +1 -0
  131. package/dist/helpers/projectDir.js +10 -0
  132. package/dist/helpers/projectDir.js.map +1 -0
  133. package/dist/helpers/readInstalledManifest.js +139 -0
  134. package/dist/helpers/readInstalledManifest.js.map +1 -0
  135. package/dist/helpers/readInstalledManifest.test.js +83 -0
  136. package/dist/helpers/readInstalledManifest.test.js.map +1 -0
  137. package/dist/helpers/runPnpm.js +16 -0
  138. package/dist/helpers/runPnpm.js.map +1 -0
  139. package/dist/helpers/tempProjects.js +85 -0
  140. package/dist/helpers/tempProjects.js.map +1 -0
  141. package/dist/helpers/toLockstepGraph.js +34 -0
  142. package/dist/helpers/toLockstepGraph.js.map +1 -0
  143. package/dist/helpers/toLockstepGraph.test.js +81 -0
  144. package/dist/helpers/toLockstepGraph.test.js.map +1 -0
  145. package/dist/helpers/versionsSnapshot.js +5 -0
  146. package/dist/helpers/versionsSnapshot.js.map +1 -0
  147. package/dist/identifyClusterFixes.js +53 -0
  148. package/dist/identifyClusterFixes.js.map +1 -0
  149. package/dist/identifyClusterFixes.test.js +307 -0
  150. package/dist/identifyClusterFixes.test.js.map +1 -0
  151. package/dist/identifyResolutionFixes.test.js +49 -0
  152. package/dist/identifyResolutionFixes.test.js.map +1 -0
  153. package/dist/index.js +86 -0
  154. package/dist/index.js.map +1 -0
  155. package/dist/pnpmLockTypes.js +7 -0
  156. package/dist/pnpmLockTypes.js.map +1 -0
  157. package/dist/readPnpmLock.js +6 -0
  158. package/dist/readPnpmLock.js.map +1 -0
  159. package/package.json +71 -0
  160. package/src/applyClusterFixes.test.ts +560 -0
  161. package/src/applyClusterFixes.ts +466 -0
  162. package/src/bin/pnpm-dedupe.ts +44 -0
  163. package/src/bin/pnpm-why-duplicate.test.ts +79 -0
  164. package/src/bin/pnpm-why-duplicate.ts +34 -0
  165. package/src/dedupe.ts +124 -0
  166. package/src/dedupeCheckConsistency.test.ts +218 -0
  167. package/src/dedupeEndToEnd.test.ts +168 -0
  168. package/src/dedupePeers.test.ts +175 -0
  169. package/src/displayMany.test.ts +208 -0
  170. package/src/displayMany.ts +104 -0
  171. package/src/helpers/buildIdentifiedFixesMap.test.ts +33 -0
  172. package/src/helpers/buildPnpmPackagesMap.test.ts +119 -0
  173. package/src/helpers/buildPnpmPackagesMap.ts +53 -0
  174. package/src/helpers/collectDependentRanges.test.ts +311 -0
  175. package/src/helpers/collectDependentRanges.ts +185 -0
  176. package/src/helpers/collectPnpmDependents.test.ts +112 -0
  177. package/src/helpers/collectPnpmDependents.ts +79 -0
  178. package/src/helpers/duplicateSnapshot.ts +18 -0
  179. package/src/helpers/parsePnpmLockPackages.test.ts +93 -0
  180. package/src/helpers/parsePnpmLockPackages.ts +99 -0
  181. package/src/helpers/pnpmProtocol.ts +12 -0
  182. package/src/helpers/pnpmVersion.ts +28 -0
  183. package/src/helpers/pnpmWorkspaceYaml.test.ts +140 -0
  184. package/src/helpers/pnpmWorkspaceYaml.ts +77 -0
  185. package/src/helpers/projectDir.ts +14 -0
  186. package/src/helpers/readInstalledManifest.test.ts +124 -0
  187. package/src/helpers/readInstalledManifest.ts +202 -0
  188. package/src/helpers/runPnpm.ts +34 -0
  189. package/src/helpers/tempProjects.ts +94 -0
  190. package/src/helpers/toLockstepGraph.test.ts +98 -0
  191. package/src/helpers/toLockstepGraph.ts +52 -0
  192. package/src/helpers/versionsSnapshot.ts +9 -0
  193. package/src/identifyClusterFixes.test.ts +430 -0
  194. package/src/identifyClusterFixes.ts +114 -0
  195. package/src/identifyResolutionFixes.test.ts +82 -0
  196. package/src/index.ts +159 -0
  197. package/src/pnpmLockTypes.ts +44 -0
  198. package/src/readPnpmLock.ts +7 -0
package/dist/dedupe.js ADDED
@@ -0,0 +1,86 @@
1
+ import { countDuplicatedPackages, createPackageFilter, diffVersionsSnapshots, renderDedupeSummary, } from "pm-dedup-core";
2
+ import { applyClusterFixes } from "./applyClusterFixes.js";
3
+ import { lockPathOf, resolvePnpmProjectDir } from "./helpers/projectDir.js";
4
+ import { runPnpm } from "./helpers/runPnpm.js";
5
+ import { readVersionsSnapshot } from "./helpers/versionsSnapshot.js";
6
+ const residualsMessage = "`pnpm dedupe` would also change the lockfile.";
7
+ // `pnpm dedupe` merges everything it can and knows nothing of the filter, so
8
+ // running it would undo the point of a filtered run.
9
+ const filteredResidualsMessage = "`pnpm dedupe` was not run: it ignores the package filter. Run `pnpm dedupe` yourself, or this command without a filter, to finish the rest.";
10
+ /**
11
+ * `pnpm dedupe` only merges what it can resolve to a single version on its own:
12
+ * it never widens a workspace range, and it never repoints an edge that resolved
13
+ * past a version the family already carries. Cluster fixes cover exactly that
14
+ * gap, so they run first and `pnpm dedupe` finishes the residuals.
15
+ */
16
+ export function dedupe({ mode = "apply", convergenceOverrides = true, filter, } = {}) {
17
+ const dryRun = mode !== "apply";
18
+ const projectDir = resolvePnpmProjectDir();
19
+ if (projectDir === null)
20
+ return;
21
+ const lockPath = lockPathOf(projectDir);
22
+ const filtered = !createPackageFilter(filter).selectsEverything;
23
+ // Read before anything runs: both the cluster pass and `pnpm dedupe` rewrite
24
+ // the lockfile, and the summary compares the whole run against this.
25
+ const before = readVersionsSnapshot(lockPath);
26
+ // What the run merged, reported once the lockfile has settled — `pnpm dedupe`
27
+ // included, since it is part of what this command does.
28
+ const summarize = () => {
29
+ const after = readVersionsSnapshot(lockPath);
30
+ renderDedupeSummary({
31
+ deduped: diffVersionsSnapshots(before, after),
32
+ remainingDuplicates: countDuplicatedPackages(after),
33
+ whyCommand: "pnpm-why-duplicate",
34
+ });
35
+ };
36
+ // Read-only, so it can run before the cluster pass. It has to, for a dry run:
37
+ // the plan report names the residuals, and only this knows about them. A
38
+ // filtered run does not run `pnpm dedupe` at all, so what it would change is
39
+ // not this command's business.
40
+ const probe = dryRun && !filtered
41
+ ? runPnpm(["dedupe", "--check"], { cwd: projectDir })
42
+ : null;
43
+ const residuals = (() => {
44
+ if (filtered)
45
+ return filteredResidualsMessage;
46
+ if (probe !== null && probe.status !== 0)
47
+ return residualsMessage;
48
+ return undefined;
49
+ })();
50
+ const outcome = applyClusterFixes({
51
+ projectDir,
52
+ dryRun,
53
+ convergenceOverrides,
54
+ filter,
55
+ packageManagerResiduals: residuals,
56
+ });
57
+ if (outcome.status === "kept-overrides") {
58
+ console.log(`${outcome.stickyOverrides.length} override(s) left in pnpm-workspace.yaml — see the comment above them`);
59
+ }
60
+ if (mode === "check") {
61
+ process.exitCode =
62
+ outcome.plannedChangeCount > 0 || (probe !== null && probe.status !== 0)
63
+ ? 1
64
+ : 0;
65
+ return;
66
+ }
67
+ if (mode === "dry-run")
68
+ return;
69
+ if (filtered) {
70
+ // Applying an override needs a real resolution, and `pnpm dedupe` is the
71
+ // only one that performs it — so a filtered run that changed something has
72
+ // already merged whatever else pnpm could reach on its own.
73
+ if (outcome.status === "applied" || outcome.status === "kept-overrides") {
74
+ console.log("`pnpm dedupe` ran to apply the fixes above and merged what it could on its own: the filter bounds the edits made here, not pnpm's resolution.");
75
+ }
76
+ console.log(filteredResidualsMessage);
77
+ summarize();
78
+ return;
79
+ }
80
+ const result = runPnpm(["dedupe"], { cwd: projectDir });
81
+ if (result.status !== null) {
82
+ process.exitCode = result.status;
83
+ }
84
+ summarize();
85
+ }
86
+ //# sourceMappingURL=dedupe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dedupe.js","sourceRoot":"","sources":["../src/dedupe.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,149 @@
1
+ import { describe, it } from "bun:test";
2
+ import { deepStrictEqual, ok, strictEqual } from "node:assert/strict";
3
+ import { spawnSync } from "node:child_process";
4
+ import { readFileSync, rmSync } from "node:fs";
5
+ import { join } from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+ import { buildIdentifiedFixesMap } from "pm-dedup-core";
8
+ import { buildPnpmPackagesMap, collectPnpmDependents, filterDuplicatesPnpmPackagesMap, parsePnpmLockPackages, readPnpmLock, } from "./index.js";
9
+ // These tests shell out to the real `pnpm` and require either network access or
10
+ // a warm pnpm store; they are skipped when pnpm is not on PATH. `--lockfile-only`
11
+ // keeps pnpm from writing node_modules, and `--frozen-lockfile` / `--check` never
12
+ // rewrite the lockfile, so pnpm runs against the committed fixtures in place
13
+ // without modifying them (asserted below). `pnpm dedupe --check` only flags the
14
+ // safely-mergeable subset of duplicates, so the invariant is: everything pnpm
15
+ // would dedupe is also reported by our listing.
16
+ const pnpmAvailable = spawnSync("pnpm", ["--version"], { encoding: "utf8" }).status === 0;
17
+ const fixturePath = (scenario) => fileURLToPath(new URL(`../test/fixtures/${scenario}`, import.meta.url));
18
+ const runPnpm = (cwd, args) => {
19
+ const result = spawnSync("pnpm", [...args, "--lockfile-only", "--prefer-offline", "--ignore-scripts"], { cwd, encoding: "utf8", timeout: 120_000 });
20
+ if (result.error) {
21
+ throw result.error;
22
+ }
23
+ return {
24
+ status: result.status,
25
+ output: `${result.stdout}\n${result.stderr}`,
26
+ };
27
+ };
28
+ // `pnpm dedupe --check` prints a tree of changes; each merged dependency is on a
29
+ // branch line `├── name fromVersion → toVersion`. We collect those names.
30
+ const parseDedupedPackages = (output) => {
31
+ const names = new Set();
32
+ for (const line of output.split("\n")) {
33
+ if (!line.includes("→"))
34
+ continue;
35
+ const name = line
36
+ .replace(/^[\s│├└─]+/u, "")
37
+ .trim()
38
+ .split(/\s+/u)[0];
39
+ if (name) {
40
+ names.add(name);
41
+ }
42
+ }
43
+ return [...names].toSorted();
44
+ };
45
+ const duplicateNames = (scenario) => {
46
+ const lock = readPnpmLock(join(fixturePath(scenario), "pnpm-lock.yaml"));
47
+ const duplicates = filterDuplicatesPnpmPackagesMap(buildPnpmPackagesMap(parsePnpmLockPackages(lock)));
48
+ return Object.keys(duplicates).toSorted();
49
+ };
50
+ const fixTargets = (scenario, packageName) => {
51
+ const lock = readPnpmLock(join(fixturePath(scenario), "pnpm-lock.yaml"));
52
+ const duplicates = filterDuplicatesPnpmPackagesMap(buildPnpmPackagesMap(parsePnpmLockPackages(lock)));
53
+ const fixes = buildIdentifiedFixesMap(duplicates, collectPnpmDependents(lock, Object.keys(duplicates)));
54
+ return (fixes.get(packageName) ?? []).map((fix) => fix.to);
55
+ };
56
+ const lockContent = (dir) => readFileSync(join(dir, "pnpm-lock.yaml"), "utf8");
57
+ // Defensive: `--lockfile-only` should never create node_modules, but if a future
58
+ // pnpm version does, do not leave it in the committed fixture directory.
59
+ const assertPristine = (dir, lockBefore) => {
60
+ strictEqual(lockContent(dir), lockBefore, "pnpm must not change the lockfile");
61
+ rmSync(join(dir, "node_modules"), { recursive: true, force: true });
62
+ };
63
+ const suite = pnpmAvailable ? describe : describe.skip;
64
+ suite("pnpm dedupe --check vs listDuplicates", () => {
65
+ // `duplicated-typescript-eslint-dedupe-peers` is the same dependency with
66
+ // `dedupePeers: true`, which flattens the peer suffixes in the lockfile: pnpm
67
+ // must still merge the same packages, and we must still list them.
68
+ const mergeable = [
69
+ {
70
+ scenario: "duplicated-typescript-eslint",
71
+ expectedFlagged: [
72
+ "@typescript-eslint/tsconfig-utils",
73
+ "@typescript-eslint/types",
74
+ ],
75
+ },
76
+ {
77
+ scenario: "duplicated-typescript-eslint-dedupe-peers",
78
+ expectedFlagged: [
79
+ "@typescript-eslint/tsconfig-utils",
80
+ "@typescript-eslint/types",
81
+ ],
82
+ },
83
+ ];
84
+ for (const { scenario, expectedFlagged } of mergeable) {
85
+ it(`flags the mergeable subset of ${scenario}, all of which listDuplicates reports`, () => {
86
+ const dir = fixturePath(scenario);
87
+ const lockBefore = lockContent(dir);
88
+ const install = runPnpm(dir, ["install", "--frozen-lockfile"]);
89
+ strictEqual(install.status, 0, install.output);
90
+ assertPristine(dir, lockBefore);
91
+ const check = runPnpm(dir, ["dedupe", "--check"]);
92
+ ok(check.status !== 0, `dedupe --check should flag issues\n${check.output}`);
93
+ assertPristine(dir, lockBefore);
94
+ deepStrictEqual(parseDedupedPackages(check.output), expectedFlagged);
95
+ const duplicates = duplicateNames(scenario);
96
+ for (const name of expectedFlagged) {
97
+ ok(duplicates.includes(name), `listDuplicates should report ${name} that pnpm dedupe flags`);
98
+ }
99
+ }, 180_000);
100
+ }
101
+ // Duplicates pnpm does not merge: `dedupe --check` exits 0 and flags nothing,
102
+ // yet listDuplicates still reports the duplicate. The `mergeable-alias*`
103
+ // fixtures go one step further: every declared range accepts the aliased
104
+ // 5.0.7 pin, so we do identify a merge target — one pnpm will never apply,
105
+ // because merging means downgrading the range from 5.3.1.
106
+ // `wildcard-not-reused` is the widest case: a `*` range resolved to 0.87.0
107
+ // instead of the installed 0.84.5, duplicating the metro family (see
108
+ // wildcardNotReused.test.ts). pnpm will not undo it either.
109
+ const unmergeableByPnpm = [
110
+ {
111
+ scenario: "duplicated-babel-frame",
112
+ expectedDuplicate: "@babel/code-frame",
113
+ },
114
+ {
115
+ scenario: "duplicated-printable-shell-command",
116
+ expectedDuplicate: "printable-shell-command",
117
+ },
118
+ {
119
+ scenario: "mergeable-alias",
120
+ expectedDuplicate: "printable-shell-command",
121
+ expectedFixTargets: ["printable-shell-command@5.0.7"],
122
+ },
123
+ {
124
+ scenario: "mergeable-alias-dedupe-peers",
125
+ expectedDuplicate: "printable-shell-command",
126
+ expectedFixTargets: ["printable-shell-command@5.0.7"],
127
+ },
128
+ {
129
+ scenario: "wildcard-not-reused",
130
+ expectedDuplicate: "metro-config",
131
+ },
132
+ ];
133
+ for (const { scenario, expectedDuplicate, expectedFixTargets, } of unmergeableByPnpm) {
134
+ it(`flags nothing for ${scenario} but still lists ${expectedDuplicate}`, () => {
135
+ const dir = fixturePath(scenario);
136
+ const lockBefore = lockContent(dir);
137
+ const install = runPnpm(dir, ["install", "--frozen-lockfile"]);
138
+ strictEqual(install.status, 0, install.output);
139
+ assertPristine(dir, lockBefore);
140
+ const check = runPnpm(dir, ["dedupe", "--check"]);
141
+ strictEqual(check.status, 0, `dedupe --check should find nothing to merge\n${check.output}`);
142
+ assertPristine(dir, lockBefore);
143
+ deepStrictEqual(parseDedupedPackages(check.output), []);
144
+ ok(duplicateNames(scenario).includes(expectedDuplicate));
145
+ deepStrictEqual(fixTargets(scenario, expectedDuplicate), expectedFixTargets ?? []);
146
+ }, 180_000);
147
+ }
148
+ });
149
+ //# sourceMappingURL=dedupeCheckConsistency.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dedupeCheckConsistency.test.js","sourceRoot":"","sources":["../src/dedupeCheckConsistency.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,121 @@
1
+ import { afterEach, describe, it } from "bun:test";
2
+ import { deepStrictEqual, ok, strictEqual } from "node:assert/strict";
3
+ import { spawnSync } from "node:child_process";
4
+ import { cpSync, existsSync, readFileSync, writeFileSync } from "node:fs";
5
+ import { join } from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+ import { buildPnpmPackagesMap } from "./helpers/buildPnpmPackagesMap.js";
8
+ import { readDuplicateSnapshot } from "./helpers/duplicateSnapshot.js";
9
+ import { parsePnpmLockPackages } from "./helpers/parsePnpmLockPackages.js";
10
+ import { createTempProjects } from "./helpers/tempProjects.js";
11
+ import { readPnpmLock } from "./readPnpmLock.js";
12
+ // The shipped tool against a real pnpm: a copy of the
13
+ // `exact-pin-forces-downgrade` fixture, a real `pnpm install`, then the built
14
+ // `pnpm-dedupe` bin as a user would run it. Every other test stubs the
15
+ // resolution step, so this is the only one that can tell whether pnpm holds the
16
+ // deduplicated result on its own once the transient override is removed again —
17
+ // the step the tool takes before reporting "applied" — and pnpm is asked for its
18
+ // own account of the tree at the end rather than only our reading of it.
19
+ //
20
+ // Requires network access or a warm pnpm store; skipped when pnpm is not on PATH.
21
+ const pnpmAvailable = spawnSync("pnpm", ["--version"], { encoding: "utf8" }).status === 0;
22
+ const fixtureDir = fileURLToPath(new URL("../test/fixtures/exact-pin-forces-downgrade", import.meta.url));
23
+ // the built bin, the one the `bin` field points at: from `src` it is one level
24
+ // up, and the compiled copy of this file sits beside it
25
+ const dedupeBin = (() => {
26
+ const built = ["../dist/bin/pnpm-dedupe.js", "./bin/pnpm-dedupe.js"]
27
+ .map((binPath) => fileURLToPath(new URL(binPath, import.meta.url)))
28
+ .find((binPath) => existsSync(binPath));
29
+ if (!built) {
30
+ throw new Error("pnpm-dedupe is not built: run `bun run build` first");
31
+ }
32
+ return built;
33
+ })();
34
+ // expo-camera's peers (expo, react-native) would drag a few hundred packages in
35
+ // and duplicates of their own, none of which this fixture is about.
36
+ const workspaceYamlContent = "autoInstallPeers: false\n";
37
+ const projects = createTempProjects("pnpm-dedup-e2e-");
38
+ afterEach(projects.cleanup);
39
+ const run = (cwd, command, args) => {
40
+ const result = spawnSync(command, args, {
41
+ cwd,
42
+ encoding: "utf8",
43
+ timeout: 120_000,
44
+ });
45
+ if (result.error) {
46
+ throw result.error;
47
+ }
48
+ return {
49
+ status: result.status,
50
+ output: `${result.stdout}\n${result.stderr}`,
51
+ };
52
+ };
53
+ const install = (cwd, ...args) => run(cwd, "pnpm", [
54
+ "install",
55
+ "--prefer-offline",
56
+ "--ignore-scripts",
57
+ ...args,
58
+ ]);
59
+ // node, as the bin's own shebang asks for
60
+ const dedupe = (cwd, ...args) => run(cwd, "node", [dedupeBin, ...args]);
61
+ const lockPath = (dir) => join(dir, "pnpm-lock.yaml");
62
+ const resolutionsOf = (dir, packageName) => {
63
+ const lock = readPnpmLock(lockPath(dir));
64
+ const packagesMap = buildPnpmPackagesMap(parsePnpmLockPackages(lock));
65
+ return (packagesMap[packageName] ?? []).map(({ resolution }) => resolution);
66
+ };
67
+ const suite = pnpmAvailable ? describe : describe.skip;
68
+ suite("pnpm-dedupe end to end", () => {
69
+ it("converges the cluster and leaves pnpm holding it without an override", () => {
70
+ const dir = projects.create();
71
+ // only the manifest: the lockfile has to be the real resolution pnpm writes
72
+ // here, not the trimmed one the unit-test fixture commits
73
+ cpSync(join(fixtureDir, "package.json"), join(dir, "package.json"));
74
+ writeFileSync(join(dir, "pnpm-workspace.yaml"), workspaceYamlContent);
75
+ const installed = install(dir);
76
+ strictEqual(installed.status, 0, installed.output);
77
+ // `@yudiel/react-qr-scanner` pins barcode-detector at exactly 3.0.3 while
78
+ // expo-camera's `^3.0.0` resolved to 3.2.2, and the pair drags zxing-wasm
79
+ // along: four resolutions for two packages.
80
+ const before = readDuplicateSnapshot(lockPath(dir));
81
+ strictEqual(before.size, 4, [...before].join(", "));
82
+ ok(before.has("barcode-detector@3.0.3"));
83
+ ok(before.has("barcode-detector@3.2.2"));
84
+ const applied = dedupe(dir);
85
+ strictEqual(applied.status, 0, applied.output);
86
+ // the tool's own verdict: the convergence override went in, came back out,
87
+ // and the result survived the re-resolution without it
88
+ ok(applied.output.includes('"barcode-detector@": "3.0.3"'), applied.output);
89
+ ok(applied.output.includes("Removing the overrides and re-resolving to check the result holds"), applied.output);
90
+ // what it deduped, named: two packages, one copy of each merged away
91
+ ok(applied.output.includes("Deduped 2 packages, 2 copies merged:"), applied.output);
92
+ ok(applied.output.includes("barcode-detector: 2 versions (3.0.3, 3.2.2) -> 1 version (3.0.3)"), applied.output);
93
+ ok(applied.output.includes("No duplicate left."), applied.output);
94
+ // what matters is the lockfile pnpm was left with
95
+ strictEqual(readDuplicateSnapshot(lockPath(dir)).size, 0);
96
+ deepStrictEqual(resolutionsOf(dir, "barcode-detector"), [
97
+ "barcode-detector@3.0.3",
98
+ ]);
99
+ // barcode-detector 3.2.2 pinned zxing-wasm at exactly 3.1.3; 3.0.3 asks for
100
+ // `^2.1.2`, so converging onto it has to bring the 2.x line back — a 3.1.3
101
+ // left standing would be a range the installed tree contradicts.
102
+ const zxing = resolutionsOf(dir, "zxing-wasm");
103
+ strictEqual(zxing.length, 1, zxing.join(", "));
104
+ ok(zxing[0]?.startsWith("zxing-wasm@2."), zxing.join(", "));
105
+ // no standing override is left behind, in a file otherwise untouched
106
+ strictEqual(readFileSync(join(dir, "pnpm-workspace.yaml"), "utf8"), workspaceYamlContent);
107
+ // and pnpm itself agrees, from the manifests alone
108
+ const frozen = install(dir, "--frozen-lockfile");
109
+ strictEqual(frozen.status, 0, frozen.output);
110
+ // pnpm's own account of the installed tree, not ours: one copy, reached
111
+ // from both dependents
112
+ const list = run(dir, "pnpm", ["list", "--depth", "Infinity"]);
113
+ strictEqual(list.status, 0, list.output);
114
+ ok(list.output.includes("barcode-detector@3.0.3 [deduped]"), list.output);
115
+ strictEqual(list.output.includes("barcode-detector@3.2.2"), false);
116
+ const check = dedupe(dir, "--check");
117
+ strictEqual(check.status, 0, check.output);
118
+ ok(check.output.includes("Nothing to dedupe."), check.output);
119
+ }, 180_000);
120
+ });
121
+ //# sourceMappingURL=dedupeEndToEnd.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dedupeEndToEnd.test.js","sourceRoot":"","sources":["../src/dedupeEndToEnd.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,105 @@
1
+ import { describe, it } from "bun:test";
2
+ import { deepStrictEqual, ok, strictEqual } from "node:assert/strict";
3
+ import { fileURLToPath } from "node:url";
4
+ import { buildIdentifiedFixesMap } from "pm-dedup-core";
5
+ import { parsePackageId } from "./helpers/parsePnpmLockPackages.js";
6
+ import { buildPnpmPackagesMap, collectPnpmDependents, filterDuplicatesPnpmPackagesMap, parsePnpmLockPackages, readPnpmLock, } from "./index.js";
7
+ // Both fixtures declare the same single dependency; only the `dedupePeers`
8
+ // setting differs. With it enabled pnpm writes peer suffixes as plain
9
+ // `name@version` instead of repeating the peer's own resolution, so snapshot
10
+ // ids lose their nesting:
11
+ // default: pkg@1(peer@2(eslint@10)(typescript@6))(eslint@10)(typescript@6)
12
+ // dedupePeers: pkg@1(peer@2)(eslint@10)(typescript@6)
13
+ const defaultScenario = "duplicated-typescript-eslint";
14
+ const dedupePeersScenario = "duplicated-typescript-eslint-dedupe-peers";
15
+ // `mergeable-alias` pins the same package twice, once through an npm alias
16
+ // (`npm:printable-shell-command@5.0.7`) and once through a range that also
17
+ // accepts that pin. It is the same pair: identical manifests, `dedupePeers`
18
+ // only set in `mergeable-alias-dedupe-peers`.
19
+ const mergeableDefaultScenario = "mergeable-alias";
20
+ const mergeableDedupePeersScenario = "mergeable-alias-dedupe-peers";
21
+ const lockFor = (scenario) => readPnpmLock(fileURLToPath(new URL(`../test/fixtures/${scenario}/pnpm-lock.yaml`, import.meta.url)));
22
+ const maxParenDepth = (id) => {
23
+ let depth = 0;
24
+ let max = 0;
25
+ for (const char of id) {
26
+ if (char === "(") {
27
+ depth += 1;
28
+ max = Math.max(max, depth);
29
+ }
30
+ else if (char === ")") {
31
+ depth -= 1;
32
+ }
33
+ }
34
+ return max;
35
+ };
36
+ const snapshotIds = (scenario) => Object.keys(lockFor(scenario).snapshots ?? {});
37
+ const duplicatesFor = (scenario) => filterDuplicatesPnpmPackagesMap(buildPnpmPackagesMap(parsePnpmLockPackages(lockFor(scenario))));
38
+ const fixesFor = (scenario) => {
39
+ const lock = lockFor(scenario);
40
+ const duplicates = duplicatesFor(scenario);
41
+ return buildIdentifiedFixesMap(duplicates, collectPnpmDependents(lock, Object.keys(duplicates)));
42
+ };
43
+ describe("dedupePeers lockfiles", () => {
44
+ it("uses a fixture generated with the setting enabled", () => {
45
+ strictEqual(lockFor(dedupePeersScenario).settings?.dedupePeers, true);
46
+ strictEqual(lockFor(defaultScenario).settings?.dedupePeers, undefined);
47
+ });
48
+ it("flattens the peer suffixes the default settings nest", () => {
49
+ ok(snapshotIds(defaultScenario).some((id) => maxParenDepth(id) > 1), "the default-settings fixture should contain nested peer suffixes");
50
+ deepStrictEqual(snapshotIds(dedupePeersScenario).filter((id) => maxParenDepth(id) > 1), []);
51
+ });
52
+ it("parses name and version out of a flattened peer suffix", () => {
53
+ const suffixed = snapshotIds(dedupePeersScenario).filter((id) => id.includes("("));
54
+ ok(suffixed.length > 0);
55
+ for (const id of suffixed) {
56
+ const { name, version } = parsePackageId(id);
57
+ ok(!name.includes("("), `name should not keep the suffix: ${id}`);
58
+ ok(/^\d/u.test(version), `version should be resolved: ${id}`);
59
+ }
60
+ });
61
+ it("groups every peer context of a resolution into installations", () => {
62
+ const { packages, installationsByResolution } = parsePnpmLockPackages(lockFor(dedupePeersScenario));
63
+ const utils = installationsByResolution.get("@typescript-eslint/utils@8.67.0");
64
+ ok(utils);
65
+ ok(utils.every((id) => id.startsWith("@typescript-eslint/utils@8.67.0")));
66
+ strictEqual([...installationsByResolution.keys()].every((resolution) => packages.has(resolution)), true);
67
+ });
68
+ it("reports the same duplicated packages as the default-settings fixture", () => {
69
+ deepStrictEqual(Object.keys(duplicatesFor(dedupePeersScenario)).toSorted(), Object.keys(duplicatesFor(defaultScenario)).toSorted());
70
+ });
71
+ it("resolves dependents for every duplicate, so fixes can be identified", () => {
72
+ const lock = lockFor(dedupePeersScenario);
73
+ const duplicates = duplicatesFor(dedupePeersScenario);
74
+ const packageNames = Object.keys(duplicates);
75
+ const dependents = collectPnpmDependents(lock, packageNames);
76
+ for (const name of packageNames) {
77
+ ok(dependents.get(name)?.length, `expected dependents for ${name}`);
78
+ }
79
+ // identifyResolutionFixes throws on a missing dependent, which is what a
80
+ // peer-suffix parsing regression would cause here.
81
+ const fixes = buildIdentifiedFixesMap(duplicates, dependents);
82
+ deepStrictEqual([...fixes.keys()].toSorted(), packageNames.toSorted());
83
+ });
84
+ });
85
+ describe("dedupePeers with an identified fix", () => {
86
+ it("flattens the peer suffixes of the pinned pair too", () => {
87
+ ok(snapshotIds(mergeableDefaultScenario).some((id) => maxParenDepth(id) > 1));
88
+ deepStrictEqual(snapshotIds(mergeableDedupePeersScenario).filter((id) => maxParenDepth(id) > 1), []);
89
+ });
90
+ it("identifies the version every declared range accepts", () => {
91
+ deepStrictEqual(fixesFor(mergeableDedupePeersScenario).get("printable-shell-command"), [
92
+ {
93
+ mergeableResolutions: [
94
+ "printable-shell-command@5.0.7",
95
+ "printable-shell-command@5.3.1",
96
+ ],
97
+ to: "printable-shell-command@5.0.7",
98
+ },
99
+ ]);
100
+ });
101
+ it("identifies the same fixes as the default-settings fixture", () => {
102
+ deepStrictEqual([...fixesFor(mergeableDedupePeersScenario)], [...fixesFor(mergeableDefaultScenario)]);
103
+ });
104
+ });
105
+ //# sourceMappingURL=dedupePeers.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dedupePeers.test.js","sourceRoot":"","sources":["../src/dedupePeers.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import type { ClusterFix, DuplicateSnapshot, PackageFilterOptions, PlannedOverride } from "pm-dedup-core";
2
+ export type ClusterApplyStatus = "applied" | "dry-run" | "kept-overrides" | "not-supported" | "nothing-to-do" | "reverted";
3
+ export interface ClusterApplyOutcome {
4
+ status: ClusterApplyStatus;
5
+ before: DuplicateSnapshot;
6
+ after: DuplicateSnapshot;
7
+ stickyOverrides: PlannedOverride[];
8
+ plannedChangeCount: number;
9
+ }
10
+ export interface ApplyClusterFixesOptions {
11
+ projectDir: string;
12
+ dryRun?: boolean;
13
+ log?: (message?: string) => void;
14
+ resolve?: () => number | null;
15
+ readFixes?: (projectDir: string) => ClusterFix[];
16
+ readDuplicates?: (lockPath: string) => DuplicateSnapshot;
17
+ pnpmVersion?: () => string | null;
18
+ convergenceOverrides?: boolean;
19
+ filter?: PackageFilterOptions;
20
+ packageManagerResiduals?: string;
21
+ color?: boolean;
22
+ }
23
+ export declare const applyClusterFixes: ({ projectDir, dryRun, log, resolve, readFixes, readDuplicates, pnpmVersion, convergenceOverrides, filter, packageManagerResiduals, color, }: ApplyClusterFixesOptions) => ClusterApplyOutcome;
24
+ //# sourceMappingURL=applyClusterFixes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"applyClusterFixes.d.ts","sourceRoot":"","sources":["../../src/applyClusterFixes.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAEV,UAAU,EACV,iBAAiB,EAEjB,oBAAoB,EAEpB,eAAe,EAEhB,MAAM,eAAe,CAAC;AAkBvB,MAAM,MAAM,kBAAkB,GAC1B,SAAS,GACT,SAAS,GACT,gBAAgB,GAChB,eAAe,GACf,eAAe,GACf,UAAU,CAAC;AAEf,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,kBAAkB,CAAC;IAC3B,MAAM,EAAE,iBAAiB,CAAC;IAC1B,KAAK,EAAE,iBAAiB,CAAC;IAEzB,eAAe,EAAE,eAAe,EAAE,CAAC;IAEnC,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,wBAAwB;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAEjC,OAAO,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;IAC9B,SAAS,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,UAAU,EAAE,CAAC;IACjD,cAAc,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,iBAAiB,CAAC;IACzD,WAAW,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;IAGlC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAG/B,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAG9B,uBAAuB,CAAC,EAAE,MAAM,CAAC;IAGjC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAkDD,eAAO,MAAM,iBAAiB,gJAiB3B,wBAAwB,KAAG,mBA6T7B,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=applyClusterFixes.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"applyClusterFixes.test.d.ts","sourceRoot":"","sources":["../../src/applyClusterFixes.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=pnpm-dedupe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pnpm-dedupe.d.ts","sourceRoot":"","sources":["../../../src/bin/pnpm-dedupe.ts"],"names":[],"mappings":""}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=pnpm-why-duplicate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pnpm-why-duplicate.d.ts","sourceRoot":"","sources":["../../../src/bin/pnpm-why-duplicate.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=pnpm-why-duplicate.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pnpm-why-duplicate.test.d.ts","sourceRoot":"","sources":["../../../src/bin/pnpm-why-duplicate.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,15 @@
1
+ import type { PackageFilterOptions } from "pm-dedup-core";
2
+ export type DedupeMode = "apply" | "check" | "dry-run";
3
+ export interface DedupeOptions {
4
+ mode?: DedupeMode;
5
+ convergenceOverrides?: boolean;
6
+ filter?: PackageFilterOptions;
7
+ }
8
+ /**
9
+ * `pnpm dedupe` only merges what it can resolve to a single version on its own:
10
+ * it never widens a workspace range, and it never repoints an edge that resolved
11
+ * past a version the family already carries. Cluster fixes cover exactly that
12
+ * gap, so they run first and `pnpm dedupe` finishes the residuals.
13
+ */
14
+ export declare function dedupe({ mode, convergenceOverrides, filter, }?: DedupeOptions): void;
15
+ //# sourceMappingURL=dedupe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dedupe.d.ts","sourceRoot":"","sources":["../../src/dedupe.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAQ1D,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG,SAAS,CAAC;AAEvD,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,UAAU,CAAC;IAIlB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAG/B,MAAM,CAAC,EAAE,oBAAoB,CAAC;CAC/B;AASD;;;;;GAKG;AACH,wBAAgB,MAAM,CAAC,EACrB,IAAc,EACd,oBAA2B,EAC3B,MAAM,GACP,GAAE,aAAkB,GAAG,IAAI,CA+E3B"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=dedupeCheckConsistency.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dedupeCheckConsistency.test.d.ts","sourceRoot":"","sources":["../../src/dedupeCheckConsistency.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=dedupeEndToEnd.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dedupeEndToEnd.test.d.ts","sourceRoot":"","sources":["../../src/dedupeEndToEnd.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=dedupePeers.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dedupePeers.test.d.ts","sourceRoot":"","sources":["../../src/dedupePeers.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,16 @@
1
+ import type { ClusterFix, DuplicatesReportTitle } from "pm-dedup-core";
2
+ import type { PackagesMap } from "./helpers/buildPnpmPackagesMap.ts";
3
+ import type { DependentRangesMap } from "./helpers/collectDependentRanges.ts";
4
+ export interface DisplayManyOptions {
5
+ title: DuplicatesReportTitle;
6
+ notice?: string;
7
+ duplicatesPackagesMap: PackagesMap;
8
+ dependents: DependentRangesMap;
9
+ totalDependencies: number;
10
+ clusterFixes?: ClusterFix[];
11
+ details?: boolean;
12
+ color?: boolean;
13
+ log?: (message?: string) => void;
14
+ }
15
+ export declare const displayMany: (options: DisplayManyOptions) => void;
16
+ //# sourceMappingURL=displayMany.d.ts.map