mirror of
https://github.com/pnpm/pnpm.git
synced 2026-05-14 11:35:56 -04:00
feat(plugin-commands-patching): add patch-remove command (#6521)
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import * as patch from './patch'
|
||||
import * as patchCommit from './patchCommit'
|
||||
import * as patchRemove from './patchRemove'
|
||||
|
||||
export { patch, patchCommit }
|
||||
export { patch, patchCommit, patchRemove }
|
||||
|
||||
79
patching/plugin-commands-patching/src/patchRemove.ts
Normal file
79
patching/plugin-commands-patching/src/patchRemove.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import path from 'path'
|
||||
import fs from 'fs/promises'
|
||||
import { docsUrl } from '@pnpm/cli-utils'
|
||||
import { install } from '@pnpm/plugin-commands-installation'
|
||||
import { type Config, types as allTypes } from '@pnpm/config'
|
||||
import { tryReadProjectManifest } from '@pnpm/read-project-manifest'
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
import renderHelp from 'render-help'
|
||||
import { prompt } from 'enquirer'
|
||||
import pick from 'ramda/src/pick'
|
||||
|
||||
export function rcOptionsTypes () {
|
||||
return pick([], allTypes)
|
||||
}
|
||||
|
||||
export function cliOptionsTypes () {
|
||||
return { ...rcOptionsTypes() }
|
||||
}
|
||||
|
||||
export const commandNames = ['patch-remove']
|
||||
|
||||
export function help () {
|
||||
return renderHelp({
|
||||
description: 'Remove existing patch files',
|
||||
url: docsUrl('patch-remove'),
|
||||
usages: ['pnpm patch-remove [pkg...]'],
|
||||
})
|
||||
}
|
||||
|
||||
export type PatchRemoveCommandOptions = install.InstallCommandOptions & Pick<Config, 'dir' | 'lockfileDir' | 'patchesDir' | 'rootProjectManifest'>
|
||||
|
||||
export async function handler (opts: PatchRemoveCommandOptions, params: string[]) {
|
||||
let patchesToRemove = params
|
||||
const lockfileDir = opts.lockfileDir ?? opts.dir ?? process.cwd()
|
||||
const { writeProjectManifest, manifest } = await tryReadProjectManifest(lockfileDir)
|
||||
const rootProjectManifest = opts.rootProjectManifest ?? manifest ?? {}
|
||||
const patchedDependencies = rootProjectManifest.pnpm?.patchedDependencies ?? {}
|
||||
|
||||
if (!params.length) {
|
||||
const allPatches = Object.keys(patchedDependencies)
|
||||
if (allPatches.length) {
|
||||
({ patches: patchesToRemove } = await prompt<{
|
||||
patches: string[]
|
||||
}>({
|
||||
type: 'multiselect',
|
||||
name: 'patches',
|
||||
message: 'Select the patch to be removed',
|
||||
choices: allPatches,
|
||||
validate (value) {
|
||||
return value.length === 0 ? 'Select at least one option.' : true
|
||||
},
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
if (!patchesToRemove.length) {
|
||||
throw new PnpmError('NO_PATCHES_TO_REMOVE', 'There are no patches that need to be removed')
|
||||
}
|
||||
|
||||
for (const patch of patchesToRemove) {
|
||||
if (Object.prototype.hasOwnProperty.call(patchedDependencies, patch)) {
|
||||
const patchFile = path.join(lockfileDir, patchedDependencies[patch])
|
||||
await fs.rm(patchFile, { force: true })
|
||||
delete rootProjectManifest.pnpm!.patchedDependencies![patch]
|
||||
}
|
||||
}
|
||||
|
||||
await writeProjectManifest(rootProjectManifest)
|
||||
|
||||
if (opts?.selectedProjectsGraph?.[lockfileDir]) {
|
||||
opts.selectedProjectsGraph[lockfileDir].package.manifest = rootProjectManifest
|
||||
}
|
||||
|
||||
if (opts?.allProjectsGraph?.[lockfileDir].package.manifest) {
|
||||
opts.allProjectsGraph[lockfileDir].package.manifest = rootProjectManifest
|
||||
}
|
||||
|
||||
return install.handler(opts)
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import { install } from '@pnpm/plugin-commands-installation'
|
||||
import { readProjects } from '@pnpm/filter-workspace-packages'
|
||||
import writeYamlFile from 'write-yaml-file'
|
||||
import tempy from 'tempy'
|
||||
import { patch, patchCommit } from '@pnpm/plugin-commands-patching'
|
||||
import { patch, patchCommit, patchRemove } from '@pnpm/plugin-commands-patching'
|
||||
import { readProjectManifest } from '@pnpm/read-project-manifest'
|
||||
import { REGISTRY_MOCK_PORT } from '@pnpm/registry-mock'
|
||||
import { DEFAULT_OPTS } from './utils/index'
|
||||
@@ -593,6 +593,77 @@ describe('patch with custom modules-dir and virtual-store-dir', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('patch-remove', () => {
|
||||
let defaultPatchRemoveOption: patchRemove.PatchRemoveCommandOptions
|
||||
let cacheDir: string
|
||||
let storeDir: string
|
||||
|
||||
beforeEach(async () => {
|
||||
prompt.mockClear()
|
||||
prepare({
|
||||
dependencies: {
|
||||
'is-positive': '1.0.0',
|
||||
},
|
||||
})
|
||||
cacheDir = path.resolve('cache')
|
||||
storeDir = path.resolve('store')
|
||||
defaultPatchRemoveOption = {
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
}
|
||||
|
||||
await install.handler({
|
||||
...DEFAULT_OPTS,
|
||||
cacheDir,
|
||||
storeDir,
|
||||
dir: process.cwd(),
|
||||
saveLockfile: true,
|
||||
})
|
||||
})
|
||||
test('patch-remove should work as expected', async () => {
|
||||
const { manifest, writeProjectManifest } = await readProjectManifest(process.cwd())
|
||||
manifest.pnpm = {
|
||||
patchedDependencies: {
|
||||
'is-positive@1.0.0': 'patches/is-positive@1.0.0.patch',
|
||||
},
|
||||
}
|
||||
await writeProjectManifest(manifest)
|
||||
fs.mkdirSync(path.join(process.cwd(), 'patches'))
|
||||
fs.writeFileSync(path.join(process.cwd(), 'patches/is-positive@1.0.0.patch'), 'test patch content', 'utf8')
|
||||
|
||||
await patchRemove.handler(defaultPatchRemoveOption, ['is-positive@1.0.0'])
|
||||
|
||||
const { manifest: newManifest } = await readProjectManifest(process.cwd())
|
||||
expect(newManifest!.pnpm!.patchedDependencies).toEqual({})
|
||||
expect(fs.existsSync(path.join(process.cwd(), 'patches/is-positive@1.0.0.patch'))).toBe(false)
|
||||
})
|
||||
|
||||
test('prompt to select patches that to be removed', async () => {
|
||||
const { manifest, writeProjectManifest } = await readProjectManifest(process.cwd())
|
||||
manifest.pnpm = {
|
||||
patchedDependencies: {
|
||||
'is-positive@1.0.0': 'patches/is-positive@1.0.0.patch',
|
||||
'chalk@4.1.2': 'patches/chalk@4.1.2.patch',
|
||||
},
|
||||
}
|
||||
await writeProjectManifest(manifest)
|
||||
prompt.mockResolvedValue({
|
||||
patches: ['is-positive@1.0.0', 'chalk@4.1.2'],
|
||||
})
|
||||
await patchRemove.handler(defaultPatchRemoveOption, [])
|
||||
expect(prompt.mock.calls[0][0].choices).toEqual(expect.arrayContaining(['is-positive@1.0.0', 'chalk@4.1.2']))
|
||||
prompt.mockClear()
|
||||
|
||||
const { manifest: newManifest } = await readProjectManifest(process.cwd())
|
||||
expect(newManifest!.pnpm!.patchedDependencies).toEqual({})
|
||||
})
|
||||
|
||||
test('should throw error when there is no patch to remove', async () => {
|
||||
await expect(() => patchRemove.handler(defaultPatchRemoveOption, []))
|
||||
.rejects.toThrow('There are no patches that need to be removed')
|
||||
})
|
||||
})
|
||||
|
||||
function getPatchDirFromPatchOutput (output: string) {
|
||||
const [firstLine] = output.split('\n')
|
||||
return firstLine.substring(firstLine.indexOf(':') + 1).trim()
|
||||
|
||||
@@ -10,7 +10,7 @@ import { list, ll, why } from '@pnpm/plugin-commands-listing'
|
||||
import { licenses } from '@pnpm/plugin-commands-licenses'
|
||||
import { outdated } from '@pnpm/plugin-commands-outdated'
|
||||
import { pack, publish } from '@pnpm/plugin-commands-publishing'
|
||||
import { patch, patchCommit } from '@pnpm/plugin-commands-patching'
|
||||
import { patch, patchCommit, patchRemove } from '@pnpm/plugin-commands-patching'
|
||||
import { rebuild } from '@pnpm/plugin-commands-rebuild'
|
||||
import {
|
||||
create,
|
||||
@@ -127,6 +127,7 @@ const commands: CommandDefinition[] = [
|
||||
pack,
|
||||
patch,
|
||||
patchCommit,
|
||||
patchRemove,
|
||||
prune,
|
||||
publish,
|
||||
rebuild,
|
||||
|
||||
@@ -168,7 +168,7 @@ export async function main (inputArgv: string[]) {
|
||||
}
|
||||
|
||||
if (
|
||||
(cmd === 'install' || cmd === 'import' || cmd === 'dedupe' || cmd === 'patch-commit' || cmd === 'patch') &&
|
||||
(cmd === 'install' || cmd === 'import' || cmd === 'dedupe' || cmd === 'patch-commit' || cmd === 'patch' || cmd === 'patch-remove') &&
|
||||
typeof workspaceDir === 'string'
|
||||
) {
|
||||
cliOptions['recursive'] = true
|
||||
|
||||
Reference in New Issue
Block a user