pm-dedup-core 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 (228) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +105 -0
  3. package/dist/buildIdentifiedFixesMap.js +6 -0
  4. package/dist/buildIdentifiedFixesMap.js.map +1 -0
  5. package/dist/buildLockstepClusters.js +119 -0
  6. package/dist/buildLockstepClusters.js.map +1 -0
  7. package/dist/buildLockstepClusters.test.js +71 -0
  8. package/dist/buildLockstepClusters.test.js.map +1 -0
  9. package/dist/clusterLabel.js +59 -0
  10. package/dist/clusterLabel.js.map +1 -0
  11. package/dist/clusterLabel.test.js +56 -0
  12. package/dist/clusterLabel.test.js.map +1 -0
  13. package/dist/compareCandidateVersions.js +18 -0
  14. package/dist/compareCandidateVersions.js.map +1 -0
  15. package/dist/compareCandidateVersions.test.js +32 -0
  16. package/dist/compareCandidateVersions.test.js.map +1 -0
  17. package/dist/createPackageFilter.js +81 -0
  18. package/dist/createPackageFilter.js.map +1 -0
  19. package/dist/createPackageFilter.test.js +115 -0
  20. package/dist/createPackageFilter.test.js.map +1 -0
  21. package/dist/definitions/buildIdentifiedFixesMap.d.ts +4 -0
  22. package/dist/definitions/buildIdentifiedFixesMap.d.ts.map +1 -0
  23. package/dist/definitions/buildLockstepClusters.d.ts +8 -0
  24. package/dist/definitions/buildLockstepClusters.d.ts.map +1 -0
  25. package/dist/definitions/buildLockstepClusters.test.d.ts +2 -0
  26. package/dist/definitions/buildLockstepClusters.test.d.ts.map +1 -0
  27. package/dist/definitions/clusterLabel.d.ts +8 -0
  28. package/dist/definitions/clusterLabel.d.ts.map +1 -0
  29. package/dist/definitions/clusterLabel.test.d.ts +2 -0
  30. package/dist/definitions/clusterLabel.test.d.ts.map +1 -0
  31. package/dist/definitions/compareCandidateVersions.d.ts +10 -0
  32. package/dist/definitions/compareCandidateVersions.d.ts.map +1 -0
  33. package/dist/definitions/compareCandidateVersions.test.d.ts +2 -0
  34. package/dist/definitions/compareCandidateVersions.test.d.ts.map +1 -0
  35. package/dist/definitions/createPackageFilter.d.ts +45 -0
  36. package/dist/definitions/createPackageFilter.d.ts.map +1 -0
  37. package/dist/definitions/createPackageFilter.test.d.ts +2 -0
  38. package/dist/definitions/createPackageFilter.test.d.ts.map +1 -0
  39. package/dist/definitions/duplicateSnapshot.d.ts +8 -0
  40. package/dist/definitions/duplicateSnapshot.d.ts.map +1 -0
  41. package/dist/definitions/fileSnapshot.d.ts +7 -0
  42. package/dist/definitions/fileSnapshot.d.ts.map +1 -0
  43. package/dist/definitions/findProjectRoot.d.ts +27 -0
  44. package/dist/definitions/findProjectRoot.d.ts.map +1 -0
  45. package/dist/definitions/findProjectRoot.test.d.ts +2 -0
  46. package/dist/definitions/findProjectRoot.test.d.ts.map +1 -0
  47. package/dist/definitions/identifyLockstepClusterFixes.d.ts +89 -0
  48. package/dist/definitions/identifyLockstepClusterFixes.d.ts.map +1 -0
  49. package/dist/definitions/identifyLockstepClusterFixes.test.d.ts +2 -0
  50. package/dist/definitions/identifyLockstepClusterFixes.test.d.ts.map +1 -0
  51. package/dist/definitions/identifyResolutionFixes.d.ts +28 -0
  52. package/dist/definitions/identifyResolutionFixes.d.ts.map +1 -0
  53. package/dist/definitions/identifyResolutionFixes.test.d.ts +2 -0
  54. package/dist/definitions/identifyResolutionFixes.test.d.ts.map +1 -0
  55. package/dist/definitions/index.d.ts +44 -0
  56. package/dist/definitions/index.d.ts.map +1 -0
  57. package/dist/definitions/packageDependenciesUtils.d.ts +47 -0
  58. package/dist/definitions/packageDependenciesUtils.d.ts.map +1 -0
  59. package/dist/definitions/packageDependenciesUtils.test.d.ts +2 -0
  60. package/dist/definitions/packageDependenciesUtils.test.d.ts.map +1 -0
  61. package/dist/definitions/packageFilterArgs.d.ts +74 -0
  62. package/dist/definitions/packageFilterArgs.d.ts.map +1 -0
  63. package/dist/definitions/packageFilterArgs.test.d.ts +2 -0
  64. package/dist/definitions/packageFilterArgs.test.d.ts.map +1 -0
  65. package/dist/definitions/packageStyles.d.ts +14 -0
  66. package/dist/definitions/packageStyles.d.ts.map +1 -0
  67. package/dist/definitions/packageStyles.test.d.ts +2 -0
  68. package/dist/definitions/packageStyles.test.d.ts.map +1 -0
  69. package/dist/definitions/parseBinArgs.d.ts +14 -0
  70. package/dist/definitions/parseBinArgs.d.ts.map +1 -0
  71. package/dist/definitions/parseBinArgs.test.d.ts +2 -0
  72. package/dist/definitions/parseBinArgs.test.d.ts.map +1 -0
  73. package/dist/definitions/planClusterApply.d.ts +39 -0
  74. package/dist/definitions/planClusterApply.d.ts.map +1 -0
  75. package/dist/definitions/planClusterApply.test.d.ts +2 -0
  76. package/dist/definitions/planClusterApply.test.d.ts.map +1 -0
  77. package/dist/definitions/renderApplyPlan.d.ts +22 -0
  78. package/dist/definitions/renderApplyPlan.d.ts.map +1 -0
  79. package/dist/definitions/renderApplyPlan.test.d.ts +2 -0
  80. package/dist/definitions/renderApplyPlan.test.d.ts.map +1 -0
  81. package/dist/definitions/renderDedupeSummary.d.ts +22 -0
  82. package/dist/definitions/renderDedupeSummary.d.ts.map +1 -0
  83. package/dist/definitions/renderDedupeSummary.test.d.ts +2 -0
  84. package/dist/definitions/renderDedupeSummary.test.d.ts.map +1 -0
  85. package/dist/definitions/renderDuplicatesReport.d.ts +39 -0
  86. package/dist/definitions/renderDuplicatesReport.d.ts.map +1 -0
  87. package/dist/definitions/renderDuplicatesReport.test.d.ts +2 -0
  88. package/dist/definitions/renderDuplicatesReport.test.d.ts.map +1 -0
  89. package/dist/definitions/reportColors.d.ts +25 -0
  90. package/dist/definitions/reportColors.d.ts.map +1 -0
  91. package/dist/definitions/reportColors.test.d.ts +2 -0
  92. package/dist/definitions/reportColors.test.d.ts.map +1 -0
  93. package/dist/definitions/reportText.d.ts +29 -0
  94. package/dist/definitions/reportText.d.ts.map +1 -0
  95. package/dist/definitions/selectExplainedPackages.d.ts +22 -0
  96. package/dist/definitions/selectExplainedPackages.d.ts.map +1 -0
  97. package/dist/definitions/selectExplainedPackages.test.d.ts +2 -0
  98. package/dist/definitions/selectExplainedPackages.test.d.ts.map +1 -0
  99. package/dist/definitions/unconditionalOverrides.d.ts +24 -0
  100. package/dist/definitions/unconditionalOverrides.d.ts.map +1 -0
  101. package/dist/definitions/unconditionalOverrides.test.d.ts +2 -0
  102. package/dist/definitions/unconditionalOverrides.test.d.ts.map +1 -0
  103. package/dist/definitions/versionsSnapshot.d.ts +40 -0
  104. package/dist/definitions/versionsSnapshot.d.ts.map +1 -0
  105. package/dist/definitions/versionsSnapshot.test.d.ts +2 -0
  106. package/dist/definitions/versionsSnapshot.test.d.ts.map +1 -0
  107. package/dist/definitions/workspaceManifest.d.ts +21 -0
  108. package/dist/definitions/workspaceManifest.d.ts.map +1 -0
  109. package/dist/definitions/workspaceManifest.test.d.ts +2 -0
  110. package/dist/definitions/workspaceManifest.test.d.ts.map +1 -0
  111. package/dist/duplicateSnapshot.js +10 -0
  112. package/dist/duplicateSnapshot.js.map +1 -0
  113. package/dist/fileSnapshot.js +21 -0
  114. package/dist/fileSnapshot.js.map +1 -0
  115. package/dist/findProjectRoot.js +48 -0
  116. package/dist/findProjectRoot.js.map +1 -0
  117. package/dist/findProjectRoot.test.js +53 -0
  118. package/dist/findProjectRoot.test.js.map +1 -0
  119. package/dist/identifyLockstepClusterFixes.js +352 -0
  120. package/dist/identifyLockstepClusterFixes.js.map +1 -0
  121. package/dist/identifyLockstepClusterFixes.test.js +372 -0
  122. package/dist/identifyLockstepClusterFixes.test.js.map +1 -0
  123. package/dist/identifyResolutionFixes.js +102 -0
  124. package/dist/identifyResolutionFixes.js.map +1 -0
  125. package/dist/identifyResolutionFixes.test.js +70 -0
  126. package/dist/identifyResolutionFixes.test.js.map +1 -0
  127. package/dist/index.js +24 -0
  128. package/dist/index.js.map +1 -0
  129. package/dist/packageDependenciesUtils.js +91 -0
  130. package/dist/packageDependenciesUtils.js.map +1 -0
  131. package/dist/packageDependenciesUtils.test.js +539 -0
  132. package/dist/packageDependenciesUtils.test.js.map +1 -0
  133. package/dist/packageFilterArgs.js +65 -0
  134. package/dist/packageFilterArgs.js.map +1 -0
  135. package/dist/packageFilterArgs.test.js +93 -0
  136. package/dist/packageFilterArgs.test.js.map +1 -0
  137. package/dist/packageStyles.js +33 -0
  138. package/dist/packageStyles.js.map +1 -0
  139. package/dist/packageStyles.test.js +58 -0
  140. package/dist/packageStyles.test.js.map +1 -0
  141. package/dist/parseBinArgs.js +28 -0
  142. package/dist/parseBinArgs.js.map +1 -0
  143. package/dist/parseBinArgs.test.js +62 -0
  144. package/dist/parseBinArgs.test.js.map +1 -0
  145. package/dist/planClusterApply.js +69 -0
  146. package/dist/planClusterApply.js.map +1 -0
  147. package/dist/planClusterApply.test.js +152 -0
  148. package/dist/planClusterApply.test.js.map +1 -0
  149. package/dist/renderApplyPlan.js +51 -0
  150. package/dist/renderApplyPlan.js.map +1 -0
  151. package/dist/renderApplyPlan.test.js +106 -0
  152. package/dist/renderApplyPlan.test.js.map +1 -0
  153. package/dist/renderDedupeSummary.js +38 -0
  154. package/dist/renderDedupeSummary.js.map +1 -0
  155. package/dist/renderDedupeSummary.test.js +91 -0
  156. package/dist/renderDedupeSummary.test.js.map +1 -0
  157. package/dist/renderDuplicatesReport.js +375 -0
  158. package/dist/renderDuplicatesReport.js.map +1 -0
  159. package/dist/renderDuplicatesReport.test.js +598 -0
  160. package/dist/renderDuplicatesReport.test.js.map +1 -0
  161. package/dist/reportColors.js +51 -0
  162. package/dist/reportColors.js.map +1 -0
  163. package/dist/reportColors.test.js +49 -0
  164. package/dist/reportColors.test.js.map +1 -0
  165. package/dist/reportText.js +29 -0
  166. package/dist/reportText.js.map +1 -0
  167. package/dist/selectExplainedPackages.js +42 -0
  168. package/dist/selectExplainedPackages.js.map +1 -0
  169. package/dist/selectExplainedPackages.test.js +55 -0
  170. package/dist/selectExplainedPackages.test.js.map +1 -0
  171. package/dist/unconditionalOverrides.js +36 -0
  172. package/dist/unconditionalOverrides.js.map +1 -0
  173. package/dist/unconditionalOverrides.test.js +71 -0
  174. package/dist/unconditionalOverrides.test.js.map +1 -0
  175. package/dist/versionsSnapshot.js +49 -0
  176. package/dist/versionsSnapshot.js.map +1 -0
  177. package/dist/versionsSnapshot.test.js +79 -0
  178. package/dist/versionsSnapshot.test.js.map +1 -0
  179. package/dist/workspaceManifest.js +63 -0
  180. package/dist/workspaceManifest.js.map +1 -0
  181. package/dist/workspaceManifest.test.js +159 -0
  182. package/dist/workspaceManifest.test.js.map +1 -0
  183. package/package.json +68 -0
  184. package/src/buildIdentifiedFixesMap.ts +22 -0
  185. package/src/buildLockstepClusters.test.ts +90 -0
  186. package/src/buildLockstepClusters.ts +155 -0
  187. package/src/clusterLabel.test.ts +79 -0
  188. package/src/clusterLabel.ts +75 -0
  189. package/src/compareCandidateVersions.test.ts +39 -0
  190. package/src/compareCandidateVersions.ts +23 -0
  191. package/src/createPackageFilter.test.ts +156 -0
  192. package/src/createPackageFilter.ts +142 -0
  193. package/src/duplicateSnapshot.ts +24 -0
  194. package/src/fileSnapshot.ts +28 -0
  195. package/src/findProjectRoot.test.ts +66 -0
  196. package/src/findProjectRoot.ts +61 -0
  197. package/src/identifyLockstepClusterFixes.test.ts +462 -0
  198. package/src/identifyLockstepClusterFixes.ts +602 -0
  199. package/src/identifyResolutionFixes.test.ts +133 -0
  200. package/src/identifyResolutionFixes.ts +183 -0
  201. package/src/index.ts +123 -0
  202. package/src/packageDependenciesUtils.test.ts +593 -0
  203. package/src/packageDependenciesUtils.ts +156 -0
  204. package/src/packageFilterArgs.test.ts +116 -0
  205. package/src/packageFilterArgs.ts +98 -0
  206. package/src/packageStyles.test.ts +77 -0
  207. package/src/packageStyles.ts +42 -0
  208. package/src/parseBinArgs.test.ts +73 -0
  209. package/src/parseBinArgs.ts +33 -0
  210. package/src/planClusterApply.test.ts +163 -0
  211. package/src/planClusterApply.ts +105 -0
  212. package/src/renderApplyPlan.test.ts +136 -0
  213. package/src/renderApplyPlan.ts +95 -0
  214. package/src/renderDedupeSummary.test.ts +121 -0
  215. package/src/renderDedupeSummary.ts +78 -0
  216. package/src/renderDuplicatesReport.test.ts +724 -0
  217. package/src/renderDuplicatesReport.ts +645 -0
  218. package/src/reportColors.test.ts +57 -0
  219. package/src/reportColors.ts +78 -0
  220. package/src/reportText.ts +58 -0
  221. package/src/selectExplainedPackages.test.ts +67 -0
  222. package/src/selectExplainedPackages.ts +79 -0
  223. package/src/unconditionalOverrides.test.ts +99 -0
  224. package/src/unconditionalOverrides.ts +60 -0
  225. package/src/versionsSnapshot.test.ts +146 -0
  226. package/src/versionsSnapshot.ts +93 -0
  227. package/src/workspaceManifest.test.ts +220 -0
  228. package/src/workspaceManifest.ts +100 -0
@@ -0,0 +1,372 @@
1
+ import { describe, expect, it } from "bun:test";
2
+ import { identifyLockstepClusterFixes } from "./identifyLockstepClusterFixes.js";
3
+ // A family published in lockstep: `root` is requested from outside and resolved
4
+ // high, `leaf` is duplicated because of it.
5
+ const members = {
6
+ root: { npmVersions: ["2.0.0"], resolutionCount: 1 },
7
+ leaf: { npmVersions: ["1.0.0", "2.0.0"], resolutionCount: 2 },
8
+ };
9
+ const dependents = (entries) => new Map(entries.map(([name, list]) => [
10
+ name,
11
+ list.map((dependent) => ({
12
+ requester: dependent.requester,
13
+ requesterName: dependent.requesterName,
14
+ range: dependent.range,
15
+ isAlias: dependent.isAlias,
16
+ resolvedVersion: dependent.resolvedVersion,
17
+ workspace: dependent.workspace,
18
+ })),
19
+ ]));
20
+ const fixFor = (dependentsMap, map = members) => identifyLockstepClusterFixes([["leaf", "root"]], map, dependentsMap)[0];
21
+ describe("identifyLockstepClusterFixes", () => {
22
+ it("collapses the duplicate onto the version its external range still accepts", () => {
23
+ const fix = fixFor(dependents([
24
+ [
25
+ "root",
26
+ [
27
+ {
28
+ requester: "outside@1",
29
+ requesterName: "outside",
30
+ range: "^1.0.0",
31
+ },
32
+ ],
33
+ ],
34
+ [
35
+ "leaf",
36
+ [
37
+ // derived internal pin: discarded, it only exists because
38
+ // root@2.0.0 did
39
+ { requester: "root@2.0.0", requesterName: "root", range: "2.0.0" },
40
+ // the real reason leaf@1.0.0 is installed
41
+ { requester: "old@1", requesterName: "old", range: "^1.0.0" },
42
+ ],
43
+ ],
44
+ ]));
45
+ expect(fix.target).toBe("1.0.0");
46
+ expect(fix.direction).toBe("down");
47
+ expect(fix.duplicatedMembers).toEqual(["leaf"]);
48
+ expect(fix.convergentMembers).toEqual(["leaf"]);
49
+ expect(fix.excludedMembers).toEqual([]);
50
+ expect(fix.externalConstraints).toEqual([
51
+ {
52
+ requester: "old@1",
53
+ requesterName: "old",
54
+ packageName: "leaf",
55
+ range: "^1.0.0",
56
+ },
57
+ {
58
+ requester: "outside@1",
59
+ requesterName: "outside",
60
+ packageName: "root",
61
+ range: "^1.0.0",
62
+ },
63
+ ]);
64
+ // root carries the external dependent and has no 1.0.0 installed
65
+ expect(fix.reResolutionSet).toEqual(["root"]);
66
+ expect(fix.needsRoundTrip).toBe(true);
67
+ expect(fix.applicable).toBe(true);
68
+ });
69
+ it("treats an exact workspace pin as binding, never as an edit to propose", () => {
70
+ const fix = fixFor(dependents([
71
+ [
72
+ "leaf",
73
+ [
74
+ {
75
+ requester: "package.json",
76
+ range: "1.0.0",
77
+ resolvedVersion: "1.0.0",
78
+ workspace: { path: ".", depType: "devDependencies" },
79
+ },
80
+ ],
81
+ ],
82
+ ]));
83
+ // the pin is a decision: 2.0.0 is off the table for leaf, and 1.0.0 cannot
84
+ // collapse either because root@2.0.0 is not asked to move by anyone
85
+ expect(fix.workspaceChanges).toEqual([]);
86
+ expect(fix.target).toBe("1.0.0");
87
+ expect(fix.convergentMembers).toEqual(["leaf"]);
88
+ });
89
+ it("repoints an open range that ignored the pinned version", () => {
90
+ const fix = fixFor(dependents([
91
+ [
92
+ "leaf",
93
+ [
94
+ {
95
+ requester: "package.json",
96
+ range: "1.0.0",
97
+ resolvedVersion: "1.0.0",
98
+ workspace: { path: ".", depType: "devDependencies" },
99
+ },
100
+ {
101
+ requester: "plugin@1",
102
+ requesterName: "plugin",
103
+ range: "*",
104
+ resolvedVersion: "2.0.0",
105
+ },
106
+ ],
107
+ ],
108
+ ]));
109
+ expect(fix.anchor).toBe("1.0.0");
110
+ expect(fix.reuseFixes).toEqual([
111
+ {
112
+ requester: "plugin@1",
113
+ requesterName: "plugin",
114
+ packageName: "leaf",
115
+ range: "*",
116
+ from: "2.0.0",
117
+ to: "1.0.0",
118
+ },
119
+ ]);
120
+ });
121
+ it("excludes the member a third-party range rejects, keeping the rest", () => {
122
+ const fix = identifyLockstepClusterFixes([["other", "leaf", "root"]], {
123
+ ...members,
124
+ other: { npmVersions: ["1.0.0", "2.0.0"], resolutionCount: 2 },
125
+ }, dependents([
126
+ [
127
+ "leaf",
128
+ [{ requester: "user@1", requesterName: "user", range: "^2.0.0" }],
129
+ ],
130
+ [
131
+ "other",
132
+ [{ requester: "pinner@1", requesterName: "pinner", range: "~1.0.0" }],
133
+ ],
134
+ ]))[0];
135
+ expect(fix.target).toBe("2.0.0");
136
+ expect(fix.convergentMembers).toEqual(["leaf"]);
137
+ expect(fix.excludedMembers).toEqual([
138
+ {
139
+ packageName: "other",
140
+ blockedBy: [
141
+ {
142
+ requester: "pinner@1",
143
+ requesterName: "pinner",
144
+ packageName: "other",
145
+ range: "~1.0.0",
146
+ },
147
+ ],
148
+ },
149
+ ]);
150
+ expect(fix.applicable).toBe(true);
151
+ });
152
+ it("does not collapse a member an unmovable sibling pins elsewhere", () => {
153
+ // `root` cannot leave 2.0.0 (exact third-party pin), and it requests leaf at
154
+ // 2.0.0, so leaf's 1.0.0 copy cannot be collapsed onto 2.0.0 either.
155
+ const fix = fixFor(dependents([
156
+ [
157
+ "root",
158
+ [
159
+ {
160
+ requester: "outside@1",
161
+ requesterName: "outside",
162
+ range: "2.0.0",
163
+ },
164
+ ],
165
+ ],
166
+ [
167
+ "leaf",
168
+ [
169
+ { requester: "root@2.0.0", requesterName: "root", range: "2.0.0" },
170
+ { requester: "old@1", requesterName: "old", range: "1.0.0" },
171
+ ],
172
+ ],
173
+ ]));
174
+ expect(fix.applicable).toBe(false);
175
+ expect(fix.target).toBeNull();
176
+ expect(fix.direction).toBe("none");
177
+ expect(fix.convergentMembers).toEqual([]);
178
+ });
179
+ it("never targets a version the duplicated member does not carry", () => {
180
+ const fix = fixFor(dependents([["leaf", []]]), {
181
+ root: { npmVersions: ["3.0.0"], resolutionCount: 1 },
182
+ leaf: { npmVersions: ["1.0.0", "2.0.0"], resolutionCount: 2 },
183
+ });
184
+ // 3.0.0 is installed for root only: copying it over leaf is impossible
185
+ expect(fix.target).toBe("2.0.0");
186
+ expect(fix.direction).toBe("up");
187
+ });
188
+ it("proposes the pin change only when nothing else deduplicates", () => {
189
+ // `other` is exactly pinned at 2.0.0 by a third party and requests leaf at
190
+ // 2.0.0, so 1.0.0 collapses nothing; the workspace's own `~1.0.0` on leaf is
191
+ // the only thing standing between the family and 2.0.0, so it is proposed.
192
+ const fix = identifyLockstepClusterFixes([["leaf", "other"]], {
193
+ leaf: { npmVersions: ["1.0.0", "2.0.0"], resolutionCount: 2 },
194
+ other: { npmVersions: ["2.0.0"], resolutionCount: 1 },
195
+ }, dependents([
196
+ [
197
+ "leaf",
198
+ [
199
+ {
200
+ requester: "package.json",
201
+ range: "~1.0.0",
202
+ resolvedVersion: "1.0.0",
203
+ workspace: { path: ".", depType: "devDependencies" },
204
+ },
205
+ {
206
+ requester: "other@2.0.0",
207
+ requesterName: "other",
208
+ range: "2.0.0",
209
+ },
210
+ ],
211
+ ],
212
+ [
213
+ "other",
214
+ [{ requester: "app@1", requesterName: "app", range: "2.0.0" }],
215
+ ],
216
+ ]))[0];
217
+ expect(fix.target).toBe("2.0.0");
218
+ expect(fix.convergentMembers).toEqual(["leaf"]);
219
+ expect(fix.workspaceChanges.map((change) => `${change.range} -> ${change.to}`)).toEqual(["~1.0.0 -> 2.0.0"]);
220
+ });
221
+ it("never proposes editing a workspace range declared under an alias key", () => {
222
+ // Same shape as above, with the workspace declaring leaf through a key of
223
+ // its own (`"leaf-1": "npm:leaf@~1.0.0"`). Moving that range to 2.0.0 would
224
+ // not converge the family, it would repoint the alias at the copy it was
225
+ // created to sit beside — so the range blocks 2.0.0 like a third party's,
226
+ // and the cluster is reported as not dedupable.
227
+ const fix = identifyLockstepClusterFixes([["leaf", "other"]], {
228
+ leaf: { npmVersions: ["1.0.0", "2.0.0"], resolutionCount: 2 },
229
+ other: { npmVersions: ["2.0.0"], resolutionCount: 1 },
230
+ }, dependents([
231
+ [
232
+ "leaf",
233
+ [
234
+ {
235
+ requester: "package.json",
236
+ range: "~1.0.0",
237
+ isAlias: true,
238
+ resolvedVersion: "1.0.0",
239
+ workspace: { path: ".", depType: "devDependencies" },
240
+ },
241
+ {
242
+ requester: "other@2.0.0",
243
+ requesterName: "other",
244
+ range: "2.0.0",
245
+ },
246
+ ],
247
+ ],
248
+ [
249
+ "other",
250
+ [{ requester: "app@1", requesterName: "app", range: "2.0.0" }],
251
+ ],
252
+ ]))[0];
253
+ expect(fix.target).toBe(null);
254
+ expect(fix.applicable).toBe(false);
255
+ expect(fix.workspaceChanges).toEqual([]);
256
+ });
257
+ it("prefers a target that respects the workspace range over one that edits it", () => {
258
+ // 1.0.0 collapses leaf without touching the workspace range, so the 2.0.0
259
+ // candidate — which would need it widened — must not win.
260
+ const fix = identifyLockstepClusterFixes([["leaf", "other"]], {
261
+ leaf: { npmVersions: ["1.0.0", "2.0.0"], resolutionCount: 2 },
262
+ other: { npmVersions: ["1.0.0"], resolutionCount: 1 },
263
+ }, dependents([
264
+ [
265
+ "leaf",
266
+ [
267
+ {
268
+ requester: "package.json",
269
+ range: "~1.0.0",
270
+ resolvedVersion: "1.0.0",
271
+ workspace: { path: ".", depType: "devDependencies" },
272
+ },
273
+ { requester: "old@1", requesterName: "old", range: "^1.0.0" },
274
+ ],
275
+ ],
276
+ ]))[0];
277
+ expect(fix.target).toBe("1.0.0");
278
+ expect(fix.workspaceChanges).toEqual([]);
279
+ });
280
+ it("lets the resolver move a member nothing pins, without naming a version", () => {
281
+ // `holder` sits at 2.0.0 and pins leaf there, but its only requester asks
282
+ // through `*`: it has to move for leaf to collapse onto 1.0.0, and which
283
+ // version it lands on is the resolver's call.
284
+ const fix = identifyLockstepClusterFixes([["holder", "leaf"]], {
285
+ holder: { npmVersions: ["2.0.0"], resolutionCount: 1 },
286
+ leaf: { npmVersions: ["1.0.0", "2.0.0"], resolutionCount: 2 },
287
+ }, dependents([
288
+ ["holder", [{ requester: "app@1", requesterName: "app", range: "*" }]],
289
+ [
290
+ "leaf",
291
+ [
292
+ {
293
+ requester: "holder@2.0.0",
294
+ requesterName: "holder",
295
+ range: "2.0.0",
296
+ },
297
+ { requester: "old@1", requesterName: "old", range: "1.0.0" },
298
+ ],
299
+ ],
300
+ ]))[0];
301
+ expect(fix.target).toBe("1.0.0");
302
+ expect(fix.convergentMembers).toEqual(["leaf"]);
303
+ expect(fix.floatingMembers).toEqual(["holder"]);
304
+ // no version is claimed for a floating member
305
+ expect(fix.reResolutionSet).toEqual([]);
306
+ expect(fix.needsRoundTrip).toBe(true);
307
+ });
308
+ it("proposes the pin upgrade when the family is only pulled forward", () => {
309
+ // The shape of a metro family under a pinned metro: a third party outside
310
+ // the cluster requires `^2.0.0`, the workspace pins 1.0.0, and every member
311
+ // carries both. Nothing collapses on 1.0.0 (the third party rules it out),
312
+ // so the only solution left edits the pin.
313
+ const fix = identifyLockstepClusterFixes([["family", "family-config"]], {
314
+ family: { npmVersions: ["1.0.0", "2.0.0"], resolutionCount: 2 },
315
+ "family-config": {
316
+ npmVersions: ["1.0.0", "2.0.0"],
317
+ resolutionCount: 2,
318
+ },
319
+ }, dependents([
320
+ [
321
+ "family",
322
+ [
323
+ {
324
+ requester: "package.json",
325
+ range: "1.0.0",
326
+ resolvedVersion: "1.0.0",
327
+ workspace: { path: ".", depType: "devDependencies" },
328
+ },
329
+ { requester: "cli@2.0.0", requesterName: "cli", range: "^2.0.0" },
330
+ {
331
+ requester: "family-config@1.0.0",
332
+ requesterName: "family-config",
333
+ range: "1.0.0",
334
+ },
335
+ {
336
+ requester: "family-config@2.0.0",
337
+ requesterName: "family-config",
338
+ range: "2.0.0",
339
+ },
340
+ ],
341
+ ],
342
+ [
343
+ "family-config",
344
+ [
345
+ { requester: "plugin@1", requesterName: "plugin", range: "*" },
346
+ {
347
+ requester: "family@1.0.0",
348
+ requesterName: "family",
349
+ range: "1.0.0",
350
+ },
351
+ {
352
+ requester: "family@2.0.0",
353
+ requesterName: "family",
354
+ range: "2.0.0",
355
+ },
356
+ ],
357
+ ],
358
+ ]))[0];
359
+ expect(fix.target).toBe("2.0.0");
360
+ expect(fix.direction).toBe("up");
361
+ expect(fix.convergentMembers).toEqual(["family", "family-config"]);
362
+ expect(fix.driverMembers).toEqual(["family"]);
363
+ expect(fix.workspaceChanges.map((change) => `${change.packageName} ${change.range} -> ${change.to}`)).toEqual(["family 1.0.0 -> 2.0.0"]);
364
+ });
365
+ it("skips a cluster with no duplicated member", () => {
366
+ expect(identifyLockstepClusterFixes([["a", "b"]], {
367
+ a: { npmVersions: ["1.0.0"], resolutionCount: 1 },
368
+ b: { npmVersions: ["1.0.0"], resolutionCount: 1 },
369
+ }, new Map())).toEqual([]);
370
+ });
371
+ });
372
+ //# sourceMappingURL=identifyLockstepClusterFixes.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identifyLockstepClusterFixes.test.js","sourceRoot":"","sources":["../src/identifyLockstepClusterFixes.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,102 @@
1
+ import semver from "semver";
2
+ import { createCandidateVersionComparator } from "./compareCandidateVersions.js";
3
+ const isNpmResolution = (resolution) => resolution.package.type === "npm";
4
+ /**
5
+ * Dedupe opportunities reachable one package at a time. A family of co-versioned
6
+ * packages whose duplicate survives only because a few members resolve high is
7
+ * invisible here and is handled by `identifyLockstepClusterFixes`.
8
+ */
9
+ export const identifyResolutionFixes = (resolutions, dependents) => {
10
+ if (resolutions.length <= 1) {
11
+ return [];
12
+ }
13
+ const onlyNpmResolutions = resolutions.filter(isNpmResolution);
14
+ const fixes = [];
15
+ const packageNames = new Set(resolutions.map((resolution) => resolution.package.name));
16
+ for (const packageName of packageNames) {
17
+ const npmVersions = onlyNpmResolutions
18
+ .filter((resolution) => resolution.package.name === packageName)
19
+ .map((resolution) => ({
20
+ resolution,
21
+ version: resolution.package.version,
22
+ satisfies: new Set(),
23
+ }));
24
+ // Every requester can declare the package through something semver cannot
25
+ // read — a git url, `file:`, `workspace:` — and such a declaration is
26
+ // dropped here rather than treated as a range nothing satisfies, which
27
+ // would suppress the merges the real dependents allow. Once they are gone
28
+ // nothing may vouch for a merge at all, and the "one candidate covers every
29
+ // dependent" test below would read the empty set as full coverage.
30
+ const packageDependents = dependents
31
+ .get(packageName)
32
+ ?.filter((dependent) => !dependent.nonSemver);
33
+ if (!packageDependents || packageDependents.length === 0)
34
+ continue;
35
+ packageDependents.forEach((dependent) => {
36
+ for (const { version, satisfies } of npmVersions) {
37
+ if (semver.satisfies(version, dependent.version, {
38
+ includePrerelease: true,
39
+ })) {
40
+ satisfies.add(dependent);
41
+ }
42
+ }
43
+ });
44
+ const byVersionAscending = (resolutionA, resolutionB) => {
45
+ const versionA = npmVersions.find((candidate) => candidate.resolution.resolution === resolutionA)?.version;
46
+ const versionB = npmVersions.find((candidate) => candidate.resolution.resolution === resolutionB)?.version;
47
+ return versionA && versionB ? semver.compare(versionA, versionB) : 0;
48
+ };
49
+ const satisfiedCountByVersion = new Map(npmVersions.map(({ version, satisfies }) => [version, satisfies.size]));
50
+ const compareCandidates = createCandidateVersionComparator({
51
+ satisfiedCountOf: (version) => satisfiedCountByVersion.get(version) ?? 0,
52
+ });
53
+ // the best-ranked candidate satisfies everyone whenever any candidate does
54
+ const [bestCandidate] = npmVersions.toSorted((candidateA, candidateB) => compareCandidates(candidateA.version, candidateB.version));
55
+ if (bestCandidate?.satisfies.size === packageDependents.length) {
56
+ const mergeableResolutions = npmVersions
57
+ .map((candidate) => candidate.resolution.resolution)
58
+ .toSorted(byVersionAscending);
59
+ if (mergeableResolutions.length > 1) {
60
+ fixes.push({
61
+ mergeableResolutions,
62
+ to: bestCandidate.resolution.resolution,
63
+ });
64
+ }
65
+ continue;
66
+ }
67
+ // no single candidate satisfies all dependents — do greedy assignment from highest -> lowest
68
+ npmVersions.sort((candidateA, candidateB) => semver.rcompare(candidateA.version, candidateB.version));
69
+ const unassigned = new Set(packageDependents);
70
+ for (const candidate of npmVersions) {
71
+ const newlyAssigned = new Set([...candidate.satisfies].filter((dependent) => unassigned.has(dependent)));
72
+ if (newlyAssigned.size === 0)
73
+ continue;
74
+ const mergeable = npmVersions.filter((other) => {
75
+ if (other === candidate)
76
+ return false;
77
+ const intersects = [...other.satisfies].some((dependent) => newlyAssigned.has(dependent));
78
+ if (!intersects)
79
+ return false;
80
+ // merging is safe only when the candidate satisfies every dependent the
81
+ // resolution being merged away was serving
82
+ return [...other.satisfies].every((dependent) => candidate.satisfies.has(dependent));
83
+ });
84
+ const mergeableResolutions = [
85
+ candidate.resolution.resolution,
86
+ ...mergeable.map((other) => other.resolution.resolution),
87
+ ].toSorted(byVersionAscending);
88
+ if (mergeableResolutions.length > 1) {
89
+ fixes.push({
90
+ mergeableResolutions,
91
+ to: candidate.resolution.resolution,
92
+ });
93
+ }
94
+ for (const dependent of newlyAssigned)
95
+ unassigned.delete(dependent);
96
+ if (unassigned.size === 0)
97
+ break;
98
+ }
99
+ }
100
+ return fixes;
101
+ };
102
+ //# sourceMappingURL=identifyResolutionFixes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identifyResolutionFixes.js","sourceRoot":"","sources":["../src/identifyResolutionFixes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ import { describe, expect, it } from "bun:test";
2
+ import { identifyResolutionFixes } from "./identifyResolutionFixes.js";
3
+ const npm = (name, version) => ({
4
+ resolution: `${name}@${version}`,
5
+ package: { type: "npm", name, version },
6
+ });
7
+ const workspace = (name) => ({
8
+ resolution: `${name}@workspace`,
9
+ package: { type: "workspace", name },
10
+ });
11
+ const dependentsOf = (name, ranges) => new Map([[name, ranges.map((version) => ({ version }))]]);
12
+ describe("identifyResolutionFixes", () => {
13
+ it("ignores a dependent whose declaration semver cannot read", () => {
14
+ // read as a range, `workspace:*` satisfies neither candidate and the merge
15
+ // the real dependent allows would be suppressed
16
+ const dependents = new Map([
17
+ [
18
+ "lib",
19
+ [{ version: "^1.0.0" }, { version: "workspace:*", nonSemver: true }],
20
+ ],
21
+ ]);
22
+ expect(identifyResolutionFixes([npm("lib", "1.0.0"), npm("lib", "1.2.0")], dependents)).toEqual([
23
+ { mergeableResolutions: ["lib@1.0.0", "lib@1.2.0"], to: "lib@1.2.0" },
24
+ ]);
25
+ });
26
+ it("vouches for no merge when every dependent is unreadable", () => {
27
+ const dependents = new Map([
28
+ ["lib", [{ version: "workspace:*", nonSemver: true }]],
29
+ ]);
30
+ expect(identifyResolutionFixes([npm("lib", "1.0.0"), npm("lib", "1.2.0")], dependents)).toEqual([]);
31
+ });
32
+ it("returns nothing for fewer than two resolutions", () => {
33
+ expect(identifyResolutionFixes([], new Map())).toEqual([]);
34
+ expect(identifyResolutionFixes([npm("lib", "1.0.0")], new Map())).toEqual([]);
35
+ });
36
+ it("merges every resolution onto the highest version satisfying all dependents", () => {
37
+ const fixes = identifyResolutionFixes([npm("lib", "1.0.0"), npm("lib", "1.2.0")], dependentsOf("lib", ["^1.0.0", "^1.2.0"]));
38
+ expect(fixes).toEqual([
39
+ { mergeableResolutions: ["lib@1.0.0", "lib@1.2.0"], to: "lib@1.2.0" },
40
+ ]);
41
+ });
42
+ it("partitions greedily when no version satisfies every dependent", () => {
43
+ const fixes = identifyResolutionFixes([npm("lib", "1.0.0"), npm("lib", "1.2.0"), npm("lib", "2.0.0")], dependentsOf("lib", ["^1.0.0", "^1.2.0", "^2.0.0"]));
44
+ expect(fixes).toEqual([
45
+ { mergeableResolutions: ["lib@1.0.0", "lib@1.2.0"], to: "lib@1.2.0" },
46
+ ]);
47
+ });
48
+ it("refuses a merge that would drop a dependent the merged resolution served", () => {
49
+ // `>=1.0.0` accepts 2.0.0, but merging 1.2.0 into it would strand `^1.0.0`
50
+ const fixes = identifyResolutionFixes([npm("lib", "1.0.0"), npm("lib", "1.2.0"), npm("lib", "2.0.0")], dependentsOf("lib", [">=1.0.0", "^1.0.0", "2.0.0"]));
51
+ expect(fixes).toEqual([
52
+ { mergeableResolutions: ["lib@1.0.0", "lib@1.2.0"], to: "lib@1.2.0" },
53
+ ]);
54
+ });
55
+ it("ignores resolutions that are not npm packages", () => {
56
+ const fixes = identifyResolutionFixes([npm("lib", "1.0.0"), workspace("lib"), npm("lib", "1.2.0")], dependentsOf("lib", ["^1.0.0", "^1.2.0"]));
57
+ expect(fixes).toEqual([
58
+ { mergeableResolutions: ["lib@1.0.0", "lib@1.2.0"], to: "lib@1.2.0" },
59
+ ]);
60
+ });
61
+ // Reachable as soon as every requester declares the package through something
62
+ // semver cannot read: those declarations are left out of the map, and no range
63
+ // is left to vouch for a merge. Reading the empty set as "one candidate covers
64
+ // every dependent" would merge the whole package on no evidence at all.
65
+ it("proposes nothing for a package with no collected dependents", () => {
66
+ expect(identifyResolutionFixes([npm("lib", "1.0.0"), npm("lib", "1.2.0")], new Map())).toEqual([]);
67
+ expect(identifyResolutionFixes([npm("lib", "1.0.0"), npm("lib", "1.2.0")], new Map([["lib", []]]))).toEqual([]);
68
+ });
69
+ });
70
+ //# sourceMappingURL=identifyResolutionFixes.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identifyResolutionFixes.test.js","sourceRoot":"","sources":["../src/identifyResolutionFixes.test.ts"],"names":[],"mappings":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,24 @@
1
+ export { PackageDescriptorNameUtils, PackageDependencyDescriptorUtils, isNpmProtocol, isSemverComparable, } from "./packageDependenciesUtils.js";
2
+ export { buildLockstepClusters } from "./buildLockstepClusters.js";
3
+ export { identifyLockstepClusterFixes } from "./identifyLockstepClusterFixes.js";
4
+ export { identifyResolutionFixes } from "./identifyResolutionFixes.js";
5
+ export { buildIdentifiedFixesMap } from "./buildIdentifiedFixesMap.js";
6
+ export { createPackageFilter, describeSkippedClusterFix, selectClusterFixes, selectPackages, } from "./createPackageFilter.js";
7
+ export { packageFilterParseArgsOptions, packageFilterUsage, toPackageFilterOptions, toWhyDuplicateRequest, whyDuplicateParseArgsOptions, whyDuplicateUsage, } from "./packageFilterArgs.js";
8
+ export { parseBinArgs } from "./parseBinArgs.js";
9
+ export { findProjectRoot, resolveProjectDir } from "./findProjectRoot.js";
10
+ export { createCandidateVersionComparator } from "./compareCandidateVersions.js";
11
+ export { clusterLabel } from "./clusterLabel.js";
12
+ export { createColorize, shouldColorize } from "./reportColors.js";
13
+ export { stylePackageName, stylePackageReference } from "./packageStyles.js";
14
+ export { renderDuplicatesReport } from "./renderDuplicatesReport.js";
15
+ export { selectExplainedPackages } from "./selectExplainedPackages.js";
16
+ export { renderApplyPlan } from "./renderApplyPlan.js";
17
+ export { renderDedupeSummary } from "./renderDedupeSummary.js";
18
+ export { buildVersionsSnapshot, countDuplicatedPackages, diffVersionsSnapshots, } from "./versionsSnapshot.js";
19
+ export { captureFiles, restoreFiles } from "./fileSnapshot.js";
20
+ export { applyWorkspaceRangeEdit, nextSelector } from "./workspaceManifest.js";
21
+ export { planClusterApply } from "./planClusterApply.js";
22
+ export { diffDuplicates } from "./duplicateSnapshot.js";
23
+ export { partitionUnconditionalOverrides } from "./unconditionalOverrides.js";
24
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,0BAA0B,EAC1B,gCAAgC,EAChC,aAAa,EACb,kBAAkB,GACnB,MAAM,+BAA+B,CAAC;AAKvC,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAKnE,OAAO,EAAE,4BAA4B,EAAE,MAAM,mCAAmC,CAAC;AAcjF,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AASvE,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAEvE,OAAO,EACL,mBAAmB,EACnB,yBAAyB,EACzB,kBAAkB,EAClB,cAAc,GACf,MAAM,0BAA0B,CAAC;AAOlC,OAAO,EACL,6BAA6B,EAC7B,kBAAkB,EAClB,sBAAsB,EACtB,qBAAqB,EACrB,4BAA4B,EAC5B,iBAAiB,GAClB,MAAM,wBAAwB,CAAC;AAMhC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAE1E,OAAO,EAAE,gCAAgC,EAAE,MAAM,+BAA+B,CAAC;AAEjF,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnE,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC7E,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AASrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAKvE,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAMvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAE/D,OAAO,EACL,qBAAqB,EACrB,uBAAuB,EACvB,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAM/B,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAE/D,OAAO,EAAE,uBAAuB,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE/E,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAMzD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,OAAO,EAAE,+BAA+B,EAAE,MAAM,6BAA6B,CAAC"}