pnpm-dedup 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +218 -0
- package/dist/applyClusterFixes.js +252 -0
- package/dist/applyClusterFixes.js.map +1 -0
- package/dist/applyClusterFixes.test.js +474 -0
- package/dist/applyClusterFixes.test.js.map +1 -0
- package/dist/bin/pnpm-dedupe.js +35 -0
- package/dist/bin/pnpm-dedupe.js.map +1 -0
- package/dist/bin/pnpm-why-duplicate.js +23 -0
- package/dist/bin/pnpm-why-duplicate.js.map +1 -0
- package/dist/bin/pnpm-why-duplicate.test.js +57 -0
- package/dist/bin/pnpm-why-duplicate.test.js.map +1 -0
- package/dist/dedupe.js +86 -0
- package/dist/dedupe.js.map +1 -0
- package/dist/dedupeCheckConsistency.test.js +149 -0
- package/dist/dedupeCheckConsistency.test.js.map +1 -0
- package/dist/dedupeEndToEnd.test.js +121 -0
- package/dist/dedupeEndToEnd.test.js.map +1 -0
- package/dist/dedupePeers.test.js +105 -0
- package/dist/dedupePeers.test.js.map +1 -0
- package/dist/definitions/applyClusterFixes.d.ts +24 -0
- package/dist/definitions/applyClusterFixes.d.ts.map +1 -0
- package/dist/definitions/applyClusterFixes.test.d.ts +2 -0
- package/dist/definitions/applyClusterFixes.test.d.ts.map +1 -0
- package/dist/definitions/bin/pnpm-dedupe.d.ts +3 -0
- package/dist/definitions/bin/pnpm-dedupe.d.ts.map +1 -0
- package/dist/definitions/bin/pnpm-why-duplicate.d.ts +3 -0
- package/dist/definitions/bin/pnpm-why-duplicate.d.ts.map +1 -0
- package/dist/definitions/bin/pnpm-why-duplicate.test.d.ts +2 -0
- package/dist/definitions/bin/pnpm-why-duplicate.test.d.ts.map +1 -0
- package/dist/definitions/dedupe.d.ts +15 -0
- package/dist/definitions/dedupe.d.ts.map +1 -0
- package/dist/definitions/dedupeCheckConsistency.test.d.ts +2 -0
- package/dist/definitions/dedupeCheckConsistency.test.d.ts.map +1 -0
- package/dist/definitions/dedupeEndToEnd.test.d.ts +2 -0
- package/dist/definitions/dedupeEndToEnd.test.d.ts.map +1 -0
- package/dist/definitions/dedupePeers.test.d.ts +2 -0
- package/dist/definitions/dedupePeers.test.d.ts.map +1 -0
- package/dist/definitions/displayMany.d.ts +16 -0
- package/dist/definitions/displayMany.d.ts.map +1 -0
- package/dist/definitions/displayMany.test.d.ts +2 -0
- package/dist/definitions/displayMany.test.d.ts.map +1 -0
- package/dist/definitions/helpers/buildIdentifiedFixesMap.test.d.ts +2 -0
- package/dist/definitions/helpers/buildIdentifiedFixesMap.test.d.ts.map +1 -0
- package/dist/definitions/helpers/buildPnpmPackagesMap.d.ts +13 -0
- package/dist/definitions/helpers/buildPnpmPackagesMap.d.ts.map +1 -0
- package/dist/definitions/helpers/buildPnpmPackagesMap.test.d.ts +2 -0
- package/dist/definitions/helpers/buildPnpmPackagesMap.test.d.ts.map +1 -0
- package/dist/definitions/helpers/collectDependentRanges.d.ts +22 -0
- package/dist/definitions/helpers/collectDependentRanges.d.ts.map +1 -0
- package/dist/definitions/helpers/collectDependentRanges.test.d.ts +2 -0
- package/dist/definitions/helpers/collectDependentRanges.test.d.ts.map +1 -0
- package/dist/definitions/helpers/collectPnpmDependents.d.ts +8 -0
- package/dist/definitions/helpers/collectPnpmDependents.d.ts.map +1 -0
- package/dist/definitions/helpers/collectPnpmDependents.test.d.ts +2 -0
- package/dist/definitions/helpers/collectPnpmDependents.test.d.ts.map +1 -0
- package/dist/definitions/helpers/duplicateSnapshot.d.ts +3 -0
- package/dist/definitions/helpers/duplicateSnapshot.d.ts.map +1 -0
- package/dist/definitions/helpers/parsePnpmLockPackages.d.ts +44 -0
- package/dist/definitions/helpers/parsePnpmLockPackages.d.ts.map +1 -0
- package/dist/definitions/helpers/parsePnpmLockPackages.test.d.ts +2 -0
- package/dist/definitions/helpers/parsePnpmLockPackages.test.d.ts.map +1 -0
- package/dist/definitions/helpers/pnpmProtocol.d.ts +2 -0
- package/dist/definitions/helpers/pnpmProtocol.d.ts.map +1 -0
- package/dist/definitions/helpers/pnpmVersion.d.ts +11 -0
- package/dist/definitions/helpers/pnpmVersion.d.ts.map +1 -0
- package/dist/definitions/helpers/pnpmWorkspaceYaml.d.ts +23 -0
- package/dist/definitions/helpers/pnpmWorkspaceYaml.d.ts.map +1 -0
- package/dist/definitions/helpers/pnpmWorkspaceYaml.test.d.ts +2 -0
- package/dist/definitions/helpers/pnpmWorkspaceYaml.test.d.ts.map +1 -0
- package/dist/definitions/helpers/projectDir.d.ts +3 -0
- package/dist/definitions/helpers/projectDir.d.ts.map +1 -0
- package/dist/definitions/helpers/readInstalledManifest.d.ts +32 -0
- package/dist/definitions/helpers/readInstalledManifest.d.ts.map +1 -0
- package/dist/definitions/helpers/readInstalledManifest.test.d.ts +2 -0
- package/dist/definitions/helpers/readInstalledManifest.test.d.ts.map +1 -0
- package/dist/definitions/helpers/runPnpm.d.ts +11 -0
- package/dist/definitions/helpers/runPnpm.d.ts.map +1 -0
- package/dist/definitions/helpers/tempProjects.d.ts +8 -0
- package/dist/definitions/helpers/tempProjects.d.ts.map +1 -0
- package/dist/definitions/helpers/toLockstepGraph.d.ts +12 -0
- package/dist/definitions/helpers/toLockstepGraph.d.ts.map +1 -0
- package/dist/definitions/helpers/toLockstepGraph.test.d.ts +2 -0
- package/dist/definitions/helpers/toLockstepGraph.test.d.ts.map +1 -0
- package/dist/definitions/helpers/versionsSnapshot.d.ts +3 -0
- package/dist/definitions/helpers/versionsSnapshot.d.ts.map +1 -0
- package/dist/definitions/identifyClusterFixes.d.ts +7 -0
- package/dist/definitions/identifyClusterFixes.d.ts.map +1 -0
- package/dist/definitions/identifyClusterFixes.test.d.ts +2 -0
- package/dist/definitions/identifyClusterFixes.test.d.ts.map +1 -0
- package/dist/definitions/identifyResolutionFixes.test.d.ts +2 -0
- package/dist/definitions/identifyResolutionFixes.test.d.ts.map +1 -0
- package/dist/definitions/index.d.ts +25 -0
- package/dist/definitions/index.d.ts.map +1 -0
- package/dist/definitions/pnpmLockTypes.d.ts +42 -0
- package/dist/definitions/pnpmLockTypes.d.ts.map +1 -0
- package/dist/definitions/readPnpmLock.d.ts +3 -0
- package/dist/definitions/readPnpmLock.d.ts.map +1 -0
- package/dist/displayMany.js +56 -0
- package/dist/displayMany.js.map +1 -0
- package/dist/displayMany.test.js +146 -0
- package/dist/displayMany.test.js.map +1 -0
- package/dist/helpers/buildIdentifiedFixesMap.test.js +27 -0
- package/dist/helpers/buildIdentifiedFixesMap.test.js.map +1 -0
- package/dist/helpers/buildPnpmPackagesMap.js +23 -0
- package/dist/helpers/buildPnpmPackagesMap.js.map +1 -0
- package/dist/helpers/buildPnpmPackagesMap.test.js +68 -0
- package/dist/helpers/buildPnpmPackagesMap.test.js.map +1 -0
- package/dist/helpers/collectDependentRanges.js +118 -0
- package/dist/helpers/collectDependentRanges.js.map +1 -0
- package/dist/helpers/collectDependentRanges.test.js +211 -0
- package/dist/helpers/collectDependentRanges.test.js.map +1 -0
- package/dist/helpers/collectPnpmDependents.js +55 -0
- package/dist/helpers/collectPnpmDependents.js.map +1 -0
- package/dist/helpers/collectPnpmDependents.test.js +78 -0
- package/dist/helpers/collectPnpmDependents.test.js.map +1 -0
- package/dist/helpers/duplicateSnapshot.js +5 -0
- package/dist/helpers/duplicateSnapshot.js.map +1 -0
- package/dist/helpers/parsePnpmLockPackages.js +62 -0
- package/dist/helpers/parsePnpmLockPackages.js.map +1 -0
- package/dist/helpers/parsePnpmLockPackages.test.js +62 -0
- package/dist/helpers/parsePnpmLockPackages.test.js.map +1 -0
- package/dist/helpers/pnpmProtocol.js +2 -0
- package/dist/helpers/pnpmProtocol.js.map +1 -0
- package/dist/helpers/pnpmVersion.js +23 -0
- package/dist/helpers/pnpmVersion.js.map +1 -0
- package/dist/helpers/pnpmWorkspaceYaml.js +48 -0
- package/dist/helpers/pnpmWorkspaceYaml.js.map +1 -0
- package/dist/helpers/pnpmWorkspaceYaml.test.js +91 -0
- package/dist/helpers/pnpmWorkspaceYaml.test.js.map +1 -0
- package/dist/helpers/projectDir.js +10 -0
- package/dist/helpers/projectDir.js.map +1 -0
- package/dist/helpers/readInstalledManifest.js +139 -0
- package/dist/helpers/readInstalledManifest.js.map +1 -0
- package/dist/helpers/readInstalledManifest.test.js +83 -0
- package/dist/helpers/readInstalledManifest.test.js.map +1 -0
- package/dist/helpers/runPnpm.js +16 -0
- package/dist/helpers/runPnpm.js.map +1 -0
- package/dist/helpers/tempProjects.js +85 -0
- package/dist/helpers/tempProjects.js.map +1 -0
- package/dist/helpers/toLockstepGraph.js +34 -0
- package/dist/helpers/toLockstepGraph.js.map +1 -0
- package/dist/helpers/toLockstepGraph.test.js +81 -0
- package/dist/helpers/toLockstepGraph.test.js.map +1 -0
- package/dist/helpers/versionsSnapshot.js +5 -0
- package/dist/helpers/versionsSnapshot.js.map +1 -0
- package/dist/identifyClusterFixes.js +53 -0
- package/dist/identifyClusterFixes.js.map +1 -0
- package/dist/identifyClusterFixes.test.js +307 -0
- package/dist/identifyClusterFixes.test.js.map +1 -0
- package/dist/identifyResolutionFixes.test.js +49 -0
- package/dist/identifyResolutionFixes.test.js.map +1 -0
- package/dist/index.js +86 -0
- package/dist/index.js.map +1 -0
- package/dist/pnpmLockTypes.js +7 -0
- package/dist/pnpmLockTypes.js.map +1 -0
- package/dist/readPnpmLock.js +6 -0
- package/dist/readPnpmLock.js.map +1 -0
- package/package.json +71 -0
- package/src/applyClusterFixes.test.ts +560 -0
- package/src/applyClusterFixes.ts +466 -0
- package/src/bin/pnpm-dedupe.ts +44 -0
- package/src/bin/pnpm-why-duplicate.test.ts +79 -0
- package/src/bin/pnpm-why-duplicate.ts +34 -0
- package/src/dedupe.ts +124 -0
- package/src/dedupeCheckConsistency.test.ts +218 -0
- package/src/dedupeEndToEnd.test.ts +168 -0
- package/src/dedupePeers.test.ts +175 -0
- package/src/displayMany.test.ts +208 -0
- package/src/displayMany.ts +104 -0
- package/src/helpers/buildIdentifiedFixesMap.test.ts +33 -0
- package/src/helpers/buildPnpmPackagesMap.test.ts +119 -0
- package/src/helpers/buildPnpmPackagesMap.ts +53 -0
- package/src/helpers/collectDependentRanges.test.ts +311 -0
- package/src/helpers/collectDependentRanges.ts +185 -0
- package/src/helpers/collectPnpmDependents.test.ts +112 -0
- package/src/helpers/collectPnpmDependents.ts +79 -0
- package/src/helpers/duplicateSnapshot.ts +18 -0
- package/src/helpers/parsePnpmLockPackages.test.ts +93 -0
- package/src/helpers/parsePnpmLockPackages.ts +99 -0
- package/src/helpers/pnpmProtocol.ts +12 -0
- package/src/helpers/pnpmVersion.ts +28 -0
- package/src/helpers/pnpmWorkspaceYaml.test.ts +140 -0
- package/src/helpers/pnpmWorkspaceYaml.ts +77 -0
- package/src/helpers/projectDir.ts +14 -0
- package/src/helpers/readInstalledManifest.test.ts +124 -0
- package/src/helpers/readInstalledManifest.ts +202 -0
- package/src/helpers/runPnpm.ts +34 -0
- package/src/helpers/tempProjects.ts +94 -0
- package/src/helpers/toLockstepGraph.test.ts +98 -0
- package/src/helpers/toLockstepGraph.ts +52 -0
- package/src/helpers/versionsSnapshot.ts +9 -0
- package/src/identifyClusterFixes.test.ts +430 -0
- package/src/identifyClusterFixes.ts +114 -0
- package/src/identifyResolutionFixes.test.ts +82 -0
- package/src/index.ts +159 -0
- package/src/pnpmLockTypes.ts +44 -0
- package/src/readPnpmLock.ts +7 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { readFileSync, readdirSync } from "node:fs";
|
|
2
|
+
import { isAbsolute, join } from "node:path";
|
|
3
|
+
import { parse as parseYaml } from "yaml";
|
|
4
|
+
import { stripPeerSuffix } from "./parsePnpmLockPackages.js";
|
|
5
|
+
const readManifestFile = (path) => {
|
|
6
|
+
try {
|
|
7
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
const readdirOrEmpty = (path) => {
|
|
14
|
+
try {
|
|
15
|
+
return readdirSync(path);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
const readModulesState = (nodeModulesDir) => {
|
|
22
|
+
const hoistedLocations = new Map();
|
|
23
|
+
const parsed = (() => {
|
|
24
|
+
try {
|
|
25
|
+
const content = parseYaml(readFileSync(join(nodeModulesDir, ".modules.yaml"), "utf8"));
|
|
26
|
+
return typeof content === "object" && content !== null
|
|
27
|
+
? content
|
|
28
|
+
: undefined;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
})();
|
|
34
|
+
const hoisted = parsed?.hoistedLocations;
|
|
35
|
+
if (typeof hoisted === "object" && hoisted !== null) {
|
|
36
|
+
for (const [depPath, paths] of Object.entries(hoisted)) {
|
|
37
|
+
if (!Array.isArray(paths))
|
|
38
|
+
continue;
|
|
39
|
+
const key = stripPeerSuffix(depPath);
|
|
40
|
+
hoistedLocations.set(key, [
|
|
41
|
+
...(hoistedLocations.get(key) ?? []),
|
|
42
|
+
...paths.filter((path) => typeof path === "string"),
|
|
43
|
+
]);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const virtualStoreDir = parsed?.virtualStoreDir;
|
|
47
|
+
return {
|
|
48
|
+
virtualStoreDir: typeof virtualStoreDir === "string" ? virtualStoreDir : ".pnpm",
|
|
49
|
+
hoistedLocations,
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Build a reader that locates an installed package's manifest, whichever
|
|
54
|
+
* node-linker the project uses:
|
|
55
|
+
*
|
|
56
|
+
* - virtual store (pnpm's default): the dep path gives the directory,
|
|
57
|
+
* `node_modules/.pnpm/<name with / replaced by +>@<version>[(peers)|_hash]/node_modules/<name>`;
|
|
58
|
+
* - `nodeLinker: hoisted`: the layout is npm-shaped and placement is a hoisting
|
|
59
|
+
* decision, so `node_modules/.modules.yaml` is consulted for the exact paths;
|
|
60
|
+
* a flat `node_modules/<name>` and one level of nesting are tried as a
|
|
61
|
+
* fallback when that file is missing.
|
|
62
|
+
*
|
|
63
|
+
* `stats` tells callers whether anything was readable at all: every declared
|
|
64
|
+
* range comes from these manifests, so a reader that finds nothing silently
|
|
65
|
+
* turns every dependent's range into an exact pin and hides every fix.
|
|
66
|
+
*/
|
|
67
|
+
export const createManifestReaderWithStats = (projectDir) => {
|
|
68
|
+
const nodeModulesDir = join(projectDir, "node_modules");
|
|
69
|
+
const modules = readModulesState(nodeModulesDir);
|
|
70
|
+
// `virtualStoreDir` is configurable and recorded in `.modules.yaml`, relative
|
|
71
|
+
// to node_modules unless absolute
|
|
72
|
+
const pnpmDir = isAbsolute(modules.virtualStoreDir)
|
|
73
|
+
? modules.virtualStoreDir
|
|
74
|
+
: join(nodeModulesDir, modules.virtualStoreDir);
|
|
75
|
+
const cache = new Map();
|
|
76
|
+
const stats = { found: 0, missed: 0 };
|
|
77
|
+
const virtualStoreEntries = readdirOrEmpty(pnpmDir);
|
|
78
|
+
const hoistedLocations = modules.hoistedLocations;
|
|
79
|
+
// top-level package dirs, for the nested lookup of the hoisted layout
|
|
80
|
+
const hoistedDependents = (() => {
|
|
81
|
+
let dirs;
|
|
82
|
+
return () => {
|
|
83
|
+
dirs ??= readdirOrEmpty(nodeModulesDir).flatMap((entry) => entry.startsWith("@")
|
|
84
|
+
? readdirOrEmpty(join(nodeModulesDir, entry)).map((scoped) => join(entry, scoped))
|
|
85
|
+
: [entry]);
|
|
86
|
+
return dirs;
|
|
87
|
+
};
|
|
88
|
+
})();
|
|
89
|
+
const fromVirtualStore = (name, version) => {
|
|
90
|
+
const target = `${name.replaceAll("/", "+")}@${version}`;
|
|
91
|
+
const dir = virtualStoreEntries.find((entry) => entry === target ||
|
|
92
|
+
entry.startsWith(`${target}(`) ||
|
|
93
|
+
entry.startsWith(`${target}_`));
|
|
94
|
+
return dir
|
|
95
|
+
? readManifestFile(join(pnpmDir, dir, "node_modules", name, "package.json"))
|
|
96
|
+
: undefined;
|
|
97
|
+
};
|
|
98
|
+
const fromHoisted = (name, version) => {
|
|
99
|
+
const atVersion = (path) => {
|
|
100
|
+
const manifest = readManifestFile(path);
|
|
101
|
+
return manifest?.version === version ? manifest : undefined;
|
|
102
|
+
};
|
|
103
|
+
// the recorded locations are relative to the project directory
|
|
104
|
+
for (const location of hoistedLocations.get(`${name}@${version}`) ?? []) {
|
|
105
|
+
const recorded = atVersion(join(projectDir, location, "package.json"));
|
|
106
|
+
if (recorded)
|
|
107
|
+
return recorded;
|
|
108
|
+
}
|
|
109
|
+
const hoisted = atVersion(join(nodeModulesDir, name, "package.json"));
|
|
110
|
+
if (hoisted)
|
|
111
|
+
return hoisted;
|
|
112
|
+
// no `.modules.yaml`: a version that could not be hoisted lives under
|
|
113
|
+
// whichever dependent needed it, so try one level of nesting
|
|
114
|
+
for (const dependent of hoistedDependents()) {
|
|
115
|
+
const nested = atVersion(join(nodeModulesDir, dependent, "node_modules", name, "package.json"));
|
|
116
|
+
if (nested)
|
|
117
|
+
return nested;
|
|
118
|
+
}
|
|
119
|
+
return undefined;
|
|
120
|
+
};
|
|
121
|
+
const readManifest = (name, version) => {
|
|
122
|
+
const cacheKey = `${name}@${version}`;
|
|
123
|
+
if (cache.has(cacheKey)) {
|
|
124
|
+
return cache.get(cacheKey);
|
|
125
|
+
}
|
|
126
|
+
const manifest = fromVirtualStore(name, version) ?? fromHoisted(name, version);
|
|
127
|
+
if (manifest) {
|
|
128
|
+
stats.found += 1;
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
stats.missed += 1;
|
|
132
|
+
}
|
|
133
|
+
cache.set(cacheKey, manifest);
|
|
134
|
+
return manifest;
|
|
135
|
+
};
|
|
136
|
+
return { readManifest, stats: () => ({ ...stats }) };
|
|
137
|
+
};
|
|
138
|
+
export const createManifestReader = (projectDir) => createManifestReaderWithStats(projectDir).readManifest;
|
|
139
|
+
//# sourceMappingURL=readInstalledManifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readInstalledManifest.js","sourceRoot":"","sources":["../../src/helpers/readInstalledManifest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { describe, it } from "bun:test";
|
|
2
|
+
import { deepStrictEqual, strictEqual } from "node:assert/strict";
|
|
3
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { createManifestReaderWithStats } from "./readInstalledManifest.js";
|
|
8
|
+
const hoistedFixture = fileURLToPath(new URL("../../test/fixtures/hoisted-node-linker", import.meta.url));
|
|
9
|
+
describe("createManifestReaderWithStats", () => {
|
|
10
|
+
it("reads a hoisted layout, top-level and nested", () => {
|
|
11
|
+
const { readManifest, stats } = createManifestReaderWithStats(hoistedFixture);
|
|
12
|
+
// hoisted at the top of node_modules
|
|
13
|
+
deepStrictEqual(readManifest("mini-metro", "0.84.5")?.dependencies, {
|
|
14
|
+
"mini-metro-config": "0.84.5",
|
|
15
|
+
});
|
|
16
|
+
// the version that lost the hoist, nested under its dependent
|
|
17
|
+
deepStrictEqual(readManifest("mini-metro", "0.87.0")?.dependencies, {
|
|
18
|
+
"mini-metro-config": "0.87.0",
|
|
19
|
+
});
|
|
20
|
+
// the open range this whole scenario turns on
|
|
21
|
+
deepStrictEqual(readManifest("mini-plugin", "1.0.0")?.dependencies, {
|
|
22
|
+
"mini-metro-config": "*",
|
|
23
|
+
});
|
|
24
|
+
deepStrictEqual(stats(), { found: 3, missed: 0 });
|
|
25
|
+
});
|
|
26
|
+
it("resolves a location only pnpm's own index knows", () => {
|
|
27
|
+
const { readManifest } = createManifestReaderWithStats(hoistedFixture);
|
|
28
|
+
// three levels down: reachable from `.modules.yaml`'s hoistedLocations,
|
|
29
|
+
// not from scanning node_modules one level deep
|
|
30
|
+
deepStrictEqual(readManifest("mini-deep", "2.0.0")?.dependencies, {
|
|
31
|
+
"mini-metro-config": "*",
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
it("does not return a manifest whose version differs", () => {
|
|
35
|
+
const { readManifest, stats } = createManifestReaderWithStats(hoistedFixture);
|
|
36
|
+
strictEqual(readManifest("mini-metro", "9.9.9"), undefined);
|
|
37
|
+
deepStrictEqual(stats(), { found: 0, missed: 1 });
|
|
38
|
+
});
|
|
39
|
+
it("reads pnpm's virtual store layout, peer suffix included", () => {
|
|
40
|
+
const dir = mkdtempSync(join(tmpdir(), "pnpm-dedup-virtual-store-"));
|
|
41
|
+
try {
|
|
42
|
+
const packageDir = join(dir, "node_modules", ".pnpm", "@scope+pkg@1.2.3(react@19.0.0)", "node_modules", "@scope", "pkg");
|
|
43
|
+
mkdirSync(packageDir, { recursive: true });
|
|
44
|
+
writeFileSync(join(packageDir, "package.json"), JSON.stringify({
|
|
45
|
+
name: "@scope/pkg",
|
|
46
|
+
version: "1.2.3",
|
|
47
|
+
peerDependencies: { react: "*" },
|
|
48
|
+
}));
|
|
49
|
+
const { readManifest, stats } = createManifestReaderWithStats(dir);
|
|
50
|
+
deepStrictEqual(readManifest("@scope/pkg", "1.2.3")?.peerDependencies, {
|
|
51
|
+
react: "*",
|
|
52
|
+
});
|
|
53
|
+
deepStrictEqual(stats(), { found: 1, missed: 0 });
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
rmSync(dir, { recursive: true, force: true });
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
it("honors a relocated virtual store", () => {
|
|
60
|
+
const dir = mkdtempSync(join(tmpdir(), "pnpm-dedup-relocated-store-"));
|
|
61
|
+
try {
|
|
62
|
+
const packageDir = join(dir, "node_modules", ".pnpm-store", "pkg@1.0.0", "node_modules", "pkg");
|
|
63
|
+
mkdirSync(packageDir, { recursive: true });
|
|
64
|
+
writeFileSync(join(packageDir, "package.json"), JSON.stringify({ name: "pkg", version: "1.0.0", dependencies: {} }));
|
|
65
|
+
writeFileSync(join(dir, "node_modules", ".modules.yaml"), JSON.stringify({
|
|
66
|
+
nodeLinker: "isolated",
|
|
67
|
+
virtualStoreDir: ".pnpm-store",
|
|
68
|
+
}));
|
|
69
|
+
const { readManifest } = createManifestReaderWithStats(dir);
|
|
70
|
+
strictEqual(readManifest("pkg", "1.0.0")?.version, "1.0.0");
|
|
71
|
+
}
|
|
72
|
+
finally {
|
|
73
|
+
rmSync(dir, { recursive: true, force: true });
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
it("reports every read as missed when nothing is installed", () => {
|
|
77
|
+
const { readManifest, stats } = createManifestReaderWithStats(join(tmpdir(), "pnpm-dedup-not-installed"));
|
|
78
|
+
strictEqual(readManifest("mini-metro", "0.84.5"), undefined);
|
|
79
|
+
strictEqual(readManifest("mini-plugin", "1.0.0"), undefined);
|
|
80
|
+
deepStrictEqual(stats(), { found: 0, missed: 2 });
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
//# sourceMappingURL=readInstalledManifest.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readInstalledManifest.test.js","sourceRoot":"","sources":["../../src/helpers/readInstalledManifest.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
export const runPnpm = (args, { cwd, stdio = "inherit" } = {}) => {
|
|
3
|
+
const result = spawnSync("pnpm", args, {
|
|
4
|
+
cwd,
|
|
5
|
+
stdio,
|
|
6
|
+
encoding: "utf8",
|
|
7
|
+
});
|
|
8
|
+
if (result.error) {
|
|
9
|
+
throw result.error;
|
|
10
|
+
}
|
|
11
|
+
return {
|
|
12
|
+
status: result.status,
|
|
13
|
+
output: stdio === "pipe" ? `${result.stdout}\n${result.stderr}` : "",
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=runPnpm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runPnpm.js","sourceRoot":"","sources":["../../src/helpers/runPnpm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAkB/C,MAAM,CAAC,MAAM,OAAO,GAAe,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,KAAK,GAAG,SAAS,EAAE,GAAG,EAAE,EAAE,EAAE;IAC3E,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE;QACrC,GAAG;QACH,KAAK;QACL,QAAQ,EAAE,MAAM;KACjB,CAAC,CAAC;IAEH,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,MAAM,MAAM,CAAC,KAAK,CAAC;IACrB,CAAC;IAED,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,MAAM,EAAE,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE;KACrE,CAAC;AACJ,CAAC,CAAC"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { mkdtempSync, readdirSync, rmSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
// Scratch projects for the tests that need a real directory on disk. The
|
|
5
|
+
// end-to-end ones run an actual install in theirs, so a leaked directory is
|
|
6
|
+
// tens of megabytes.
|
|
7
|
+
//
|
|
8
|
+
// `afterEach` covers the normal path — a passing, failing or timed-out test all
|
|
9
|
+
// reach it — but not a signal: on Ctrl-C the test runner exits without running
|
|
10
|
+
// the remaining hooks, and on SIGKILL nothing runs at all. So cleanup does not
|
|
11
|
+
// rest on it alone:
|
|
12
|
+
// - the directory name carries the pid that owns it, and every run first
|
|
13
|
+
// reaps the leftovers of pids that are gone,
|
|
14
|
+
// - SIGINT/SIGTERM clean up before the signal is allowed through.
|
|
15
|
+
const isRunning = (pid) => {
|
|
16
|
+
try {
|
|
17
|
+
process.kill(pid, 0);
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
// EPERM: alive, but owned by another user — not ours to reap either way
|
|
22
|
+
return error.code === "EPERM";
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
const remove = (dir) => {
|
|
26
|
+
try {
|
|
27
|
+
rmSync(dir, { recursive: true, force: true });
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// a directory a still-running install is writing into can fail to unlink;
|
|
31
|
+
// the next run reaps it once that pid is gone
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
// the `${pid}-${mkdtemp suffix}` a `create` call leaves after the prefix. Names
|
|
35
|
+
// that do not match are left alone: a random suffix read as a pid would reap
|
|
36
|
+
// directories this helper never made.
|
|
37
|
+
const ownerPidPattern = /^(?<pid>[1-9]\d*)-/u;
|
|
38
|
+
const reapAbandoned = (prefix) => {
|
|
39
|
+
const root = tmpdir();
|
|
40
|
+
for (const name of readdirSync(root)) {
|
|
41
|
+
if (!name.startsWith(prefix))
|
|
42
|
+
continue;
|
|
43
|
+
const owner = ownerPidPattern.exec(name.slice(prefix.length))?.groups?.pid;
|
|
44
|
+
if (!owner || isRunning(Number(owner)))
|
|
45
|
+
continue;
|
|
46
|
+
remove(join(root, name));
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
const pendingCleanups = new Set();
|
|
50
|
+
let signalsHooked = false;
|
|
51
|
+
const hookSignals = () => {
|
|
52
|
+
if (signalsHooked)
|
|
53
|
+
return;
|
|
54
|
+
signalsHooked = true;
|
|
55
|
+
for (const signal of ["SIGINT", "SIGTERM"]) {
|
|
56
|
+
const handler = () => {
|
|
57
|
+
for (const cleanup of pendingCleanups)
|
|
58
|
+
cleanup();
|
|
59
|
+
// a listener suppresses the default termination: drop it and re-raise so
|
|
60
|
+
// the process still dies of the signal it was sent
|
|
61
|
+
process.removeListener(signal, handler);
|
|
62
|
+
process.kill(process.pid, signal);
|
|
63
|
+
};
|
|
64
|
+
process.on(signal, handler);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
export const createTempProjects = (prefix) => {
|
|
68
|
+
reapAbandoned(prefix);
|
|
69
|
+
hookSignals();
|
|
70
|
+
const dirs = [];
|
|
71
|
+
const cleanup = () => {
|
|
72
|
+
for (const dir of dirs.splice(0))
|
|
73
|
+
remove(dir);
|
|
74
|
+
};
|
|
75
|
+
pendingCleanups.add(cleanup);
|
|
76
|
+
return {
|
|
77
|
+
create: () => {
|
|
78
|
+
const dir = mkdtempSync(join(tmpdir(), `${prefix}${process.pid}-`));
|
|
79
|
+
dirs.push(dir);
|
|
80
|
+
return dir;
|
|
81
|
+
},
|
|
82
|
+
cleanup,
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
//# sourceMappingURL=tempProjects.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tempProjects.js","sourceRoot":"","sources":["../../src/helpers/tempProjects.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { resolveSnapshotDependency } from "./parsePnpmLockPackages.js";
|
|
2
|
+
const snapshotDepTypes = ["dependencies", "optionalDependencies"];
|
|
3
|
+
const snapshotDependencies = (lock, snapshotKey) => {
|
|
4
|
+
const snapshot = lock.snapshots?.[snapshotKey];
|
|
5
|
+
const dependencies = {};
|
|
6
|
+
snapshotDepTypes.forEach((depType) => {
|
|
7
|
+
for (const [depName, depValue] of Object.entries(snapshot?.[depType] ?? {})) {
|
|
8
|
+
const resolved = resolveSnapshotDependency(depName, depValue);
|
|
9
|
+
dependencies[resolved.name] = resolved.version;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
return dependencies;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Adapt the pnpm lockfile model into the package-manager-neutral graph consumed
|
|
16
|
+
* by `buildLockstepClusters`. pnpm snapshots store the *resolved* version of
|
|
17
|
+
* each dependency instead of the requested range, which co-version detection
|
|
18
|
+
* handles; one entry is emitted per installation, so a resolution installed in
|
|
19
|
+
* several peer contexts contributes every context it appears in.
|
|
20
|
+
*/
|
|
21
|
+
export const toLockstepGraph = (lock, packagesMap) => Object.fromEntries(Object.entries(packagesMap).map(([name, resolutions]) => [
|
|
22
|
+
name,
|
|
23
|
+
resolutions.flatMap((resolution) => {
|
|
24
|
+
if (resolution.package.type !== "npm") {
|
|
25
|
+
return [{ version: "", isNpm: false, dependencies: {} }];
|
|
26
|
+
}
|
|
27
|
+
return resolution.installations.map((snapshotKey) => ({
|
|
28
|
+
version: resolution.package.version,
|
|
29
|
+
isNpm: true,
|
|
30
|
+
dependencies: snapshotDependencies(lock, snapshotKey),
|
|
31
|
+
}));
|
|
32
|
+
}),
|
|
33
|
+
]));
|
|
34
|
+
//# sourceMappingURL=toLockstepGraph.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toLockstepGraph.js","sourceRoot":"","sources":["../../src/helpers/toLockstepGraph.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAEvE,MAAM,gBAAgB,GAAG,CAAC,cAAc,EAAE,sBAAsB,CAAU,CAAC;AAE3E,MAAM,oBAAoB,GAAG,CAC3B,IAAkB,EAClB,WAAmB,EACK,EAAE;IAC1B,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC,WAAW,CAAC,CAAC;IAC/C,MAAM,YAAY,GAA2B,EAAE,CAAC;IAEhD,gBAAgB,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QACnC,KAAK,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAC9C,QAAQ,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,CAC1B,EAAE,CAAC;YACF,MAAM,QAAQ,GAAG,yBAAyB,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;YAC9D,YAAY,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,OAAO,CAAC;QACjD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,YAAY,CAAC;AACtB,CAAC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,IAAkB,EAClB,WAAwB,EACT,EAAE,CACjB,MAAM,CAAC,WAAW,CAChB,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,WAAW,CAAC,EAAE,EAAE,CAAC;IACvD,IAAI;IACJ,WAAW,CAAC,OAAO,CAAC,CAAC,UAAU,EAAwB,EAAE;QACvD,IAAI,UAAU,CAAC,OAAO,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;YACtC,OAAO,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,EAAE,CAAC,CAAC;QAC3D,CAAC;QACD,OAAO,UAAU,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;YACpD,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,OAAO;YACnC,KAAK,EAAE,IAAI;YACX,YAAY,EAAE,oBAAoB,CAAC,IAAI,EAAE,WAAW,CAAC;SACtD,CAAC,CAAC,CAAC;IACN,CAAC,CAAC;CACH,CAAC,CACH,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { describe, it } from "bun:test";
|
|
2
|
+
import { deepStrictEqual, ok, strictEqual } from "node:assert/strict";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { readPnpmLock } from "../readPnpmLock.js";
|
|
5
|
+
import { buildPnpmPackagesMap } from "./buildPnpmPackagesMap.js";
|
|
6
|
+
import { parsePnpmLockPackages } from "./parsePnpmLockPackages.js";
|
|
7
|
+
import { toLockstepGraph } from "./toLockstepGraph.js";
|
|
8
|
+
const fixturesBase = (rel) => fileURLToPath(new URL(rel, import.meta.url));
|
|
9
|
+
const graphOf = (lock) => toLockstepGraph(lock, buildPnpmPackagesMap(parsePnpmLockPackages(lock)));
|
|
10
|
+
const graphFor = (scenario) => graphOf(readPnpmLock(fixturesBase(`../../test/fixtures/${scenario}/pnpm-lock.yaml`)));
|
|
11
|
+
describe("toLockstepGraph", () => {
|
|
12
|
+
it("strips peer suffixes, resolves aliases, and keeps every installation", () => {
|
|
13
|
+
const graph = graphOf({
|
|
14
|
+
lockfileVersion: "9.0",
|
|
15
|
+
packages: {
|
|
16
|
+
"a@1.0.0": {},
|
|
17
|
+
"b@1.0.0": {},
|
|
18
|
+
"alias-target@2.0.0": {},
|
|
19
|
+
},
|
|
20
|
+
snapshots: {
|
|
21
|
+
"a@1.0.0(peer@1.0.0)": {
|
|
22
|
+
dependencies: {
|
|
23
|
+
b: "1.0.0(peer@1.0.0)",
|
|
24
|
+
aliased: "alias-target@2.0.0",
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
"a@1.0.0(peer@2.0.0)": {
|
|
28
|
+
dependencies: { b: "1.0.0" },
|
|
29
|
+
},
|
|
30
|
+
"b@1.0.0": {},
|
|
31
|
+
"alias-target@2.0.0": {},
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
// one entry per peer context, both carrying the same resolved version
|
|
35
|
+
deepStrictEqual(graph.a, [
|
|
36
|
+
{
|
|
37
|
+
version: "1.0.0",
|
|
38
|
+
isNpm: true,
|
|
39
|
+
dependencies: { b: "1.0.0", "alias-target": "2.0.0" },
|
|
40
|
+
},
|
|
41
|
+
{ version: "1.0.0", isNpm: true, dependencies: { b: "1.0.0" } },
|
|
42
|
+
]);
|
|
43
|
+
});
|
|
44
|
+
it("maps metro's exact self-pins to co-version edges", () => {
|
|
45
|
+
const metro = graphFor("wildcard-not-reused").metro;
|
|
46
|
+
ok(metro);
|
|
47
|
+
deepStrictEqual(metro.map((resolution) => resolution.version).toSorted(), [
|
|
48
|
+
"0.84.5",
|
|
49
|
+
"0.87.0",
|
|
50
|
+
]);
|
|
51
|
+
for (const resolution of metro) {
|
|
52
|
+
strictEqual(resolution.isNpm, true);
|
|
53
|
+
strictEqual(resolution.dependencies["metro-config"], resolution.version);
|
|
54
|
+
// an unrelated dependency keeps its own version line
|
|
55
|
+
ok(resolution.dependencies.debug?.startsWith("4."));
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
// `aliased-swapped-names`: the keys the importer uses name each other's
|
|
59
|
+
// package. The graph is keyed by npm name, so `@typescript/typescript6` has to
|
|
60
|
+
// stay its own name and `tool`'s edge has to land on the real `typescript`.
|
|
61
|
+
it("keeps swapped alias keys apart", () => {
|
|
62
|
+
const graph = graphFor("aliased-swapped-names");
|
|
63
|
+
deepStrictEqual(Object.keys(graph).toSorted(), [
|
|
64
|
+
"@typescript/typescript6",
|
|
65
|
+
"tool",
|
|
66
|
+
"typescript",
|
|
67
|
+
]);
|
|
68
|
+
deepStrictEqual(graph.tool?.[0]?.dependencies, { typescript: "5.9.3" });
|
|
69
|
+
strictEqual(graph.typescript?.length, 2);
|
|
70
|
+
});
|
|
71
|
+
it("marks non-npm resolutions so cluster detection skips them", () => {
|
|
72
|
+
const nonNpm = Object.values(graphFor("non-npm"))
|
|
73
|
+
.flat()
|
|
74
|
+
.filter((resolution) => !resolution.isNpm);
|
|
75
|
+
ok(nonNpm.length > 0);
|
|
76
|
+
for (const resolution of nonNpm) {
|
|
77
|
+
deepStrictEqual(resolution.dependencies, {});
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
//# sourceMappingURL=toLockstepGraph.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toLockstepGraph.test.js","sourceRoot":"","sources":["../../src/helpers/toLockstepGraph.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { buildVersionsSnapshot } from "pm-dedup-core";
|
|
2
|
+
import { readPnpmLock } from "../readPnpmLock.js";
|
|
3
|
+
import { parsePnpmLockPackages } from "./parsePnpmLockPackages.js";
|
|
4
|
+
export const readVersionsSnapshot = (lockPath) => buildVersionsSnapshot(parsePnpmLockPackages(readPnpmLock(lockPath)).packages.values());
|
|
5
|
+
//# sourceMappingURL=versionsSnapshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"versionsSnapshot.js","sourceRoot":"","sources":["../../src/helpers/versionsSnapshot.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAEtD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAEnE,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,QAAgB,EAAoB,EAAE,CACzE,qBAAqB,CACnB,qBAAqB,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,EAAE,CAChE,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { buildLockstepClusters, identifyLockstepClusterFixes, } from "pm-dedup-core";
|
|
2
|
+
import { collectDependentRanges } from "./helpers/collectDependentRanges.js";
|
|
3
|
+
import { toLockstepGraph } from "./helpers/toLockstepGraph.js";
|
|
4
|
+
// A lone duplicated package is a cluster of one: nothing co-versions with it,
|
|
5
|
+
// but it converges through the same mechanism — the dependents that hold its
|
|
6
|
+
// copies apart — and it is applied the same way, by an override the resolver
|
|
7
|
+
// then has to confirm.
|
|
8
|
+
const singletonClusters = (packagesMap, clustered) => Object.entries(packagesMap)
|
|
9
|
+
.filter(([name, resolutions]) => !clustered.has(name) &&
|
|
10
|
+
resolutions.filter((resolution) => resolution.package.type === "npm")
|
|
11
|
+
.length > 1)
|
|
12
|
+
.map(([name]) => [name]);
|
|
13
|
+
const toClusterMembers = (packagesMap, memberNames) => Object.fromEntries(memberNames.map((name) => {
|
|
14
|
+
const resolutions = packagesMap[name] ?? [];
|
|
15
|
+
return [
|
|
16
|
+
name,
|
|
17
|
+
{
|
|
18
|
+
npmVersions: resolutions
|
|
19
|
+
.filter((resolution) => resolution.package.type === "npm")
|
|
20
|
+
.map((resolution) => resolution.package.version),
|
|
21
|
+
resolutionCount: resolutions.length,
|
|
22
|
+
},
|
|
23
|
+
];
|
|
24
|
+
}));
|
|
25
|
+
// The pnpm lockfile stores resolved versions, not requested ranges, so the
|
|
26
|
+
// constraints have to come from `collectDependentRanges`, which recovers the
|
|
27
|
+
// declared range from each requester's installed manifest.
|
|
28
|
+
const toClusterDependents = (lock, memberNames, readManifest) => {
|
|
29
|
+
const ranges = collectDependentRanges(lock, new Set(memberNames), readManifest);
|
|
30
|
+
return new Map([...ranges].map(([name, dependents]) => [
|
|
31
|
+
name,
|
|
32
|
+
dependents.map((dependent) => ({
|
|
33
|
+
requester: dependent.key,
|
|
34
|
+
requesterName: dependent.requesterName,
|
|
35
|
+
range: dependent.range,
|
|
36
|
+
isAlias: dependent.aliasKey !== undefined,
|
|
37
|
+
resolvedVersion: dependent.resolvedVersion,
|
|
38
|
+
workspace: dependent.workspace,
|
|
39
|
+
})),
|
|
40
|
+
]));
|
|
41
|
+
};
|
|
42
|
+
export const identifyClusterFixes = (lock, packagesMap, readManifest) => {
|
|
43
|
+
const lockstepClusters = buildLockstepClusters(toLockstepGraph(lock, packagesMap));
|
|
44
|
+
const clusters = [
|
|
45
|
+
...lockstepClusters,
|
|
46
|
+
...singletonClusters(packagesMap, new Set(lockstepClusters.flat())),
|
|
47
|
+
];
|
|
48
|
+
if (clusters.length === 0)
|
|
49
|
+
return [];
|
|
50
|
+
const memberNames = [...new Set(clusters.flat())];
|
|
51
|
+
return identifyLockstepClusterFixes(clusters, toClusterMembers(packagesMap, memberNames), toClusterDependents(lock, memberNames, readManifest)).filter((fix) => fix.members.length > 1 || fix.workspaceChanges.length === 0);
|
|
52
|
+
};
|
|
53
|
+
//# sourceMappingURL=identifyClusterFixes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identifyClusterFixes.js","sourceRoot":"","sources":["../src/identifyClusterFixes.ts"],"names":[],"mappings":"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"}
|