Skip to content

Commit

Permalink
feat: support peerDependencyRules for muting peer dep issues (#4212)
Browse files Browse the repository at this point in the history
close #4183
  • Loading branch information
zkochan authored Jan 11, 2022
1 parent a004d07 commit 26cd01b
Show file tree
Hide file tree
Showing 9 changed files with 128 additions and 2 deletions.
21 changes: 21 additions & 0 deletions .changeset/famous-toys-provide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
"pnpm": minor
"@pnpm/plugin-commands-installation": minor
---

In order to mute some types of peer dependency warnings, a new section in `package.json` may be used for declaring peer dependency warning rules. For example, the next configuration will turn off any warnings about missing `babel-loader` peer dependency and about `@angular/common`, when the wanted version of `@angular/common` is not v13.

```json
{
"name": "foo",
"version": "0.0.0",
"pnpm": {
"peerDependencyRules": {
"ignoreMissing": ["babel-loader"],
"allowedVersions": {
"@angular/common": "13"
}
}
}
}
```
5 changes: 5 additions & 0 deletions .changeset/itchy-berries-wink.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@pnpm/core": minor
---

New optional option supported: `peerDependencyRules`. This setting allows to mute specific peer dependency warnings.
5 changes: 5 additions & 0 deletions .changeset/plenty-mayflies-hug.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@pnpm/types": minor
---

New field added to package.json.pnpm section: peerDependencyRules.
26 changes: 26 additions & 0 deletions packages/core/src/install/createPeerDependencyPatcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { PeerDependencyRules, ReadPackageHook } from '@pnpm/types'
import isEmpty from 'ramda/src/isEmpty'

export default function (
peerDependencyRules: PeerDependencyRules
): ReadPackageHook {
const ignoreMissing = new Set(peerDependencyRules.ignoreMissing ?? [])
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) {
pkg.peerDependenciesMeta = pkg.peerDependenciesMeta ?? {}
pkg.peerDependenciesMeta[peerName] = {
optional: true,
}
}
if (
peerDependencyRules.allowedVersions?.[peerName] &&
peerVersion !== '*'
) {
pkg.peerDependencies![peerName] += ` || ${peerDependencyRules.allowedVersions[peerName]}`
}
}
return pkg
}) as ReadPackageHook
}
2 changes: 2 additions & 0 deletions packages/core/src/install/extendInstallOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { WorkspacePackages } from '@pnpm/resolver-base'
import { StoreController } from '@pnpm/store-controller-types'
import {
PackageExtension,
PeerDependencyRules,
ReadPackageHook,
Registries,
} from '@pnpm/types'
Expand Down Expand Up @@ -80,6 +81,7 @@ export interface StrictInstallOptions {
symlink: boolean
enableModulesDir: boolean
modulesCacheMaxAge: number
peerDependencyRules: PeerDependencyRules

hoistPattern: string[] | undefined
forceHoistPattern: boolean
Expand Down
11 changes: 11 additions & 0 deletions packages/core/src/install/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import {
DependenciesField,
DependencyManifest,
PackageExtension,
PeerDependencyRules,
ProjectManifest,
ReadPackageHook,
} from '@pnpm/types'
Expand All @@ -68,6 +69,7 @@ import removeDeps from '../uninstall/removeDeps'
import allProjectsAreUpToDate from './allProjectsAreUpToDate'
import createPackageExtender from './createPackageExtender'
import createVersionsOverrider from './createVersionsOverrider'
import createPeerDependencyPatcher from './createPeerDependencyPatcher'
import extendOptions, {
InstallOptions,
StrictInstallOptions,
Expand Down Expand Up @@ -170,6 +172,7 @@ export async function mutateModules (
overrides: opts.overrides,
lockfileDir: opts.lockfileDir,
packageExtensions: opts.packageExtensions,
peerDependencyRules: opts.peerDependencyRules,
})
const ctx = await getContext(projects, opts)
const pruneVirtualStore = ctx.modulesFile?.prunedAt && opts.modulesCacheMaxAge > 0
Expand Down Expand Up @@ -483,11 +486,13 @@ export function createReadPackageHook (
lockfileDir,
overrides,
packageExtensions,
peerDependencyRules,
readPackageHook,
}: {
lockfileDir: string
overrides?: Record<string, string>
packageExtensions?: Record<string, PackageExtension>
peerDependencyRules?: PeerDependencyRules
readPackageHook?: ReadPackageHook
}
): ReadPackageHook | undefined {
Expand All @@ -498,6 +503,12 @@ export function createReadPackageHook (
if (!isEmpty(packageExtensions ?? {})) {
hooks.push(createPackageExtender(packageExtensions!))
}
if (
peerDependencyRules != null &&
(!isEmpty(peerDependencyRules.ignoreMissing) || !isEmpty(peerDependencyRules.allowedVersions))
) {
hooks.push(createPeerDependencyPatcher(peerDependencyRules))
}
if (hooks.length === 0) {
return readPackageHook
}
Expand Down
39 changes: 39 additions & 0 deletions packages/core/test/install/createPeerDependencyPatcher.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import createPeerDependencyPatcher from '@pnpm/core/lib/install/createPeerDependencyPatcher'

test('createPeerDependencyPatcher() ignores missing', () => {
const patcher = createPeerDependencyPatcher({
ignoreMissing: ['foo'],
})
const patchedPkg = patcher({
peerDependencies: {
foo: '*',
bar: '*',
},
})
expect(patchedPkg['peerDependenciesMeta']).toStrictEqual({
foo: {
optional: true,
},
})
})

test('createPeerDependencyPatcher() extends peer ranges', () => {
const patcher = createPeerDependencyPatcher({
allowedVersions: {
foo: '1',
qar: '1',
},
})
const patchedPkg = patcher({
peerDependencies: {
foo: '0',
bar: '0',
qar: '*',
},
})
expect(patchedPkg['peerDependencies']).toStrictEqual({
foo: '0 || 1',
bar: '0',
qar: '*',
})
})
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
import { ProjectManifest } from '@pnpm/types'
import {
PackageExtension,
PeerDependencyRules,
ProjectManifest,
} from '@pnpm/types'

export default function getOptionsFromRootManifest (manifest: ProjectManifest) {
export default function getOptionsFromRootManifest (manifest: ProjectManifest): {
overrides?: Record<string, string>
neverBuiltDependencies?: string[]
packageExtensions?: Record<string, PackageExtension>
peerDependencyRules?: PeerDependencyRules
} {
// 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 neverBuiltDependencies = manifest.pnpm?.neverBuiltDependencies ?? []
const packageExtensions = manifest.pnpm?.packageExtensions
const peerDependencyRules = manifest.pnpm?.peerDependencyRules
return {
overrides,
neverBuiltDependencies,
packageExtensions,
peerDependencyRules,
}
}
6 changes: 6 additions & 0 deletions packages/types/src/package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,17 @@ export type DependencyManifest = BaseManifest & Required<Pick<BaseManifest, 'nam

export type PackageExtension = Pick<BaseManifest, 'dependencies' | 'optionalDependencies' | 'peerDependencies' | 'peerDependenciesMeta'>

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

export type ProjectManifest = BaseManifest & {
pnpm?: {
neverBuiltDependencies?: string[]
overrides?: Record<string, string>
packageExtensions?: Record<string, PackageExtension>
peerDependencyRules?: PeerDependencyRules
}
private?: boolean
resolutions?: Record<string, string>
Expand Down

0 comments on commit 26cd01b

Please sign in to comment.