import fs from 'fs' import path from 'path' import { applyPatchToDir } from '@pnpm/patching.apply-patch' import { docsUrl } from '@pnpm/cli-utils' import { Config, types as allTypes } from '@pnpm/config' import { LogBase } from '@pnpm/logger' import { CreateStoreControllerOptions, } from '@pnpm/store-connection-manager' import pick from 'ramda/src/pick' import renderHelp from 'render-help' import tempy from 'tempy' import { PnpmError } from '@pnpm/error' import { writePackage, ParseWantedDependencyResult } from './writePackage' export function rcOptionsTypes () { return pick([], allTypes) } export function cliOptionsTypes () { return { ...rcOptionsTypes(), 'edit-dir': String, 'ignore-existing': Boolean } } export const shorthands = { d: '--edit-dir', } export const commandNames = ['patch'] export function help () { return renderHelp({ description: 'Prepare a package for patching', descriptionLists: [{ title: 'Options', list: [ { description: 'The package that needs to be modified will be extracted to this directory', name: '--edit-dir', }, { description: 'Ignore existing patch files when patching', name: '--ignore-existing', }, ], }], url: docsUrl('patch'), usages: ['pnpm patch @'], }) } export type PatchCommandOptions = Pick & CreateStoreControllerOptions & { editDir?: string reporter?: (logObj: LogBase) => void ignoreExisting?: boolean } export async function handler (opts: PatchCommandOptions, params: string[]) { if (opts.editDir && fs.existsSync(opts.editDir) && fs.readdirSync(opts.editDir).length > 0) { throw new PnpmError('PATCH_EDIT_DIR_EXISTS', `The target directory already exists: '${opts.editDir}'`) } const editDir = opts.editDir ?? tempy.directory() const patchedDep = await writePackage(params[0], editDir, opts) if (!opts.ignoreExisting && opts.rootProjectManifest?.pnpm?.patchedDependencies) { tryPatchWithExistingPatchFile({ patchedDep, patchedDir: editDir, patchedDependencies: opts.rootProjectManifest.pnpm.patchedDependencies, lockfileDir: opts.lockfileDir ?? opts.dir ?? process.cwd(), }) } return `You can now edit the following folder: ${editDir} Once you're done with your changes, run "pnpm patch-commit ${editDir}"` } function tryPatchWithExistingPatchFile ( { patchedDep, patchedDir, patchedDependencies, lockfileDir, }: { patchedDep: ParseWantedDependencyResult patchedDir: string patchedDependencies: Record lockfileDir: string } ) { if (!patchedDep.alias || !patchedDep.pref) { return } const existingPatchFile = patchedDependencies[`${patchedDep.alias}@${patchedDep.pref}`] if (!existingPatchFile) { return } const existingPatchFilePath = path.resolve(lockfileDir, existingPatchFile) if (!fs.existsSync(existingPatchFilePath)) { throw new PnpmError('PATCH_FILE_NOT_FOUND', `Unable to find patch file ${existingPatchFilePath}`) } applyPatchToDir({ patchedDir, patchFilePath: existingPatchFilePath }) }