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: pnpm.ignoredOptionalDependencies #7714

Merged
merged 16 commits into from
Feb 28, 2024
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
26 changes: 26 additions & 0 deletions .changeset/thin-icons-scream.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
"@pnpm/resolve-dependencies": minor
"@pnpm/merge-lockfile-changes": minor
"@pnpm/package-requester": minor
"@pnpm/directory-fetcher": minor
"@pnpm/tarball-fetcher": minor
"@pnpm/exec.pkg-requires-build": minor
"@pnpm/hooks.read-package-hook": minor
"@pnpm/lockfile-types": minor
"@pnpm/prune-lockfile": minor
"@pnpm/create-cafs-store": minor
"@pnpm/lockfile-file": minor
"@pnpm/fetcher-base": minor
"@pnpm/headless": minor
"@pnpm/deps.graph-builder": minor
"@pnpm/node.fetcher": minor
"@pnpm/core": minor
"@pnpm/cafs-types": minor
"@pnpm/types": minor
"@pnpm/config": minor
"@pnpm/store.cafs": minor
"@pnpm/worker": minor
"pnpm": minor
---

Add a field named `ignoredOptionalDependencies`. This is an array of strings. If an optional dependency has its name included in this array, it will be skipped.
3 changes: 3 additions & 0 deletions config/config/src/getOptionsFromRootManifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export interface OptionsFromRootManifest {
onlyBuiltDependencies?: string[]
onlyBuiltDependenciesFile?: string
packageExtensions?: Record<string, PackageExtension>
ignoredOptionalDependencies?: string[]
patchedDependencies?: Record<string, string>
peerDependencyRules?: PeerDependencyRules
supportedArchitectures?: SupportedArchitectures
Expand All @@ -37,6 +38,7 @@ export function getOptionsFromRootManifest (manifestDir: string, manifest: Proje
const onlyBuiltDependencies = manifest.pnpm?.onlyBuiltDependencies
const onlyBuiltDependenciesFile = manifest.pnpm?.onlyBuiltDependenciesFile
const packageExtensions = manifest.pnpm?.packageExtensions
const ignoredOptionalDependencies = manifest.pnpm?.ignoredOptionalDependencies
const peerDependencyRules = manifest.pnpm?.peerDependencyRules
const allowedDeprecatedVersions = manifest.pnpm?.allowedDeprecatedVersions
const allowNonAppliedPatches = manifest.pnpm?.allowNonAppliedPatches
Expand All @@ -61,6 +63,7 @@ export function getOptionsFromRootManifest (manifestDir: string, manifest: Proje
overrides,
neverBuiltDependencies,
packageExtensions,
ignoredOptionalDependencies,
peerDependencyRules,
patchedDependencies,
supportedArchitectures,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { BaseManifest, ReadPackageHook } from '@pnpm/types'
import { createMatcher } from '@pnpm/matcher'

export function createOptionalDependenciesRemover (toBeRemoved: string[]): ReadPackageHook {
if (!toBeRemoved.length) return <Manifest extends BaseManifest>(manifest: Manifest) => manifest
const shouldBeRemoved = createMatcher(toBeRemoved)
return <Manifest extends BaseManifest> (manifest: Manifest) => removeOptionalDependencies(manifest, shouldBeRemoved)
}

function removeOptionalDependencies<Manifest extends BaseManifest> (
manifest: Manifest,
shouldBeRemoved: (input: string) => boolean
): Manifest {
for (const optionalDependency in manifest.optionalDependencies) {
if (shouldBeRemoved(optionalDependency)) {
delete manifest.optionalDependencies[optionalDependency]
delete manifest.dependencies?.[optionalDependency]
}
}
return manifest
}
6 changes: 6 additions & 0 deletions hooks/read-package-hook/src/createReadPackageHook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
} from '@pnpm/types'
import isEmpty from 'ramda/src/isEmpty'
import pipeWith from 'ramda/src/pipeWith'
import { createOptionalDependenciesRemover } from './createOptionalDependenciesRemover'
import { createPackageExtender } from './createPackageExtender'
import { createVersionsOverrider } from './createVersionsOverrider'
import { createPeerDependencyPatcher } from './createPeerDependencyPatcher'
Expand All @@ -17,13 +18,15 @@ export function createReadPackageHook (
ignoreCompatibilityDb,
lockfileDir,
overrides,
ignoredOptionalDependencies,
packageExtensions,
peerDependencyRules,
readPackageHook,
}: {
ignoreCompatibilityDb?: boolean
lockfileDir: string
overrides?: Record<string, string>
ignoredOptionalDependencies?: string[]
packageExtensions?: Record<string, PackageExtension>
peerDependencyRules?: PeerDependencyRules
readPackageHook?: ReadPackageHook[] | ReadPackageHook
Expand All @@ -44,6 +47,9 @@ export function createReadPackageHook (
if (!isEmpty(overrides ?? {})) {
hooks.push(createVersionsOverrider(overrides!, lockfileDir))
}
if (ignoredOptionalDependencies && !isEmpty(ignoredOptionalDependencies)) {
hooks.push(createOptionalDependenciesRemover(ignoredOptionalDependencies))
}
if (
peerDependencyRules != null &&
(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { createOptionalDependenciesRemover } from '../lib/createOptionalDependenciesRemover'
import type { BaseManifest } from '@pnpm/types'

test('createOptionalDependenciesRemover() does not modify the manifest if provided array is empty', async () => {
const removeOptionalDependencies = createOptionalDependenciesRemover([])
const manifest: BaseManifest = Object.freeze({
dependencies: {
foo: '0.1.2',
bar: '2.1.0',
baz: '1.0.0',
qux: '2.0.0',
},
optionalDependencies: {
foo: '0.1.2',
bar: '2.1.0',
baz: '1.0.0',
},
})
expect(await removeOptionalDependencies(manifest)).toBe(manifest)
})

test('createOptionalDependenciesRemover() removes optional dependencies', async () => {
const removeOptionalDependencies = createOptionalDependenciesRemover(['foo', 'bar'])
expect(
await removeOptionalDependencies({
dependencies: {
foo: '0.1.2',
bar: '2.1.0',
baz: '1.0.0',
qux: '2.0.0',
},
optionalDependencies: {
foo: '0.1.2',
bar: '2.1.0',
baz: '1.0.0',
},
})
).toStrictEqual({
dependencies: {
baz: '1.0.0',
qux: '2.0.0',
},
optionalDependencies: {
baz: '1.0.0',
},
})
})

test('createOptionalDependenciesRemover() does not remove non-optional packages', async () => {
const removeOptionalDependencies = createOptionalDependenciesRemover(['foo', 'bar'])
expect(
await removeOptionalDependencies({
dependencies: {
foo: '0.1.2',
bar: '2.1.0',
baz: '1.0.0',
qux: '2.0.0',
},
optionalDependencies: {
foo: '0.1.2',
baz: '1.0.0',
},
})
).toStrictEqual({
dependencies: {
bar: '2.1.0',
baz: '1.0.0',
qux: '2.0.0',
},
optionalDependencies: {
baz: '1.0.0',
},
})
})

test('createOptionalDependenciesRemover() removes all optional dependencies if the pattern is a star', async () => {
const removeOptionalDependencies = createOptionalDependenciesRemover(['*'])
expect(
await removeOptionalDependencies({
dependencies: {
foo: '0.1.2',
bar: '2.1.0',
baz: '1.0.0',
qux: '2.0.0',
},
optionalDependencies: {
foo: '0.1.2',
bar: '2.1.0',
baz: '1.0.0',
},
})
).toStrictEqual({
dependencies: {
qux: '2.0.0',
},
optionalDependencies: {},
})
})

test('createOptionalDependenciesRemover() only removes optional dependencies that match one of the patterns', async () => {
const removeOptionalDependencies = createOptionalDependenciesRemover(['@foo/*', '@bar/*'])
expect(
await removeOptionalDependencies({
dependencies: {
'@foo/abc': '0.0.0',
'@foo/def': '0.0.0',
'@foo/not-optional': '0.0.0',
'@bar/ghi': '0.0.0',
'@bar/required': '0.0.0',
'@baz/jkl': '0.0.0',
},
optionalDependencies: {
'@foo/abc': '0.0.0',
'@foo/def': '0.0.0',
'@bar/ghi': '0.0.0',
'@baz/jkl': '0.0.0',
},
})
).toStrictEqual({
dependencies: {
'@foo/not-optional': '0.0.0',
'@bar/required': '0.0.0',
'@baz/jkl': '0.0.0',
},
optionalDependencies: {
'@baz/jkl': '0.0.0',
},
})
})
3 changes: 3 additions & 0 deletions lockfile/lockfile-file/src/lockfileFormatConverters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ function normalizeLockfile (lockfile: InlineSpecifiersLockfile, opts: NormalizeL
if (!lockfileToSave.packageExtensionsChecksum) {
delete lockfileToSave.packageExtensionsChecksum
}
if (!lockfileToSave.ignoredOptionalDependencies?.length) {
delete lockfileToSave.ignoredOptionalDependencies
}
if (!lockfileToSave.pnpmfileChecksum) {
delete lockfileToSave.pnpmfileChecksum
}
Expand Down
1 change: 1 addition & 0 deletions lockfile/lockfile-types/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface Lockfile {
packages?: PackageSnapshots
overrides?: Record<string, string>
packageExtensionsChecksum?: string
ignoredOptionalDependencies?: string[]
patchedDependencies?: Record<string, PatchFile>
pnpmfileChecksum?: string
settings?: LockfileSettings
Expand Down
8 changes: 8 additions & 0 deletions lockfile/merge-lockfile-changes/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ export function mergeLockfileChanges (ours: Lockfile, theirs: Lockfile): Lockfil
newLockfile.pnpmfileChecksum = pnpmfileChecksum
}

const ignoredOptionalDependencies = [...new Set([
...ours.ignoredOptionalDependencies ?? [],
...theirs.ignoredOptionalDependencies ?? [],
])]
if (ignoredOptionalDependencies.length) {
newLockfile.ignoredOptionalDependencies = ignoredOptionalDependencies
}

for (const importerId of Array.from(new Set([...Object.keys(ours.importers), ...Object.keys(theirs.importers)]))) {
newLockfile.importers[importerId] = {
specifiers: {},
Expand Down
3 changes: 3 additions & 0 deletions lockfile/prune-lockfile/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ export function pruneLockfile (
if (lockfile.pnpmfileChecksum) {
prunedLockfile.pnpmfileChecksum = lockfile.pnpmfileChecksum
}
if (lockfile.ignoredOptionalDependencies && !isEmpty(lockfile.ignoredOptionalDependencies)) {
prunedLockfile.ignoredOptionalDependencies = lockfile.ignoredOptionalDependencies
}
return pruneSharedLockfile(prunedLockfile, opts)
}

Expand Down
1 change: 1 addition & 0 deletions packages/types/src/package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ export type ProjectManifest = BaseManifest & {
onlyBuiltDependenciesFile?: string
overrides?: Record<string, string>
packageExtensions?: Record<string, PackageExtension>
ignoredOptionalDependencies?: string[]
peerDependencyRules?: PeerDependencyRules
allowedDeprecatedVersions?: AllowedDeprecatedVersions
allowNonAppliedPatches?: boolean
Expand Down
2 changes: 2 additions & 0 deletions pkg-manager/core/src/getPeerDependencyIssues.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export type ListMissingPeersOptions = Partial<GetContextOptions>
| 'nodeLinker'
| 'overrides'
| 'packageExtensions'
| 'ignoredOptionalDependencies'
| 'preferWorkspacePackages'
| 'saveWorkspaceProtocol'
| 'storeController'
Expand Down Expand Up @@ -69,6 +70,7 @@ export async function getPeerDependencyIssues (
overrides: opts.overrides,
packageExtensions: opts.packageExtensions,
readPackageHook: opts.hooks?.readPackage,
ignoredOptionalDependencies: opts.ignoredOptionalDependencies,
}),
},
linkWorkspacePackagesDepth: opts.linkWorkspacePackagesDepth ?? (opts.saveWorkspaceProtocol ? 0 : -1),
Expand Down
3 changes: 3 additions & 0 deletions pkg-manager/core/src/install/extendInstallOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export interface StrictInstallOptions {
nodeLinker: 'isolated' | 'hoisted' | 'pnp'
nodeVersion: string
packageExtensions: Record<string, PackageExtension>
ignoredOptionalDependencies: string[]
pnpmfile: string
ignorePnpmfile: boolean
packageManager: {
Expand Down Expand Up @@ -197,6 +198,7 @@ const defaults = (opts: InstallOptions) => {
ignoreCompatibilityDb: false,
ignorePackageManifest: false,
packageExtensions: {},
ignoredOptionalDependencies: [] as string[],
packageManager,
preferFrozenLockfile: true,
preferWorkspacePackages: false,
Expand Down Expand Up @@ -272,6 +274,7 @@ export function extendOptions (
lockfileDir: extendedOpts.lockfileDir,
packageExtensions: extendedOpts.packageExtensions,
peerDependencyRules: extendedOpts.peerDependencyRules,
ignoredOptionalDependencies: extendedOpts.ignoredOptionalDependencies,
})
if (extendedOpts.lockfileOnly) {
extendedOpts.ignoreScripts = true
Expand Down
7 changes: 7 additions & 0 deletions pkg-manager/core/src/install/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ export async function mutateModules (
autoInstallPeers: opts.autoInstallPeers,
excludeLinksFromLockfile: opts.excludeLinksFromLockfile,
overrides: opts.overrides,
ignoredOptionalDependencies: opts.ignoredOptionalDependencies?.sort(),
packageExtensionsChecksum,
patchedDependencies,
pnpmfileChecksum,
Expand All @@ -359,6 +360,7 @@ export async function mutateModules (
}
ctx.wantedLockfile.overrides = opts.overrides
ctx.wantedLockfile.packageExtensionsChecksum = packageExtensionsChecksum
ctx.wantedLockfile.ignoredOptionalDependencies = opts.ignoredOptionalDependencies
ctx.wantedLockfile.pnpmfileChecksum = pnpmfileChecksum
ctx.wantedLockfile.patchedDependencies = patchedDependencies
} else if (!frozenLockfile) {
Expand Down Expand Up @@ -710,6 +712,7 @@ function getOutdatedLockfileSetting (
onlyBuiltDependencies,
overrides,
packageExtensionsChecksum,
ignoredOptionalDependencies,
patchedDependencies,
autoInstallPeers,
excludeLinksFromLockfile,
Expand All @@ -719,6 +722,7 @@ function getOutdatedLockfileSetting (
overrides?: Record<string, string>
packageExtensionsChecksum?: string
patchedDependencies?: Record<string, PatchFile>
ignoredOptionalDependencies?: string[]
autoInstallPeers?: boolean
excludeLinksFromLockfile?: boolean
pnpmfileChecksum?: string
Expand All @@ -730,6 +734,9 @@ function getOutdatedLockfileSetting (
if (lockfile.packageExtensionsChecksum !== packageExtensionsChecksum) {
return 'packageExtensionsChecksum'
}
if (!equals(lockfile.ignoredOptionalDependencies?.sort() ?? [], ignoredOptionalDependencies?.sort() ?? [])) {
return 'ignoredOptionalDependencies'
}
if (!equals(lockfile.patchedDependencies ?? {}, patchedDependencies ?? {})) {
return 'patchedDependencies'
}
Expand Down