1pub mod finder;
2pub mod package;
3pub mod workspace;
4
5pub use finder::NodeProjectFinder;
6
7use std::path::Path;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum PackageManager {
12 Npm,
13 Yarn,
14 Pnpm,
15 Bun,
16}
17
18impl PackageManager {
19 pub fn publish_command(&self) -> &'static str {
21 match self {
22 PackageManager::Npm => "npm publish",
23 PackageManager::Yarn => "yarn npm publish",
24 PackageManager::Pnpm => "pnpm publish",
25 PackageManager::Bun => "bun publish",
26 }
27 }
28}
29
30pub fn detect_package_manager(dir: &Path) -> PackageManager {
33 if dir.join("bun.lockb").exists() || dir.join("bun.lock").exists() {
34 PackageManager::Bun
35 } else if dir.join("pnpm-lock.yaml").exists() {
36 PackageManager::Pnpm
37 } else if dir.join("yarn.lock").exists() {
38 PackageManager::Yarn
39 } else if dir.join("package-lock.json").exists() {
40 PackageManager::Npm
41 } else {
42 PackageManager::Npm
44 }
45}
46
47pub fn detect_package_manager_recursive(path: &Path) -> PackageManager {
49 let mut current = if path.is_file() {
50 path.parent()
51 } else {
52 Some(path)
53 };
54
55 while let Some(dir) = current {
56 let pm = detect_package_manager(dir);
57 if pm != PackageManager::Npm || dir.join("package-lock.json").exists() {
58 return pm;
59 }
60 current = dir.parent();
61 }
62
63 PackageManager::Npm
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69 use std::fs;
70 use tempfile::TempDir;
71
72 #[test]
73 fn test_detect_bun_lockb() {
74 let temp_dir = TempDir::new().unwrap();
75 fs::write(temp_dir.path().join("bun.lockb"), "").unwrap();
76 assert_eq!(detect_package_manager(temp_dir.path()), PackageManager::Bun);
77 }
78
79 #[test]
80 fn test_detect_bun_lock() {
81 let temp_dir = TempDir::new().unwrap();
82 fs::write(temp_dir.path().join("bun.lock"), "").unwrap();
83 assert_eq!(detect_package_manager(temp_dir.path()), PackageManager::Bun);
84 }
85
86 #[test]
87 fn test_detect_pnpm() {
88 let temp_dir = TempDir::new().unwrap();
89 fs::write(temp_dir.path().join("pnpm-lock.yaml"), "").unwrap();
90 assert_eq!(
91 detect_package_manager(temp_dir.path()),
92 PackageManager::Pnpm
93 );
94 }
95
96 #[test]
97 fn test_detect_yarn() {
98 let temp_dir = TempDir::new().unwrap();
99 fs::write(temp_dir.path().join("yarn.lock"), "").unwrap();
100 assert_eq!(
101 detect_package_manager(temp_dir.path()),
102 PackageManager::Yarn
103 );
104 }
105
106 #[test]
107 fn test_detect_npm() {
108 let temp_dir = TempDir::new().unwrap();
109 fs::write(temp_dir.path().join("package-lock.json"), "{}").unwrap();
110 assert_eq!(detect_package_manager(temp_dir.path()), PackageManager::Npm);
111 }
112
113 #[test]
114 fn test_detect_default_npm() {
115 let temp_dir = TempDir::new().unwrap();
116 assert_eq!(detect_package_manager(temp_dir.path()), PackageManager::Npm);
117 }
118
119 #[test]
120 fn test_bun_priority_over_others() {
121 let temp_dir = TempDir::new().unwrap();
122 fs::write(temp_dir.path().join("bun.lockb"), "").unwrap();
123 fs::write(temp_dir.path().join("pnpm-lock.yaml"), "").unwrap();
124 fs::write(temp_dir.path().join("yarn.lock"), "").unwrap();
125 assert_eq!(detect_package_manager(temp_dir.path()), PackageManager::Bun);
126 }
127
128 #[test]
129 fn test_pnpm_priority_over_yarn() {
130 let temp_dir = TempDir::new().unwrap();
131 fs::write(temp_dir.path().join("pnpm-lock.yaml"), "").unwrap();
132 fs::write(temp_dir.path().join("yarn.lock"), "").unwrap();
133 assert_eq!(
134 detect_package_manager(temp_dir.path()),
135 PackageManager::Pnpm
136 );
137 }
138
139 #[test]
140 fn test_publish_commands() {
141 assert_eq!(PackageManager::Npm.publish_command(), "npm publish");
142 assert_eq!(PackageManager::Yarn.publish_command(), "yarn npm publish");
143 assert_eq!(PackageManager::Pnpm.publish_command(), "pnpm publish");
144 assert_eq!(PackageManager::Bun.publish_command(), "bun publish");
145 }
146
147 #[test]
148 fn test_detect_recursive() {
149 let temp_dir = TempDir::new().unwrap();
150 let sub_dir = temp_dir.path().join("packages").join("core");
151 fs::create_dir_all(&sub_dir).unwrap();
152 fs::write(temp_dir.path().join("pnpm-lock.yaml"), "").unwrap();
153 fs::write(sub_dir.join("package.json"), "{}").unwrap();
154
155 assert_eq!(
156 detect_package_manager_recursive(&sub_dir.join("package.json")),
157 PackageManager::Pnpm
158 );
159 }
160}