diff --git a/common/changes/@microsoft/rush/warn-about-ignored-optional-dependencies-pnpm-pre-9_2024-11-09-12-28.json b/common/changes/@microsoft/rush/warn-about-ignored-optional-dependencies-pnpm-pre-9_2024-11-09-12-28.json new file mode 100644 index 00000000000..c3a40cdd88c --- /dev/null +++ b/common/changes/@microsoft/rush/warn-about-ignored-optional-dependencies-pnpm-pre-9_2024-11-09-12-28.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@microsoft/rush", + "comment": "Add warning when the `globalIgnoredOptionalDependencies` property is specified in `common/config/rush/pnpm-config.json` and the repo is configured to use pnpm <9.0.0.", + "type": "none" + } + ], + "packageName": "@microsoft/rush" +} \ No newline at end of file diff --git a/libraries/rush-lib/src/logic/installManager/InstallHelpers.ts b/libraries/rush-lib/src/logic/installManager/InstallHelpers.ts index a9d0415d330..f18605a61f3 100644 --- a/libraries/rush-lib/src/logic/installManager/InstallHelpers.ts +++ b/libraries/rush-lib/src/logic/installManager/InstallHelpers.ts @@ -9,7 +9,7 @@ import { JsonFile, LockFile } from '@rushstack/node-core-library'; -import { Colorize } from '@rushstack/terminal'; +import { Colorize, type ITerminal } from '@rushstack/terminal'; import { LastInstallFlag } from '../../api/LastInstallFlag'; import type { PackageManagerName } from '../../api/packageManager/PackageManager'; @@ -21,6 +21,7 @@ import type { PnpmOptionsConfiguration } from '../pnpm/PnpmOptionsConfiguration' import { merge } from '../../utilities/objectUtilities'; import type { Subspace } from '../../api/Subspace'; import { RushConstants } from '../RushConstants'; +import * as semver from 'semver'; interface ICommonPackageJson extends IPackageJson { pnpm?: { @@ -38,7 +39,8 @@ export class InstallHelpers { public static generateCommonPackageJson( rushConfiguration: RushConfiguration, subspace: Subspace, - dependencies: Map = new Map() + dependencies: Map = new Map(), + terminal: ITerminal ): void { const commonPackageJson: ICommonPackageJson = { dependencies: {}, @@ -71,6 +73,20 @@ export class InstallHelpers { } if (pnpmOptions.globalIgnoredOptionalDependencies) { + if ( + rushConfiguration.rushConfigurationJson.pnpmVersion !== undefined && + semver.lt(rushConfiguration.rushConfigurationJson.pnpmVersion, '9.0.0') + ) { + terminal.writeWarningLine( + Colorize.yellow( + `Your version of pnpm (${rushConfiguration.rushConfigurationJson.pnpmVersion}) ` + + `doesn't support the "globalIgnoredOptionalDependencies" field in ` + + `${rushConfiguration.commonRushConfigFolder}/${RushConstants.pnpmConfigFilename}. ` + + 'Remove this field or upgrade to pnpm 9.' + ) + ); + } + commonPackageJson.pnpm.ignoredOptionalDependencies = pnpmOptions.globalIgnoredOptionalDependencies; } diff --git a/libraries/rush-lib/src/logic/installManager/RushInstallManager.ts b/libraries/rush-lib/src/logic/installManager/RushInstallManager.ts index 03b9c506d8d..5761b45c529 100644 --- a/libraries/rush-lib/src/logic/installManager/RushInstallManager.ts +++ b/libraries/rush-lib/src/logic/installManager/RushInstallManager.ts @@ -379,7 +379,8 @@ export class RushInstallManager extends BaseInstallManager { InstallHelpers.generateCommonPackageJson( this.rushConfiguration, this.rushConfiguration.defaultSubspace, - commonDependencies + commonDependencies, + this._terminal ); stopwatch.stop(); diff --git a/libraries/rush-lib/src/logic/installManager/WorkspaceInstallManager.ts b/libraries/rush-lib/src/logic/installManager/WorkspaceInstallManager.ts index 8308aef00c8..c6f59c5264b 100644 --- a/libraries/rush-lib/src/logic/installManager/WorkspaceInstallManager.ts +++ b/libraries/rush-lib/src/logic/installManager/WorkspaceInstallManager.ts @@ -395,7 +395,7 @@ export class WorkspaceInstallManager extends BaseInstallManager { } // Write the common package.json - InstallHelpers.generateCommonPackageJson(this.rushConfiguration, subspace, undefined); + InstallHelpers.generateCommonPackageJson(this.rushConfiguration, subspace, undefined, this._terminal); // Save the generated workspace file. Don't update the file timestamp unless the content has changed, // since "rush install" will consider this timestamp diff --git a/libraries/rush-lib/src/logic/pnpm/PnpmOptionsConfiguration.ts b/libraries/rush-lib/src/logic/pnpm/PnpmOptionsConfiguration.ts index bc8ba24a594..adec7035a54 100644 --- a/libraries/rush-lib/src/logic/pnpm/PnpmOptionsConfiguration.ts +++ b/libraries/rush-lib/src/logic/pnpm/PnpmOptionsConfiguration.ts @@ -333,6 +333,8 @@ export class PnpmOptionsConfiguration extends PackageManagerOptionsConfiguration * dependencies. The settings are copied into the pnpm.ignoredOptionalDependencies field of the common/temp/package.json * file that is generated by Rush during installation. * + * (SUPPORTED ONLY IN PNPM 9.0.0 AND NEWER) + * * PNPM documentation: https://pnpm.io/package_json#pnpmignoredoptionaldependencies */ public readonly globalIgnoredOptionalDependencies: string[] | undefined; diff --git a/libraries/rush-lib/src/logic/test/InstallHelpers.test.ts b/libraries/rush-lib/src/logic/test/InstallHelpers.test.ts index e943f877654..32be66eabd8 100644 --- a/libraries/rush-lib/src/logic/test/InstallHelpers.test.ts +++ b/libraries/rush-lib/src/logic/test/InstallHelpers.test.ts @@ -4,17 +4,35 @@ import { InstallHelpers } from '../installManager/InstallHelpers'; import { RushConfiguration } from '../../api/RushConfiguration'; import { type IPackageJson, JsonFile } from '@rushstack/node-core-library'; +import { StringBufferTerminalProvider, Terminal } from '@rushstack/terminal'; describe('InstallHelpers', () => { describe('generateCommonPackageJson', () => { const originalJsonFileSave = JsonFile.save; const mockJsonFileSave: jest.Mock = jest.fn(); + let terminal: Terminal; + let terminalProvider: StringBufferTerminalProvider; + beforeAll(() => { JsonFile.save = mockJsonFileSave; }); + + beforeEach(() => { + terminalProvider = new StringBufferTerminalProvider(); + terminal = new Terminal(terminalProvider); + }); + afterEach(() => { + expect({ + output: terminalProvider.getOutput({ normalizeSpecialCharacters: true }), + verbose: terminalProvider.getVerbose({ normalizeSpecialCharacters: true }), + error: terminalProvider.getDebugOutput({ normalizeSpecialCharacters: true }), + warning: terminalProvider.getWarningOutput({ normalizeSpecialCharacters: true }), + debug: terminalProvider.getDebugOutput({ normalizeSpecialCharacters: true }) + }).toMatchSnapshot('Terminal Output'); mockJsonFileSave.mockClear(); }); + afterAll(() => { JsonFile.save = originalJsonFileSave; }); @@ -26,7 +44,8 @@ describe('InstallHelpers', () => { InstallHelpers.generateCommonPackageJson( rushConfiguration, rushConfiguration.defaultSubspace, - undefined + undefined, + terminal ); const packageJson: IPackageJson = mockJsonFileSave.mock.calls[0][0]; expect(packageJson).toEqual( diff --git a/libraries/rush-lib/src/logic/test/__snapshots__/InstallHelpers.test.ts.snap b/libraries/rush-lib/src/logic/test/__snapshots__/InstallHelpers.test.ts.snap new file mode 100644 index 00000000000..7f122921b53 --- /dev/null +++ b/libraries/rush-lib/src/logic/test/__snapshots__/InstallHelpers.test.ts.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`InstallHelpers generateCommonPackageJson generates correct package json with pnpm configurations: Terminal Output 1`] = ` +Object { + "debug": "", + "error": "", + "output": "", + "verbose": "", + "warning": "", +} +`; diff --git a/libraries/rush-lib/src/schemas/pnpm-config.schema.json b/libraries/rush-lib/src/schemas/pnpm-config.schema.json index 3193f069b88..7347662e472 100644 --- a/libraries/rush-lib/src/schemas/pnpm-config.schema.json +++ b/libraries/rush-lib/src/schemas/pnpm-config.schema.json @@ -146,7 +146,7 @@ }, "globalIgnoredOptionalDependencies": { - "description": "This field allows you to skip the installation of specific optional dependencies. The listed packages will be treated as if they are not present in the dependency tree during installation, meaning they will not be installed even if required by other packages.", + "description": "This field allows you to skip the installation of specific optional dependencies. The listed packages will be treated as if they are not present in the dependency tree during installation, meaning they will not be installed even if required by other packages.\n\n(SUPPORTED ONLY IN PNPM 9.0.0 AND NEWER)\n\nPNPM documentation: https://pnpm.io/package_json#pnpmalloweddeprecatedversions", "type": "array", "items": { "description": "Specify the package name of the optional dependency to be ignored.",