feat: enhance peer dependency rules (#4876)

Co-authored-by: Zoltan Kochan <z@kochan.io>

close #4835
This commit is contained in:
Travis Ryan
2022-06-13 15:39:32 -04:00
committed by GitHub
parent 9d5bf09c04
commit fb5bbfd7a8
9 changed files with 114 additions and 21 deletions

View 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`.

View 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/*"]
}
}
}
```

View File

@@ -32,6 +32,7 @@
"@pnpm/lockfile-utils": "workspace:4.0.4",
"@pnpm/lockfile-walker": "workspace:5.0.4",
"@pnpm/manifest-utils": "workspace:3.0.3",
"@pnpm/matcher": "workspace:3.0.0",
"@pnpm/modules-cleaner": "workspace:12.0.7",
"@pnpm/modules-yaml": "workspace:10.0.2",
"@pnpm/normalize-registries": "workspace:3.0.2",

View File

@@ -1,38 +1,48 @@
import { PeerDependencyRules, ReadPackageHook } from '@pnpm/types'
import matcher from '@pnpm/matcher'
import isEmpty from 'ramda/src/isEmpty'
export default function (
peerDependencyRules: PeerDependencyRules
): 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) => {
if (isEmpty(pkg.peerDependencies)) return pkg
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[peerName] = {
optional: true,
}
}
if (
peerDependencyRules.allowedVersions?.[peerName] &&
peerVersion !== '*'
) {
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 (allowAnyMatcher(peerName)) {
pkg.peerDependencies![peerName] = '*'
continue
}
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
}) as ReadPackageHook

View File

@@ -527,7 +527,11 @@ export function createReadPackageHook (
}
if (
peerDependencyRules != null &&
(!isEmpty(peerDependencyRules.ignoreMissing) || !isEmpty(peerDependencyRules.allowedVersions))
(
!isEmpty(peerDependencyRules.ignoreMissing) ||
!isEmpty(peerDependencyRules.allowedVersions) ||
!isEmpty(peerDependencyRules.allowAny)
)
) {
hooks.push(createPeerDependencyPatcher(peerDependencyRules))
}

View File

@@ -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', () => {
const patcher = createPeerDependencyPatcher({
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 () => {
const patcher = createPeerDependencyPatcher({
allowedVersions: {

View File

@@ -78,6 +78,9 @@
{
"path": "../manifest-utils"
},
{
"path": "../matcher"
},
{
"path": "../modules-cleaner"
},

View File

@@ -112,6 +112,7 @@ export type PackageExtension = Pick<BaseManifest, 'dependencies' | 'optionalDepe
export interface PeerDependencyRules {
ignoreMissing?: string[]
allowAny?: string[]
allowedVersions?: Record<string, string>
}

2
pnpm-lock.yaml generated
View File

@@ -414,6 +414,7 @@ importers:
'@pnpm/lockfile-walker': workspace:5.0.4
'@pnpm/logger': ^4.0.0
'@pnpm/manifest-utils': workspace:3.0.3
'@pnpm/matcher': workspace:3.0.0
'@pnpm/modules-cleaner': workspace:12.0.7
'@pnpm/modules-yaml': workspace:10.0.2
'@pnpm/normalize-registries': workspace:3.0.2
@@ -490,6 +491,7 @@ importers:
'@pnpm/lockfile-utils': link:../lockfile-utils
'@pnpm/lockfile-walker': link:../lockfile-walker
'@pnpm/manifest-utils': link:../manifest-utils
'@pnpm/matcher': link:../matcher
'@pnpm/modules-cleaner': link:../modules-cleaner
'@pnpm/modules-yaml': link:../modules-yaml
'@pnpm/normalize-registries': link:../normalize-registries