fix(ci): merge nested shrinkwrap override pins

This commit is contained in:
Vincent Koc
2026-05-27 18:37:00 +02:00
parent 8ee767baa7
commit c285766d62
2 changed files with 127 additions and 0 deletions
+50
View File
@@ -305,11 +305,60 @@ function mergeOverrideEntry(merged, name, spec) {
}
return;
}
if (
typeof current === "string" &&
isPlainObject(spec) &&
typeof spec["."] === "string" &&
exactOverrideVersionsMatch(current, spec["."])
) {
merged[name] = { ".": preferredExactOverrideRootSpec(current, spec["."]) };
for (const [nestedName, nestedSpec] of Object.entries(spec)) {
if (nestedName === ".") {
continue;
}
mergeOverrideEntry(merged[name], nestedName, nestedSpec);
}
return;
}
if (
isPlainObject(current) &&
typeof spec === "string" &&
typeof current["."] === "string" &&
exactOverrideVersionsMatch(current["."], spec)
) {
current["."] = preferredExactOverrideRootSpec(current["."], spec);
return;
}
if (JSON.stringify(current) !== JSON.stringify(spec)) {
throw new Error(`package.json overrides.${name} conflicts with pnpm lock policy for ${name}`);
}
}
function preferredExactOverrideRootSpec(current, incoming) {
return incoming.startsWith("npm:") ? incoming : current;
}
function exactOverrideVersionsMatch(left, right) {
const leftVersion = exactVersionFromOverrideSpec(left);
if (leftVersion === null || leftVersion !== exactVersionFromOverrideSpec(right)) {
return false;
}
const leftAlias = parseNpmAliasOverrideSpec(left);
const rightAlias = parseNpmAliasOverrideSpec(right);
return !leftAlias || !rightAlias || leftAlias.name === rightAlias.name;
}
function parseNpmAliasOverrideSpec(spec) {
if (!spec.startsWith("npm:")) {
return null;
}
const versionIndex = spec.lastIndexOf("@");
if (versionIndex <= "npm:".length) {
return null;
}
return { name: spec.slice("npm:".length, versionIndex) };
}
function mergeOverrides(packageOverrides, workspaceOverrides, pnpmLockOverrides) {
const merged = normalizeOverrides(packageOverrides);
for (const [name, spec] of [
@@ -1085,6 +1134,7 @@ export {
disableShrinkwrappedOverrideConflictSources,
exactOverrideRulesFromOverrides,
exactVersionFromOverrideSpec,
mergeOverrides,
applyPackageExtensionPeerMetadata,
normalizeNpmVersionDrift,
packageJsonForShrinkwrap,
+77
View File
@@ -4,6 +4,7 @@ import { parse } from "yaml";
import {
collectCurrentShrinkwrapOverrides,
collectPnpmLockViolations,
mergeOverrides,
parsePnpmPackageKey,
readShrinkwrapOverrides,
} from "../scripts/generate-npm-shrinkwrap.mjs";
@@ -132,6 +133,82 @@ describe("package manager build policy", () => {
});
});
it("merges exact current shrinkwrap pins with nested lock-derived pins", () => {
expect(
mergeOverrides(
{ "@mistralai/mistralai": "2.2.1" },
{ "@mistralai/mistralai": { ".": "2.2.1", zod: "4.4.3" } },
{},
),
).toEqual({
"@mistralai/mistralai": { ".": "2.2.1", zod: "4.4.3" },
});
});
it("preserves npm alias pins when merging nested lock-derived pins", () => {
expect(
mergeOverrides(
{ "node-domexception": "npm:@nolyfill/domexception@1.0.28" },
{ "node-domexception": { ".": "1.0.28", child: "2.0.0" } },
{},
),
).toEqual({
"node-domexception": {
".": "npm:@nolyfill/domexception@1.0.28",
child: "2.0.0",
},
});
});
it("preserves later npm alias pins when nested pins are already merged", () => {
expect(
mergeOverrides(
{ "node-domexception": { ".": "1.0.28", child: "2.0.0" } },
{ "node-domexception": "npm:@nolyfill/domexception@1.0.28" },
{},
),
).toEqual({
"node-domexception": {
".": "npm:@nolyfill/domexception@1.0.28",
child: "2.0.0",
},
});
});
it("rejects non-exact root pins when merging nested pins", () => {
expect(() =>
mergeOverrides(
{ "floating-package": "^1.0.0" },
{ "floating-package": { ".": "~1.0.0", child: "2.0.0" } },
{},
),
).toThrow(/conflicts with pnpm lock policy/u);
expect(() =>
mergeOverrides(
{ "floating-package": { ".": "^1.0.0", child: "2.0.0" } },
{ "floating-package": "~1.0.0" },
{},
),
).toThrow(/conflicts with pnpm lock policy/u);
});
it("rejects distinct npm alias targets with matching versions", () => {
expect(() =>
mergeOverrides(
{ "aliased-package": "npm:@safe/foo@1.0.0" },
{ "aliased-package": { ".": "npm:@other/foo@1.0.0", child: "2.0.0" } },
{},
),
).toThrow(/conflicts with pnpm lock policy/u);
expect(() =>
mergeOverrides(
{ "aliased-package": { ".": "npm:@safe/foo@1.0.0", child: "2.0.0" } },
{ "aliased-package": "npm:@other/foo@1.0.0" },
{},
),
).toThrow(/conflicts with pnpm lock policy/u);
});
it("keeps npm shrinkwrap package versions inside the pnpm lock graph", () => {
const pnpmLockPackages = collectPnpmLockPackages();
const shrinkwrapPaths = [