diff --git a/common/changes/@microsoft/rush/fix-3205_2024-07-17-01-52.json b/common/changes/@microsoft/rush/fix-3205_2024-07-17-01-52.json new file mode 100644 index 00000000000..b7b9c62884a --- /dev/null +++ b/common/changes/@microsoft/rush/fix-3205_2024-07-17-01-52.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@microsoft/rush", + "comment": "Fix an issue where PreferredVersions are ignored when a project contains an overlapping dependency entry (https://github.com/microsoft/rushstack/issues/3205)", + "type": "none" + } + ], + "packageName": "@microsoft/rush" +} \ No newline at end of file diff --git a/libraries/rush-lib/src/logic/pnpm/PnpmfileConfiguration.ts b/libraries/rush-lib/src/logic/pnpm/PnpmfileConfiguration.ts index a7090c1746f..6dece515a8a 100644 --- a/libraries/rush-lib/src/logic/pnpm/PnpmfileConfiguration.ts +++ b/libraries/rush-lib/src/logic/pnpm/PnpmfileConfiguration.ts @@ -2,6 +2,7 @@ // See LICENSE in the project root for license information. import * as path from 'path'; +import * as semver from 'semver'; import { FileSystem, Import, type IPackageJson, JsonFile, MapExtensions } from '@rushstack/node-core-library'; import type { PnpmPackageManager } from '../../api/packageManager/PnpmPackageManager'; @@ -91,11 +92,16 @@ export class PnpmfileConfiguration { if ((rushConfiguration.packageManagerOptions as PnpmOptionsConfiguration).useWorkspaces) { const commonVersionsConfiguration: CommonVersionsConfiguration = subspace.getCommonVersions(); const preferredVersions: Map = new Map(); - MapExtensions.mergeFromMap(preferredVersions, commonVersionsConfiguration.getAllPreferredVersions()); MapExtensions.mergeFromMap( preferredVersions, rushConfiguration.getImplicitlyPreferredVersions(subspace) ); + for (const [name, version] of commonVersionsConfiguration.getAllPreferredVersions()) { + // Use the most restrictive version range available + if (!preferredVersions.has(name) || semver.subset(version, preferredVersions.get(name)!)) { + preferredVersions.set(name, version); + } + } allPreferredVersions = MapExtensions.toObject(preferredVersions); allowedAlternativeVersions = MapExtensions.toObject( commonVersionsConfiguration.allowedAlternativeVersions diff --git a/libraries/rush-lib/src/logic/pnpm/test/PnpmfileConfiguration.test.ts b/libraries/rush-lib/src/logic/pnpm/test/PnpmfileConfiguration.test.ts new file mode 100644 index 00000000000..b1875dbde8f --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/PnpmfileConfiguration.test.ts @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { RushConfiguration } from '../../../api/RushConfiguration'; +import { PnpmfileConfiguration } from '../PnpmfileConfiguration'; +import { JsonFile, type JsonObject } from '@rushstack/node-core-library'; + +describe(PnpmfileConfiguration.name, () => { + const repoPath: string = `${__dirname}/repo`; + const rushFilename: string = `${repoPath}/rush3.json`; + const rushConfiguration: RushConfiguration = RushConfiguration.loadFromConfigurationFile(rushFilename); + const shimPath: string = `${rushConfiguration.defaultSubspace.getSubspaceTempFolderPath()}/pnpmfileSettings.json`; + + beforeAll(async () => { + const subspace = rushConfiguration.defaultSubspace; + await PnpmfileConfiguration.writeCommonTempPnpmfileShimAsync( + rushConfiguration, + subspace.getSubspaceTempFolderPath(), + subspace + ); + }); + + it('should use the smallest-available SemVer range (preferredVersions)', async () => { + const shimJson: JsonObject = await JsonFile.loadAsync(shimPath); + expect(shimJson.allPreferredVersions).toHaveProperty('core-js', '3.6.5'); + }); + + it('should use the smallest-available SemVer range (per-project)', async () => { + const shimJson: JsonObject = await JsonFile.loadAsync(shimPath); + expect(shimJson.allPreferredVersions).toHaveProperty('delay', '5.0.0'); + }); + + it('should override preferredVersions when per-project versions conflict', async () => { + const shimJson: JsonObject = await JsonFile.loadAsync(shimPath); + expect(shimJson.allPreferredVersions).toHaveProperty('find-up', '5.0.0'); + }); +}); diff --git a/libraries/rush-lib/src/logic/pnpm/test/repo/apps/baz/package.json b/libraries/rush-lib/src/logic/pnpm/test/repo/apps/baz/package.json new file mode 100644 index 00000000000..524e9d4cb89 --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/repo/apps/baz/package.json @@ -0,0 +1,9 @@ +{ + "name": "baz", + "version": "1.0.0", + "dependencies": { + "core-js": "^3.0.0", + "delay": "5.0.0", + "find-up": "*" + } +} diff --git a/libraries/rush-lib/src/logic/pnpm/test/repo/common/config/rush/common-versions.json b/libraries/rush-lib/src/logic/pnpm/test/repo/common/config/rush/common-versions.json new file mode 100644 index 00000000000..30d53549184 --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/repo/common/config/rush/common-versions.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/common-versions.schema.json", + "preferredVersions": { + "core-js": "3.6.5", + "delay": "4.0.0", + "find-up": "5.0.0" + } +} diff --git a/libraries/rush-lib/src/logic/pnpm/test/repo/rush3.json b/libraries/rush-lib/src/logic/pnpm/test/repo/rush3.json new file mode 100644 index 00000000000..d5b3a4e1d82 --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/repo/rush3.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush.schema.json", + "pnpmVersion": "7.0.0", + "rushVersion": "5.46.1", + "projects": [ + { + "packageName": "baz", + "projectFolder": "apps/baz" + } + ], + "pnpmOptions": { + "useWorkspaces": true + } +}