workspace_node_tools/
manager.rs

1//! # Package Manager
2//!
3//! This package ables to detect which package manager is being used in the monorepo.
4#![allow(clippy::all)]
5use std::{
6    collections::HashMap, fmt::Display, fmt::Formatter, fmt::Result as FmtResult, path::Path,
7};
8
9#[cfg(feature = "napi")]
10#[napi(string_enum)]
11#[derive(Debug, PartialEq)]
12pub enum PackageManager {
13    Npm,
14    Yarn,
15    Pnpm,
16    Bun,
17}
18
19#[cfg(not(feature = "napi"))]
20#[derive(Debug, Clone, Copy, PartialEq)]
21/// Package manager used in the monorepo.
22pub enum PackageManager {
23    Npm,
24    Yarn,
25    Pnpm,
26    Bun,
27}
28
29impl Display for PackageManager {
30    fn fmt(&self, f: &mut Formatter) -> FmtResult {
31        let package_manager = match self {
32            PackageManager::Npm => "npm".to_string(),
33            PackageManager::Yarn => "yarn".to_string(),
34            PackageManager::Pnpm => "pnpm".to_string(),
35            PackageManager::Bun => "bun".to_string(),
36        };
37
38        write!(f, "{}", package_manager)
39    }
40}
41
42/// Detects which package manager is available in the workspace.
43pub fn detect_package_manager(path: &Path) -> Option<PackageManager> {
44    let package_manager_files = HashMap::from([
45        ("package-lock.json", PackageManager::Npm),
46        ("npm-shrinkwrap.json", PackageManager::Npm),
47        ("yarn.lock", PackageManager::Yarn),
48        ("pnpm-lock.yaml", PackageManager::Pnpm),
49        ("bun.lockb", PackageManager::Bun),
50    ]);
51
52    for (file, package_manager) in package_manager_files.iter() {
53        let lock_file = path.join(file);
54
55        if lock_file.exists() {
56            return Some(*package_manager);
57        }
58    }
59
60    if let Some(parent) = path.parent() {
61        return detect_package_manager(&parent);
62    }
63
64    None
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70    use crate::{paths::get_project_root_path, utils::create_test_monorepo};
71    use std::{fs::remove_dir_all, path::PathBuf};
72
73    #[test]
74    fn package_manager_for_npm_lock() -> Result<(), std::io::Error> {
75        let ref monorepo_dir = create_test_monorepo(&PackageManager::Npm)?;
76        let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf()));
77
78        let package_manager =
79            detect_package_manager(&PathBuf::from(project_root.unwrap()).as_path());
80
81        assert_eq!(package_manager, Some(PackageManager::Npm));
82        remove_dir_all(&monorepo_dir)?;
83        Ok(())
84    }
85
86    #[test]
87    fn package_manager_for_yarn_lock() -> Result<(), std::io::Error> {
88        let ref monorepo_dir = create_test_monorepo(&PackageManager::Yarn)?;
89        let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf()));
90
91        let package_manager =
92            detect_package_manager(&PathBuf::from(project_root.unwrap()).as_path());
93
94        assert_eq!(package_manager, Some(PackageManager::Yarn));
95        remove_dir_all(&monorepo_dir)?;
96        Ok(())
97    }
98
99    #[test]
100    fn package_manager_for_pnpm_lock() -> Result<(), std::io::Error> {
101        let ref monorepo_dir = create_test_monorepo(&PackageManager::Pnpm)?;
102        let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf()));
103
104        let package_manager =
105            detect_package_manager(&PathBuf::from(project_root.unwrap()).as_path());
106
107        assert_eq!(package_manager, Some(PackageManager::Pnpm));
108        remove_dir_all(&monorepo_dir)?;
109        Ok(())
110    }
111
112    #[test]
113    fn package_manager_for_bun_lock() -> Result<(), std::io::Error> {
114        let ref monorepo_dir = create_test_monorepo(&PackageManager::Bun)?;
115        let project_root = get_project_root_path(Some(monorepo_dir.to_path_buf()));
116
117        let package_manager =
118            detect_package_manager(&PathBuf::from(project_root.unwrap()).as_path());
119
120        assert_eq!(package_manager, Some(PackageManager::Bun));
121        remove_dir_all(&monorepo_dir)?;
122        Ok(())
123    }
124
125    #[test]
126    fn package_manager_not_present() {
127        let path = std::env::current_dir().expect("Current user home directory");
128        let package_manager = detect_package_manager(&path);
129
130        assert_eq!(package_manager, None);
131    }
132
133    #[test]
134    #[should_panic]
135    fn package_manager_empty_display_should_panic() {
136        let path = std::env::current_dir().expect("Current user home directory");
137        let package_manager = detect_package_manager(&path);
138
139        assert_eq!(package_manager.unwrap().to_string(), String::from(""));
140    }
141}