diff --git a/common/reviews/api/rush-lib.api.md b/common/reviews/api/rush-lib.api.md index da3c87d6bd4..e0f49f952c7 100644 --- a/common/reviews/api/rush-lib.api.md +++ b/common/reviews/api/rush-lib.api.md @@ -743,6 +743,7 @@ export interface _IPnpmOptionsJson extends IPackageManagerOptionsJsonBase { globalCatalogs?: Record>; globalIgnoredOptionalDependencies?: string[]; globalNeverBuiltDependencies?: string[]; + globalOnlyBuiltDependencies?: string[]; globalOverrides?: Record; globalPackageExtensions?: Record; globalPatchedDependencies?: Record; @@ -1155,6 +1156,7 @@ export class PnpmOptionsConfiguration extends PackageManagerOptionsConfiguration readonly globalCatalogs: Record> | undefined; readonly globalIgnoredOptionalDependencies: string[] | undefined; readonly globalNeverBuiltDependencies: string[] | undefined; + readonly globalOnlyBuiltDependencies: string[] | undefined; readonly globalOverrides: Record | undefined; readonly globalPackageExtensions: Record | undefined; get globalPatchedDependencies(): 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 d677fa2179f..ef0b93f659c 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 @@ -306,6 +306,23 @@ /*[LINE "HYPOTHETICAL"]*/ "fsevents" ], + /** + * The `globalOnlyBuiltDependencies` setting specifies an allowlist of dependencies that are permitted + * to run build scripts (`preinstall`, `install`, `postinstall`). In PNPM 10.x, build scripts are + * disabled by default for security. Use this setting to explicitly permit specific packages to run + * their build scripts. This is the inverse of `globalNeverBuiltDependencies`. + * + * The settings are copied into the `pnpm.onlyBuiltDependencies` field of the `common/temp/package.json` + * file that is generated by Rush during installation. + * + * (SUPPORTED ONLY IN PNPM 10.1.0 AND NEWER) + * + * PNPM documentation: https://pnpm.io/package_json#pnpmonlybuiltdependencies + */ + "globalOnlyBuiltDependencies": [ + /*[LINE "HYPOTHETICAL"]*/ "esbuild" + ], + /** * The `globalIgnoredOptionalDependencies` setting suppresses the installation of optional NPM * dependencies specified in the list. This is useful when certain optional dependencies are diff --git a/libraries/rush-lib/src/logic/installManager/InstallHelpers.ts b/libraries/rush-lib/src/logic/installManager/InstallHelpers.ts index 8f850b4e73d..35a1998c6a9 100644 --- a/libraries/rush-lib/src/logic/installManager/InstallHelpers.ts +++ b/libraries/rush-lib/src/logic/installManager/InstallHelpers.ts @@ -31,6 +31,7 @@ interface ICommonPackageJson extends IPackageJson { packageExtensions?: typeof PnpmOptionsConfiguration.prototype.globalPackageExtensions; peerDependencyRules?: typeof PnpmOptionsConfiguration.prototype.globalPeerDependencyRules; neverBuiltDependencies?: typeof PnpmOptionsConfiguration.prototype.globalNeverBuiltDependencies; + onlyBuiltDependencies?: typeof PnpmOptionsConfiguration.prototype.globalOnlyBuiltDependencies; ignoredOptionalDependencies?: typeof PnpmOptionsConfiguration.prototype.globalIgnoredOptionalDependencies; allowedDeprecatedVersions?: typeof PnpmOptionsConfiguration.prototype.globalAllowedDeprecatedVersions; patchedDependencies?: typeof PnpmOptionsConfiguration.prototype.globalPatchedDependencies; @@ -76,6 +77,24 @@ export class InstallHelpers { commonPackageJson.pnpm.neverBuiltDependencies = pnpmOptions.globalNeverBuiltDependencies; } + if (pnpmOptions.globalOnlyBuiltDependencies) { + if ( + rushConfiguration.rushConfigurationJson.pnpmVersion !== undefined && + semver.lt(rushConfiguration.rushConfigurationJson.pnpmVersion, '10.1.0') + ) { + terminal.writeWarningLine( + Colorize.yellow( + `Your version of pnpm (${rushConfiguration.rushConfigurationJson.pnpmVersion}) ` + + `doesn't support the "globalOnlyBuiltDependencies" field in ` + + `${rushConfiguration.commonRushConfigFolder}/${RushConstants.pnpmConfigFilename}. ` + + 'Remove this field or upgrade to pnpm 10.1.0 or newer.' + ) + ); + } + + commonPackageJson.pnpm.onlyBuiltDependencies = pnpmOptions.globalOnlyBuiltDependencies; + } + if (pnpmOptions.globalIgnoredOptionalDependencies) { if ( rushConfiguration.rushConfigurationJson.pnpmVersion !== undefined && diff --git a/libraries/rush-lib/src/logic/pnpm/PnpmOptionsConfiguration.ts b/libraries/rush-lib/src/logic/pnpm/PnpmOptionsConfiguration.ts index 555f02e7dd0..d0289c9ff4d 100644 --- a/libraries/rush-lib/src/logic/pnpm/PnpmOptionsConfiguration.ts +++ b/libraries/rush-lib/src/logic/pnpm/PnpmOptionsConfiguration.ts @@ -114,6 +114,10 @@ export interface IPnpmOptionsJson extends IPackageManagerOptionsJsonBase { * {@inheritDoc PnpmOptionsConfiguration.globalNeverBuiltDependencies} */ globalNeverBuiltDependencies?: string[]; + /** + * {@inheritDoc PnpmOptionsConfiguration.globalOnlyBuiltDependencies} + */ + globalOnlyBuiltDependencies?: string[]; /** * {@inheritDoc PnpmOptionsConfiguration.globalIgnoredOptionalDependencies} */ @@ -365,6 +369,21 @@ export class PnpmOptionsConfiguration extends PackageManagerOptionsConfiguration */ public readonly globalNeverBuiltDependencies: string[] | undefined; + /** + * The `globalOnlyBuiltDependencies` setting specifies an allowlist of dependencies that are permitted + * to run build scripts (`preinstall`, `install`, `postinstall`). In PNPM 10.x, build scripts are + * disabled by default for security. Use this setting to explicitly permit specific packages to run + * their build scripts. This is the inverse of `globalNeverBuiltDependencies`. + * + * The settings are copied into the `pnpm.onlyBuiltDependencies` field of the `common/temp/package.json` + * file that is generated by Rush during installation. + * + * (SUPPORTED ONLY IN PNPM 10.1.0 AND NEWER) + * + * PNPM documentation: https://pnpm.io/package_json#pnpmonlybuiltdependencies + */ + public readonly globalOnlyBuiltDependencies: string[] | undefined; + /** * The ignoredOptionalDependencies setting allows you to exclude certain optional dependencies from being installed * during the Rush installation process. This can be useful when optional dependencies are not required or are @@ -468,6 +487,7 @@ export class PnpmOptionsConfiguration extends PackageManagerOptionsConfiguration this.globalPeerDependencyRules = json.globalPeerDependencyRules; this.globalPackageExtensions = json.globalPackageExtensions; this.globalNeverBuiltDependencies = json.globalNeverBuiltDependencies; + this.globalOnlyBuiltDependencies = json.globalOnlyBuiltDependencies; this.globalIgnoredOptionalDependencies = json.globalIgnoredOptionalDependencies; this.globalAllowedDeprecatedVersions = json.globalAllowedDeprecatedVersions; this.unsupportedPackageJsonSettings = json.unsupportedPackageJsonSettings; diff --git a/libraries/rush-lib/src/logic/pnpm/test/PnpmOptionsConfiguration.test.ts b/libraries/rush-lib/src/logic/pnpm/test/PnpmOptionsConfiguration.test.ts index c590ca689fe..ad9dd94b3e9 100644 --- a/libraries/rush-lib/src/logic/pnpm/test/PnpmOptionsConfiguration.test.ts +++ b/libraries/rush-lib/src/logic/pnpm/test/PnpmOptionsConfiguration.test.ts @@ -74,6 +74,19 @@ describe(PnpmOptionsConfiguration.name, () => { ]); }); + it('loads onlyBuiltDependencies', () => { + const pnpmConfiguration: PnpmOptionsConfiguration = PnpmOptionsConfiguration.loadFromJsonFileOrThrow( + `${__dirname}/jsonFiles/pnpm-config-onlyBuiltDependencies.json`, + fakeCommonTempFolder + ); + + expect(TestUtilities.stripAnnotations(pnpmConfiguration.globalOnlyBuiltDependencies)).toEqual([ + 'esbuild', + 'canvas', + '@prisma/client' + ]); + }); + it('loads minimumReleaseAge', () => { const pnpmConfiguration: PnpmOptionsConfiguration = PnpmOptionsConfiguration.loadFromJsonFileOrThrow( `${__dirname}/jsonFiles/pnpm-config-minimumReleaseAge.json`, diff --git a/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-onlyBuiltDependencies.json b/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-onlyBuiltDependencies.json new file mode 100644 index 00000000000..94beb1042b3 --- /dev/null +++ b/libraries/rush-lib/src/logic/pnpm/test/jsonFiles/pnpm-config-onlyBuiltDependencies.json @@ -0,0 +1,3 @@ +{ + "globalOnlyBuiltDependencies": ["esbuild", "canvas", "@prisma/client"] +} diff --git a/libraries/rush-lib/src/schemas/pnpm-config.schema.json b/libraries/rush-lib/src/schemas/pnpm-config.schema.json index c7e628af851..078e673fd73 100644 --- a/libraries/rush-lib/src/schemas/pnpm-config.schema.json +++ b/libraries/rush-lib/src/schemas/pnpm-config.schema.json @@ -150,6 +150,15 @@ } }, + "globalOnlyBuiltDependencies": { + "description": "This field specifies an allowlist of dependencies that are permitted to run build scripts (preinstall, install, postinstall). In PNPM 10.x, build scripts are disabled by default for security. Use this setting to explicitly permit specific packages to run their build scripts. This is the inverse of \"globalNeverBuiltDependencies\".\n\n(SUPPORTED ONLY IN PNPM 10.1.0 AND NEWER)\n\nPNPM documentation: https://pnpm.io/package_json#pnpmonlybuiltdependencies", + "type": "array", + "items": { + "description": "Specify package name of the dependency", + "type": "string" + } + }, + "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.\n\n(SUPPORTED ONLY IN PNPM 9.0.0 AND NEWER)\n\nPNPM documentation: https://pnpm.io/package_json#pnpmalloweddeprecatedversions", "type": "array",