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 <damon@deeplearning.ai>
Co-authored-by: Zoltan Kochan <z@kochan.io>
This commit is contained in:
shiminshen
2026-05-18 06:13:49 +08:00
committed by GitHub
parent 06d2d3deb2
commit 8df408c901
6 changed files with 105 additions and 0 deletions

View File

@@ -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).

View File

@@ -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<string>([
'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<string, unknown>).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('@')

View File

@@ -0,0 +1,10 @@
{
"pnpm": {
"overrides": {
"lodash": "^4.17.21"
},
"patchedDependencies": {
"is-odd": "patches/is-odd.patch"
}
}
}

View File

@@ -0,0 +1,7 @@
{
"pnpm": {
"app": {
"entry": "dist/index.js"
}
}
}

View File

@@ -0,0 +1,5 @@
{
"pnpm": {
"foo": "bar"
}
}

View File

@@ -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'