mirror of
https://github.com/pnpm/pnpm.git
synced 2025-12-23 23:29:17 -05:00
feat(patch): allow non-applied patches (#5354)
close #5234 Co-authored-by: Zoltan Kochan <z@kochan.io>
This commit is contained in:
23
.changeset/angry-hounds-bow.md
Normal file
23
.changeset/angry-hounds-bow.md
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
"@pnpm/core": minor
|
||||
"@pnpm/plugin-commands-installation": minor
|
||||
"@pnpm/resolve-dependencies": minor
|
||||
"@pnpm/types": minor
|
||||
"pnpm": minor
|
||||
---
|
||||
|
||||
A new setting supported in the pnpm section of the `package.json` file: `allowNonAppliedPatches`. When it is set to `true`, non-applied patches will not cause an error, just a warning will be printed. For example:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "foo",
|
||||
"version": "1.0.0",
|
||||
"pnpm": {
|
||||
"patchedDependencies": {
|
||||
"express@4.18.1": "patches/express@4.18.1.patch"
|
||||
},
|
||||
"allowNonAppliedPatches": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -51,6 +51,7 @@ export async function getPeerDependencyIssues (
|
||||
{
|
||||
currentLockfile: ctx.currentLockfile,
|
||||
allowedDeprecatedVersions: {},
|
||||
allowNonAppliedPatches: false,
|
||||
defaultUpdateDepth: -1,
|
||||
dryRun: true,
|
||||
engineStrict: false,
|
||||
|
||||
@@ -98,6 +98,7 @@ export interface StrictInstallOptions {
|
||||
modulesCacheMaxAge: number
|
||||
peerDependencyRules: PeerDependencyRules
|
||||
allowedDeprecatedVersions: AllowedDeprecatedVersions
|
||||
allowNonAppliedPatches: boolean
|
||||
preferSymlinkedExecutables: boolean
|
||||
resolutionMode: 'highest' | 'time-based'
|
||||
|
||||
@@ -124,6 +125,7 @@ const defaults = async (opts: InstallOptions) => {
|
||||
}
|
||||
return {
|
||||
allowedDeprecatedVersions: {},
|
||||
allowNonAppliedPatches: false,
|
||||
autoInstallPeers: false,
|
||||
childConcurrency: 5,
|
||||
depth: 0,
|
||||
|
||||
@@ -819,6 +819,7 @@ const _installInContext: InstallFunction = async (projects, ctx, opts) => {
|
||||
{
|
||||
allowBuild: createAllowBuildFunction(opts),
|
||||
allowedDeprecatedVersions: opts.allowedDeprecatedVersions,
|
||||
allowNonAppliedPatches: opts.allowNonAppliedPatches,
|
||||
autoInstallPeers: opts.autoInstallPeers,
|
||||
currentLockfile: ctx.currentLockfile,
|
||||
defaultUpdateDepth: (opts.update || (opts.updateMatching != null)) ? opts.depth : -1,
|
||||
|
||||
@@ -96,6 +96,36 @@ test('patch package', async () => {
|
||||
expect(fs.readFileSync('node_modules/is-positive/index.js', 'utf8')).not.toContain('// patched')
|
||||
})
|
||||
|
||||
test('patch package reports warning if not all patches are applied and allowNonAppliedPatches is set', async () => {
|
||||
prepareEmpty()
|
||||
const reporter = jest.fn()
|
||||
const patchPath = path.join(f.find('patch-pkg'), 'is-positive@1.0.0.patch')
|
||||
|
||||
const patchedDependencies = {
|
||||
'is-positive@1.0.0': path.relative(process.cwd(), patchPath),
|
||||
'is-negative@1.0.0': path.relative(process.cwd(), patchPath),
|
||||
}
|
||||
const opts = await testDefaults({
|
||||
fastUnpack: false,
|
||||
sideEffectsCacheRead: true,
|
||||
sideEffectsCacheWrite: true,
|
||||
patchedDependencies,
|
||||
allowNonAppliedPatches: true,
|
||||
reporter,
|
||||
}, {}, {}, { packageImportMethod: 'hardlink' })
|
||||
await install({
|
||||
dependencies: {
|
||||
'is-positive': '1.0.0',
|
||||
},
|
||||
}, opts)
|
||||
expect(reporter).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
level: 'warn',
|
||||
message: 'The following patches were not applied: is-negative@1.0.0',
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
test('patch package throws an exception if not all patches are applied', async () => {
|
||||
prepareEmpty()
|
||||
const patchPath = path.join(f.find('patch-pkg'), 'is-positive@1.0.0.patch')
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
|
||||
export default function getOptionsFromRootManifest (manifest: ProjectManifest): {
|
||||
allowedDeprecatedVersions?: AllowedDeprecatedVersions
|
||||
allowNonAppliedPatches?: boolean
|
||||
overrides?: Record<string, string>
|
||||
neverBuiltDependencies?: string[]
|
||||
onlyBuiltDependencies?: string[]
|
||||
@@ -23,9 +24,11 @@ export default function getOptionsFromRootManifest (manifest: ProjectManifest):
|
||||
const packageExtensions = manifest.pnpm?.packageExtensions
|
||||
const peerDependencyRules = manifest.pnpm?.peerDependencyRules
|
||||
const allowedDeprecatedVersions = manifest.pnpm?.allowedDeprecatedVersions
|
||||
const allowNonAppliedPatches = manifest.pnpm?.allowNonAppliedPatches
|
||||
const patchedDependencies = manifest.pnpm?.patchedDependencies
|
||||
const settings = {
|
||||
allowedDeprecatedVersions,
|
||||
allowNonAppliedPatches,
|
||||
overrides,
|
||||
neverBuiltDependencies,
|
||||
packageExtensions,
|
||||
|
||||
@@ -4,6 +4,7 @@ import PnpmError from '@pnpm/error'
|
||||
import {
|
||||
packageManifestLogger,
|
||||
} from '@pnpm/core-loggers'
|
||||
import { globalWarn } from '@pnpm/logger'
|
||||
import {
|
||||
Lockfile,
|
||||
ProjectSnapshot,
|
||||
@@ -83,6 +84,7 @@ export default async function (
|
||||
preserveWorkspaceProtocol: boolean
|
||||
saveWorkspaceProtocol: 'rolling' | boolean
|
||||
lockfileIncludeTarballUrl?: boolean
|
||||
allowNonAppliedPatches?: boolean
|
||||
}
|
||||
) {
|
||||
const _toResolveImporter = toResolveImporter.bind(null, {
|
||||
@@ -110,7 +112,11 @@ export default async function (
|
||||
(opts.forceFullResolution || !opts.wantedLockfile.packages?.length) &&
|
||||
Object.keys(opts.wantedLockfile.importers).length === importers.length
|
||||
) {
|
||||
verifyPatches(Object.keys(opts.patchedDependencies), appliedPatches)
|
||||
verifyPatches({
|
||||
patchedDependencies: Object.keys(opts.patchedDependencies),
|
||||
appliedPatches,
|
||||
allowNonAppliedPatches: opts.allowNonAppliedPatches,
|
||||
})
|
||||
}
|
||||
|
||||
const linkedDependenciesByProjectId: Record<string, LinkedDependency[]> = {}
|
||||
@@ -266,13 +272,27 @@ export default async function (
|
||||
}
|
||||
}
|
||||
|
||||
function verifyPatches (patchedDependencies: string[], appliedPatches: Set<string>) {
|
||||
const nonAppliedPatches: string[] = patchedDependencies.filter((patchKey) => !appliedPatches.has(patchKey))
|
||||
if (nonAppliedPatches.length) {
|
||||
throw new PnpmError('PATCH_NOT_APPLIED', `The following patches were not applied: ${nonAppliedPatches.join(', ')}`, {
|
||||
hint: 'Either remove them from "patchedDependencies" or update them to match packages in your dependencies.',
|
||||
})
|
||||
function verifyPatches (
|
||||
{
|
||||
patchedDependencies,
|
||||
appliedPatches,
|
||||
allowNonAppliedPatches,
|
||||
}: {
|
||||
patchedDependencies: string[]
|
||||
appliedPatches: Set<string>
|
||||
allowNonAppliedPatches: boolean
|
||||
}
|
||||
): void {
|
||||
const nonAppliedPatches: string[] = patchedDependencies.filter((patchKey) => !appliedPatches.has(patchKey))
|
||||
if (!nonAppliedPatches.length) return
|
||||
const message = `The following patches were not applied: ${nonAppliedPatches.join(', ')}`
|
||||
if (allowNonAppliedPatches) {
|
||||
globalWarn(message)
|
||||
return
|
||||
}
|
||||
throw new PnpmError('PATCH_NOT_APPLIED', message, {
|
||||
hint: 'Either remove them from "patchedDependencies" or update them to match packages in your dependencies.',
|
||||
})
|
||||
}
|
||||
|
||||
async function finishLockfileUpdates (
|
||||
|
||||
@@ -62,6 +62,7 @@ export interface ResolveDependenciesOptions {
|
||||
autoInstallPeers?: boolean
|
||||
allowBuild?: (pkgName: string) => boolean
|
||||
allowedDeprecatedVersions: AllowedDeprecatedVersions
|
||||
allowNonAppliedPatches: boolean
|
||||
currentLockfile: Lockfile
|
||||
dryRun: boolean
|
||||
engineStrict: boolean
|
||||
|
||||
@@ -129,6 +129,7 @@ export type ProjectManifest = BaseManifest & {
|
||||
packageExtensions?: Record<string, PackageExtension>
|
||||
peerDependencyRules?: PeerDependencyRules
|
||||
allowedDeprecatedVersions?: AllowedDeprecatedVersions
|
||||
allowNonAppliedPatches?: boolean
|
||||
patchedDependencies?: Record<string, string>
|
||||
}
|
||||
private?: boolean
|
||||
|
||||
Reference in New Issue
Block a user