diff --git a/common/changes/@microsoft/rush/chao-auto-install-peers_2023-10-03-20-48.json b/common/changes/@microsoft/rush/chao-auto-install-peers_2023-10-03-20-48.json new file mode 100644 index 00000000000..777312048eb --- /dev/null +++ b/common/changes/@microsoft/rush/chao-auto-install-peers_2023-10-03-20-48.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@microsoft/rush", + "comment": "(IMPORTANT) Add a new setting `autoInstallPeers` in pnpm-config.json; be aware that Rush changes PNPM's default if you are using PNPM 8 or newer", + "type": "none" + } + ], + "packageName": "@microsoft/rush" +} \ No newline at end of file diff --git a/common/reviews/api/rush-lib.api.md b/common/reviews/api/rush-lib.api.md index e7e22736cb8..bc7e774a23c 100644 --- a/common/reviews/api/rush-lib.api.md +++ b/common/reviews/api/rush-lib.api.md @@ -651,6 +651,7 @@ export interface IPhasedCommand extends IRushCommand { // @internal export interface _IPnpmOptionsJson extends IPackageManagerOptionsJsonBase { + autoInstallPeers?: boolean; globalAllowedDeprecatedVersions?: Record; globalNeverBuiltDependencies?: string[]; globalOverrides?: Record; @@ -965,6 +966,7 @@ export class PhasedCommandHooks { // @public export class PnpmOptionsConfiguration extends PackageManagerOptionsConfigurationBase { + readonly autoInstallPeers: boolean | undefined; readonly globalAllowedDeprecatedVersions: Record | undefined; readonly globalNeverBuiltDependencies: string[] | undefined; readonly globalOverrides: Record | undefined; diff --git a/libraries/rush-lib/assets/rush-init/common/config/rush/pnpm-config.json b/libraries/rush-lib/assets/rush-init/common/config/rush/pnpm-config.json index 0d27d52e64d..0254b0310f1 100644 --- a/libraries/rush-lib/assets/rush-init/common/config/rush/pnpm-config.json +++ b/libraries/rush-lib/assets/rush-init/common/config/rush/pnpm-config.json @@ -43,6 +43,25 @@ */ /*[LINE "DEMO"]*/ "resolutionMode": "time-based", + /** + * This setting determines whether PNPM will automatically install (non-optional) + * missing peer dependencies instead of reporting an error. Doing so conveniently + * avoids the need to specify peer versions in package.json, but in a large monorepo + * this often creates worse problems. The reason is that peer dependency behavior + * is inherently complicated, and it is easier to troubleshoot consequences of an explicit + * version than an invisible heuristic. The original NPM RFC discussion pointed out + * some other problems with this feature: https://github.com/npm/rfcs/pull/43 + + * IMPORTANT: Without Rush, the setting defaults to true for PNPM 8 and newer; however, + * as of Rush version 5.109.0 the default is always false unless `autoInstallPeers` + * is specified in pnpm-config.json or .npmrc, regardless of your PNPM version. + + * PNPM documentation: https://pnpm.io/npmrc#auto-install-peers + + * The default value is false. + */ + /*[LINE "DEMO"]*/ "autoInstallPeers": false, + /** * If true, then Rush will add the `--strict-peer-dependencies` command-line parameter when * invoking PNPM. This causes `rush update` to fail if there are unsatisfied peer dependencies, diff --git a/libraries/rush-lib/src/logic/base/BaseInstallManager.ts b/libraries/rush-lib/src/logic/base/BaseInstallManager.ts index b8003306505..5df4655228a 100644 --- a/libraries/rush-lib/src/logic/base/BaseInstallManager.ts +++ b/libraries/rush-lib/src/logic/base/BaseInstallManager.ts @@ -643,6 +643,34 @@ ${gitLfsHookHandling} args.push('--strict-peer-dependencies'); } + /* + If user set auto-install-peers in pnpm-config.json only, use the value in pnpm-config.json + If user set auto-install-peers in pnpm-config.json and .npmrc, use the value in pnpm-config.json + If user set auto-install-peers in .npmrc only, do nothing, let pnpm handle it + If user does not set auto-install-peers in both pnpm-config.json and .npmrc, rush will default it to "false" + */ + const isAutoInstallPeersInNpmrc: boolean = isVariableSetInNpmrcFile( + this.rushConfiguration.commonRushConfigFolder, + 'auto-install-peers' + ); + + let autoInstallPeers: boolean | undefined = this.rushConfiguration.pnpmOptions.autoInstallPeers; + if (autoInstallPeers !== undefined) { + if (isAutoInstallPeersInNpmrc) { + this._terminal.writeWarningLine( + `Warning: PNPM's auto-install-peers is specified in both .npmrc and pnpm-config.json. ` + + `The value in pnpm-config.json will take precedence.` + ); + } + } else if (!isAutoInstallPeersInNpmrc) { + // if auto-install-peers isn't specified in either .npmrc or pnpm-config.json, + // then rush will default it to "false" + autoInstallPeers = false; + } + if (autoInstallPeers !== undefined) { + args.push(`--config.auto-install-peers=${autoInstallPeers}`); + } + /* If user set resolution-mode in pnpm-config.json only, use the value in pnpm-config.json If user set resolution-mode in pnpm-config.json and .npmrc, use the value in pnpm-config.json diff --git a/libraries/rush-lib/src/logic/pnpm/PnpmOptionsConfiguration.ts b/libraries/rush-lib/src/logic/pnpm/PnpmOptionsConfiguration.ts index d24dda84905..8b7d5739dd1 100644 --- a/libraries/rush-lib/src/logic/pnpm/PnpmOptionsConfiguration.ts +++ b/libraries/rush-lib/src/logic/pnpm/PnpmOptionsConfiguration.ts @@ -107,6 +107,10 @@ export interface IPnpmOptionsJson extends IPackageManagerOptionsJsonBase { * {@inheritDoc PnpmOptionsConfiguration.resolutionMode} */ resolutionMode?: PnpmResolutionMode; + /** + * {@inheritDoc PnpmOptionsConfiguration.autoInstallPeers} + */ + autoInstallPeers?: boolean; } /** @@ -209,6 +213,14 @@ export class PnpmOptionsConfiguration extends PackageManagerOptionsConfiguration */ public readonly useWorkspaces: boolean; + /** + * When true, any missing non-optional peer dependencies are automatically installed. + * + * @remarks + * The default value is same as PNPM default value. (In PNPM 8.x, this value is true) + */ + public readonly autoInstallPeers: boolean | undefined; + /** * The "globalOverrides" setting provides a simple mechanism for overriding version selections * for all dependencies of all projects in the monorepo workspace. The settings are copied @@ -332,6 +344,7 @@ export class PnpmOptionsConfiguration extends PackageManagerOptionsConfiguration this.unsupportedPackageJsonSettings = json.unsupportedPackageJsonSettings; this._globalPatchedDependencies = json.globalPatchedDependencies; this.resolutionMode = json.resolutionMode; + this.autoInstallPeers = json.autoInstallPeers; } /** @internal */ diff --git a/libraries/rush-lib/src/schemas/pnpm-config.schema.json b/libraries/rush-lib/src/schemas/pnpm-config.schema.json index 6eb3e97db46..6d87e34a5b1 100644 --- a/libraries/rush-lib/src/schemas/pnpm-config.schema.json +++ b/libraries/rush-lib/src/schemas/pnpm-config.schema.json @@ -165,6 +165,11 @@ "description": "This option overrides the resolution-mode in PNPM. Use it if you want to change the default resolution behavior when installing dependencies. Defaults to \"highest\".\n\nPNPM documentation: https://pnpm.io/npmrc#resolution-mode.", "type": "string", "enum": ["highest", "time-based", "lowest-direct"] + }, + + "autoInstallPeers": { + "description": "This setting determines whether PNPM will automatically install (non-optional) missing peer dependencies instead of reporting an error. With Rush, the default value is always false.\n\nPNPM documentation: https://pnpm.io/npmrc#auto-install-peers", + "type": "boolean" } } }