mirror of
https://github.com/pnpm/pnpm.git
synced 2026-05-18 05:42:27 -04:00
feat: enhance peer dependency rules (#4876)
Co-authored-by: Zoltan Kochan <z@kochan.io> close #4835
This commit is contained in:
19
.changeset/calm-pugs-breathe.md
Normal file
19
.changeset/calm-pugs-breathe.md
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
"@pnpm/core": minor
|
||||||
|
"@pnpm/types": minor
|
||||||
|
"pnpm": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
A new setting added: `pnpm.peerDependencyRules.allowAny`. `allowAny` is an array of package name patterns, any peer dependency matching the pattern will be resolved from any version, regardless of the range specified in `peerDependencies`. For instance:
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"pnpm": {
|
||||||
|
"peerDependencyRules": {
|
||||||
|
"allowAny": ["@babel/*", "eslint"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The above setting will mute any warnings about peer dependency version mismatches related to `@babel/` packages or `eslint`.
|
||||||
16
.changeset/real-news-mate.md
Normal file
16
.changeset/real-news-mate.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
"@pnpm/core": minor
|
||||||
|
"pnpm": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
The `pnpm.peerDependencyRules.ignoreMissing` setting may accept package name patterns. So you may ignore any missing `@babel/*` peer dependencies, for instance:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"pnpm": {
|
||||||
|
"peerDependencyRules": {
|
||||||
|
"ignoreMissing": ["@babel/*"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -32,6 +32,7 @@
|
|||||||
"@pnpm/lockfile-utils": "workspace:4.0.4",
|
"@pnpm/lockfile-utils": "workspace:4.0.4",
|
||||||
"@pnpm/lockfile-walker": "workspace:5.0.4",
|
"@pnpm/lockfile-walker": "workspace:5.0.4",
|
||||||
"@pnpm/manifest-utils": "workspace:3.0.3",
|
"@pnpm/manifest-utils": "workspace:3.0.3",
|
||||||
|
"@pnpm/matcher": "workspace:3.0.0",
|
||||||
"@pnpm/modules-cleaner": "workspace:12.0.7",
|
"@pnpm/modules-cleaner": "workspace:12.0.7",
|
||||||
"@pnpm/modules-yaml": "workspace:10.0.2",
|
"@pnpm/modules-yaml": "workspace:10.0.2",
|
||||||
"@pnpm/normalize-registries": "workspace:3.0.2",
|
"@pnpm/normalize-registries": "workspace:3.0.2",
|
||||||
|
|||||||
@@ -1,38 +1,48 @@
|
|||||||
import { PeerDependencyRules, ReadPackageHook } from '@pnpm/types'
|
import { PeerDependencyRules, ReadPackageHook } from '@pnpm/types'
|
||||||
|
import matcher from '@pnpm/matcher'
|
||||||
import isEmpty from 'ramda/src/isEmpty'
|
import isEmpty from 'ramda/src/isEmpty'
|
||||||
|
|
||||||
export default function (
|
export default function (
|
||||||
peerDependencyRules: PeerDependencyRules
|
peerDependencyRules: PeerDependencyRules
|
||||||
): ReadPackageHook {
|
): ReadPackageHook {
|
||||||
const ignoreMissing = new Set(peerDependencyRules.ignoreMissing ?? [])
|
const ignoreMissingPatterns = [...new Set(peerDependencyRules.ignoreMissing ?? [])]
|
||||||
|
const ignoreMissingMatcher = matcher(ignoreMissingPatterns)
|
||||||
|
const allowAnyPatterns = [...new Set(peerDependencyRules.allowAny ?? [])]
|
||||||
|
const allowAnyMatcher = matcher(allowAnyPatterns)
|
||||||
return ((pkg) => {
|
return ((pkg) => {
|
||||||
if (isEmpty(pkg.peerDependencies)) return pkg
|
if (isEmpty(pkg.peerDependencies)) return pkg
|
||||||
for (const [peerName, peerVersion] of Object.entries(pkg.peerDependencies ?? {})) {
|
for (const [peerName, peerVersion] of Object.entries(pkg.peerDependencies ?? {})) {
|
||||||
if (ignoreMissing.has(peerName) && !pkg.peerDependenciesMeta?.[peerName]?.optional) {
|
if (
|
||||||
|
ignoreMissingMatcher(peerName) &&
|
||||||
|
!pkg.peerDependenciesMeta?.[peerName]?.optional
|
||||||
|
) {
|
||||||
pkg.peerDependenciesMeta = pkg.peerDependenciesMeta ?? {}
|
pkg.peerDependenciesMeta = pkg.peerDependenciesMeta ?? {}
|
||||||
pkg.peerDependenciesMeta[peerName] = {
|
pkg.peerDependenciesMeta[peerName] = {
|
||||||
optional: true,
|
optional: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (
|
if (allowAnyMatcher(peerName)) {
|
||||||
peerDependencyRules.allowedVersions?.[peerName] &&
|
pkg.peerDependencies![peerName] = '*'
|
||||||
peerVersion !== '*'
|
continue
|
||||||
) {
|
|
||||||
if (peerDependencyRules.allowedVersions[peerName] === '*') {
|
|
||||||
pkg.peerDependencies![peerName] = '*'
|
|
||||||
} else {
|
|
||||||
const allowedVersions = parseVersions(peerDependencyRules.allowedVersions[peerName])
|
|
||||||
const currentVersions = parseVersions(pkg.peerDependencies![peerName])
|
|
||||||
|
|
||||||
allowedVersions.forEach(allowedVersion => {
|
|
||||||
if (!currentVersions.includes(allowedVersion)) {
|
|
||||||
currentVersions.push(allowedVersion)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
pkg.peerDependencies![peerName] = currentVersions.join(' || ')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
|
!peerDependencyRules.allowedVersions?.[peerName] ||
|
||||||
|
peerVersion === '*'
|
||||||
|
) continue
|
||||||
|
if (peerDependencyRules.allowedVersions[peerName] === '*') {
|
||||||
|
pkg.peerDependencies![peerName] = '*'
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const allowedVersions = parseVersions(peerDependencyRules.allowedVersions[peerName])
|
||||||
|
const currentVersions = parseVersions(pkg.peerDependencies![peerName])
|
||||||
|
|
||||||
|
allowedVersions.forEach(allowedVersion => {
|
||||||
|
if (!currentVersions.includes(allowedVersion)) {
|
||||||
|
currentVersions.push(allowedVersion)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
pkg.peerDependencies![peerName] = currentVersions.join(' || ')
|
||||||
}
|
}
|
||||||
return pkg
|
return pkg
|
||||||
}) as ReadPackageHook
|
}) as ReadPackageHook
|
||||||
|
|||||||
@@ -527,7 +527,11 @@ export function createReadPackageHook (
|
|||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
peerDependencyRules != null &&
|
peerDependencyRules != null &&
|
||||||
(!isEmpty(peerDependencyRules.ignoreMissing) || !isEmpty(peerDependencyRules.allowedVersions))
|
(
|
||||||
|
!isEmpty(peerDependencyRules.ignoreMissing) ||
|
||||||
|
!isEmpty(peerDependencyRules.allowedVersions) ||
|
||||||
|
!isEmpty(peerDependencyRules.allowAny)
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
hooks.push(createPeerDependencyPatcher(peerDependencyRules))
|
hooks.push(createPeerDependencyPatcher(peerDependencyRules))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,23 @@ test('createPeerDependencyPatcher() ignores missing', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('createPeerDependencyPatcher() pattern matches to ignore missing', () => {
|
||||||
|
const patcher = createPeerDependencyPatcher({
|
||||||
|
ignoreMissing: ['f*r'],
|
||||||
|
})
|
||||||
|
const patchedPkg = patcher({
|
||||||
|
peerDependencies: {
|
||||||
|
foobar: '*',
|
||||||
|
bar: '*',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(patchedPkg['peerDependenciesMeta']).toStrictEqual({
|
||||||
|
foobar: {
|
||||||
|
optional: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
test('createPeerDependencyPatcher() extends peer ranges', () => {
|
test('createPeerDependencyPatcher() extends peer ranges', () => {
|
||||||
const patcher = createPeerDependencyPatcher({
|
const patcher = createPeerDependencyPatcher({
|
||||||
allowedVersions: {
|
allowedVersions: {
|
||||||
@@ -41,6 +58,26 @@ test('createPeerDependencyPatcher() extends peer ranges', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('createPeerDependencyPatcher() ignores peer versions from allowAny', () => {
|
||||||
|
const patcher = createPeerDependencyPatcher({
|
||||||
|
allowAny: ['foo', 'bar'],
|
||||||
|
})
|
||||||
|
const patchedPkg = patcher({
|
||||||
|
peerDependencies: {
|
||||||
|
foo: '2',
|
||||||
|
bar: '2',
|
||||||
|
qar: '2',
|
||||||
|
baz: '2',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(patchedPkg['peerDependencies']).toStrictEqual({
|
||||||
|
foo: '*',
|
||||||
|
bar: '*',
|
||||||
|
qar: '2',
|
||||||
|
baz: '2',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
test('createPeerDependencyPatcher() does not create duplicate extended ranges', async () => {
|
test('createPeerDependencyPatcher() does not create duplicate extended ranges', async () => {
|
||||||
const patcher = createPeerDependencyPatcher({
|
const patcher = createPeerDependencyPatcher({
|
||||||
allowedVersions: {
|
allowedVersions: {
|
||||||
|
|||||||
@@ -78,6 +78,9 @@
|
|||||||
{
|
{
|
||||||
"path": "../manifest-utils"
|
"path": "../manifest-utils"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "../matcher"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "../modules-cleaner"
|
"path": "../modules-cleaner"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ export type PackageExtension = Pick<BaseManifest, 'dependencies' | 'optionalDepe
|
|||||||
|
|
||||||
export interface PeerDependencyRules {
|
export interface PeerDependencyRules {
|
||||||
ignoreMissing?: string[]
|
ignoreMissing?: string[]
|
||||||
|
allowAny?: string[]
|
||||||
allowedVersions?: Record<string, string>
|
allowedVersions?: Record<string, string>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
2
pnpm-lock.yaml
generated
2
pnpm-lock.yaml
generated
@@ -414,6 +414,7 @@ importers:
|
|||||||
'@pnpm/lockfile-walker': workspace:5.0.4
|
'@pnpm/lockfile-walker': workspace:5.0.4
|
||||||
'@pnpm/logger': ^4.0.0
|
'@pnpm/logger': ^4.0.0
|
||||||
'@pnpm/manifest-utils': workspace:3.0.3
|
'@pnpm/manifest-utils': workspace:3.0.3
|
||||||
|
'@pnpm/matcher': workspace:3.0.0
|
||||||
'@pnpm/modules-cleaner': workspace:12.0.7
|
'@pnpm/modules-cleaner': workspace:12.0.7
|
||||||
'@pnpm/modules-yaml': workspace:10.0.2
|
'@pnpm/modules-yaml': workspace:10.0.2
|
||||||
'@pnpm/normalize-registries': workspace:3.0.2
|
'@pnpm/normalize-registries': workspace:3.0.2
|
||||||
@@ -490,6 +491,7 @@ importers:
|
|||||||
'@pnpm/lockfile-utils': link:../lockfile-utils
|
'@pnpm/lockfile-utils': link:../lockfile-utils
|
||||||
'@pnpm/lockfile-walker': link:../lockfile-walker
|
'@pnpm/lockfile-walker': link:../lockfile-walker
|
||||||
'@pnpm/manifest-utils': link:../manifest-utils
|
'@pnpm/manifest-utils': link:../manifest-utils
|
||||||
|
'@pnpm/matcher': link:../matcher
|
||||||
'@pnpm/modules-cleaner': link:../modules-cleaner
|
'@pnpm/modules-cleaner': link:../modules-cleaner
|
||||||
'@pnpm/modules-yaml': link:../modules-yaml
|
'@pnpm/modules-yaml': link:../modules-yaml
|
||||||
'@pnpm/normalize-registries': link:../normalize-registries
|
'@pnpm/normalize-registries': link:../normalize-registries
|
||||||
|
|||||||
Reference in New Issue
Block a user