1mod config;
6
7pub use config::{
8 create_install_config, get_install_methods, get_manual_instructions, supports_auto_install,
9 Config, PnpmUrlBuilder,
10};
11
12use anyhow::Result;
13use std::path::Path;
14use vx_plugin::{Ecosystem, PackageSpec, VxPackageManager, VxPlugin, VxTool};
15
16#[derive(Default)]
18pub struct PnpmPackageManager;
19
20#[async_trait::async_trait]
21impl VxPackageManager for PnpmPackageManager {
22 fn name(&self) -> &str {
23 "pnpm"
24 }
25
26 fn ecosystem(&self) -> Ecosystem {
27 Ecosystem::Node
28 }
29
30 fn description(&self) -> &str {
31 "Fast, disk space efficient package manager"
32 }
33
34 fn is_preferred_for_project(&self, project_path: &Path) -> bool {
36 project_path.join("pnpm-lock.yaml").exists()
37 }
38
39 fn get_config_files(&self) -> Vec<&str> {
40 vec!["package.json", "pnpm-lock.yaml", "pnpm-workspace.yaml"]
41 }
42
43 async fn install_packages(&self, packages: &[PackageSpec], project_path: &Path) -> Result<()> {
44 if packages.is_empty() {
45 self.run_command(&["install"], &[], project_path).await
46 } else {
47 let package_names: Vec<String> = packages
48 .iter()
49 .map(|pkg| {
50 if let Some(version) = &pkg.version {
51 format!("{}@{}", pkg.name, version)
52 } else {
53 pkg.name.clone()
54 }
55 })
56 .collect();
57
58 let command = if packages.iter().any(|pkg| pkg.dev_dependency) {
59 vec!["add", "--save-dev"]
60 } else {
61 vec!["add"]
62 };
63
64 self.run_command(&command, &package_names, project_path)
65 .await
66 }
67 }
68
69 async fn remove_packages(&self, packages: &[String], project_path: &Path) -> Result<()> {
70 self.run_command(&["remove"], packages, project_path).await
71 }
72
73 async fn update_packages(&self, packages: &[String], project_path: &Path) -> Result<()> {
74 if packages.is_empty() {
75 self.run_command(&["update"], &[], project_path).await
76 } else {
77 self.run_command(&["update"], packages, project_path).await
78 }
79 }
80}
81
82#[derive(Default)]
84pub struct PnpmPlugin;
85
86#[async_trait::async_trait]
87impl VxPlugin for PnpmPlugin {
88 fn name(&self) -> &str {
89 "pnpm"
90 }
91
92 fn description(&self) -> &str {
93 "PNPM package manager support for vx"
94 }
95
96 fn version(&self) -> &str {
97 "1.0.0"
98 }
99
100 fn tools(&self) -> Vec<Box<dyn VxTool>> {
101 vec![]
102 }
103
104 fn package_managers(&self) -> Vec<Box<dyn VxPackageManager>> {
105 vec![Box::new(PnpmPackageManager)]
106 }
107
108 fn supports_tool(&self, _tool_name: &str) -> bool {
109 false
110 }
111}
112
113pub fn create_pnpm_plugin() -> Box<dyn VxPlugin> {
115 Box::new(PnpmPlugin)
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121
122 #[test]
123 fn test_pnpm_package_manager() {
124 let pm = PnpmPackageManager;
125 assert_eq!(pm.name(), "pnpm");
126 assert_eq!(pm.ecosystem(), Ecosystem::Node);
127 assert_eq!(
128 pm.description(),
129 "Fast, disk space efficient package manager"
130 );
131 }
132
133 #[test]
134 fn test_pnpm_plugin() {
135 let plugin = PnpmPlugin;
136 assert_eq!(plugin.name(), "pnpm");
137 assert_eq!(plugin.version(), "1.0.0");
138 }
139}