diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/cli_helper_message/snapshots/cli_helper_message.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/cli_helper_message/snapshots/cli_helper_message.md index 358e131abc..78e5de5537 100644 --- a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/cli_helper_message/snapshots/cli_helper_message.md +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/cli_helper_message/snapshots/cli_helper_message.md @@ -398,6 +398,8 @@ Forward a command to the package manager Commands: approve-builds Approve dependency lifecycle scripts (install/postinstall) to run prune Remove unnecessary packages + patch Prepare a package for local patching + patch-commit Commit a prepared package patch pack Create a tarball of the package list, ls List installed packages view, info, show View package information from the registry diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pm_patch/bun/package.json b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pm_patch/bun/package.json new file mode 100644 index 0000000000..bcb0fd946b --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pm_patch/bun/package.json @@ -0,0 +1,7 @@ +{ + "name": "pm-patch-bun", + "version": "1.0.0", + "private": true, + "license": "MIT", + "packageManager": "bun@1.3.5" +} diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pm_patch/npm/package.json b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pm_patch/npm/package.json new file mode 100644 index 0000000000..3d874a76f0 --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pm_patch/npm/package.json @@ -0,0 +1,7 @@ +{ + "name": "pm-patch-npm", + "version": "1.0.0", + "private": true, + "license": "MIT", + "packageManager": "npm@11.11.1" +} diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pm_patch/pnpm/package.json b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pm_patch/pnpm/package.json new file mode 100644 index 0000000000..b532b946a6 --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pm_patch/pnpm/package.json @@ -0,0 +1,7 @@ +{ + "name": "pm-patch-pnpm", + "version": "1.0.0", + "private": true, + "license": "MIT", + "packageManager": "pnpm@10.34.4" +} diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pm_patch/snapshots.toml b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pm_patch/snapshots.toml new file mode 100644 index 0000000000..d9020a5ade --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pm_patch/snapshots.toml @@ -0,0 +1,45 @@ +[[case]] +name = "pm_patch_npm" +vp = "local" +cwd = "npm" +steps = [ + { argv = ["vp", "pm", "patch", "placeholder"], comment = "npm warns and exits successfully because patch is unsupported" }, + { argv = ["vp", "pm", "patch-commit", "placeholder"], comment = "npm warns and exits successfully because patch-commit is unsupported" }, +] + +[[case]] +name = "pm_patch_pnpm" +vp = "local" +cwd = "pnpm" +steps = [ + { argv = ["vp", "pm", "patch", "placeholder", "--", "--version"], comment = "pnpm receives the patch command" }, + { argv = ["vp", "pm", "patch-commit", "placeholder", "--", "--version"], comment = "pnpm receives the patch-commit command" }, +] + +[[case]] +name = "pm_patch_yarn" +vp = "local" +cwd = "yarn" +steps = [ + { argv = ["vp", "pm", "patch", "placeholder"], comment = "Yarn Classic warns and exits successfully because patch is unsupported" }, + { argv = ["vp", "pm", "patch-commit", "placeholder"], comment = "Yarn Classic warns and exits successfully because patch-commit is unsupported" }, +] + +[[case]] +name = "pm_patch_yarn_berry" +vp = "local" +cwd = "yarn_berry" +env = { YARN_ENABLE_TELEMETRY = "0" } +steps = [ + { argv = ["vp", "pm", "patch", "placeholder", "--", "--version"], comment = "Yarn Berry receives the patch command", continue-on-failure = true }, + { argv = ["vp", "pm", "patch-commit", "placeholder", "--", "--version"], comment = "Yarn Berry receives the patch-commit command", continue-on-failure = true }, +] + +[[case]] +name = "pm_patch_bun" +vp = "local" +cwd = "bun" +steps = [ + { argv = ["vp", "pm", "patch", "placeholder", "--", "--version"], comment = "Bun receives the patch command", continue-on-failure = true }, + { argv = ["vp", "pm", "patch-commit", "placeholder", "--", "--version"], comment = "Bun receives patch commit through the --commit flag", continue-on-failure = true }, +] diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pm_patch/snapshots/pm_patch_bun.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pm_patch/snapshots/pm_patch_bun.md new file mode 100644 index 0000000000..16e9368bc0 --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pm_patch/snapshots/pm_patch_bun.md @@ -0,0 +1,27 @@ +# pm_patch_bun + +## `vp pm patch placeholder -- --version` + +Bun receives the patch command + +**Exit code:** 1 + +``` +bun patch (1e86cebd) +No packages! Deleted empty lockfile + +[] done + +error: package placeholder not found +``` + +## `vp pm patch-commit placeholder -- --version` + +Bun receives patch commit through the --commit flag + +**Exit code:** 1 + +``` +bun patch (1e86cebd) +error: Cannot find lockfile. Install packages with `bun install` before patching them. +``` diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pm_patch/snapshots/pm_patch_npm.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pm_patch/snapshots/pm_patch_npm.md new file mode 100644 index 0000000000..fc6e6c913b --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pm_patch/snapshots/pm_patch_npm.md @@ -0,0 +1,17 @@ +# pm_patch_npm + +## `vp pm patch placeholder` + +npm warns and exits successfully because patch is unsupported + +``` +warn: npm does not have a patch command. +``` + +## `vp pm patch-commit placeholder` + +npm warns and exits successfully because patch-commit is unsupported + +``` +warn: npm does not have a patch-commit command. +``` diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pm_patch/snapshots/pm_patch_pnpm.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pm_patch/snapshots/pm_patch_pnpm.md new file mode 100644 index 0000000000..cff49de0a3 --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pm_patch/snapshots/pm_patch_pnpm.md @@ -0,0 +1,17 @@ +# pm_patch_pnpm + +## `vp pm patch placeholder -- --version` + +pnpm receives the patch command + +``` +10.34.4 +``` + +## `vp pm patch-commit placeholder -- --version` + +pnpm receives the patch-commit command + +``` +10.34.4 +``` diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pm_patch/snapshots/pm_patch_yarn.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pm_patch/snapshots/pm_patch_yarn.md new file mode 100644 index 0000000000..257d3b1bf6 --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pm_patch/snapshots/pm_patch_yarn.md @@ -0,0 +1,17 @@ +# pm_patch_yarn + +## `vp pm patch placeholder` + +Yarn Classic warns and exits successfully because patch is unsupported + +``` +warn: yarn classic does not have a patch command. +``` + +## `vp pm patch-commit placeholder` + +Yarn Classic warns and exits successfully because patch-commit is unsupported + +``` +warn: yarn classic does not have a patch-commit command. +``` diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pm_patch/snapshots/pm_patch_yarn_berry.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pm_patch/snapshots/pm_patch_yarn_berry.md new file mode 100644 index 0000000000..bcfbaca9f5 --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pm_patch/snapshots/pm_patch_yarn_berry.md @@ -0,0 +1,25 @@ +# pm_patch_yarn_berry + +## `vp pm patch placeholder -- --version` + +Yarn Berry receives the patch command + +**Exit code:** 1 + +``` +Unknown Syntax Error: Unsupported option name ("--version"). + +$ yarn patch [-u,--update] [--json] +``` + +## `vp pm patch-commit placeholder -- --version` + +Yarn Berry receives the patch-commit command + +**Exit code:** 1 + +``` +Unknown Syntax Error: Unsupported option name ("--version"). + +$ yarn patch-commit [-s,--save] +``` diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pm_patch/yarn/package.json b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pm_patch/yarn/package.json new file mode 100644 index 0000000000..041153f2aa --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pm_patch/yarn/package.json @@ -0,0 +1,7 @@ +{ + "name": "pm-patch-yarn", + "version": "1.0.0", + "private": true, + "license": "MIT", + "packageManager": "yarn@1.22.22" +} diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pm_patch/yarn_berry/package.json b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pm_patch/yarn_berry/package.json new file mode 100644 index 0000000000..d89ab73950 --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pm_patch/yarn_berry/package.json @@ -0,0 +1,7 @@ +{ + "name": "pm-patch-yarn-berry", + "version": "1.0.0", + "private": true, + "license": "MIT", + "packageManager": "yarn@4.12.0" +} diff --git a/crates/vite_install/src/commands/mod.rs b/crates/vite_install/src/commands/mod.rs index d875f643fe..0f93b4f51e 100644 --- a/crates/vite_install/src/commands/mod.rs +++ b/crates/vite_install/src/commands/mod.rs @@ -16,6 +16,8 @@ pub mod logout; pub mod outdated; pub mod owner; pub mod pack; +pub mod patch; +pub mod patch_commit; pub mod ping; pub mod prune; pub mod publish; diff --git a/crates/vite_install/src/commands/patch.rs b/crates/vite_install/src/commands/patch.rs new file mode 100644 index 0000000000..8e86777f04 --- /dev/null +++ b/crates/vite_install/src/commands/patch.rs @@ -0,0 +1,172 @@ +use std::{collections::HashMap, process::ExitStatus}; + +use vite_command::run_command; +use vite_error::Error; +use vite_path::AbsolutePath; +use vite_shared::output; + +use crate::package_manager::{ + PackageManager, PackageManagerType, ResolveCommandResult, format_path_env, +}; + +/// Options for the patch command. +#[derive(Debug, Default)] +pub struct PatchCommandOptions<'a> { + pub package: &'a str, + pub pass_through_args: Option<&'a [String]>, +} + +impl PackageManager { + /// Run the patch command with the package manager. + /// Returns `ExitStatus` with success (0) if the command is not supported. + pub async fn run_patch_command( + &self, + options: &PatchCommandOptions<'_>, + cwd: impl AsRef, + ) -> Result { + let Some(resolve_command) = self.resolve_patch_command(options) else { + return Ok(ExitStatus::default()); + }; + run_command(&resolve_command.bin_path, &resolve_command.args, &resolve_command.envs, cwd) + .await + } + + /// Resolve the patch command. + /// Returns None if the command is not supported by the package manager. + pub fn resolve_patch_command( + &self, + options: &PatchCommandOptions, + ) -> Option { + let bin_name: String; + let mut args = Vec::new(); + + match self.client { + PackageManagerType::Pnpm => { + bin_name = "pnpm".into(); + args.push("patch".into()); + } + PackageManagerType::Bun => { + bin_name = "bun".into(); + args.push("patch".into()); + } + PackageManagerType::Yarn => { + if !self.is_yarn_berry() { + output::warn("yarn classic does not have a patch command."); + return None; + } + bin_name = "yarn".into(); + args.push("patch".into()); + } + PackageManagerType::Npm => { + output::warn("npm does not have a patch command."); + return None; + } + } + + args.push(options.package.into()); + if let Some(pass_through_args) = options.pass_through_args { + args.extend_from_slice(pass_through_args); + } + + let envs = HashMap::from([("PATH".to_string(), format_path_env(self.get_bin_prefix()))]); + Some(ResolveCommandResult { bin_path: bin_name, args, envs }) + } +} + +#[cfg(test)] +mod tests { + use tempfile::{TempDir, tempdir}; + use vite_path::AbsolutePathBuf; + use vite_str::Str; + + use super::*; + + fn create_temp_dir() -> TempDir { + tempdir().expect("Failed to create temp directory") + } + + fn create_mock_package_manager(pm_type: PackageManagerType, version: &str) -> PackageManager { + let temp_dir = create_temp_dir(); + let temp_dir_path = AbsolutePathBuf::new(temp_dir.path().to_path_buf()).unwrap(); + let install_dir = temp_dir_path.join("install"); + + PackageManager { + client: pm_type, + package_name: pm_type.to_string().into(), + version: Str::from(version), + hash: None, + bin_name: pm_type.to_string().into(), + workspace_root: temp_dir_path.clone(), + is_monorepo: false, + install_dir, + } + } + + #[test] + fn pnpm_patch() { + let pm = create_mock_package_manager(PackageManagerType::Pnpm, "10.0.0"); + let result = pm.resolve_patch_command(&PatchCommandOptions { + package: "left-pad", + ..Default::default() + }); + let result = result.expect("supported"); + assert_eq!(result.bin_path, "pnpm"); + assert_eq!(result.args, vec!["patch", "left-pad"]); + } + + #[test] + fn yarn_berry_patch() { + let pm = create_mock_package_manager(PackageManagerType::Yarn, "4.0.0"); + let result = pm.resolve_patch_command(&PatchCommandOptions { + package: "left-pad", + ..Default::default() + }); + let result = result.expect("supported"); + assert_eq!(result.bin_path, "yarn"); + assert_eq!(result.args, vec!["patch", "left-pad"]); + } + + #[test] + fn bun_patch() { + let pm = create_mock_package_manager(PackageManagerType::Bun, "1.3.0"); + let result = pm.resolve_patch_command(&PatchCommandOptions { + package: "left-pad", + ..Default::default() + }); + let result = result.expect("supported"); + assert_eq!(result.bin_path, "bun"); + assert_eq!(result.args, vec!["patch", "left-pad"]); + } + + #[test] + fn npm_patch_not_supported() { + let pm = create_mock_package_manager(PackageManagerType::Npm, "11.0.0"); + let result = pm.resolve_patch_command(&PatchCommandOptions { + package: "left-pad", + ..Default::default() + }); + assert!(result.is_none()); + } + + #[test] + fn yarn_classic_patch_not_supported() { + let pm = create_mock_package_manager(PackageManagerType::Yarn, "1.22.22"); + let result = pm.resolve_patch_command(&PatchCommandOptions { + package: "left-pad", + ..Default::default() + }); + assert!(result.is_none()); + } + + #[test] + fn appends_pass_through_args() { + let pm = create_mock_package_manager(PackageManagerType::Pnpm, "10.0.0"); + let extra = vec!["--edit-dir".to_string(), ".patches".to_string()]; + let result = pm.resolve_patch_command(&PatchCommandOptions { + package: "left-pad", + pass_through_args: Some(&extra), + }); + let result = result.expect("supported"); + assert_eq!(result.args, vec!["patch", "left-pad", "--edit-dir", ".patches"]); + } +} diff --git a/crates/vite_install/src/commands/patch_commit.rs b/crates/vite_install/src/commands/patch_commit.rs new file mode 100644 index 0000000000..6675b12492 --- /dev/null +++ b/crates/vite_install/src/commands/patch_commit.rs @@ -0,0 +1,175 @@ +use std::{collections::HashMap, process::ExitStatus}; + +use vite_command::run_command; +use vite_error::Error; +use vite_path::AbsolutePath; +use vite_shared::output; + +use crate::package_manager::{ + PackageManager, PackageManagerType, ResolveCommandResult, format_path_env, +}; + +/// Options for the patch-commit command. +#[derive(Debug, Default)] +pub struct PatchCommitCommandOptions<'a> { + pub patch_dir: &'a str, + pub pass_through_args: Option<&'a [String]>, +} + +impl PackageManager { + /// Run the patch-commit command with the package manager. + /// Returns `ExitStatus` with success (0) if the command is not supported. + pub async fn run_patch_commit_command( + &self, + options: &PatchCommitCommandOptions<'_>, + cwd: impl AsRef, + ) -> Result { + let Some(resolve_command) = self.resolve_patch_commit_command(options) else { + return Ok(ExitStatus::default()); + }; + run_command(&resolve_command.bin_path, &resolve_command.args, &resolve_command.envs, cwd) + .await + } + + /// Resolve the patch-commit command. + /// Returns None if the command is not supported by the package manager. + pub fn resolve_patch_commit_command( + &self, + options: &PatchCommitCommandOptions, + ) -> Option { + let bin_name: String; + let mut args = Vec::new(); + + match self.client { + PackageManagerType::Pnpm => { + bin_name = "pnpm".into(); + args.push("patch-commit".into()); + } + PackageManagerType::Bun => { + bin_name = "bun".into(); + args.extend(["patch".into(), "--commit".into()]); + } + PackageManagerType::Yarn => { + if !self.is_yarn_berry() { + output::warn("yarn classic does not have a patch-commit command."); + return None; + } + bin_name = "yarn".into(); + args.push("patch-commit".into()); + } + PackageManagerType::Npm => { + output::warn("npm does not have a patch-commit command."); + return None; + } + } + + args.push(options.patch_dir.into()); + if let Some(pass_through_args) = options.pass_through_args { + args.extend_from_slice(pass_through_args); + } + + let envs = HashMap::from([("PATH".to_string(), format_path_env(self.get_bin_prefix()))]); + Some(ResolveCommandResult { bin_path: bin_name, args, envs }) + } +} + +#[cfg(test)] +mod tests { + use tempfile::{TempDir, tempdir}; + use vite_path::AbsolutePathBuf; + use vite_str::Str; + + use super::*; + + fn create_temp_dir() -> TempDir { + tempdir().expect("Failed to create temp directory") + } + + fn create_mock_package_manager(pm_type: PackageManagerType, version: &str) -> PackageManager { + let temp_dir = create_temp_dir(); + let temp_dir_path = AbsolutePathBuf::new(temp_dir.path().to_path_buf()).unwrap(); + let install_dir = temp_dir_path.join("install"); + + PackageManager { + client: pm_type, + package_name: pm_type.to_string().into(), + version: Str::from(version), + hash: None, + bin_name: pm_type.to_string().into(), + workspace_root: temp_dir_path.clone(), + is_monorepo: false, + install_dir, + } + } + + #[test] + fn pnpm_patch_commit() { + let pm = create_mock_package_manager(PackageManagerType::Pnpm, "10.0.0"); + let result = pm.resolve_patch_commit_command(&PatchCommitCommandOptions { + patch_dir: "patches/left-pad", + ..Default::default() + }); + let result = result.expect("supported"); + assert_eq!(result.bin_path, "pnpm"); + assert_eq!(result.args, vec!["patch-commit", "patches/left-pad"]); + } + + #[test] + fn yarn_berry_patch_commit() { + let pm = create_mock_package_manager(PackageManagerType::Yarn, "4.0.0"); + let result = pm.resolve_patch_commit_command(&PatchCommitCommandOptions { + patch_dir: "patches/left-pad", + ..Default::default() + }); + let result = result.expect("supported"); + assert_eq!(result.bin_path, "yarn"); + assert_eq!(result.args, vec!["patch-commit", "patches/left-pad"]); + } + + #[test] + fn bun_patch_commit_uses_flag() { + let pm = create_mock_package_manager(PackageManagerType::Bun, "1.3.0"); + let result = pm.resolve_patch_commit_command(&PatchCommitCommandOptions { + patch_dir: "patches/left-pad", + ..Default::default() + }); + let result = result.expect("supported"); + assert_eq!(result.bin_path, "bun"); + assert_eq!(result.args, vec!["patch", "--commit", "patches/left-pad"]); + } + + #[test] + fn npm_patch_commit_not_supported() { + let pm = create_mock_package_manager(PackageManagerType::Npm, "11.0.0"); + let result = pm.resolve_patch_commit_command(&PatchCommitCommandOptions { + patch_dir: "patches/left-pad", + ..Default::default() + }); + assert!(result.is_none()); + } + + #[test] + fn yarn_classic_patch_commit_not_supported() { + let pm = create_mock_package_manager(PackageManagerType::Yarn, "1.22.22"); + let result = pm.resolve_patch_commit_command(&PatchCommitCommandOptions { + patch_dir: "patches/left-pad", + ..Default::default() + }); + assert!(result.is_none()); + } + + #[test] + fn appends_pass_through_args() { + let pm = create_mock_package_manager(PackageManagerType::Bun, "1.3.0"); + let extra = vec!["--patches-dir".to_string(), ".patches".to_string()]; + let result = pm.resolve_patch_commit_command(&PatchCommitCommandOptions { + patch_dir: "patches/left-pad", + pass_through_args: Some(&extra), + }); + let result = result.expect("supported"); + assert_eq!( + result.args, + vec!["patch", "--commit", "patches/left-pad", "--patches-dir", ".patches"] + ); + } +} diff --git a/crates/vite_pm_cli/src/cli.rs b/crates/vite_pm_cli/src/cli.rs index c7fbba6db0..fbff998b0c 100644 --- a/crates/vite_pm_cli/src/cli.rs +++ b/crates/vite_pm_cli/src/cli.rs @@ -594,6 +594,27 @@ pub enum PmCommands { pass_through_args: Option>, }, + /// Prepare a package for local patching + Patch { + /// Package to patch + package: String, + + /// Additional arguments + #[arg(last = true, allow_hyphen_values = true)] + pass_through_args: Option>, + }, + + /// Commit a prepared package patch + #[command(name = "patch-commit")] + PatchCommit { + /// Patch directory to commit + patch_dir: String, + + /// Additional arguments + #[arg(last = true, allow_hyphen_values = true)] + pass_through_args: Option>, + }, + /// Create a tarball of the package Pack { /// Pack all workspace packages diff --git a/crates/vite_pm_cli/src/handlers.rs b/crates/vite_pm_cli/src/handlers.rs index 1ac72f7b4b..18e738f750 100644 --- a/crates/vite_pm_cli/src/handlers.rs +++ b/crates/vite_pm_cli/src/handlers.rs @@ -25,6 +25,8 @@ use vite_install::{ outdated::OutdatedCommandOptions, owner::OwnerSubcommand, pack::PackCommandOptions, + patch::PatchCommandOptions, + patch_commit::PatchCommitCommandOptions, ping::PingCommandOptions, prune::PruneCommandOptions, publish::PublishCommandOptions, @@ -174,6 +176,8 @@ pub async fn run_pm_subcommand( command, PmCommands::ApproveBuilds { .. } | PmCommands::Prune { .. } + | PmCommands::Patch { .. } + | PmCommands::PatchCommit { .. } | PmCommands::Pack { .. } | PmCommands::List { .. } | PmCommands::Version { .. } @@ -215,6 +219,22 @@ pub async fn run_pm_subcommand( Ok(pm.run_prune_command(&options, cwd).await?) } + PmCommands::Patch { package, pass_through_args } => { + let options = PatchCommandOptions { + package: &package, + pass_through_args: pass_through_args.as_deref(), + }; + Ok(pm.run_patch_command(&options, cwd).await?) + } + + PmCommands::PatchCommit { patch_dir, pass_through_args } => { + let options = PatchCommitCommandOptions { + patch_dir: &patch_dir, + pass_through_args: pass_through_args.as_deref(), + }; + Ok(pm.run_patch_commit_command(&options, cwd).await?) + } + PmCommands::Pack { recursive, filter,