Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support $ in overrides instead of specific version #5703 #5704

Merged
merged 4 commits into from
Nov 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
20 changes: 20 additions & 0 deletions .changeset/flat-lobsters-whisper.md
Original file line number Diff line number Diff line change
@@ -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"
}
}
```
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand All @@ -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<string, string>, 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}".`
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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".')
})