From 8df408c9017609088da9a5f77ea3fd669c75425f Mon Sep 17 00:00:00 2001 From: shiminshen Date: Mon, 18 May 2026 06:13:49 +0800 Subject: [PATCH] fix(config): warn when package.json has a legacy "pnpm" field with migrated settings (#11680) * fix(config): warn when package.json has a legacy "pnpm" field In v11, pnpm stopped reading settings from the `pnpm` field of package.json (#10086). Most former pnpm-field settings now live in `pnpm-workspace.yaml`; a few (e.g. `onlyBuiltDependencies`, `executionEnv`) were removed entirely. Until now the old field was silently ignored, so users upgrading from v10 had no signal that their overrides or patched dependencies had stopped taking effect. Emit a warning whenever the `pnpm` field contains any key that pnpm no longer reads from package.json. The check is an allowlist (only `pnpm.app`, consumed by `pnpm pack-app`, is still active), so the warning won't go stale as new settings are added or removed in future versions. The message points users at https://pnpm.io/settings rather than prescribing a single fix, since the new home depends on the key. Closes #11677. * fix(config): only warn for migrated pnpm-field keys, not unrelated ones Previously the warning fired for every key under `pnpm` except `app`, which would surface false positives for third-party tooling that piggybacks on the `pnpm` namespace. Switch to an explicit denylist of the v10 settings that moved to pnpm-workspace.yaml, matching the PR's stated contract. --------- Co-authored-by: Damon Co-authored-by: Zoltan Kochan --- .../warn-deprecated-pnpm-field-11677.md | 6 +++ config/reader/src/index.ts | 36 ++++++++++++++++ .../pkg-with-legacy-pnpm-field/package.json | 10 +++++ .../pkg-with-pnpm-app-field/package.json | 7 ++++ .../pkg-with-unknown-pnpm-field/package.json | 5 +++ config/reader/test/index.ts | 41 +++++++++++++++++++ 6 files changed, 105 insertions(+) create mode 100644 .changeset/warn-deprecated-pnpm-field-11677.md create mode 100644 config/reader/test/fixtures/pkg-with-legacy-pnpm-field/package.json create mode 100644 config/reader/test/fixtures/pkg-with-pnpm-app-field/package.json create mode 100644 config/reader/test/fixtures/pkg-with-unknown-pnpm-field/package.json diff --git a/.changeset/warn-deprecated-pnpm-field-11677.md b/.changeset/warn-deprecated-pnpm-field-11677.md new file mode 100644 index 0000000000..0c177c42ea --- /dev/null +++ b/.changeset/warn-deprecated-pnpm-field-11677.md @@ -0,0 +1,6 @@ +--- +"@pnpm/config.reader": patch +"pnpm": patch +--- + +Warn when `package.json` contains a legacy `pnpm` field with settings pnpm no longer reads from `package.json` (e.g. `pnpm.overrides`, `pnpm.patchedDependencies`). Previously these were silently ignored after the upgrade from v10, leaving users unaware that their overrides/patched dependencies had stopped taking effect [#11677](https://github.com/pnpm/pnpm/issues/11677). diff --git a/config/reader/src/index.ts b/config/reader/src/index.ts index b0cc36a4ef..1d8eb41f37 100644 --- a/config/reader/src/index.ts +++ b/config/reader/src/index.ts @@ -404,6 +404,10 @@ export async function getConfig (opts: { if (pnpmConfig.rootProjectManifest.workspaces?.length && !pnpmConfig.workspaceDir) { warnings.push('The "workspaces" field in package.json is not supported by pnpm. Create a "pnpm-workspace.yaml" file instead.') } + const ignoredPnpmFieldKeys = getIgnoredPnpmFieldKeys(pnpmConfig.rootProjectManifest) + if (ignoredPnpmFieldKeys.length > 0) { + warnings.push(`The "pnpm" field in package.json is no longer read by pnpm. The following keys were ignored: ${ignoredPnpmFieldKeys.map(k => `"pnpm.${k}"`).join(', ')}. See https://pnpm.io/settings for the new home of each setting.`) + } const wantedPmResult = getWantedPackageManager(pnpmConfig.rootProjectManifest) if (wantedPmResult.pm) { pnpmConfig.wantedPackageManager = wantedPmResult.pm @@ -750,6 +754,38 @@ function getWantedPackageManager (manifest: ProjectManifest): { pm?: WantedPacka return { warnings } } +// Settings that used to be read from the `pnpm` field of `package.json` in v10 +// but moved to `pnpm-workspace.yaml` in v11. Keys not in this set (e.g. `app`, +// or anything set by third-party tooling that piggybacks on the `pnpm` namespace) +// are left alone to avoid false-positive warnings. +const MIGRATED_PNPM_FIELD_KEYS = new Set([ + 'allowBuilds', + 'allowedDeprecatedVersions', + 'allowUnusedPatches', + 'auditConfig', + 'configDependencies', + 'executionEnv', + 'ignoredOptionalDependencies', + 'neverBuiltDependencies', + 'onlyBuiltDependencies', + 'onlyBuiltDependenciesFile', + 'overrides', + 'packageExtensions', + 'patchedDependencies', + 'peerDependencyRules', + 'requiredScripts', + 'supportedArchitectures', + 'updateConfig', +]) + +function getIgnoredPnpmFieldKeys (manifest: ProjectManifest): string[] { + const legacyField = (manifest as { pnpm?: unknown }).pnpm + if (legacyField == null || typeof legacyField !== 'object' || Array.isArray(legacyField)) { + return [] + } + return Object.keys(legacyField as Record).filter(k => MIGRATED_PNPM_FIELD_KEYS.has(k)) +} + export function parsePackageManager (packageManager: string): { name: string, version: string | undefined } { if (!packageManager.includes('@')) return { name: packageManager, version: undefined } const [name, pmReference] = packageManager.split('@') diff --git a/config/reader/test/fixtures/pkg-with-legacy-pnpm-field/package.json b/config/reader/test/fixtures/pkg-with-legacy-pnpm-field/package.json new file mode 100644 index 0000000000..adcad06407 --- /dev/null +++ b/config/reader/test/fixtures/pkg-with-legacy-pnpm-field/package.json @@ -0,0 +1,10 @@ +{ + "pnpm": { + "overrides": { + "lodash": "^4.17.21" + }, + "patchedDependencies": { + "is-odd": "patches/is-odd.patch" + } + } +} diff --git a/config/reader/test/fixtures/pkg-with-pnpm-app-field/package.json b/config/reader/test/fixtures/pkg-with-pnpm-app-field/package.json new file mode 100644 index 0000000000..9cfaa842a7 --- /dev/null +++ b/config/reader/test/fixtures/pkg-with-pnpm-app-field/package.json @@ -0,0 +1,7 @@ +{ + "pnpm": { + "app": { + "entry": "dist/index.js" + } + } +} diff --git a/config/reader/test/fixtures/pkg-with-unknown-pnpm-field/package.json b/config/reader/test/fixtures/pkg-with-unknown-pnpm-field/package.json new file mode 100644 index 0000000000..3e90c1aada --- /dev/null +++ b/config/reader/test/fixtures/pkg-with-unknown-pnpm-field/package.json @@ -0,0 +1,5 @@ +{ + "pnpm": { + "foo": "bar" + } +} diff --git a/config/reader/test/index.ts b/config/reader/test/index.ts index cee7de949f..a4403fc924 100644 --- a/config/reader/test/index.ts +++ b/config/reader/test/index.ts @@ -1668,6 +1668,47 @@ test('do not return a warning if a package.json has workspaces field and there i expect(warnings).toStrictEqual([]) }) +test('return a warning if a package.json has a legacy "pnpm" field with ignored settings', async () => { + const prefix = f.find('pkg-with-legacy-pnpm-field') + const { warnings } = await getConfig({ + cliOptions: { dir: prefix }, + packageManager: { + name: 'pnpm', + version: '1.0.0', + }, + }) + + expect(warnings).toStrictEqual([ + 'The "pnpm" field in package.json is no longer read by pnpm. The following keys were ignored: "pnpm.overrides", "pnpm.patchedDependencies". See https://pnpm.io/settings for the new home of each setting.', + ]) +}) + +test('do not return a warning if a package.json "pnpm" field only contains keys that are still actively read (e.g. "pnpm.app")', async () => { + const prefix = f.find('pkg-with-pnpm-app-field') + const { warnings } = await getConfig({ + cliOptions: { dir: prefix }, + packageManager: { + name: 'pnpm', + version: '1.0.0', + }, + }) + + expect(warnings).toStrictEqual([]) +}) + +test('do not return a warning if a package.json "pnpm" field only contains keys unrelated to migrated settings (e.g. set by third-party tooling)', async () => { + const prefix = f.find('pkg-with-unknown-pnpm-field') + const { warnings } = await getConfig({ + cliOptions: { dir: prefix }, + packageManager: { + name: 'pnpm', + version: '1.0.0', + }, + }) + + expect(warnings).toStrictEqual([]) +}) + test('read PNPM_HOME defined in environment variables', async () => { const oldEnv = process.env const homeDir = './specified-dir'