Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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 List installed packages [alias: ls]
view, info, show View package information from the registry
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "pm-patch-npm",
"version": "1.0.0",
"private": true,
"license": "MIT",
"packageManager": "npm@11.11.1"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "pm-patch-pnpm",
"version": "1.0.0",
"private": true,
"license": "MIT",
"packageManager": "pnpm@10.34.4"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[[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"
Comment thread
fengmk2 marked this conversation as resolved.
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" },
]
Original file line number Diff line number Diff line change
@@ -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.
```
Original file line number Diff line number Diff line change
@@ -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
```
Original file line number Diff line number Diff line change
@@ -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.
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "pm-patch-yarn",
"version": "1.0.0",
"private": true,
"license": "MIT",
"packageManager": "yarn@1.22.22"
}
15 changes: 12 additions & 3 deletions crates/vite_pm_cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ use crate::{
resolution::{
AddArgs, ApproveBuildsArgs, AuditArgs, CacheArgs, ConfigCommand, DedupeArgs, DeprecateArgs,
DistTagCommand, DlxArgs, FundArgs, InstallArgs, LinkArgs, ListArgs, LoginArgs, LogoutArgs,
OutdatedArgs, OutdatedFormat, OwnerCommand, PackArgs, PingArgs, PruneArgs, PublishArgs,
RebuildArgs, RemoveArgs, Resolution, SearchArgs, StageCommand, TokenCommand, UnlinkArgs,
UpdateArgs, VersionArgs, ViewArgs, WhoamiArgs, WhyArgs,
OutdatedArgs, OutdatedFormat, OwnerCommand, PackArgs, PatchArgs, PatchCommitArgs, PingArgs,
PruneArgs, PublishArgs, RebuildArgs, RemoveArgs, Resolution, SearchArgs, StageCommand,
TokenCommand, UnlinkArgs, UpdateArgs, VersionArgs, ViewArgs, WhoamiArgs, WhyArgs,
resolve_for_manager as resolve_args_for_manager,
},
};
Expand Down Expand Up @@ -79,6 +79,13 @@ pub enum PmCommand {
/// Remove unnecessary packages
Prune(PruneArgs),

/// Prepare a package for local patching
Patch(PatchArgs),

/// Commit a prepared package patch
#[command(name = "patch-commit")]
PatchCommit(PatchCommitArgs),

/// Create a tarball of the package
Pack(PackArgs),

Expand Down Expand Up @@ -311,6 +318,8 @@ impl PmCommand {
match self {
Self::ApproveBuilds(args) => resolve_args_for_manager(manager, args),
Self::Prune(args) => resolve_args_for_manager(manager, args),
Self::Patch(args) => resolve_args_for_manager(manager, args),
Self::PatchCommit(args) => resolve_args_for_manager(manager, args),
Self::Pack(args) => resolve_args_for_manager(manager, args),
Self::List(args) => resolve_args_for_manager(manager, args),
Self::View(args) => resolve_args_for_manager(manager, args),
Expand Down
2 changes: 2 additions & 0 deletions crates/vite_pm_cli/src/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ fn pm_manager_policy(command: &PmCommand) -> ManagerPolicy {
match command {
PmCommand::ApproveBuilds(_)
| PmCommand::Prune(_)
| PmCommand::Patch(_)
| PmCommand::PatchCommit(_)
| PmCommand::Pack(_)
| PmCommand::List(_)
| PmCommand::Version(_)
Expand Down
4 changes: 4 additions & 0 deletions crates/vite_pm_cli/src/resolution/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ mod logout;
mod outdated;
mod owner;
mod pack;
mod patch;
mod patch_commit;
mod ping;
mod prune;
mod publish;
Expand Down Expand Up @@ -50,6 +52,8 @@ pub use logout::LogoutArgs;
pub use outdated::{OutdatedArgs, OutdatedFormat};
pub use owner::OwnerCommand;
pub use pack::PackArgs;
pub use patch::PatchArgs;
pub use patch_commit::PatchCommitArgs;
pub use ping::PingArgs;
pub use prune::PruneArgs;
pub use publish::PublishArgs;
Expand Down
148 changes: 148 additions & 0 deletions crates/vite_pm_cli/src/resolution/commands/patch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
use vite_pm_cli_macros::pm_args;

use crate::resolution::{
Bun, CommandBuilder, CommandResolution, DiagnosticKind, Diagnostics, Npm, Pnpm, Resolve, Yarn,
};

#[pm_args]
#[derive(clap::Args, Clone, Debug, Default, PartialEq, Eq)]
pub struct PatchArgs {
/// Package to patch
pub(crate) package: String,

/// Additional arguments
#[arg(last = true, allow_hyphen_values = true)]
pub(crate) pass_through_args: Vec<String>,
}

impl Resolve<PatchArgs> for Pnpm {
fn resolve(&self, args: &PatchArgs, _diag: &mut Diagnostics) -> CommandResolution {
let mut cmd = CommandBuilder::new("pnpm");
cmd.arg("patch").arg(&args.package).extend(args.pass_through_args.iter());
cmd.into()
}
}

impl Resolve<PatchArgs> for Npm {
fn resolve(&self, _args: &PatchArgs, diag: &mut Diagnostics) -> CommandResolution {
diag.warn(DiagnosticKind::UnsupportedCommandNoop, "npm does not have a 'patch' command.");
CommandResolution::Noop
}
}

impl Resolve<PatchArgs> for Yarn {
fn resolve(&self, args: &PatchArgs, diag: &mut Diagnostics) -> CommandResolution {
if !self.is_berry() {
diag.warn(
DiagnosticKind::UnsupportedCommandNoop,
"yarn classic does not have a 'patch' command.",
);
return CommandResolution::Noop;
}
let mut cmd = CommandBuilder::new("yarn");
cmd.arg("patch").arg(&args.package).extend(args.pass_through_args.iter());
cmd.into()
}
}

impl Resolve<PatchArgs> for Bun {
fn resolve(&self, args: &PatchArgs, _diag: &mut Diagnostics) -> CommandResolution {
let mut cmd = CommandBuilder::new("bun");
cmd.arg("patch").arg(&args.package).extend(args.pass_through_args.iter());
cmd.into()
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::resolution::{
resolve,
test_utils::{bun, npm, parse_args, pnpm, yarn},
};

fn patch_args(package: &str) -> PatchArgs {
PatchArgs { package: package.to_string(), ..Default::default() }
}

#[test]
fn test_pnpm_patch() {
let CommandResolution::Run(command) =
resolve(&pnpm("10.0.0"), patch_args("left-pad")).outcome
else {
panic!("expected command resolution");
};

assert_eq!(command.program, "pnpm");
assert_eq!(command.args, vec!["patch", "left-pad"]);
}

#[test]
fn test_yarn_berry_patch() {
let CommandResolution::Run(command) =
resolve(&yarn("4.0.0"), patch_args("left-pad")).outcome
else {
panic!("expected command resolution");
};

assert_eq!(command.program, "yarn");
assert_eq!(command.args, vec!["patch", "left-pad"]);
}

#[test]
fn test_bun_patch() {
let CommandResolution::Run(command) =
resolve(&bun("1.3.11"), patch_args("left-pad")).outcome
else {
panic!("expected command resolution");
};

assert_eq!(command.program, "bun");
assert_eq!(command.args, vec!["patch", "left-pad"]);
}

#[test]
fn test_npm_patch_not_supported() {
let result = resolve(&npm("11.0.0"), patch_args("left-pad"));

assert_eq!(result.outcome, CommandResolution::Noop);
assert_eq!(result.diagnostics.len(), 1);
assert_eq!(result.diagnostics[0].kind, DiagnosticKind::UnsupportedCommandNoop);
assert_eq!(result.diagnostics[0].message, "npm does not have a 'patch' command.");
}

#[test]
fn test_yarn_classic_patch_not_supported() {
let result = resolve(&yarn("1.22.22"), patch_args("left-pad"));

assert_eq!(result.outcome, CommandResolution::Noop);
assert_eq!(result.diagnostics.len(), 1);
assert_eq!(result.diagnostics[0].kind, DiagnosticKind::UnsupportedCommandNoop);
assert_eq!(result.diagnostics[0].message, "yarn classic does not have a 'patch' command.");
}

#[test]
fn test_patch_with_pass_through_args() {
let CommandResolution::Run(command) = resolve(
&pnpm("10.0.0"),
PatchArgs {
package: "left-pad".to_string(),
pass_through_args: vec!["--edit-dir".to_string(), ".patches".to_string()],
},
)
.outcome
else {
panic!("expected command resolution");
};

assert_eq!(command.args, vec!["patch", "left-pad", "--edit-dir", ".patches"]);
}

#[test]
fn parser_captures_package_and_pass_through_args() {
let args = parse_args::<PatchArgs>(["left-pad", "--", "--edit-dir", ".patches"]).unwrap();

assert_eq!(args.package, "left-pad");
assert_eq!(args.pass_through_args, vec!["--edit-dir".to_string(), ".patches".to_string()]);
}
}
Loading
Loading