Skip to main content

vx_tool_pnpm/
lib.rs

1//! PNPM package manager support for vx
2//!
3//! This provides PNPM package manager integration for the vx tool.
4
5mod 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/// PNPM package manager implementation
17#[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    /// Detect if this is a pnpm project by looking for pnpm-lock.yaml
35    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/// PNPM plugin
83#[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
113/// Factory function to create the plugin
114pub 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}