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

feat: enhance peer dependency rules #4876

Merged
merged 7 commits into from Jun 13, 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
19 changes: 19 additions & 0 deletions .changeset/calm-pugs-breathe.md
@@ -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 changes: 16 additions & 0 deletions .changeset/real-news-mate.md
@@ -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/*"]
}
}
}
```
1 change: 1 addition & 0 deletions packages/core/package.json
Expand Up @@ -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",
Expand Down
46 changes: 28 additions & 18 deletions packages/core/src/install/createPeerDependencyPatcher.ts
@@ -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 (allowAnyMatcher(peerName)) {
pkg.peerDependencies![peerName] = '*'
continue
}
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)
}
})
!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])

pkg.peerDependencies![peerName] = currentVersions.join(' || ')
allowedVersions.forEach(allowedVersion => {
if (!currentVersions.includes(allowedVersion)) {
currentVersions.push(allowedVersion)
}
}
})

pkg.peerDependencies![peerName] = currentVersions.join(' || ')
}
return pkg
}) as ReadPackageHook
Expand Down
6 changes: 5 additions & 1 deletion packages/core/src/install/index.ts
Expand Up @@ -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))
}
Expand Down
37 changes: 37 additions & 0 deletions packages/core/test/install/createPeerDependencyPatcher.test.ts
Expand Up @@ -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: {
Expand All @@ -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: {
Expand Down
3 changes: 3 additions & 0 deletions packages/core/tsconfig.json
Expand Up @@ -78,6 +78,9 @@
{
"path": "../manifest-utils"
},
{
"path": "../matcher"
},
{
"path": "../modules-cleaner"
},
Expand Down
1 change: 1 addition & 0 deletions packages/types/src/package.ts
Expand Up @@ -112,6 +112,7 @@ export type PackageExtension = Pick<BaseManifest, 'dependencies' | 'optionalDepe

export interface PeerDependencyRules {
ignoreMissing?: string[]
allowAny?: string[]
allowedVersions?: Record<string, string>
}

Expand Down
2 changes: 2 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.