diff --git a/.changeset/dev-engines-package-manager-compatible.md b/.changeset/dev-engines-package-manager-compatible.md new file mode 100644 index 0000000000..a1f217a5fe --- /dev/null +++ b/.changeset/dev-engines-package-manager-compatible.md @@ -0,0 +1,6 @@ +--- +"@pnpm/config.reader": patch +"pnpm": patch +--- + +Do not print the `Cannot use both "packageManager" and "devEngines.packageManager" in package.json. "packageManager" will be ignored` warning when the two fields specify the exact same package manager name and version string. This lets projects keep both fields during the migration from `packageManager` to `devEngines.packageManager` without a noisy warning [#11301](https://github.com/pnpm/pnpm/issues/11301). diff --git a/config/reader/src/index.ts b/config/reader/src/index.ts index 2800876c3d..b30e6bfbc9 100644 --- a/config/reader/src/index.ts +++ b/config/reader/src/index.ts @@ -669,7 +669,10 @@ function getWantedPackageManager (manifest: ProjectManifest): { pm?: WantedPacka pmFromDevEngines.version = undefined } if (manifest.packageManager) { - warnings.push('Cannot use both "packageManager" and "devEngines.packageManager" in package.json. "packageManager" will be ignored') + const legacyPm = parsePackageManager(manifest.packageManager) + if (legacyPm.name !== pmFromDevEngines.name || legacyPm.version !== pmFromDevEngines.version) { + warnings.push('Cannot use both "packageManager" and "devEngines.packageManager" in package.json. "packageManager" will be ignored') + } } return { pm: { ...pmFromDevEngines, fromDevEngines: true }, warnings } } diff --git a/pnpm/test/packageManagerCheck.test.ts b/pnpm/test/packageManagerCheck.test.ts index 126b9dd98d..a4a1648a0c 100644 --- a/pnpm/test/packageManagerCheck.test.ts +++ b/pnpm/test/packageManagerCheck.test.ts @@ -247,6 +247,57 @@ test('devEngines.packageManager takes precedence over packageManager field', asy expect(stderr.toString()).toContain('"packageManager" will be ignored') }) +test('no warning when packageManager and devEngines.packageManager specify the same exact version', async () => { + prepare({ + packageManager: 'pnpm@1.2.3', + devEngines: { + packageManager: { + name: 'pnpm', + version: '1.2.3', + onFail: 'ignore', + }, + }, + }) + + const { stderr } = execPnpmSync(['install']) + + expect(stderr.toString()).not.toContain('Cannot use both') +}) + +test('warns when packageManager specifies a different package manager from devEngines.packageManager', async () => { + prepare({ + packageManager: 'yarn@1.2.3', + devEngines: { + packageManager: { + name: 'pnpm', + version: '1.2.3', + onFail: 'ignore', + }, + }, + }) + + const { stderr } = execPnpmSync(['install']) + + expect(stderr.toString()).toContain('Cannot use both "packageManager" and "devEngines.packageManager"') +}) + +test('warns when packageManager version does not match the devEngines.packageManager version string exactly', async () => { + prepare({ + packageManager: 'pnpm@1.2.3', + devEngines: { + packageManager: { + name: 'pnpm', + version: '>=1.0.0', + onFail: 'ignore', + }, + }, + }) + + const { stderr } = execPnpmSync(['install']) + + expect(stderr.toString()).toContain('Cannot use both "packageManager" and "devEngines.packageManager"') +}) + test('pmOnFail=ignore via env var bypasses the devEngines.packageManager check', async () => { prepare({ devEngines: {