diff --git a/engine/pm/commands/src/self-updater/selfUpdate.ts b/engine/pm/commands/src/self-updater/selfUpdate.ts index a4b850019f..22b926be22 100644 --- a/engine/pm/commands/src/self-updater/selfUpdate.ts +++ b/engine/pm/commands/src/self-updater/selfUpdate.ts @@ -30,6 +30,15 @@ export function cliOptionsTypes (): Record { export const commandNames = ['self-update'] +// Migration guidance printed once when `pnpm self-update` crosses a major +// boundary. Add an entry here for each future major that ships breaking +// changes users need to act on. +const MAJOR_UPGRADE_HINTS: Record = { + 11: + 'pnpm v11 removed or renamed several v10 settings. ' + + 'See https://pnpm.io/11.x/migration for migration instructions.', +} + export const skipPackageManagerCheck = true export function help (): string { @@ -76,6 +85,30 @@ export async function handler ( throw new PnpmError('CANNOT_RESOLVE_PNPM', `Cannot find "${bareSpecifier}" version of pnpm`) } + // Determine the "previous" pnpm version being upgraded FROM. If the + // project pins pnpm via `packageManager`/`devEngines.packageManager`, + // the pin is the source of truth — the running pnpm binary may already + // be at a newer major (e.g. a globally-installed v11 operating on a + // project still pinned to v10). Otherwise fall back to the running + // binary. Skip the hint entirely on a no-op (target === previous). + const targetVersion = resolution.manifest.version + let previousVersion: string | undefined + if (opts.wantedPackageManager?.name === packageManager.name) { + if (opts.wantedPackageManager.version !== targetVersion) { + previousVersion = opts.wantedPackageManager.version + } + } else if (packageManager.version !== targetVersion) { + previousVersion = packageManager.version + } + const previousMajor = previousVersion != null + ? semver.coerce(previousVersion)?.major + : undefined + const targetMajor = semver.major(targetVersion) + if (previousMajor != null && targetMajor > previousMajor) { + const hint = MAJOR_UPGRADE_HINTS[targetMajor] + if (hint) globalWarn(hint) + } + if (opts.wantedPackageManager?.name === packageManager.name) { if (opts.wantedPackageManager?.version !== resolution.manifest.version) { const { manifest, writeProjectManifest } = await readProjectManifest(opts.rootProjectManifestDir)