mirror of
https://github.com/pnpm/pnpm.git
synced 2026-02-01 02:32:28 -05:00
feat!: remove deprecated patch options (#10505)
* refactor: remove allowNonAppliedPatches * refactor: remove ignorePatchFailures * refactor: remove `strict` field in groupPatchedDependencies * test: update test failure in package patching * test: fix * docs: update changesets --------- Co-authored-by: Zoltan Kochan <z@kochan.io>
This commit is contained in:
committed by
Zoltan Kochan
parent
f8b4895e0a
commit
7b1c189f2e
18
.changeset/remove-deprecated-patch-options.md
Normal file
18
.changeset/remove-deprecated-patch-options.md
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
"@pnpm/plugin-commands-installation": major
|
||||
"@pnpm/plugin-commands-deploy": major
|
||||
"@pnpm/plugin-commands-patching": major
|
||||
"@pnpm/package-bins": major
|
||||
"@pnpm/patching.apply-patch": major
|
||||
"@pnpm/patching.config": major
|
||||
"@pnpm/patching.types": major
|
||||
"@pnpm/headless": major
|
||||
"@pnpm/build-modules": major
|
||||
"@pnpm/core": major
|
||||
"@pnpm/types": major
|
||||
"@pnpm/config": major
|
||||
"pnpm": major
|
||||
---
|
||||
|
||||
Removed the deprecated `allowNonAppliedPatches` completely in favor of `allowUnusedPatches`.
|
||||
Remove `ignorePatchFailures` so all patch application failures should throw an error.
|
||||
@@ -9,13 +9,11 @@ import {
|
||||
type ProjectManifest,
|
||||
type PnpmSettings,
|
||||
} from '@pnpm/types'
|
||||
import { map as mapValues, omit, pick } from 'ramda'
|
||||
import { globalWarn } from '@pnpm/logger'
|
||||
import { map as mapValues, pick } from 'ramda'
|
||||
|
||||
export type OptionsFromRootManifest = {
|
||||
allowedDeprecatedVersions?: AllowedDeprecatedVersions
|
||||
allowUnusedPatches?: boolean
|
||||
ignorePatchFailures?: boolean
|
||||
overrides?: Record<string, string>
|
||||
packageExtensions?: Record<string, PackageExtension>
|
||||
ignoredOptionalDependencies?: string[]
|
||||
@@ -29,13 +27,11 @@ export type OptionsFromRootManifest = {
|
||||
export function getOptionsFromRootManifest (manifestDir: string, manifest: ProjectManifest): OptionsFromRootManifest {
|
||||
const settings: OptionsFromRootManifest = getOptionsFromPnpmSettings(manifestDir, {
|
||||
...pick([
|
||||
'allowNonAppliedPatches',
|
||||
'allowBuilds',
|
||||
'allowUnusedPatches',
|
||||
'allowedDeprecatedVersions',
|
||||
'auditConfig',
|
||||
'configDependencies',
|
||||
'ignorePatchFailures',
|
||||
'ignoredOptionalDependencies',
|
||||
'overrides',
|
||||
'packageExtensions',
|
||||
@@ -57,8 +53,7 @@ export function getOptionsFromRootManifest (manifestDir: string, manifest: Proje
|
||||
}
|
||||
|
||||
export function getOptionsFromPnpmSettings (manifestDir: string | undefined, pnpmSettings: PnpmSettings, manifest?: ProjectManifest): OptionsFromRootManifest {
|
||||
const renamedKeys = ['allowNonAppliedPatches'] as const satisfies Array<keyof PnpmSettings>
|
||||
const settings: OptionsFromRootManifest = omit(renamedKeys, replaceEnvInSettings(pnpmSettings))
|
||||
const settings: OptionsFromRootManifest = replaceEnvInSettings(pnpmSettings)
|
||||
if (settings.overrides) {
|
||||
if (Object.keys(settings.overrides).length === 0) {
|
||||
delete settings.overrides
|
||||
@@ -73,13 +68,6 @@ export function getOptionsFromPnpmSettings (manifestDir: string | undefined, pnp
|
||||
settings.patchedDependencies[dep] = path.join(manifestDir, patchFile)
|
||||
}
|
||||
}
|
||||
if (pnpmSettings.allowNonAppliedPatches != null) {
|
||||
globalWarn('allowNonAppliedPatches is deprecated, use allowUnusedPatches instead.')
|
||||
settings.allowUnusedPatches ??= pnpmSettings.allowNonAppliedPatches
|
||||
}
|
||||
if (pnpmSettings.ignorePatchFailures != null) {
|
||||
settings.ignorePatchFailures = pnpmSettings.ignorePatchFailures
|
||||
}
|
||||
|
||||
return settings
|
||||
}
|
||||
|
||||
@@ -99,62 +99,6 @@ test('getOptionsFromRootManifest() should return allowBuilds', () => {
|
||||
expect(options.allowBuilds).toStrictEqual({ electron: true })
|
||||
})
|
||||
|
||||
test('getOptionsFromRootManifest() should derive allowUnusedPatches from allowNonAppliedPatches (legacy behavior)', () => {
|
||||
expect(getOptionsFromRootManifest(process.cwd(), {
|
||||
pnpm: {
|
||||
allowNonAppliedPatches: false,
|
||||
},
|
||||
})).toStrictEqual({
|
||||
allowUnusedPatches: false,
|
||||
})
|
||||
|
||||
expect(getOptionsFromRootManifest(process.cwd(), {
|
||||
pnpm: {
|
||||
allowNonAppliedPatches: true,
|
||||
},
|
||||
})).toStrictEqual({
|
||||
allowUnusedPatches: true,
|
||||
})
|
||||
})
|
||||
|
||||
test('allowUnusedPatches should override allowNonAppliedPatches', () => {
|
||||
expect(getOptionsFromRootManifest(process.cwd(), {
|
||||
pnpm: {
|
||||
allowNonAppliedPatches: false,
|
||||
allowUnusedPatches: false,
|
||||
},
|
||||
})).toStrictEqual({
|
||||
allowUnusedPatches: false,
|
||||
})
|
||||
|
||||
expect(getOptionsFromRootManifest(process.cwd(), {
|
||||
pnpm: {
|
||||
allowNonAppliedPatches: true,
|
||||
allowUnusedPatches: false,
|
||||
},
|
||||
})).toStrictEqual({
|
||||
allowUnusedPatches: false,
|
||||
})
|
||||
|
||||
expect(getOptionsFromRootManifest(process.cwd(), {
|
||||
pnpm: {
|
||||
allowNonAppliedPatches: false,
|
||||
allowUnusedPatches: false,
|
||||
},
|
||||
})).toStrictEqual({
|
||||
allowUnusedPatches: false,
|
||||
})
|
||||
|
||||
expect(getOptionsFromRootManifest(process.cwd(), {
|
||||
pnpm: {
|
||||
allowNonAppliedPatches: true,
|
||||
allowUnusedPatches: false,
|
||||
},
|
||||
})).toStrictEqual({
|
||||
allowUnusedPatches: false,
|
||||
})
|
||||
})
|
||||
|
||||
test('getOptionsFromRootManifest() should return patchedDependencies', () => {
|
||||
const options = getOptionsFromRootManifest(process.cwd(), {
|
||||
pnpm: {
|
||||
|
||||
@@ -29,7 +29,6 @@ export async function buildModules<T extends string> (
|
||||
rootDepPaths: T[],
|
||||
opts: {
|
||||
allowBuild?: AllowBuild
|
||||
ignorePatchFailures?: boolean
|
||||
childConcurrency?: number
|
||||
depsToBuild?: Set<string>
|
||||
depsStateCache: DepsStateCache
|
||||
@@ -112,7 +111,6 @@ async function buildDependency<T extends string> (
|
||||
depPath: T,
|
||||
depGraph: DependenciesGraph<T>,
|
||||
opts: {
|
||||
ignorePatchFailures?: boolean
|
||||
extraBinPaths?: string[]
|
||||
extraNodePaths?: string[]
|
||||
extraEnv?: Record<string, string>
|
||||
@@ -147,11 +145,8 @@ async function buildDependency<T extends string> (
|
||||
await linkBinsOfDependencies(depNode, depGraph, opts)
|
||||
let isPatched = false
|
||||
if (depNode.patch) {
|
||||
const { file, strict } = depNode.patch
|
||||
// `strict` is a legacy property which was kept to preserve backward compatibility.
|
||||
// Once a major version of pnpm is released, `strict` should be removed completely.
|
||||
const allowFailure: boolean = opts.ignorePatchFailures ?? !strict
|
||||
isPatched = applyPatchToDir({ allowFailure, patchedDir: depNode.dir, patchFilePath: file.path })
|
||||
const { file } = depNode.patch
|
||||
isPatched = applyPatchToDir({ patchedDir: depNode.dir, patchFilePath: file.path })
|
||||
}
|
||||
const hasSideEffects = !opts.ignoreScripts && await runPostinstallHooks({
|
||||
depPath,
|
||||
|
||||
@@ -161,9 +161,7 @@ export interface PnpmSettings {
|
||||
ignoredOptionalDependencies?: string[]
|
||||
peerDependencyRules?: PeerDependencyRules
|
||||
allowedDeprecatedVersions?: AllowedDeprecatedVersions
|
||||
allowNonAppliedPatches?: boolean // deprecated: use allowUnusedPatches instead
|
||||
allowUnusedPatches?: boolean
|
||||
ignorePatchFailures?: boolean
|
||||
patchedDependencies?: Record<string, string>
|
||||
updateConfig?: {
|
||||
ignoreDependencies?: string[]
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
import { applyPatch } from '@pnpm/patch-package/dist/applyPatches.js'
|
||||
import { globalWarn } from '@pnpm/logger'
|
||||
|
||||
export interface ApplyPatchToDirOpts {
|
||||
allowFailure?: boolean
|
||||
patchedDir: string
|
||||
patchFilePath: string
|
||||
}
|
||||
@@ -27,12 +25,7 @@ export function applyPatchToDir (opts: ApplyPatchToDirOpts): boolean {
|
||||
process.chdir(cwd)
|
||||
}
|
||||
if (!success) {
|
||||
const message = `Could not apply patch ${opts.patchFilePath} to ${opts.patchedDir}`
|
||||
if (opts.allowFailure) {
|
||||
globalWarn(message)
|
||||
} else {
|
||||
throw new PnpmError('PATCH_FAILED', message)
|
||||
}
|
||||
throw new PnpmError('PATCH_FAILED', `Could not apply patch ${opts.patchFilePath} to ${opts.patchedDir}`)
|
||||
}
|
||||
return success
|
||||
}
|
||||
|
||||
@@ -27,15 +27,13 @@ function prepareDirToPatch () {
|
||||
return dir
|
||||
}
|
||||
|
||||
describe('applyPatchToDir() without allowFailure', () => {
|
||||
const allowFailure = false
|
||||
describe('applyPatchToDir()', () => {
|
||||
it('should succeed when patch is applicable', () => {
|
||||
const patchFilePath = f.find('applicable.patch')
|
||||
const successfullyPatched = f.find('successfully-patched.txt')
|
||||
const patchedDir = prepareDirToPatch()
|
||||
expect(
|
||||
applyPatchToDir({
|
||||
allowFailure,
|
||||
patchFilePath,
|
||||
patchedDir,
|
||||
})
|
||||
@@ -48,7 +46,6 @@ describe('applyPatchToDir() without allowFailure', () => {
|
||||
const patchedDir = prepareDirToPatch()
|
||||
expect(() => {
|
||||
applyPatchToDir({
|
||||
allowFailure,
|
||||
patchFilePath,
|
||||
patchedDir,
|
||||
})
|
||||
@@ -59,7 +56,6 @@ describe('applyPatchToDir() without allowFailure', () => {
|
||||
const patchFilePath = f.find('invalid.patch')
|
||||
expect(() => {
|
||||
applyPatchToDir({
|
||||
allowFailure,
|
||||
patchFilePath,
|
||||
patchedDir: tempDir(),
|
||||
})
|
||||
@@ -68,59 +64,6 @@ describe('applyPatchToDir() without allowFailure', () => {
|
||||
it('should fail if the patch file is not found', () => {
|
||||
expect(() => {
|
||||
applyPatchToDir({
|
||||
allowFailure,
|
||||
patchFilePath: 'does-not-exist.patch',
|
||||
patchedDir: tempDir(),
|
||||
})
|
||||
}).toThrow('Patch file not found')
|
||||
})
|
||||
})
|
||||
|
||||
describe('applyPatchToDir() with allowFailure', () => {
|
||||
const allowFailure = true
|
||||
it('should succeed when patch is applicable', () => {
|
||||
const patchFilePath = f.find('applicable.patch')
|
||||
const successfullyPatched = f.find('successfully-patched.txt')
|
||||
const patchedDir = prepareDirToPatch()
|
||||
expect(
|
||||
applyPatchToDir({
|
||||
allowFailure,
|
||||
patchFilePath,
|
||||
patchedDir,
|
||||
})
|
||||
).toBe(true)
|
||||
const patchTarget = path.join(patchedDir, 'patch-target.txt')
|
||||
expect(fs.readFileSync(patchTarget, 'utf-8')).toBe(fs.readFileSync(successfullyPatched, 'utf-8'))
|
||||
})
|
||||
it('should warn when patch fails to apply', () => {
|
||||
const patchFilePath = f.find('non-applicable.patch')
|
||||
const patchedDir = prepareDirToPatch()
|
||||
expect(
|
||||
applyPatchToDir({
|
||||
allowFailure,
|
||||
patchFilePath,
|
||||
patchedDir,
|
||||
})
|
||||
).toBe(false)
|
||||
expect(jest.mocked(globalWarn).mock.calls).toStrictEqual([[
|
||||
`Could not apply patch ${patchFilePath} to ${patchedDir}`,
|
||||
]])
|
||||
expect(fs.readFileSync(path.join(patchedDir, 'patch-target.txt'), 'utf-8')).toBe(fs.readFileSync(f.find('patch-target.txt'), 'utf-8'))
|
||||
})
|
||||
it('should fail on invalid patch', () => {
|
||||
const patchFilePath = f.find('invalid.patch')
|
||||
expect(() => {
|
||||
applyPatchToDir({
|
||||
allowFailure,
|
||||
patchFilePath,
|
||||
patchedDir: tempDir(),
|
||||
})
|
||||
}).toThrow(`Applying patch "${patchFilePath}" failed: hunk header integrity check failed`)
|
||||
})
|
||||
it('should fail if the patch file is not found', () => {
|
||||
expect(() => {
|
||||
applyPatchToDir({
|
||||
allowFailure,
|
||||
patchFilePath: 'does-not-exist.patch',
|
||||
patchedDir: tempDir(),
|
||||
})
|
||||
|
||||
@@ -22,7 +22,7 @@ export function groupPatchedDependencies (patchedDependencies: Record<string, Pa
|
||||
const { name, version, nonSemverVersion } = dp.parse(key)
|
||||
|
||||
if (name && version) {
|
||||
getGroup(name).exact[version] = { strict: true, file, key }
|
||||
getGroup(name).exact[version] = { file, key }
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -31,18 +31,17 @@ export function groupPatchedDependencies (patchedDependencies: Record<string, Pa
|
||||
throw new PnpmError('PATCH_NON_SEMVER_RANGE', `${nonSemverVersion} is not a valid semantic version range.`)
|
||||
}
|
||||
if (nonSemverVersion.trim() === '*') {
|
||||
getGroup(name).all = { strict: true, file, key }
|
||||
getGroup(name).all = { file, key }
|
||||
} else {
|
||||
getGroup(name).range.push({
|
||||
version: nonSemverVersion,
|
||||
patch: { strict: true, file, key },
|
||||
patch: { file, key },
|
||||
})
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Set `strict` to `false` to preserve backward compatibility.
|
||||
getGroup(key).all = { strict: false, file, key }
|
||||
getGroup(key).all = { file, key }
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
@@ -15,7 +15,6 @@ test('getPatchInfo() returns an exact version patch if the name and version matc
|
||||
hash: '00000000000000000000000000000000',
|
||||
},
|
||||
key: 'foo@1.0.0',
|
||||
strict: true,
|
||||
},
|
||||
},
|
||||
range: [],
|
||||
@@ -40,7 +39,6 @@ test('getPatchInfo() returns a range version patch if the name matches and the v
|
||||
hash: '00000000000000000000000000000000',
|
||||
},
|
||||
key: 'foo@1',
|
||||
strict: true,
|
||||
},
|
||||
}],
|
||||
all: undefined,
|
||||
@@ -63,7 +61,6 @@ test('getPatchInfo() returns name-only patch if the name matches', () => {
|
||||
hash: '00000000000000000000000000000000',
|
||||
},
|
||||
key: 'foo',
|
||||
strict: true,
|
||||
},
|
||||
},
|
||||
} satisfies PatchGroupRecord
|
||||
@@ -83,7 +80,6 @@ test('exact version patches override version range patches, version range patche
|
||||
hash: '00000000000000000000000000000000',
|
||||
},
|
||||
key: 'foo@1.0.0',
|
||||
strict: true,
|
||||
},
|
||||
'1.1.0': {
|
||||
file: {
|
||||
@@ -91,7 +87,6 @@ test('exact version patches override version range patches, version range patche
|
||||
hash: '00000000000000000000000000000000',
|
||||
},
|
||||
key: 'foo@1.1.0',
|
||||
strict: true,
|
||||
},
|
||||
},
|
||||
range: [
|
||||
@@ -103,7 +98,6 @@ test('exact version patches override version range patches, version range patche
|
||||
hash: '00000000000000000000000000000000',
|
||||
},
|
||||
key: 'foo@1',
|
||||
strict: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -114,7 +108,6 @@ test('exact version patches override version range patches, version range patche
|
||||
hash: '00000000000000000000000000000000',
|
||||
},
|
||||
key: 'foo@2',
|
||||
strict: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -124,7 +117,6 @@ test('exact version patches override version range patches, version range patche
|
||||
hash: '00000000000000000000000000000000',
|
||||
},
|
||||
key: 'foo',
|
||||
strict: true,
|
||||
},
|
||||
},
|
||||
} satisfies PatchGroupRecord
|
||||
@@ -150,7 +142,6 @@ test('getPatchInfo(_, name, version) throws an error when name@version matches m
|
||||
hash: '00000000000000000000000000000000',
|
||||
},
|
||||
key: 'foo@>=1.0.0 <3.0.0',
|
||||
strict: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -161,7 +152,6 @@ test('getPatchInfo(_, name, version) throws an error when name@version matches m
|
||||
hash: '00000000000000000000000000000000',
|
||||
},
|
||||
key: 'foo@>=2.0.0',
|
||||
strict: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -185,7 +175,6 @@ test('getPatchInfo(_, name, version) does not throw an error when name@version m
|
||||
hash: '00000000000000000000000000000000',
|
||||
},
|
||||
key: 'foo@>=1.0.0 <3.0.0',
|
||||
strict: true,
|
||||
},
|
||||
},
|
||||
range: [
|
||||
@@ -197,7 +186,6 @@ test('getPatchInfo(_, name, version) does not throw an error when name@version m
|
||||
hash: '00000000000000000000000000000000',
|
||||
},
|
||||
key: 'foo@>=1.0.0 <3.0.0',
|
||||
strict: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -208,7 +196,6 @@ test('getPatchInfo(_, name, version) does not throw an error when name@version m
|
||||
hash: '00000000000000000000000000000000',
|
||||
},
|
||||
key: 'foo@>=2.0.0',
|
||||
strict: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
@@ -53,17 +53,16 @@ test('groups patchedDependencies according to names, match types, and versions',
|
||||
path: 'patches/mixed-style.patch',
|
||||
},
|
||||
} satisfies Record<string, PatchFile>
|
||||
const info = (strict: boolean, key: keyof typeof patchedDependencies): ExtendedPatchInfo => ({
|
||||
strict,
|
||||
const info = (key: keyof typeof patchedDependencies): ExtendedPatchInfo => ({
|
||||
key,
|
||||
file: patchedDependencies[key],
|
||||
})
|
||||
expect(_groupPatchedDependencies(patchedDependencies)).toStrictEqual({
|
||||
'exact-version-only': {
|
||||
exact: {
|
||||
'0.0.0': info(true, 'exact-version-only@0.0.0'),
|
||||
'1.2.3': info(true, 'exact-version-only@1.2.3'),
|
||||
'2.1.0': info(true, 'exact-version-only@2.1.0'),
|
||||
'0.0.0': info('exact-version-only@0.0.0'),
|
||||
'1.2.3': info('exact-version-only@1.2.3'),
|
||||
'2.1.0': info('exact-version-only@2.1.0'),
|
||||
},
|
||||
range: [],
|
||||
all: undefined,
|
||||
@@ -73,11 +72,11 @@ test('groups patchedDependencies according to names, match types, and versions',
|
||||
range: [
|
||||
{
|
||||
version: '~1.2.0',
|
||||
patch: info(true, 'version-range-only@~1.2.0'),
|
||||
patch: info('version-range-only@~1.2.0'),
|
||||
},
|
||||
{
|
||||
version: '4',
|
||||
patch: info(true, 'version-range-only@4'),
|
||||
patch: info('version-range-only@4'),
|
||||
},
|
||||
],
|
||||
all: undefined,
|
||||
@@ -85,24 +84,24 @@ test('groups patchedDependencies according to names, match types, and versions',
|
||||
'star-version-range': {
|
||||
exact: {},
|
||||
range: [],
|
||||
all: info(true, 'star-version-range@*'),
|
||||
all: info('star-version-range@*'),
|
||||
},
|
||||
'without-versions': {
|
||||
exact: {},
|
||||
range: [],
|
||||
all: info(false, 'without-versions'),
|
||||
all: info('without-versions'),
|
||||
},
|
||||
'mixed-style': {
|
||||
exact: {
|
||||
'0.1.2': info(true, 'mixed-style@0.1.2'),
|
||||
'0.1.2': info('mixed-style@0.1.2'),
|
||||
},
|
||||
range: [
|
||||
{
|
||||
version: '1.x.x',
|
||||
patch: info(true, 'mixed-style@1.x.x'),
|
||||
patch: info('mixed-style@1.x.x'),
|
||||
},
|
||||
],
|
||||
all: info(false, 'mixed-style'),
|
||||
all: info('mixed-style'),
|
||||
},
|
||||
} as PatchGroupRecord)
|
||||
})
|
||||
|
||||
@@ -8,7 +8,7 @@ test('getPatchInfo(undefined, ...) returns undefined', () => {
|
||||
expect(getPatchInfo(undefined, 'foo', '1.0.0')).toBeUndefined()
|
||||
})
|
||||
|
||||
test('getPatchInfo(_, name, version) returns strict=true if name@version exists', () => {
|
||||
test('getPatchInfo(_, name, version) if name@version exists', () => {
|
||||
expect(_getPatchInfo({
|
||||
'foo@1.0.0': {
|
||||
path: 'patches/foo@1.0.0.patch',
|
||||
@@ -20,11 +20,10 @@ test('getPatchInfo(_, name, version) returns strict=true if name@version exists'
|
||||
hash: expect.any(String),
|
||||
},
|
||||
key: 'foo@1.0.0',
|
||||
strict: true,
|
||||
})
|
||||
})
|
||||
|
||||
test('getPatchInfo(_, name, version) returns strict=false if name exists and name@version does not exist', () => {
|
||||
test('getPatchInfo(_, name, version) if name exists but name@version does not exist', () => {
|
||||
expect(_getPatchInfo({
|
||||
foo: {
|
||||
path: 'patches/foo.patch',
|
||||
@@ -36,7 +35,6 @@ test('getPatchInfo(_, name, version) returns strict=false if name exists and nam
|
||||
hash: expect.any(String),
|
||||
},
|
||||
key: 'foo',
|
||||
strict: false,
|
||||
})
|
||||
})
|
||||
|
||||
@@ -56,7 +54,6 @@ test('getPatchInfo(_, name, version) prioritizes name@version over name if both
|
||||
hash: expect.any(String),
|
||||
},
|
||||
key: 'foo@1.0.0',
|
||||
strict: true,
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -108,7 +108,6 @@ export async function handler (opts: PatchCommandOptions, params: string[]): Pro
|
||||
|
||||
if (!opts.ignoreExisting && opts.patchedDependencies) {
|
||||
tryPatchWithExistingPatchFile({
|
||||
allowFailure: patchedDep.applyToAll,
|
||||
patchedDep,
|
||||
patchedDir: editDir,
|
||||
patchedDependencies: opts.patchedDependencies,
|
||||
@@ -129,13 +128,11 @@ To commit your changes, run:
|
||||
|
||||
function tryPatchWithExistingPatchFile (
|
||||
{
|
||||
allowFailure,
|
||||
patchedDep: { applyToAll, alias, bareSpecifier },
|
||||
patchedDir,
|
||||
patchedDependencies,
|
||||
lockfileDir,
|
||||
}: {
|
||||
allowFailure: boolean
|
||||
patchedDep: GetPatchedDependencyResult
|
||||
patchedDir: string
|
||||
patchedDependencies: Record<string, string>
|
||||
@@ -157,5 +154,5 @@ function tryPatchWithExistingPatchFile (
|
||||
if (!fs.existsSync(existingPatchFilePath)) {
|
||||
throw new PnpmError('PATCH_FILE_NOT_FOUND', `Unable to find patch file ${existingPatchFilePath}`)
|
||||
}
|
||||
applyPatchToDir({ patchedDir, patchFilePath: existingPatchFilePath, allowFailure })
|
||||
applyPatchToDir({ patchedDir, patchFilePath: existingPatchFilePath })
|
||||
}
|
||||
|
||||
@@ -849,9 +849,9 @@ describe('prompt to choose version', () => {
|
||||
expect(path.basename(patchDir)).toMatch(/^chalk@\d+\.\d+\.\d+$/)
|
||||
expect(fs.existsSync(patchDir)).toBe(true)
|
||||
expect(JSON.parse(fs.readFileSync(path.join(patchDir, 'package.json'), 'utf8')).version).toBe('5.3.0')
|
||||
expect(fs.existsSync(path.join(patchDir, 'source/index.js'))).toBe(true)
|
||||
expect(fs.existsSync(path.join(patchDir, 'license'))).toBe(true)
|
||||
|
||||
fs.appendFileSync(path.join(patchDir, 'source/index.js'), '// test patching', 'utf8')
|
||||
fs.appendFileSync(path.join(patchDir, 'license'), '\ntest patching', 'utf8')
|
||||
await patchCommit.handler({
|
||||
...DEFAULT_OPTS,
|
||||
cacheDir,
|
||||
@@ -868,8 +868,8 @@ describe('prompt to choose version', () => {
|
||||
})
|
||||
const patchContent = fs.readFileSync('patches/chalk.patch', 'utf8')
|
||||
expect(patchContent).toContain('diff --git')
|
||||
expect(patchContent).toContain('// test patching')
|
||||
expect(fs.readFileSync('node_modules/.pnpm/@pnpm.e2e+requires-chalk-530@1.0.0/node_modules/chalk/source/index.js', 'utf8')).toContain('// test patching')
|
||||
expect(patchContent).toContain('test patching')
|
||||
expect(fs.readFileSync('node_modules/.pnpm/@pnpm.e2e+requires-chalk-530@1.0.0/node_modules/chalk/license', 'utf8')).toContain('test patching')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ export interface PatchFile {
|
||||
|
||||
// TODO: replace all occurrences of PatchInfo with PatchFile before the next major version is released
|
||||
export interface PatchInfo {
|
||||
strict: boolean
|
||||
file: PatchFile
|
||||
}
|
||||
|
||||
|
||||
@@ -118,7 +118,6 @@ export interface StrictInstallOptions {
|
||||
modulesCacheMaxAge: number
|
||||
peerDependencyRules: PeerDependencyRules
|
||||
allowedDeprecatedVersions: AllowedDeprecatedVersions
|
||||
ignorePatchFailures?: boolean
|
||||
allowUnusedPatches: boolean
|
||||
preferSymlinkedExecutables: boolean
|
||||
resolutionMode: 'highest' | 'time-based' | 'lowest-direct'
|
||||
@@ -183,7 +182,6 @@ const defaults = (opts: InstallOptions): StrictInstallOptions => {
|
||||
return {
|
||||
allowedDeprecatedVersions: {},
|
||||
allowUnusedPatches: false,
|
||||
ignorePatchFailures: undefined,
|
||||
autoInstallPeers: true,
|
||||
autoInstallPeersFromHighestMatch: false,
|
||||
catalogs: {},
|
||||
|
||||
@@ -1373,7 +1373,6 @@ const _installInContext: InstallFunction = async (projects, ctx, opts) => {
|
||||
}
|
||||
ignoredBuilds = (await buildModules(dependenciesGraph, rootNodes, {
|
||||
allowBuild: opts.allowBuild,
|
||||
ignorePatchFailures: opts.ignorePatchFailures,
|
||||
childConcurrency: opts.childConcurrency,
|
||||
depsStateCache,
|
||||
depsToBuild: new Set(result.newDepPaths),
|
||||
|
||||
@@ -551,62 +551,7 @@ test('patch package should fail when the version range patch fails to apply', as
|
||||
expect(fs.readFileSync('node_modules/is-positive/index.js', 'utf8')).not.toContain('// patched')
|
||||
})
|
||||
|
||||
test('patch package should print a warning when the patch fails to apply and ignorePatchFailures is set to true', async () => {
|
||||
prepareEmpty()
|
||||
const reporter = jest.fn()
|
||||
const patchPath = path.join(f.find('patch-pkg'), 'is-positive@1.0.0.patch')
|
||||
|
||||
const patchedDependencies = {
|
||||
'is-positive@3.1.0': patchPath,
|
||||
}
|
||||
const opts = testDefaults({
|
||||
fastUnpack: false,
|
||||
ignorePatchFailures: true,
|
||||
sideEffectsCacheRead: true,
|
||||
sideEffectsCacheWrite: true,
|
||||
patchedDependencies,
|
||||
reporter,
|
||||
}, {}, {}, { packageImportMethod: 'hardlink' })
|
||||
await install({
|
||||
dependencies: {
|
||||
'is-positive': '3.1.0',
|
||||
},
|
||||
}, opts)
|
||||
|
||||
expect(fs.readFileSync('node_modules/is-positive/index.js', 'utf8')).not.toContain('// patched')
|
||||
expect(reporter).toHaveBeenCalledWith(expect.objectContaining({
|
||||
message: expect.stringMatching(/Could not apply patch/),
|
||||
}))
|
||||
})
|
||||
|
||||
test('patch package should print a warning when the name-only patch fails to apply (legacy behavior)', async () => {
|
||||
prepareEmpty()
|
||||
const reporter = jest.fn()
|
||||
const patchPath = path.join(f.find('patch-pkg'), 'is-positive@1.0.0.patch')
|
||||
|
||||
const patchedDependencies = {
|
||||
'is-positive': patchPath,
|
||||
}
|
||||
const opts = testDefaults({
|
||||
fastUnpack: false,
|
||||
sideEffectsCacheRead: true,
|
||||
sideEffectsCacheWrite: true,
|
||||
patchedDependencies,
|
||||
reporter,
|
||||
}, {}, {}, { packageImportMethod: 'hardlink' })
|
||||
await install({
|
||||
dependencies: {
|
||||
'is-positive': '3.1.0',
|
||||
},
|
||||
}, opts)
|
||||
|
||||
expect(fs.readFileSync('node_modules/is-positive/index.js', 'utf8')).not.toContain('// patched')
|
||||
expect(reporter).toHaveBeenCalledWith(expect.objectContaining({
|
||||
message: expect.stringMatching(/Could not apply patch/),
|
||||
}))
|
||||
})
|
||||
|
||||
test('patch package should fail when the name-only range patch fails to apply and ignorePatchFailures is explicitly set to false', async () => {
|
||||
test('patch package should fail when the name-only range patch fails to apply', async () => {
|
||||
prepareEmpty()
|
||||
const patchPath = path.join(f.find('patch-pkg'), 'is-positive@1.0.0.patch')
|
||||
|
||||
@@ -615,7 +560,6 @@ test('patch package should fail when the name-only range patch fails to apply an
|
||||
}
|
||||
const opts = testDefaults({
|
||||
fastUnpack: false,
|
||||
ignorePatchFailures: false,
|
||||
sideEffectsCacheRead: true,
|
||||
sideEffectsCacheWrite: true,
|
||||
patchedDependencies,
|
||||
|
||||
@@ -103,7 +103,6 @@ export interface Project {
|
||||
}
|
||||
|
||||
export interface HeadlessOptions {
|
||||
ignorePatchFailures?: boolean
|
||||
allowBuilds?: Record<string, boolean | string>
|
||||
autoInstallPeers?: boolean
|
||||
childConcurrency?: number
|
||||
@@ -536,7 +535,6 @@ export async function headlessInstall (opts: HeadlessOptions): Promise<Installat
|
||||
}
|
||||
ignoredBuilds = (await buildModules(graph, Array.from(directNodes), {
|
||||
allowBuild,
|
||||
ignorePatchFailures: opts.ignorePatchFailures,
|
||||
childConcurrency: opts.childConcurrency,
|
||||
extraBinPaths,
|
||||
extraEnv,
|
||||
|
||||
@@ -99,7 +99,7 @@ test('bare package name as a patchedDependencies key should apply to all version
|
||||
expect(globalWarn).not.toHaveBeenCalledWith(expect.stringContaining('Could not apply patch'))
|
||||
})
|
||||
|
||||
test('bare package name as a patchedDependencies key should apply to all possible versions and skip non-applicable versions', async () => {
|
||||
test('bare package name as a patchedDependencies key should apply to all possible versions and error on non-applicable versions', async () => {
|
||||
const patchFixture = f.find('patchedDependencies/console-log-replace-3rd-line.patch')
|
||||
prepareEmpty()
|
||||
|
||||
@@ -111,7 +111,7 @@ test('bare package name as a patchedDependencies key should apply to all possibl
|
||||
|
||||
const rootProjectManifest = addPatch('@pnpm.e2e/console-log', patchFixture, 'patches/console-log.patch')
|
||||
|
||||
await install.handler({
|
||||
const promise = install.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
frozenLockfile: false,
|
||||
@@ -119,8 +119,8 @@ test('bare package name as a patchedDependencies key should apply to all possibl
|
||||
})
|
||||
|
||||
// the common patch does not apply to v1
|
||||
await expect(promise).rejects.toThrow(`Could not apply patch ${path.resolve('patches/console-log.patch')}`)
|
||||
expect(patchedFileContent(1)).toBe(unpatchedFileContent(1))
|
||||
expect(globalWarn).toHaveBeenCalledWith(expect.stringContaining(`Could not apply patch ${path.resolve('patches/console-log.patch')}`))
|
||||
|
||||
// the common patch applies to v2
|
||||
{
|
||||
|
||||
@@ -1,500 +0,0 @@
|
||||
import fs from 'fs'
|
||||
import { preparePackages } from '@pnpm/prepare'
|
||||
import { fixtures } from '@pnpm/test-fixtures'
|
||||
import { sync as writeYamlFile } from 'write-yaml-file'
|
||||
import { execPnpmSync } from '../utils/index.js'
|
||||
|
||||
const f = fixtures(import.meta.dirname)
|
||||
|
||||
describe('ignorePatchFailures=undefined (necessary for backward-compatibility)', () => {
|
||||
test('errors on exact version patch failures', async () => {
|
||||
preparePackages([
|
||||
{
|
||||
name: 'foo',
|
||||
version: '0.0.0',
|
||||
private: true,
|
||||
dependencies: {
|
||||
'is-positive': '1.0.0', // is-positive@1.0.0.patch should succeed
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'bar',
|
||||
version: '0.0.0',
|
||||
private: true,
|
||||
dependencies: {
|
||||
'is-positive': '3.1.0', // is-positive@1.0.0.patch should fail
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const patchFile = f.find('patch-pkg/is-positive@1.0.0.patch')
|
||||
|
||||
writeYamlFile('pnpm-workspace.yaml', {
|
||||
packages: ['**', '!store/**'],
|
||||
patchedDependencies: {
|
||||
'is-positive@1.0.0': patchFile, // should succeed
|
||||
'is-positive@3.1.0': patchFile, // should fail
|
||||
},
|
||||
})
|
||||
|
||||
// pnpm install should fail
|
||||
const { status, stdout } = execPnpmSync(['install'])
|
||||
expect(status).not.toBe(0)
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_PATCH_FAILED')
|
||||
const errorLines = stdout.toString().split('\n').filter(line => line.includes('ERR_PNPM_PATCH_FAILED'))
|
||||
expect(errorLines).toStrictEqual([expect.stringContaining(patchFile)])
|
||||
expect(errorLines).toStrictEqual([expect.stringContaining('is-positive@3.1.0')])
|
||||
})
|
||||
|
||||
test('errors on version range patch failures', async () => {
|
||||
preparePackages([
|
||||
{
|
||||
name: 'foo',
|
||||
version: '0.0.0',
|
||||
private: true,
|
||||
dependencies: {
|
||||
'is-positive': '1.0.0', // is-positive@1.0.0.patch should succeed
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'bar',
|
||||
version: '0.0.0',
|
||||
private: true,
|
||||
dependencies: {
|
||||
'is-positive': '3.1.0', // is-positive@1.0.0.patch should fail
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const patchFile = f.find('patch-pkg/is-positive@1.0.0.patch')
|
||||
|
||||
writeYamlFile('pnpm-workspace.yaml', {
|
||||
packages: ['**', '!store/**'],
|
||||
patchedDependencies: {
|
||||
'is-positive@>=1': patchFile,
|
||||
},
|
||||
})
|
||||
|
||||
// pnpm install should fail
|
||||
const { status, stdout } = execPnpmSync(['install'])
|
||||
expect(status).not.toBe(0)
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_PATCH_FAILED')
|
||||
const errorLines = stdout.toString().split('\n').filter(line => line.includes('ERR_PNPM_PATCH_FAILED'))
|
||||
expect(errorLines).toStrictEqual([expect.stringContaining(patchFile)])
|
||||
expect(errorLines).toStrictEqual([expect.stringContaining('is-positive@3.1.0')])
|
||||
})
|
||||
|
||||
test('errors on star version range patch failures', async () => {
|
||||
preparePackages([
|
||||
{
|
||||
name: 'foo',
|
||||
version: '0.0.0',
|
||||
private: true,
|
||||
dependencies: {
|
||||
'is-positive': '1.0.0', // is-positive@1.0.0.patch should succeed
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'bar',
|
||||
version: '0.0.0',
|
||||
private: true,
|
||||
dependencies: {
|
||||
'is-positive': '3.1.0', // is-positive@1.0.0.patch should fail
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const patchFile = f.find('patch-pkg/is-positive@1.0.0.patch')
|
||||
|
||||
writeYamlFile('pnpm-workspace.yaml', {
|
||||
packages: ['**', '!store/**'],
|
||||
patchedDependencies: {
|
||||
'is-positive@*': patchFile,
|
||||
},
|
||||
})
|
||||
|
||||
// pnpm install should fail
|
||||
const { status, stdout } = execPnpmSync(['install'])
|
||||
expect(status).not.toBe(0)
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_PATCH_FAILED')
|
||||
const errorLines = stdout.toString().split('\n').filter(line => line.includes('ERR_PNPM_PATCH_FAILED'))
|
||||
expect(errorLines).toStrictEqual([expect.stringContaining(patchFile)])
|
||||
expect(errorLines).toStrictEqual([expect.stringContaining('is-positive@3.1.0')])
|
||||
})
|
||||
|
||||
test('allows name-only patches to fail with a warning (legacy behavior)', async () => {
|
||||
preparePackages([
|
||||
{
|
||||
name: 'foo',
|
||||
version: '0.0.0',
|
||||
private: true,
|
||||
dependencies: {
|
||||
'is-positive': '1.0.0', // is-positive@1.0.0.patch should succeed
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'bar',
|
||||
version: '0.0.0',
|
||||
private: true,
|
||||
dependencies: {
|
||||
'is-positive': '3.1.0', // is-positive@1.0.0.patch should fail
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const patchFile = f.find('patch-pkg/is-positive@1.0.0.patch')
|
||||
|
||||
writeYamlFile('pnpm-workspace.yaml', {
|
||||
packages: ['**', '!store/**'],
|
||||
patchedDependencies: {
|
||||
'is-positive': patchFile,
|
||||
},
|
||||
})
|
||||
|
||||
// pnpm install should not fail
|
||||
const { stdout } = execPnpmSync(['install'], { expectSuccess: true })
|
||||
|
||||
// pnpm install should print a warning about patch application failure
|
||||
expect(stdout.toString()).toContain('Could not apply patch')
|
||||
|
||||
// the patch should apply to is-positive@1.0.0
|
||||
expect(fs.readFileSync('foo/node_modules/is-positive/index.js', 'utf-8')).toContain('// patched')
|
||||
|
||||
// the patch should not apply to is-positive@3.2.1
|
||||
expect(fs.readFileSync('bar/node_modules/is-positive/index.js', 'utf-8')).not.toContain('// patched')
|
||||
})
|
||||
})
|
||||
|
||||
describe('ignorePatchFailures=true', () => {
|
||||
test('allows exact version patches to fail with a warning', async () => {
|
||||
preparePackages([
|
||||
{
|
||||
name: 'foo',
|
||||
version: '0.0.0',
|
||||
private: true,
|
||||
dependencies: {
|
||||
'is-positive': '1.0.0', // is-positive@1.0.0.patch should succeed
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'bar',
|
||||
version: '0.0.0',
|
||||
private: true,
|
||||
dependencies: {
|
||||
'is-positive': '3.1.0', // is-positive@1.0.0.patch should fail
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const patchFile = f.find('patch-pkg/is-positive@1.0.0.patch')
|
||||
|
||||
writeYamlFile('pnpm-workspace.yaml', {
|
||||
packages: ['**', '!store/**'],
|
||||
patchedDependencies: {
|
||||
'is-positive@1.0.0': patchFile, // should succeed
|
||||
'is-positive@3.1.0': patchFile, // should fail
|
||||
},
|
||||
ignorePatchFailures: true,
|
||||
})
|
||||
|
||||
// pnpm install should not fail
|
||||
const { stdout } = execPnpmSync(['install'], { expectSuccess: true })
|
||||
|
||||
// pnpm install should print a warning about patch application failure
|
||||
expect(stdout.toString()).toContain('Could not apply patch')
|
||||
|
||||
// the patch should apply to is-positive@1.0.0
|
||||
expect(fs.readFileSync('foo/node_modules/is-positive/index.js', 'utf-8')).toContain('// patched')
|
||||
|
||||
// the patch should not apply to is-positive@3.2.1
|
||||
expect(fs.readFileSync('bar/node_modules/is-positive/index.js', 'utf-8')).not.toContain('// patched')
|
||||
})
|
||||
|
||||
test('allows version range patches to fail with a warning', async () => {
|
||||
preparePackages([
|
||||
{
|
||||
name: 'foo',
|
||||
version: '0.0.0',
|
||||
private: true,
|
||||
dependencies: {
|
||||
'is-positive': '1.0.0', // is-positive@1.0.0.patch should succeed
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'bar',
|
||||
version: '0.0.0',
|
||||
private: true,
|
||||
dependencies: {
|
||||
'is-positive': '3.1.0', // is-positive@1.0.0.patch should fail
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const patchFile = f.find('patch-pkg/is-positive@1.0.0.patch')
|
||||
|
||||
writeYamlFile('pnpm-workspace.yaml', {
|
||||
packages: ['**', '!store/**'],
|
||||
patchedDependencies: {
|
||||
'is-positive@>=1': patchFile,
|
||||
},
|
||||
ignorePatchFailures: true,
|
||||
})
|
||||
|
||||
// pnpm install should not fail
|
||||
const { stdout } = execPnpmSync(['install'], { expectSuccess: true })
|
||||
|
||||
// pnpm install should print a warning about patch application failure
|
||||
expect(stdout.toString()).toContain('Could not apply patch')
|
||||
|
||||
// the patch should apply to is-positive@1.0.0
|
||||
expect(fs.readFileSync('foo/node_modules/is-positive/index.js', 'utf-8')).toContain('// patched')
|
||||
|
||||
// the patch should not apply to is-positive@3.2.1
|
||||
expect(fs.readFileSync('bar/node_modules/is-positive/index.js', 'utf-8')).not.toContain('// patched')
|
||||
})
|
||||
|
||||
test('allows star version range patches to fail with a warning', async () => {
|
||||
preparePackages([
|
||||
{
|
||||
name: 'foo',
|
||||
version: '0.0.0',
|
||||
private: true,
|
||||
dependencies: {
|
||||
'is-positive': '1.0.0', // is-positive@1.0.0.patch should succeed
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'bar',
|
||||
version: '0.0.0',
|
||||
private: true,
|
||||
dependencies: {
|
||||
'is-positive': '3.1.0', // is-positive@1.0.0.patch should fail
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const patchFile = f.find('patch-pkg/is-positive@1.0.0.patch')
|
||||
|
||||
writeYamlFile('pnpm-workspace.yaml', {
|
||||
packages: ['**', '!store/**'],
|
||||
patchedDependencies: {
|
||||
'is-positive@*': patchFile,
|
||||
},
|
||||
ignorePatchFailures: true,
|
||||
})
|
||||
|
||||
// pnpm install should not fail
|
||||
const { stdout } = execPnpmSync(['install'], { expectSuccess: true })
|
||||
|
||||
// pnpm install should print a warning about patch application failure
|
||||
expect(stdout.toString()).toContain('Could not apply patch')
|
||||
|
||||
// the patch should apply to is-positive@1.0.0
|
||||
expect(fs.readFileSync('foo/node_modules/is-positive/index.js', 'utf-8')).toContain('// patched')
|
||||
|
||||
// the patch should not apply to is-positive@3.2.1
|
||||
expect(fs.readFileSync('bar/node_modules/is-positive/index.js', 'utf-8')).not.toContain('// patched')
|
||||
})
|
||||
|
||||
test('allows name-only patches to fail with a warning', async () => {
|
||||
preparePackages([
|
||||
{
|
||||
name: 'foo',
|
||||
version: '0.0.0',
|
||||
private: true,
|
||||
dependencies: {
|
||||
'is-positive': '1.0.0', // is-positive@1.0.0.patch should succeed
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'bar',
|
||||
version: '0.0.0',
|
||||
private: true,
|
||||
dependencies: {
|
||||
'is-positive': '3.1.0', // is-positive@1.0.0.patch should fail
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const patchFile = f.find('patch-pkg/is-positive@1.0.0.patch')
|
||||
|
||||
writeYamlFile('pnpm-workspace.yaml', {
|
||||
packages: ['**', '!store/**'],
|
||||
patchedDependencies: {
|
||||
'is-positive': patchFile,
|
||||
},
|
||||
ignorePatchFailures: true,
|
||||
})
|
||||
|
||||
// pnpm install should not fail
|
||||
const { stdout } = execPnpmSync(['install'], { expectSuccess: true })
|
||||
|
||||
// pnpm install should print a warning about patch application failure
|
||||
expect(stdout.toString()).toContain('Could not apply patch')
|
||||
|
||||
// the patch should apply to is-positive@1.0.0
|
||||
expect(fs.readFileSync('foo/node_modules/is-positive/index.js', 'utf-8')).toContain('// patched')
|
||||
|
||||
// the patch should not apply to is-positive@3.2.1
|
||||
expect(fs.readFileSync('bar/node_modules/is-positive/index.js', 'utf-8')).not.toContain('// patched')
|
||||
})
|
||||
})
|
||||
|
||||
describe('ignorePatchFailures=false', () => {
|
||||
test('errors on exact version patch failures', async () => {
|
||||
preparePackages([
|
||||
{
|
||||
name: 'foo',
|
||||
version: '0.0.0',
|
||||
private: true,
|
||||
dependencies: {
|
||||
'is-positive': '1.0.0', // is-positive@1.0.0.patch should succeed
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'bar',
|
||||
version: '0.0.0',
|
||||
private: true,
|
||||
dependencies: {
|
||||
'is-positive': '3.1.0', // is-positive@1.0.0.patch should fail
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const patchFile = f.find('patch-pkg/is-positive@1.0.0.patch')
|
||||
|
||||
writeYamlFile('pnpm-workspace.yaml', {
|
||||
packages: ['**', '!store/**'],
|
||||
patchedDependencies: {
|
||||
'is-positive@1.0.0': patchFile, // should succeed
|
||||
'is-positive@3.1.0': patchFile, // should fail
|
||||
},
|
||||
ignorePatchFailures: false,
|
||||
})
|
||||
|
||||
// pnpm install should fail
|
||||
const { status, stdout } = execPnpmSync(['install'])
|
||||
expect(status).not.toBe(0)
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_PATCH_FAILED')
|
||||
const errorLines = stdout.toString().split('\n').filter(line => line.includes('ERR_PNPM_PATCH_FAILED'))
|
||||
expect(errorLines).toStrictEqual([expect.stringContaining(patchFile)])
|
||||
expect(errorLines).toStrictEqual([expect.stringContaining('is-positive@3.1.0')])
|
||||
})
|
||||
|
||||
test('errors on version range patch failures', async () => {
|
||||
preparePackages([
|
||||
{
|
||||
name: 'foo',
|
||||
version: '0.0.0',
|
||||
private: true,
|
||||
dependencies: {
|
||||
'is-positive': '1.0.0', // is-positive@1.0.0.patch should succeed
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'bar',
|
||||
version: '0.0.0',
|
||||
private: true,
|
||||
dependencies: {
|
||||
'is-positive': '3.1.0', // is-positive@1.0.0.patch should fail
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const patchFile = f.find('patch-pkg/is-positive@1.0.0.patch')
|
||||
|
||||
writeYamlFile('pnpm-workspace.yaml', {
|
||||
packages: ['**', '!store/**'],
|
||||
patchedDependencies: {
|
||||
'is-positive@>=1': patchFile,
|
||||
},
|
||||
ignorePatchFailures: false,
|
||||
})
|
||||
|
||||
// pnpm install not fail
|
||||
const { status, stdout } = execPnpmSync(['install'])
|
||||
expect(status).not.toBe(0)
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_PATCH_FAILED')
|
||||
const errorLines = stdout.toString().split('\n').filter(line => line.includes('ERR_PNPM_PATCH_FAILED'))
|
||||
expect(errorLines).toStrictEqual([expect.stringContaining(patchFile)])
|
||||
expect(errorLines).toStrictEqual([expect.stringContaining('is-positive@3.1.0')])
|
||||
})
|
||||
|
||||
test('errors on star version range patch failures', async () => {
|
||||
preparePackages([
|
||||
{
|
||||
name: 'foo',
|
||||
version: '0.0.0',
|
||||
private: true,
|
||||
dependencies: {
|
||||
'is-positive': '1.0.0', // is-positive@1.0.0.patch should succeed
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'bar',
|
||||
version: '0.0.0',
|
||||
private: true,
|
||||
dependencies: {
|
||||
'is-positive': '3.1.0', // is-positive@1.0.0.patch should fail
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const patchFile = f.find('patch-pkg/is-positive@1.0.0.patch')
|
||||
|
||||
writeYamlFile('pnpm-workspace.yaml', {
|
||||
packages: ['**', '!store/**'],
|
||||
patchedDependencies: {
|
||||
'is-positive@*': patchFile,
|
||||
},
|
||||
ignorePatchFailures: false,
|
||||
})
|
||||
|
||||
// pnpm install not fail
|
||||
const { status, stdout } = execPnpmSync(['install'])
|
||||
expect(status).not.toBe(0)
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_PATCH_FAILED')
|
||||
const errorLines = stdout.toString().split('\n').filter(line => line.includes('ERR_PNPM_PATCH_FAILED'))
|
||||
expect(errorLines).toStrictEqual([expect.stringContaining(patchFile)])
|
||||
expect(errorLines).toStrictEqual([expect.stringContaining('is-positive@3.1.0')])
|
||||
})
|
||||
|
||||
test('allows name-only patches to fail with a warning', async () => {
|
||||
preparePackages([
|
||||
{
|
||||
name: 'foo',
|
||||
version: '0.0.0',
|
||||
private: true,
|
||||
dependencies: {
|
||||
'is-positive': '1.0.0', // is-positive@1.0.0.patch should succeed
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'bar',
|
||||
version: '0.0.0',
|
||||
private: true,
|
||||
dependencies: {
|
||||
'is-positive': '3.1.0', // is-positive@1.0.0.patch should fail
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const patchFile = f.find('patch-pkg/is-positive@1.0.0.patch')
|
||||
|
||||
writeYamlFile('pnpm-workspace.yaml', {
|
||||
packages: ['**', '!store/**'],
|
||||
patchedDependencies: {
|
||||
'is-positive': patchFile,
|
||||
},
|
||||
ignorePatchFailures: false,
|
||||
})
|
||||
|
||||
// pnpm install should fail
|
||||
const { status, stdout } = execPnpmSync(['install'])
|
||||
expect(status).not.toBe(0)
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_PATCH_FAILED')
|
||||
const errorLines = stdout.toString().split('\n').filter(line => line.includes('ERR_PNPM_PATCH_FAILED'))
|
||||
expect(errorLines).toStrictEqual([expect.stringContaining(patchFile)])
|
||||
expect(errorLines).toStrictEqual([expect.stringContaining('is-positive@3.1.0')])
|
||||
})
|
||||
})
|
||||
@@ -9,8 +9,8 @@ diff --git a/package.json b/package.json
|
||||
index 5feb15ba194c74ad48a2ee15abec9887ec1f9e83..27e24feff1bbfc20b3735c23b332bfdc16803362 100644
|
||||
--- a/package.json
|
||||
+++ b/package.json
|
||||
@@ -16,6 +16,7 @@
|
||||
"test": "xo && ava"
|
||||
@@ -19,6 +19,7 @@
|
||||
"test": "node test.js"
|
||||
},
|
||||
"files": [
|
||||
+ "PATCH.txt",
|
||||
|
||||
Reference in New Issue
Block a user