From 4097af6b5c09d9de1a3570d531bb4bb89c093a04 Mon Sep 17 00:00:00 2001 From: thinkhalo <1750367868@qq.com> Date: Tue, 29 Nov 2022 08:32:03 +0800 Subject: [PATCH] feat: support $ in overrides instead of specific version #5703 (#5704) Co-authored-by: Zoltan Kochan --- .changeset/flat-lobsters-whisper.md | 20 +++++++ .../src/getOptionsFromRootManifest.ts | 27 ++++++++- .../test/getOptionsFromRootManifest.test.ts | 58 +++++++++++++++++++ 3 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 .changeset/flat-lobsters-whisper.md diff --git a/.changeset/flat-lobsters-whisper.md b/.changeset/flat-lobsters-whisper.md new file mode 100644 index 00000000000..c422256921a --- /dev/null +++ b/.changeset/flat-lobsters-whisper.md @@ -0,0 +1,20 @@ +--- +"@pnpm/plugin-commands-installation": minor +"pnpm": minor +--- + +Overrides may be defined as a reference to a spec for a direct dependency by prefixing the name of the package you wish the version to match with a `$`. + +```json +{ + "dependencies": { + "foo": "^1.0.0" + }, + "overrides": { + // the override is defined as a reference to the dependency + "foo": "$foo", + // the referenced package does not need to match the overridden one + "bar": "$foo" + } +} +``` diff --git a/pkg-manager/plugin-commands-installation/src/getOptionsFromRootManifest.ts b/pkg-manager/plugin-commands-installation/src/getOptionsFromRootManifest.ts index f984e268b97..a68daa1498d 100644 --- a/pkg-manager/plugin-commands-installation/src/getOptionsFromRootManifest.ts +++ b/pkg-manager/plugin-commands-installation/src/getOptionsFromRootManifest.ts @@ -1,9 +1,11 @@ +import { PnpmError } from '@pnpm/error' import { AllowedDeprecatedVersions, PackageExtension, PeerDependencyRules, ProjectManifest, } from '@pnpm/types' +import mapValues from 'ramda/src/map' export function getOptionsFromRootManifest (manifest: ProjectManifest): { allowedDeprecatedVersions?: AllowedDeprecatedVersions @@ -18,7 +20,10 @@ export function getOptionsFromRootManifest (manifest: ProjectManifest): { // We read Yarn's resolutions field for compatibility // but we really replace the version specs to any other version spec, not only to exact versions, // so we cannot call it resolutions - const overrides = manifest.pnpm?.overrides ?? manifest.resolutions + const overrides = mapValues( + createVersionReferencesReplacer(manifest), + manifest.pnpm?.overrides ?? manifest.resolutions ?? {} + ) const neverBuiltDependencies = manifest.pnpm?.neverBuiltDependencies const onlyBuiltDependencies = manifest.pnpm?.onlyBuiltDependencies const packageExtensions = manifest.pnpm?.packageExtensions @@ -40,3 +45,23 @@ export function getOptionsFromRootManifest (manifest: ProjectManifest): { } return settings } + +function createVersionReferencesReplacer (manifest: ProjectManifest) { + const allDeps = { + ...manifest.devDependencies, + ...manifest.dependencies, + ...manifest.optionalDependencies, + } + return replaceVersionReferences.bind(null, allDeps) +} + +function replaceVersionReferences (dep: Record, spec: string) { + if (!spec.startsWith('$')) return spec + const dependencyName = spec.slice(1) + const newSpec = dep[dependencyName] + if (newSpec) return newSpec + throw new PnpmError( + 'CANNOT_RESOLVE_OVERRIDE_VERSION', + `Cannot resolve version ${spec} in overrides. The direct dependencies don't have dependency "${dependencyName}".` + ) +} diff --git a/pkg-manager/plugin-commands-installation/test/getOptionsFromRootManifest.test.ts b/pkg-manager/plugin-commands-installation/test/getOptionsFromRootManifest.test.ts index 51980e08879..9e8d7f7c9b1 100644 --- a/pkg-manager/plugin-commands-installation/test/getOptionsFromRootManifest.test.ts +++ b/pkg-manager/plugin-commands-installation/test/getOptionsFromRootManifest.test.ts @@ -19,3 +19,61 @@ test('getOptionsFromRootManifest() should read "overrides" field', () => { }) expect(options.overrides).toStrictEqual({ foo: '1.0.0' }) }) + +test('getOptionsFromRootManifest() Support $ in overrides by dependencies', () => { + const options = getOptionsFromRootManifest({ + dependencies: { + foo: '1.0.0', + }, + pnpm: { + overrides: { + foo: '$foo', + }, + }, + }) + expect(options.overrides).toStrictEqual({ foo: '1.0.0' }) +}) + +test('getOptionsFromRootManifest() Support $ in overrides by devDependencies', () => { + const options = getOptionsFromRootManifest({ + devDependencies: { + foo: '1.0.0', + }, + pnpm: { + overrides: { + foo: '$foo', + }, + }, + }) + expect(options.overrides).toStrictEqual({ foo: '1.0.0' }) +}) + +test('getOptionsFromRootManifest() Support $ in overrides by dependencies and devDependencies', () => { + const options = getOptionsFromRootManifest({ + dependencies: { + foo: '1.0.0', + }, + devDependencies: { + foo: '2.0.0', + }, + pnpm: { + overrides: { + foo: '$foo', + }, + }, + }) + expect(options.overrides).toStrictEqual({ foo: '1.0.0' }) +}) + +test('getOptionsFromRootManifest() throws an error if cannot resolve an override version reference', () => { + expect(() => getOptionsFromRootManifest({ + dependencies: { + bar: '1.0.0', + }, + pnpm: { + overrides: { + foo: '$foo', + }, + }, + })).toThrow('Cannot resolve version $foo in overrides. The direct dependencies don\'t have dependency "foo".') +})