

















@@ -10,6 +10,7 @@ import { resolveNpmRunner } from "./npm-runner.mjs";
10101111const ROOT_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
1212const EXACT_VERSION_PATTERN = /^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/u;
13+const STABLE_VERSION_PATTERN = /^(\d+)\.(\d+)\.(\d+)$/u;
13141415function usage() {
1516return [
@@ -108,17 +109,55 @@ function collectPnpmLockPackageVersions(lockfile) {
108109return versionsByName;
109110}
110111111-function readPnpmLockSingleVersionOverrides() {
112+function stableVersionParts(version) {
113+const match = version.match(STABLE_VERSION_PATTERN);
114+return match
115+ ? {
116+major: Number(match[1]),
117+minor: Number(match[2]),
118+patch: Number(match[3]),
119+}
120+ : null;
121+}
122+123+function pnpmLockOverrideVersionForVersions(versions) {
124+const sortedVersions = [...versions].toSorted((left, right) => left.localeCompare(right));
125+if (sortedVersions.length === 1) {
126+return exactVersionFromOverrideSpec(sortedVersions[0]) === null ? null : sortedVersions[0];
127+}
128+129+const parsedVersions = sortedVersions.map((version) => ({
130+ version,
131+parts: stableVersionParts(version),
132+}));
133+if (parsedVersions.some(({ parts }) => parts === null)) {
134+return null;
135+}
136+137+const [{ parts: firstParts }] = parsedVersions;
138+if (
139+parsedVersions.some(
140+({ parts }) => parts.major !== firstParts.major || parts.minor !== firstParts.minor,
141+)
142+) {
143+return null;
144+}
145+146+// npm patch ranges can float past the pnpm lock. Pin to the newest locked patch
147+// when the lock only contains one major/minor line, but keep true version forks free.
148+return parsedVersions.toSorted((left, right) => right.parts.patch - left.parts.patch)[0].version;
149+}
150+151+function readPnpmLockVersionOverrides() {
112152const lockfile = parseYaml(readFileSync(path.join(ROOT_DIR, "pnpm-lock.yaml"), "utf8"));
113153const versionsByName = collectPnpmLockPackageVersions(lockfile);
114154if (versionsByName.size === 0) {
115155throw new Error("pnpm-lock.yaml is missing package resolution data.");
116156}
117157return Object.fromEntries(
118158[...versionsByName.entries()]
119-.filter(([, versions]) => versions.size === 1)
120-.map(([name, versions]) => [name, [...versions][0]])
121-.filter(([, version]) => exactVersionFromOverrideSpec(version) !== null)
159+.map(([name, versions]) => [name, pnpmLockOverrideVersionForVersions(versions)])
160+.filter(([, version]) => version !== null)
122161.toSorted(([left], [right]) => left.localeCompare(right)),
123162);
124163}
@@ -143,7 +182,7 @@ function mergeOverrides(packageOverrides, workspaceOverrides, pnpmLockOverrides)
143182}
144183145184function readShrinkwrapOverrides() {
146-return mergeOverrides(undefined, readWorkspaceOverrides(), readPnpmLockSingleVersionOverrides());
185+return mergeOverrides(undefined, readWorkspaceOverrides(), readPnpmLockVersionOverrides());
147186}
148187149188function packageJsonForShrinkwrap(packageJson, shrinkwrapOverrides) {
@@ -709,6 +748,7 @@ export {
709748applyPackageExtensionPeerMetadata,
710749normalizeNpmVersionDrift,
711750packageJsonForShrinkwrap,
751+pnpmLockOverrideVersionForVersions,
712752parsePnpmPackageKey,
713753parseLockPackagePath,
714754readShrinkwrapOverrides,
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。