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
@@ -0,0 +1,307 @@
1
+ import { describe, it } from "bun:test";
2
+ import { deepStrictEqual, ok, strictEqual } from "node:assert/strict";
3
+ import { join } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { buildPnpmPackagesMap } from "./helpers/buildPnpmPackagesMap.js";
6
+ import { parsePnpmLockPackages } from "./helpers/parsePnpmLockPackages.js";
7
+ import { createManifestReader } from "./helpers/readInstalledManifest.js";
8
+ import { identifyClusterFixes } from "./identifyClusterFixes.js";
9
+ import { readPnpmLock } from "./readPnpmLock.js";
10
+ const fixturesBase = (rel) => fileURLToPath(new URL(rel, import.meta.url));
11
+ // The fixtures are committed without node_modules, so the declared ranges of
12
+ // the requesters that matter are stubbed; the others fall back to their
13
+ // resolved version, as an uninstalled project would.
14
+ const manifests = {
15
+ "@tamagui/metro-plugin@1.139.4": {
16
+ "metro-config": "*",
17
+ "metro-transform-worker": "*",
18
+ },
19
+ "@react-native/community-cli-plugin@0.87.0": { metro: "^0.87.0" },
20
+ "@react-native/metro-config@0.87.0": { "metro-config": "^0.87.0" },
21
+ "react-native-web@0.21.2": { "@react-native/normalize-colors": "^0.74.1" },
22
+ "react-native-reanimated@4.5.3": { "react-native": "0.83 - 0.86" },
23
+ "react-native-worklets@0.11.4": {
24
+ "react-native": "0.83 - 0.86",
25
+ "@react-native/metro-config": "*",
26
+ },
27
+ "@pob/eslint-config@65.5.0": {
28
+ "@typescript-eslint/eslint-plugin": "^8.59.1",
29
+ "@typescript-eslint/parser": "^8.59.1",
30
+ "typescript-eslint": "^8.59.1",
31
+ },
32
+ "@pob/eslint-plugin@65.5.0": { "@typescript-eslint/utils": "8.59.1" },
33
+ "eslint-plugin-import-x@4.16.2": {
34
+ "@typescript-eslint/types": "^8.56.0",
35
+ "@typescript-eslint/utils": "^8.56.0",
36
+ },
37
+ };
38
+ // The @tamagui/* and react-native-* packages peer-depend on react-native with
39
+ // an open range (`*`, or `0.83 - 0.86` for reanimated/worklets, both above):
40
+ // without them react-native's ranges read as exact resolved versions and the
41
+ // "nobody asks for this version" guard is never exercised.
42
+ const readStubbedManifest = (name, version) => {
43
+ const dependencies = manifests[`${name}@${version}`];
44
+ if (dependencies)
45
+ return { dependencies };
46
+ if (name.startsWith("@tamagui/") || name.startsWith("react-native-")) {
47
+ return { peerDependencies: { "react-native": "*" } };
48
+ }
49
+ return undefined;
50
+ };
51
+ const clusterFixesFor = (scenario, readManifest = readStubbedManifest) => {
52
+ const lock = readPnpmLock(fixturesBase(`../test/fixtures/${scenario}/pnpm-lock.yaml`));
53
+ return identifyClusterFixes(lock, buildPnpmPackagesMap(parsePnpmLockPackages(lock)), readManifest);
54
+ };
55
+ // Only the families: a duplicate with no lockstep sibling is reported as a
56
+ // cluster of one, which the tests below single out on its own.
57
+ const lockstepFixesFor = (scenario, readManifest = readStubbedManifest) => clusterFixesFor(scenario, readManifest).filter((fix) => fix.members.length > 1);
58
+ const metroCluster = () => {
59
+ const fix = clusterFixesFor("wildcard-not-reused").find((candidate) => candidate.members.includes("metro"));
60
+ ok(fix, "expected a cluster containing metro");
61
+ return fix;
62
+ };
63
+ describe("identifyClusterFixes", () => {
64
+ // Same expectations as bun-dedup's identifyClusterFixes on its own
65
+ // `duplicated-typescript-eslint` fixture: the pnpm adapter must reach the
66
+ // same conclusion from a lockfile shaped very differently.
67
+ it("identifies the @typescript-eslint cluster dedupable to 8.59.1", () => {
68
+ const fixes = lockstepFixesFor("duplicated-typescript-eslint");
69
+ strictEqual(fixes.length, 1);
70
+ const fix = fixes[0];
71
+ strictEqual(fix.applicable, true);
72
+ strictEqual(fix.target, "8.59.1");
73
+ strictEqual(fix.direction, "down");
74
+ strictEqual(fix.needsRoundTrip, true);
75
+ ok(fix.members.includes("typescript-eslint"));
76
+ deepStrictEqual(fix.duplicatedMembers, [
77
+ "@typescript-eslint/project-service",
78
+ "@typescript-eslint/scope-manager",
79
+ "@typescript-eslint/tsconfig-utils",
80
+ "@typescript-eslint/types",
81
+ "@typescript-eslint/typescript-estree",
82
+ "@typescript-eslint/utils",
83
+ "@typescript-eslint/visitor-keys",
84
+ ]);
85
+ deepStrictEqual(fix.reResolutionSet, [
86
+ "@typescript-eslint/eslint-plugin",
87
+ "@typescript-eslint/parser",
88
+ "typescript-eslint",
89
+ ]);
90
+ });
91
+ it("finds no target when the requesters' manifests are unavailable", () => {
92
+ // Without node_modules every transitive range degrades to the resolved
93
+ // version, so `@pob/eslint-config`'s `^8.59.1` reads as `8.61.0` and
94
+ // `@pob/eslint-plugin`'s pin keeps utils at 8.59.1: no version is left that
95
+ // any duplicate can collapse onto.
96
+ const fixes = lockstepFixesFor("duplicated-typescript-eslint", () => undefined);
97
+ strictEqual(fixes.length, 1);
98
+ const fix = fixes[0];
99
+ strictEqual(fix.applicable, false);
100
+ strictEqual(fix.target, null);
101
+ deepStrictEqual(fix.reuseFixes, []);
102
+ });
103
+ // `hoisted-node-linker` is the shape of a real repo that reported no fix at
104
+ // all: with `nodeLinker: hoisted` there is no `node_modules/.pnpm/<pkg>` to
105
+ // read, so every declared range degraded to the resolved version. It is the
106
+ // only fixture whose manifests are committed, so the real reader runs here.
107
+ const hoistedFixtureDir = fixturesBase("../test/fixtures/hoisted-node-linker");
108
+ const hoistedClusterFix = (readManifest) => {
109
+ const lock = readPnpmLock(join(hoistedFixtureDir, "pnpm-lock.yaml"));
110
+ const fixes = identifyClusterFixes(lock, buildPnpmPackagesMap(parsePnpmLockPackages(lock)), readManifest);
111
+ strictEqual(fixes.length, 1);
112
+ return fixes[0];
113
+ };
114
+ it("reads the ranges out of a hoisted node_modules", () => {
115
+ const fix = hoistedClusterFix(createManifestReader(hoistedFixtureDir));
116
+ // the pinned 0.84.5 wins: `mini-plugin` asks for `*`, so nothing external
117
+ // wants 0.87.0 and the pin needs no editing
118
+ strictEqual(fix.applicable, true);
119
+ strictEqual(fix.target, "0.84.5");
120
+ strictEqual(fix.direction, "down");
121
+ deepStrictEqual(fix.convergentMembers, ["mini-metro", "mini-metro-config"]);
122
+ deepStrictEqual(fix.driverMembers, ["mini-metro"]);
123
+ deepStrictEqual(fix.workspaceChanges, []);
124
+ strictEqual(fix.needsRoundTrip, false);
125
+ deepStrictEqual(fix.reuseFixes.map((reuse) => `${reuse.requesterName}>${reuse.packageName} ${reuse.from} -> ${reuse.to}`), [
126
+ // read from a nested manifest three levels deep, via `.modules.yaml`
127
+ "mini-deep>mini-metro-config 0.87.0 -> 0.84.5",
128
+ "mini-plugin>mini-metro-config 0.87.0 -> 0.84.5",
129
+ ]);
130
+ });
131
+ it("inverts the advice when the layout hides the manifests", () => {
132
+ // What the same lockfile reports when the manifests cannot be read:
133
+ // `mini-plugin`'s `*` reads as an exact `0.87.0`, so keeping the pin
134
+ // collapses nothing and the fix becomes "upgrade the pin" — the opposite of
135
+ // the answer above. Hence the warning when nothing is readable.
136
+ const fix = hoistedClusterFix(() => undefined);
137
+ strictEqual(fix.target, "0.87.0");
138
+ strictEqual(fix.direction, "up");
139
+ deepStrictEqual(fix.workspaceChanges.map((change) => `${change.packageName} ${change.range} -> ${change.to}`), ["mini-metro 0.84.5 -> 0.87.0"]);
140
+ deepStrictEqual(fix.reuseFixes, []);
141
+ });
142
+ it("returns no cluster fix when there is no lockstep family", () => {
143
+ deepStrictEqual(lockstepFixesFor("duplicated-printable-shell-command"), []);
144
+ });
145
+ // `exact-pin-forces-downgrade` commits its virtual store, so the real reader
146
+ // runs: `@yudiel/react-qr-scanner` pins barcode-detector at exactly 3.0.3
147
+ // while `expo-camera` declares `^3.0.0` and resolved 3.2.2.
148
+ const downgradeFixtureDir = fixturesBase("../test/fixtures/exact-pin-forces-downgrade");
149
+ it("converges a lone duplicate on the version every range accepts", () => {
150
+ const fixes = clusterFixesFor("exact-pin-forces-downgrade", createManifestReader(downgradeFixtureDir));
151
+ const fix = fixes.find((candidate) => candidate.members.includes("barcode-detector"));
152
+ ok(fix, "expected a fix for barcode-detector");
153
+ deepStrictEqual(fix.members, ["barcode-detector"]);
154
+ strictEqual(fix.applicable, true);
155
+ // 3.2.2 is rejected by the exact pin, so converging means moving the caret
156
+ // dependent down — which only an override can do
157
+ strictEqual(fix.target, "3.0.3");
158
+ strictEqual(fix.direction, "down");
159
+ deepStrictEqual(fix.convergentMembers, ["barcode-detector"]);
160
+ deepStrictEqual(fix.driverMembers, ["barcode-detector"]);
161
+ deepStrictEqual(fix.workspaceChanges, []);
162
+ deepStrictEqual(fix.externalConstraints.map((constraint) => [
163
+ constraint.requesterName,
164
+ constraint.range,
165
+ ]), [
166
+ ["@yudiel/react-qr-scanner", "3.0.3"],
167
+ ["expo-camera", "^3.0.0"],
168
+ ]);
169
+ });
170
+ it("finds nothing for the duplicate a convergence drags along", () => {
171
+ const fixes = clusterFixesFor("exact-pin-forces-downgrade", createManifestReader(downgradeFixtureDir));
172
+ const fix = fixes.find((candidate) => candidate.members.includes("zxing-wasm"));
173
+ ok(fix, "expected a fix record for zxing-wasm");
174
+ // barcode-detector@3.0.3 asks `^2.1.2` and 3.2.2 asks exactly 3.1.3
175
+ strictEqual(fix.applicable, false);
176
+ strictEqual(fix.target, null);
177
+ });
178
+ it("leaves a lone duplicate alone when only a workspace pin could move", () => {
179
+ // `printable-shell-command` is pinned at 5.0.7 by the workspace and at
180
+ // 5.3.1 by `betterdisplaycli`: converging means bumping the user's own pin,
181
+ // which is an upgrade decision rather than a dedupe.
182
+ deepStrictEqual(clusterFixesFor("duplicated-printable-shell-command"), []);
183
+ });
184
+ it("skips clusters whose members are all single-version", () => {
185
+ for (const fix of lockstepFixesFor("wildcard-not-reused")) {
186
+ ok(fix.duplicatedMembers.length > 0);
187
+ }
188
+ });
189
+ it("detects the metro family the non-reused wildcard duplicated", () => {
190
+ const fix = metroCluster();
191
+ for (const member of [
192
+ "metro",
193
+ "metro-config",
194
+ "metro-transform-worker",
195
+ "ob1",
196
+ ]) {
197
+ ok(fix.duplicatedMembers.includes(member), `expected ${member}`);
198
+ }
199
+ strictEqual(fix.duplicatedMembers.length, 16);
200
+ });
201
+ it("keeps external constraints and drops the derived internal pins", () => {
202
+ const byRequester = metroCluster().externalConstraints.map((constraint) => `${constraint.requesterName ?? "workspace"} -> ${constraint.packageName} @ ${constraint.range}`);
203
+ for (const expected of [
204
+ "workspace -> metro @ 0.84.5",
205
+ "@tamagui/metro-plugin -> metro-config @ *",
206
+ "@tamagui/metro-plugin -> metro-transform-worker @ *",
207
+ "react-native-web -> @react-native/normalize-colors @ ^0.74.1",
208
+ ]) {
209
+ ok(byRequester.includes(expected), `expected constraint ${expected}`);
210
+ }
211
+ // metro's own `metro-config: 0.84.5` / `0.87.0` pins are internal: they
212
+ // exist only because metro resolved where it did.
213
+ const requesterNames = new Set(metroCluster().externalConstraints.map((constraint) => constraint.requesterName));
214
+ strictEqual(requesterNames.has("metro"), false);
215
+ strictEqual(requesterNames.has("metro-config"), false);
216
+ });
217
+ it("converges the family on the pinned version, without touching the pin", () => {
218
+ const fix = metroCluster();
219
+ // The root pins metro at 0.84.5 on purpose, so the pin is a constraint to
220
+ // resolve around, not something to upgrade: the family collapses onto
221
+ // 0.84.5 and `workspaceChanges` stays empty.
222
+ strictEqual(fix.applicable, true);
223
+ strictEqual(fix.target, "0.84.5");
224
+ strictEqual(fix.direction, "down");
225
+ strictEqual(fix.convergentMembers.length, 15);
226
+ deepStrictEqual(fix.workspaceChanges, []);
227
+ // metro is the only member a real range applies to (the pin): the 14
228
+ // metro-* packages are requested through `*` or through metro's own exact
229
+ // pins, so they carry no decision and follow it.
230
+ deepStrictEqual(fix.driverMembers, ["metro"]);
231
+ });
232
+ it("leaves react-native's version to the resolver", () => {
233
+ const fix = metroCluster();
234
+ // react-native@0.87.0 and @react-native/metro-config@0.87.0 are what hold
235
+ // the 0.87.0 metro subtree, and nothing pins either of them there — both
236
+ // arrive through `*` / `0.83 - 0.86` peer ranges (0.87.0 does not even
237
+ // satisfy the latter). They have to move, but which version they land on is
238
+ // the resolver's call, so no version is asserted for them.
239
+ deepStrictEqual(fix.floatingMembers, [
240
+ "@react-native/metro-config",
241
+ "react-native",
242
+ ]);
243
+ deepStrictEqual(fix.reResolutionSet, []);
244
+ strictEqual(fix.needsRoundTrip, true);
245
+ });
246
+ it("repoints the wildcards that ignored the pinned version", () => {
247
+ const fix = metroCluster();
248
+ // What the fixture is actually about: `@tamagui/metro-plugin` declares
249
+ // `metro-config: "*"` / `metro-transform-worker: "*"`, and pnpm resolved
250
+ // both to 0.87.0 although the pinned 0.84.5 was already in the tree and the
251
+ // range accepts it — so the plugin runs against a different metro than the
252
+ // app's.
253
+ strictEqual(fix.anchor, "0.84.5");
254
+ deepStrictEqual(fix.reuseFixes.map((reuse) => `${reuse.requesterName}>${reuse.packageName} "${reuse.range}" ${reuse.from} -> ${reuse.to}`), [
255
+ '@tamagui/metro-plugin>metro-config "*" 0.87.0 -> 0.84.5',
256
+ '@tamagui/metro-plugin>metro-transform-worker "*" 0.87.0 -> 0.84.5',
257
+ ]);
258
+ });
259
+ it("leaves alone the ranges that cannot accept the pinned version", () => {
260
+ const repointed = new Set(metroCluster().reuseFixes.map((reuse) => reuse.packageName));
261
+ // `@react-native/metro-config` requires `metro-config: ^0.87.0`, and
262
+ // `react-native-web` requires `@react-native/normalize-colors: ^0.74.1`:
263
+ // neither accepts 0.84.5, so neither is repointed.
264
+ strictEqual(repointed.has("@react-native/normalize-colors"), false);
265
+ deepStrictEqual(metroCluster()
266
+ .reuseFixes.filter((reuse) => reuse.requesterName === "@react-native/metro-config")
267
+ .map((reuse) => reuse.packageName), []);
268
+ });
269
+ // `aliased-swapped-names`: the importer pins the real `typescript` at 7.0.2
270
+ // through the `@typescript/native` key while `tool` needs 5.9.3, and the
271
+ // `typescript` key holds an unrelated package. Nothing converges, and nothing
272
+ // may propose editing the declaration that names the other package.
273
+ it("leaves an exact pin declared under a swapped alias key alone", () => {
274
+ const fixes = clusterFixesFor("aliased-swapped-names", () => undefined);
275
+ deepStrictEqual(fixes.map((fix) => fix.members), [["typescript"]]);
276
+ strictEqual(fixes[0].applicable, false);
277
+ deepStrictEqual(fixes[0].workspaceChanges, []);
278
+ });
279
+ // The same swapped keys, with `typescript` sitting in a lockstep family (its
280
+ // platform binaries) as it does from 7.0.0 on: the family makes the cluster
281
+ // big enough to escape the singleton guard above, and the pin is then the only
282
+ // thing standing between the dedupe and a rewrite of `"@typescript/native":
283
+ // "npm:typescript@7.0.2"` into `npm:typescript@6.0.3` — the alias repointed at
284
+ // the very package it exists to sit beside.
285
+ it("never repoints an alias to converge its family", () => {
286
+ const fix = clusterFixesFor("aliased-swapped-names-cluster", (name, version) => name === "@typescript/typescript6" && version === "6.0.2"
287
+ ? { dependencies: { typescript: "^6" } }
288
+ : undefined).find((candidate) => candidate.members.includes("typescript"));
289
+ ok(fix);
290
+ strictEqual(fix.members.length, 3);
291
+ deepStrictEqual(fix.duplicatedMembers, ["typescript"]);
292
+ strictEqual(fix.applicable, false);
293
+ strictEqual(fix.target, null);
294
+ deepStrictEqual(fix.workspaceChanges, []);
295
+ });
296
+ it("reports a cluster no member can leave as not dedupable", () => {
297
+ // metro pins hermes-parser exactly at 0.35.0 and @react-native/* at 0.36.1,
298
+ // and hermes-estree only exists twice because hermes-parser does: nothing
299
+ // can collapse.
300
+ const fix = clusterFixesFor("wildcard-not-reused").find((candidate) => candidate.members.includes("hermes-parser"));
301
+ ok(fix);
302
+ strictEqual(fix.applicable, false);
303
+ strictEqual(fix.target, null);
304
+ deepStrictEqual(fix.convergentMembers, []);
305
+ });
306
+ });
307
+ //# sourceMappingURL=identifyClusterFixes.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identifyClusterFixes.test.js","sourceRoot":"","sources":["../src/identifyClusterFixes.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ import { describe, expect, it } from "bun:test";
2
+ import fs from "node:fs";
3
+ import { fileURLToPath } from "node:url";
4
+ import { identifyResolutionFixes } from "pm-dedup-core";
5
+ const loadResolutionsFixture = (fileName) => {
6
+ return JSON.parse(fs.readFileSync(fileURLToPath(new URL(`../test/fixtures/resolutions/${fileName}`, import.meta.url)),
7
+ // eslint-disable-next-line unicorn/prefer-json-parse-buffer
8
+ "utf8"));
9
+ };
10
+ const loadDependentsFixture = (fileName) => {
11
+ return JSON.parse(fs.readFileSync(fileURLToPath(new URL(`../test/fixtures/dependents/${fileName}`, import.meta.url)),
12
+ // eslint-disable-next-line unicorn/prefer-json-parse-buffer
13
+ "utf8"));
14
+ };
15
+ const objetToMap = (obj) => new Map(Object.entries(obj));
16
+ describe("identifyResolutionFixes", () => {
17
+ it("should return an empty array when there are no resolutions", () => {
18
+ const resolutions = [];
19
+ const fixes = identifyResolutionFixes(resolutions, objetToMap({}));
20
+ expect(fixes).toEqual([]);
21
+ });
22
+ it("should return an empty array when there is only one resolution", () => {
23
+ const resolutions = loadResolutionsFixture("semver-7.7.3.json");
24
+ const dependents = loadDependentsFixture("semver-7.7.3.json");
25
+ const fixes = identifyResolutionFixes(resolutions, objetToMap(dependents));
26
+ expect(fixes).toEqual([]);
27
+ });
28
+ it("should not identify fixes when dependencies are not compatible", () => {
29
+ const resolutions = loadResolutionsFixture("babel-code-frame-7.26.2-7.27.1.json");
30
+ const dependents = loadDependentsFixture("babel-code-frame-7.26.2-7.27.1.json");
31
+ const fixes = identifyResolutionFixes(resolutions, objetToMap(dependents));
32
+ expect(fixes).toEqual([]);
33
+ });
34
+ it("should identify resolution fixes when dependencies are compatible", () => {
35
+ const resolutions = loadResolutionsFixture("printable-shell-command-5.0.7-5.0.8.json");
36
+ const dependents = loadDependentsFixture("printable-shell-command-5.0.7-5.0.8.json");
37
+ const fixes = identifyResolutionFixes(resolutions, objetToMap(dependents));
38
+ expect(fixes).toEqual([
39
+ {
40
+ mergeableResolutions: [
41
+ "printable-shell-command@5.0.7",
42
+ "printable-shell-command@5.0.8",
43
+ ],
44
+ to: "printable-shell-command@5.0.8",
45
+ },
46
+ ]);
47
+ });
48
+ });
49
+ //# sourceMappingURL=identifyResolutionFixes.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identifyResolutionFixes.test.js","sourceRoot":"","sources":["../src/identifyResolutionFixes.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAIxD,MAAM,sBAAsB,GAAG,CAAC,QAAgB,EAAuB,EAAE;IACvE,OAAO,IAAI,CAAC,KAAK,CACf,EAAE,CAAC,YAAY,CACb,aAAa,CACX,IAAI,GAAG,CAAC,gCAAgC,QAAQ,EAAE,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CACrE;IACD,4DAA4D;IAC5D,MAAM,CACP,CACF,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,qBAAqB,GAAG,CAC5B,QAAgB,EACa,EAAE;IAC/B,OAAO,IAAI,CAAC,KAAK,CACf,EAAE,CAAC,YAAY,CACb,aAAa,CACX,IAAI,GAAG,CAAC,+BAA+B,QAAQ,EAAE,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CACpE;IACD,4DAA4D;IAC5D,MAAM,CACP,CACF,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,UAAU,GAAG,CAAsB,GAAiB,EAAa,EAAE,CACvE,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAa,CAAC,CAAC;AAE3C,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;IACvC,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,MAAM,WAAW,GAAwB,EAAE,CAAC;QAE5C,MAAM,KAAK,GAAG,uBAAuB,CAAC,WAAW,EAAE,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;QACnE,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gEAAgE,EAAE,GAAG,EAAE;QACxE,MAAM,WAAW,GAAG,sBAAsB,CAAC,mBAAmB,CAAC,CAAC;QAChE,MAAM,UAAU,GAAG,qBAAqB,CAAC,mBAAmB,CAAC,CAAC;QAC9D,MAAM,KAAK,GAAG,uBAAuB,CAAC,WAAW,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC;QAC3E,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gEAAgE,EAAE,GAAG,EAAE;QACxE,MAAM,WAAW,GAAG,sBAAsB,CACxC,qCAAqC,CACtC,CAAC;QACF,MAAM,UAAU,GAAG,qBAAqB,CACtC,qCAAqC,CACtC,CAAC;QACF,MAAM,KAAK,GAAG,uBAAuB,CAAC,WAAW,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC;QAC3E,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mEAAmE,EAAE,GAAG,EAAE;QAC3E,MAAM,WAAW,GAAG,sBAAsB,CACxC,0CAA0C,CAC3C,CAAC;QACF,MAAM,UAAU,GAAG,qBAAqB,CACtC,0CAA0C,CAC3C,CAAC;QAEF,MAAM,KAAK,GAAG,uBAAuB,CAAC,WAAW,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC;QAC3E,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC;YACpB;gBACE,oBAAoB,EAAE;oBACpB,+BAA+B;oBAC/B,+BAA+B;iBAChC;gBACD,EAAE,EAAE,+BAA+B;aACpC;SACF,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,86 @@
1
+ import { createPackageFilter, selectExplainedPackages, selectPackages, } from "pm-dedup-core";
2
+ import { displayMany } from "./displayMany.js";
3
+ import { buildPnpmPackagesMap, filterDuplicatesPnpmPackagesMap, } from "./helpers/buildPnpmPackagesMap.js";
4
+ import { collectDependentRanges } from "./helpers/collectDependentRanges.js";
5
+ import { parsePnpmLockPackages } from "./helpers/parsePnpmLockPackages.js";
6
+ import { lockPathOf, resolvePnpmProjectDir } from "./helpers/projectDir.js";
7
+ import { createManifestReaderWithStats } from "./helpers/readInstalledManifest.js";
8
+ import { identifyClusterFixes } from "./identifyClusterFixes.js";
9
+ import { readPnpmLock } from "./readPnpmLock.js";
10
+ export { dedupe } from "./dedupe.js";
11
+ export { applyClusterFixes } from "./applyClusterFixes.js";
12
+ export { identifyClusterFixes } from "./identifyClusterFixes.js";
13
+ export { toLockstepGraph } from "./helpers/toLockstepGraph.js";
14
+ export { displayMany } from "./displayMany.js";
15
+ export { readPnpmLock } from "./readPnpmLock.js";
16
+ export { parsePnpmLockPackages } from "./helpers/parsePnpmLockPackages.js";
17
+ export { buildPnpmPackagesMap, filterDuplicatesPnpmPackagesMap, } from "./helpers/buildPnpmPackagesMap.js";
18
+ export { collectPnpmDependents } from "./helpers/collectPnpmDependents.js";
19
+ export { collectDependentRanges } from "./helpers/collectDependentRanges.js";
20
+ export { readVersionsSnapshot } from "./helpers/versionsSnapshot.js";
21
+ export { createManifestReader, createManifestReaderWithStats, } from "./helpers/readInstalledManifest.js";
22
+ /**
23
+ * Every declared range of a transitive dependent is read from its installed
24
+ * manifest; the lockfile only stores resolved versions. When nothing is
25
+ * readable, each range degrades to an exact version and the reported fixes are
26
+ * not merely fewer but wrong — so say so instead of printing a confident answer.
27
+ */
28
+ function warnWhenNoManifests(stats) {
29
+ if (stats.found > 0 || stats.missed === 0)
30
+ return;
31
+ console.log();
32
+ console.log("Warning: no installed manifest could be read under node_modules, so every dependent's range fell back to its resolved version and the fixes above are unreliable. Run `pnpm install` first.");
33
+ }
34
+ // The report and the fixes must agree on what every requester asked for, so
35
+ // both read the declared ranges the lockfile does not store.
36
+ function collectRanges(lock, packagesMap, readManifest) {
37
+ return collectDependentRanges(lock, new Set(Object.keys(packagesMap)), readManifest);
38
+ }
39
+ export function whyDuplicate({ filter: filterOptions, all = false, details = false, }) {
40
+ const filter = createPackageFilter(filterOptions);
41
+ const projectDir = resolvePnpmProjectDir();
42
+ if (projectDir === null)
43
+ return;
44
+ const lock = readPnpmLock(lockPathOf(projectDir));
45
+ const parsed = parsePnpmLockPackages(lock);
46
+ const packagesMap = buildPnpmPackagesMap(parsed);
47
+ const { packages: filteredPackages, title, notice, } = selectExplainedPackages({ packagesMap, filter, all });
48
+ const { readManifest, stats } = createManifestReaderWithStats(projectDir);
49
+ // A duplicate is often only explainable by its family: show the cluster fixes
50
+ // covering the matched packages, not just their own dependents.
51
+ const matchedClusterFixes = identifyClusterFixes(lock, packagesMap, readManifest).filter((fix) => fix.members.some(filter.selects));
52
+ displayMany({
53
+ title,
54
+ notice,
55
+ duplicatesPackagesMap: filteredPackages,
56
+ dependents: collectRanges(lock, filteredPackages, readManifest),
57
+ // the whole lockfile, so the count means the same whatever the filter
58
+ totalDependencies: Object.keys(packagesMap).length,
59
+ clusterFixes: matchedClusterFixes,
60
+ details,
61
+ });
62
+ warnWhenNoManifests(stats());
63
+ }
64
+ export function listDuplicates({ filter: filterOptions, details = false, } = {}) {
65
+ const filter = createPackageFilter(filterOptions);
66
+ const projectDir = resolvePnpmProjectDir();
67
+ if (projectDir === null)
68
+ return;
69
+ const lock = readPnpmLock(lockPathOf(projectDir));
70
+ const parsed = parsePnpmLockPackages(lock);
71
+ const packagesMap = buildPnpmPackagesMap(parsed);
72
+ const duplicatesPackagesMap = selectPackages(filterDuplicatesPnpmPackagesMap(packagesMap), filter);
73
+ const { readManifest, stats } = createManifestReaderWithStats(projectDir);
74
+ displayMany({
75
+ title: "duplicates",
76
+ duplicatesPackagesMap,
77
+ dependents: collectRanges(lock, duplicatesPackagesMap, readManifest),
78
+ totalDependencies: Object.keys(packagesMap).length,
79
+ // as in `whyDuplicate`: a family is shown whole as soon as it holds a
80
+ // selected member, because that is what explains the duplicate
81
+ clusterFixes: identifyClusterFixes(lock, packagesMap, readManifest).filter((fix) => fix.members.some(filter.selects)),
82
+ details,
83
+ });
84
+ warnWhenNoManifests(stats());
85
+ }
86
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Minimal subset of the pnpm lockfile v9 structure consumed by this package.
3
+ *
4
+ * @see https://github.com/pnpm/spec/blob/master/lockfile/9.0.md
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=pnpmLockTypes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pnpmLockTypes.js","sourceRoot":"","sources":["../src/pnpmLockTypes.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
@@ -0,0 +1,6 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { parse } from "yaml";
3
+ export const readPnpmLock = (filepath) => {
4
+ return parse(readFileSync(filepath, "utf8"));
5
+ };
6
+ //# sourceMappingURL=readPnpmLock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readPnpmLock.js","sourceRoot":"","sources":["../src/readPnpmLock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,KAAK,EAAE,MAAM,MAAM,CAAC;AAG7B,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,QAAgB,EAAgB,EAAE;IAC7D,OAAO,KAAK,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAiB,CAAC;AAC/D,CAAC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "pnpm-dedup",
3
+ "version": "2.1.0",
4
+ "description": "List duplicates and dedupe pnpm lock file",
5
+ "keywords": [
6
+ "dedupe",
7
+ "duplicate",
8
+ "pnpm"
9
+ ],
10
+ "homepage": "https://github.com/christophehurpeau/pm-tools",
11
+ "bugs": {
12
+ "url": "https://github.com/christophehurpeau/pm-tools/issues"
13
+ },
14
+ "license": "MIT",
15
+ "author": "Christophe Hurpeau <christophe@hurpeau.com> (https://christophe.hurpeau.com)",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://github.com/christophehurpeau/pm-tools.git",
19
+ "directory": "packages/pnpm-dedup"
20
+ },
21
+ "bin": {
22
+ "pnpm-dedup": "./dist/bin/pnpm-dedupe.js",
23
+ "pnpm-dedupe": "./dist/bin/pnpm-dedupe.js",
24
+ "pnpm-why-duplicate": "./dist/bin/pnpm-why-duplicate.js"
25
+ },
26
+ "files": [
27
+ "src",
28
+ "dist"
29
+ ],
30
+ "type": "module",
31
+ "sideEffects": false,
32
+ "main": "./dist/index.js",
33
+ "types": "./dist/definitions/index.d.ts",
34
+ "exports": {
35
+ "./package.json": "./package.json",
36
+ ".": {
37
+ "types": "./dist/definitions/index.d.ts",
38
+ "node": {
39
+ "import": "./dist/index.js"
40
+ }
41
+ }
42
+ },
43
+ "scripts": {
44
+ "build": "tsc",
45
+ "lint": "bun run lint:oxlint && bun run lint:eslint",
46
+ "lint:eslint": "bun run --cwd '../..' eslint --quiet 'packages/pnpm-dedup'",
47
+ "lint:oxlint": "bun run --cwd '../..' oxlint --quiet 'packages/pnpm-dedup'",
48
+ "test": "bun run --cwd '../..' test -- 'packages/pnpm-dedup'",
49
+ "tsc": "tsc"
50
+ },
51
+ "dependencies": {
52
+ "pm-dedup-core": "workspace:*",
53
+ "semver": "7.8.5",
54
+ "tslib": "^2.8.1",
55
+ "yaml": "2.9.1"
56
+ },
57
+ "devDependencies": {
58
+ "@types/bun": "1.4.2",
59
+ "@typescript/native": "npm:typescript@7.0.2",
60
+ "typescript": "npm:@typescript/typescript6@6.0.2"
61
+ },
62
+ "engines": {
63
+ "node": ">=22.18.0"
64
+ },
65
+ "pob": {
66
+ "bundler": "tsc",
67
+ "entries": [
68
+ "index"
69
+ ]
70
+ }
71
+ }