Skip to content

Commit 9cedb9c

Browse files
committed
feat(cli): add package manager utility commands
1 parent 35b4b99 commit 9cedb9c

15 files changed

Lines changed: 4011 additions & 1 deletion

File tree

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
use std::{collections::HashMap, process::ExitStatus};
2+
3+
use vite_error::Error;
4+
use vite_path::AbsolutePath;
5+
6+
use crate::package_manager::{
7+
PackageManager, PackageManagerType, ResolveCommandResult, format_path_env, run_command,
8+
};
9+
10+
/// Options for the cache command.
11+
#[derive(Debug, Default)]
12+
pub struct CacheCommandOptions<'a> {
13+
pub subcommand: Option<&'a str>,
14+
pub force: bool,
15+
pub pass_through_args: Option<&'a [String]>,
16+
}
17+
18+
impl PackageManager {
19+
/// Run the cache command with the package manager.
20+
#[must_use]
21+
pub async fn run_cache_command(
22+
&self,
23+
options: &CacheCommandOptions<'_>,
24+
cwd: impl AsRef<AbsolutePath>,
25+
) -> Result<ExitStatus, Error> {
26+
let resolve_command = self.resolve_cache_command(options);
27+
run_command(&resolve_command.bin_path, &resolve_command.args, &resolve_command.envs, cwd)
28+
.await
29+
}
30+
31+
/// Resolve the cache command.
32+
#[must_use]
33+
pub fn resolve_cache_command(&self, options: &CacheCommandOptions) -> ResolveCommandResult {
34+
let bin_name: String;
35+
let envs = HashMap::from([("PATH".to_string(), format_path_env(self.get_bin_prefix()))]);
36+
let mut args: Vec<String> = Vec::new();
37+
38+
match self.client {
39+
PackageManagerType::Pnpm => {
40+
bin_name = "pnpm".into();
41+
42+
if let Some(subcommand) = options.subcommand {
43+
match subcommand {
44+
"dir" | "path" => {
45+
args.push("store".into());
46+
args.push("path".into());
47+
}
48+
"clean" | "clear" => {
49+
args.push("store".into());
50+
args.push("prune".into());
51+
}
52+
"list" => {
53+
args.push("store".into());
54+
args.push("list".into());
55+
}
56+
"verify" => {
57+
println!("Warning: pnpm does not support 'cache verify'");
58+
return ResolveCommandResult {
59+
bin_path: "echo".into(),
60+
args: vec!["pnpm does not support cache verify".into()],
61+
envs,
62+
};
63+
}
64+
_ => {
65+
args.push("store".into());
66+
args.push(subcommand.to_string());
67+
}
68+
}
69+
} else {
70+
// Default: show cache directory
71+
args.push("store".into());
72+
args.push("path".into());
73+
}
74+
}
75+
PackageManagerType::Npm => {
76+
bin_name = "npm".into();
77+
args.push("cache".into());
78+
79+
if let Some(subcommand) = options.subcommand {
80+
match subcommand {
81+
"path" => args.push("dir".into()), // npm uses 'dir' not 'path'
82+
"clear" => args.push("clean".into()), // npm uses 'clean' not 'clear'
83+
_ => args.push(subcommand.to_string()),
84+
}
85+
}
86+
87+
if options.force {
88+
args.push("--force".into());
89+
}
90+
}
91+
PackageManagerType::Yarn => {
92+
bin_name = "yarn".into();
93+
args.push("cache".into());
94+
95+
if let Some(subcommand) = options.subcommand {
96+
match subcommand {
97+
"path" => args.push("dir".into()), // yarn uses 'dir' not 'path'
98+
"clear" => args.push("clean".into()), // yarn uses 'clean' not 'clear'
99+
"verify" => {
100+
println!("Warning: yarn does not support 'cache verify'");
101+
return ResolveCommandResult {
102+
bin_path: "echo".into(),
103+
args: vec!["yarn does not support cache verify".into()],
104+
envs,
105+
};
106+
}
107+
_ => args.push(subcommand.to_string()),
108+
}
109+
}
110+
}
111+
}
112+
113+
// Add pass-through args
114+
if let Some(pass_through_args) = options.pass_through_args {
115+
args.extend_from_slice(pass_through_args);
116+
}
117+
118+
ResolveCommandResult { bin_path: bin_name, args, envs }
119+
}
120+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
use std::{collections::HashMap, process::ExitStatus};
2+
3+
use vite_error::Error;
4+
use vite_path::AbsolutePath;
5+
6+
use crate::package_manager::{
7+
PackageManager, PackageManagerType, ResolveCommandResult, format_path_env, run_command,
8+
};
9+
10+
/// Options for the config command.
11+
#[derive(Debug, Default)]
12+
pub struct ConfigCommandOptions<'a> {
13+
pub subcommand: Option<&'a str>,
14+
pub key: Option<&'a str>,
15+
pub value: Option<&'a str>,
16+
pub json: bool,
17+
pub global: bool,
18+
pub pass_through_args: Option<&'a [String]>,
19+
}
20+
21+
impl PackageManager {
22+
/// Run the config command with the package manager.
23+
#[must_use]
24+
pub async fn run_config_command(
25+
&self,
26+
options: &ConfigCommandOptions<'_>,
27+
cwd: impl AsRef<AbsolutePath>,
28+
) -> Result<ExitStatus, Error> {
29+
let resolve_command = self.resolve_config_command(options);
30+
run_command(&resolve_command.bin_path, &resolve_command.args, &resolve_command.envs, cwd)
31+
.await
32+
}
33+
34+
/// Resolve the config command.
35+
#[must_use]
36+
pub fn resolve_config_command(&self, options: &ConfigCommandOptions) -> ResolveCommandResult {
37+
let bin_name: String = self.client.to_string();
38+
let envs = HashMap::from([("PATH".to_string(), format_path_env(self.get_bin_prefix()))]);
39+
let mut args: Vec<String> = Vec::new();
40+
41+
args.push("config".into());
42+
43+
if let Some(subcommand) = options.subcommand {
44+
// yarn@2+ uses 'unset' instead of 'delete'
45+
if subcommand == "delete"
46+
&& self.client == PackageManagerType::Yarn
47+
&& !self.version.starts_with("1.")
48+
{
49+
args.push("unset".into());
50+
} else {
51+
args.push(subcommand.to_string());
52+
}
53+
}
54+
55+
if let Some(key) = options.key {
56+
args.push(key.to_string());
57+
}
58+
59+
if let Some(value) = options.value {
60+
args.push(value.to_string());
61+
}
62+
63+
if options.json && self.client == PackageManagerType::Npm {
64+
args.push("--json".into());
65+
}
66+
67+
if options.global {
68+
args.push("--global".into());
69+
}
70+
71+
// Add pass-through args
72+
if let Some(pass_through_args) = options.pass_through_args {
73+
args.extend_from_slice(pass_through_args);
74+
}
75+
76+
ResolveCommandResult { bin_path: bin_name, args, envs }
77+
}
78+
}

0 commit comments

Comments
 (0)