Skip to content

Commit a212a27

Browse files
committed
feat(pm): add pm command group
1 parent d2604ee commit a212a27

13 files changed

Lines changed: 4009 additions & 1 deletion

File tree

Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
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+
#[derive(Debug)]
11+
pub enum CacheSubcommand {
12+
Dir,
13+
Path,
14+
Clean,
15+
Clear,
16+
Verify,
17+
List,
18+
}
19+
20+
#[derive(Debug, Default)]
21+
pub struct CacheCommandOptions<'a> {
22+
pub subcommand: Option<CacheSubcommand>,
23+
pub force: bool,
24+
pub pass_through_args: Option<&'a [String]>,
25+
}
26+
27+
impl PackageManager {
28+
/// Run the cache command with the package manager.
29+
#[must_use]
30+
pub async fn run_cache_command(
31+
&self,
32+
options: &CacheCommandOptions<'_>,
33+
cwd: impl AsRef<AbsolutePath>,
34+
) -> Result<ExitStatus, Error> {
35+
let resolve_command = self.resolve_cache_command(options);
36+
run_command(&resolve_command.bin_path, &resolve_command.args, &resolve_command.envs, cwd)
37+
.await
38+
}
39+
40+
/// Resolve the cache command.
41+
#[must_use]
42+
pub fn resolve_cache_command(&self, options: &CacheCommandOptions) -> ResolveCommandResult {
43+
let bin_name: String;
44+
let envs = HashMap::from([("PATH".to_string(), format_path_env(self.get_bin_prefix()))]);
45+
let mut args: Vec<String> = Vec::new();
46+
47+
match self.client {
48+
PackageManagerType::Pnpm => {
49+
bin_name = "pnpm".into();
50+
args.push("store".into());
51+
52+
match options.subcommand {
53+
Some(CacheSubcommand::Dir) | Some(CacheSubcommand::Path) | None => {
54+
args.push("path".into());
55+
}
56+
Some(CacheSubcommand::Clean) | Some(CacheSubcommand::Clear) => {
57+
args.push("prune".into());
58+
}
59+
Some(CacheSubcommand::List) => {
60+
args.push("list".into());
61+
}
62+
Some(CacheSubcommand::Verify) => {
63+
eprintln!("Warning: pnpm does not support 'cache verify'");
64+
return ResolveCommandResult {
65+
bin_path: "echo".into(),
66+
args: vec!["pnpm does not support cache verify".into()],
67+
envs,
68+
};
69+
}
70+
}
71+
72+
if options.force {
73+
eprintln!("Warning: --force not supported by pnpm store");
74+
}
75+
}
76+
PackageManagerType::Npm => {
77+
bin_name = "npm".into();
78+
args.push("cache".into());
79+
80+
match options.subcommand {
81+
Some(CacheSubcommand::Dir) | Some(CacheSubcommand::Path) | None => {
82+
args.push("dir".into());
83+
}
84+
Some(CacheSubcommand::Clean) | Some(CacheSubcommand::Clear) => {
85+
args.push("clean".into());
86+
if options.force {
87+
args.push("--force".into());
88+
}
89+
}
90+
Some(CacheSubcommand::Verify) => {
91+
args.push("verify".into());
92+
}
93+
Some(CacheSubcommand::List) => {
94+
eprintln!("Warning: npm does not support 'cache list'");
95+
return ResolveCommandResult {
96+
bin_path: "echo".into(),
97+
args: vec!["npm does not support cache list".into()],
98+
envs,
99+
};
100+
}
101+
}
102+
}
103+
PackageManagerType::Yarn => {
104+
bin_name = "yarn".into();
105+
args.push("cache".into());
106+
107+
match options.subcommand {
108+
Some(CacheSubcommand::Dir) | Some(CacheSubcommand::Path) | None => {
109+
args.push("dir".into());
110+
}
111+
Some(CacheSubcommand::Clean) | Some(CacheSubcommand::Clear) => {
112+
args.push("clean".into());
113+
}
114+
Some(CacheSubcommand::List) => {
115+
if self.version.starts_with("1.") {
116+
args.push("list".into());
117+
} else {
118+
eprintln!("Warning: yarn@2+ does not support 'cache list'");
119+
return ResolveCommandResult {
120+
bin_path: "echo".into(),
121+
args: vec!["yarn@2+ does not support cache list".into()],
122+
envs,
123+
};
124+
}
125+
}
126+
Some(CacheSubcommand::Verify) => {
127+
eprintln!("Warning: yarn does not support 'cache verify'");
128+
return ResolveCommandResult {
129+
bin_path: "echo".into(),
130+
args: vec!["yarn does not support cache verify".into()],
131+
envs,
132+
};
133+
}
134+
}
135+
136+
if options.force {
137+
eprintln!("Warning: --force not supported by yarn cache");
138+
}
139+
}
140+
}
141+
142+
// Pass through args
143+
if let Some(pass_through_args) = options.pass_through_args {
144+
args.extend_from_slice(pass_through_args);
145+
}
146+
147+
ResolveCommandResult { bin_path: bin_name, args, envs }
148+
}
149+
}
150+
151+
#[cfg(test)]
152+
mod tests {
153+
use tempfile::{TempDir, tempdir};
154+
use vite_path::AbsolutePathBuf;
155+
use vite_str::Str;
156+
157+
use super::*;
158+
159+
fn create_temp_dir() -> TempDir {
160+
tempdir().expect("Failed to create temp directory")
161+
}
162+
163+
fn create_mock_package_manager(pm_type: PackageManagerType) -> PackageManager {
164+
let temp_dir = create_temp_dir();
165+
let temp_dir_path = AbsolutePathBuf::new(temp_dir.path().to_path_buf()).unwrap();
166+
let install_dir = temp_dir_path.join("install");
167+
168+
PackageManager {
169+
client: pm_type,
170+
package_name: pm_type.to_string().into(),
171+
version: Str::from("1.0.0"),
172+
hash: None,
173+
bin_name: pm_type.to_string().into(),
174+
workspace_root: temp_dir_path.clone(),
175+
install_dir,
176+
}
177+
}
178+
179+
#[test]
180+
fn test_pnpm_cache_dir() {
181+
let pm = create_mock_package_manager(PackageManagerType::Pnpm);
182+
let result = pm.resolve_cache_command(&CacheCommandOptions {
183+
subcommand: Some(CacheSubcommand::Dir),
184+
..Default::default()
185+
});
186+
assert_eq!(result.bin_path, "pnpm");
187+
assert_eq!(result.args, vec!["store", "path"]);
188+
}
189+
190+
#[test]
191+
fn test_pnpm_cache_path() {
192+
let pm = create_mock_package_manager(PackageManagerType::Pnpm);
193+
let result = pm.resolve_cache_command(&CacheCommandOptions {
194+
subcommand: Some(CacheSubcommand::Path),
195+
..Default::default()
196+
});
197+
assert_eq!(result.bin_path, "pnpm");
198+
assert_eq!(result.args, vec!["store", "path"]);
199+
}
200+
201+
#[test]
202+
fn test_pnpm_cache_clean() {
203+
let pm = create_mock_package_manager(PackageManagerType::Pnpm);
204+
let result = pm.resolve_cache_command(&CacheCommandOptions {
205+
subcommand: Some(CacheSubcommand::Clean),
206+
..Default::default()
207+
});
208+
assert_eq!(result.bin_path, "pnpm");
209+
assert_eq!(result.args, vec!["store", "prune"]);
210+
}
211+
212+
#[test]
213+
fn test_pnpm_cache_list() {
214+
let pm = create_mock_package_manager(PackageManagerType::Pnpm);
215+
let result = pm.resolve_cache_command(&CacheCommandOptions {
216+
subcommand: Some(CacheSubcommand::List),
217+
..Default::default()
218+
});
219+
assert_eq!(result.bin_path, "pnpm");
220+
assert_eq!(result.args, vec!["store", "list"]);
221+
}
222+
223+
#[test]
224+
fn test_pnpm_cache_default_is_path() {
225+
let pm = create_mock_package_manager(PackageManagerType::Pnpm);
226+
let result = pm.resolve_cache_command(&CacheCommandOptions::default());
227+
assert_eq!(result.bin_path, "pnpm");
228+
assert_eq!(result.args, vec!["store", "path"]);
229+
}
230+
231+
#[test]
232+
fn test_npm_cache_dir() {
233+
let pm = create_mock_package_manager(PackageManagerType::Npm);
234+
let result = pm.resolve_cache_command(&CacheCommandOptions {
235+
subcommand: Some(CacheSubcommand::Dir),
236+
..Default::default()
237+
});
238+
assert_eq!(result.bin_path, "npm");
239+
assert_eq!(result.args, vec!["cache", "dir"]);
240+
}
241+
242+
#[test]
243+
fn test_npm_cache_clean() {
244+
let pm = create_mock_package_manager(PackageManagerType::Npm);
245+
let result = pm.resolve_cache_command(&CacheCommandOptions {
246+
subcommand: Some(CacheSubcommand::Clean),
247+
..Default::default()
248+
});
249+
assert_eq!(result.bin_path, "npm");
250+
assert_eq!(result.args, vec!["cache", "clean"]);
251+
}
252+
253+
#[test]
254+
fn test_npm_cache_clean_with_force() {
255+
let pm = create_mock_package_manager(PackageManagerType::Npm);
256+
let result = pm.resolve_cache_command(&CacheCommandOptions {
257+
subcommand: Some(CacheSubcommand::Clean),
258+
force: true,
259+
..Default::default()
260+
});
261+
assert_eq!(result.bin_path, "npm");
262+
assert_eq!(result.args, vec!["cache", "clean", "--force"]);
263+
}
264+
265+
#[test]
266+
fn test_npm_cache_verify() {
267+
let pm = create_mock_package_manager(PackageManagerType::Npm);
268+
let result = pm.resolve_cache_command(&CacheCommandOptions {
269+
subcommand: Some(CacheSubcommand::Verify),
270+
..Default::default()
271+
});
272+
assert_eq!(result.bin_path, "npm");
273+
assert_eq!(result.args, vec!["cache", "verify"]);
274+
}
275+
276+
#[test]
277+
fn test_yarn_cache_dir() {
278+
let pm = create_mock_package_manager(PackageManagerType::Yarn);
279+
let result = pm.resolve_cache_command(&CacheCommandOptions {
280+
subcommand: Some(CacheSubcommand::Dir),
281+
..Default::default()
282+
});
283+
assert_eq!(result.bin_path, "yarn");
284+
assert_eq!(result.args, vec!["cache", "dir"]);
285+
}
286+
287+
#[test]
288+
fn test_yarn_cache_clean() {
289+
let pm = create_mock_package_manager(PackageManagerType::Yarn);
290+
let result = pm.resolve_cache_command(&CacheCommandOptions {
291+
subcommand: Some(CacheSubcommand::Clean),
292+
..Default::default()
293+
});
294+
assert_eq!(result.bin_path, "yarn");
295+
assert_eq!(result.args, vec!["cache", "clean"]);
296+
}
297+
298+
#[test]
299+
fn test_yarn1_cache_list() {
300+
let pm = create_mock_package_manager(PackageManagerType::Yarn);
301+
let result = pm.resolve_cache_command(&CacheCommandOptions {
302+
subcommand: Some(CacheSubcommand::List),
303+
..Default::default()
304+
});
305+
assert_eq!(result.bin_path, "yarn");
306+
assert_eq!(result.args, vec!["cache", "list"]);
307+
}
308+
}

0 commit comments

Comments
 (0)