use std::path::Path;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NpmPkgManager {
Npm,
Pnpm,
YarnClassic,
YarnBerryPnP,
Bun,
Unknown,
}
pub fn detect_npm_pkg_manager(project_root: &Path) -> NpmPkgManager {
if project_root.join(".pnp.cjs").is_file()
|| project_root.join(".pnp.loader.mjs").is_file()
{
return NpmPkgManager::YarnBerryPnP;
}
let node_modules = project_root.join("node_modules");
if (project_root.join("bun.lock").is_file()
|| project_root.join("bun.lockb").is_file())
&& node_modules.is_dir()
{
return NpmPkgManager::Bun;
}
if node_modules.join(".modules.yaml").is_file()
|| node_modules.join(".pnpm").is_dir()
{
return NpmPkgManager::Pnpm;
}
if project_root.join("yarn.lock").is_file() && node_modules.is_dir() {
return NpmPkgManager::YarnClassic;
}
if node_modules.is_dir() {
return NpmPkgManager::Npm;
}
NpmPkgManager::Unknown
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn unknown_for_empty_dir() {
let d = tempfile::tempdir().unwrap();
assert_eq!(detect_npm_pkg_manager(d.path()), NpmPkgManager::Unknown);
}
#[test]
fn npm_for_bare_node_modules() {
let d = tempfile::tempdir().unwrap();
std::fs::create_dir_all(d.path().join("node_modules")).unwrap();
assert_eq!(detect_npm_pkg_manager(d.path()), NpmPkgManager::Npm);
}
#[test]
fn pnpm_via_modules_yaml() {
let d = tempfile::tempdir().unwrap();
std::fs::create_dir_all(d.path().join("node_modules")).unwrap();
std::fs::write(d.path().join("node_modules/.modules.yaml"), "").unwrap();
assert_eq!(detect_npm_pkg_manager(d.path()), NpmPkgManager::Pnpm);
}
#[test]
fn pnpm_via_pnpm_dir() {
let d = tempfile::tempdir().unwrap();
std::fs::create_dir_all(d.path().join("node_modules/.pnpm")).unwrap();
assert_eq!(detect_npm_pkg_manager(d.path()), NpmPkgManager::Pnpm);
}
#[test]
fn yarn_classic_via_lockfile() {
let d = tempfile::tempdir().unwrap();
std::fs::create_dir_all(d.path().join("node_modules")).unwrap();
std::fs::write(d.path().join("yarn.lock"), "").unwrap();
assert_eq!(detect_npm_pkg_manager(d.path()), NpmPkgManager::YarnClassic);
}
#[test]
fn yarn_classic_requires_installed_node_modules() {
let d = tempfile::tempdir().unwrap();
std::fs::write(d.path().join("yarn.lock"), "").unwrap();
assert_eq!(detect_npm_pkg_manager(d.path()), NpmPkgManager::Unknown);
}
#[test]
fn yarn_berry_pnp_via_pnp_cjs() {
let d = tempfile::tempdir().unwrap();
std::fs::write(d.path().join(".pnp.cjs"), "").unwrap();
assert_eq!(
detect_npm_pkg_manager(d.path()),
NpmPkgManager::YarnBerryPnP
);
}
#[test]
fn yarn_berry_pnp_priority_over_pnpm() {
let d = tempfile::tempdir().unwrap();
std::fs::write(d.path().join(".pnp.cjs"), "").unwrap();
std::fs::create_dir_all(d.path().join("node_modules/.pnpm")).unwrap();
assert_eq!(
detect_npm_pkg_manager(d.path()),
NpmPkgManager::YarnBerryPnP
);
}
#[test]
fn bun_via_text_lockfile() {
let d = tempfile::tempdir().unwrap();
std::fs::create_dir_all(d.path().join("node_modules")).unwrap();
std::fs::write(d.path().join("bun.lock"), "").unwrap();
assert_eq!(detect_npm_pkg_manager(d.path()), NpmPkgManager::Bun);
}
#[test]
fn bun_via_binary_lockfile() {
let d = tempfile::tempdir().unwrap();
std::fs::create_dir_all(d.path().join("node_modules")).unwrap();
std::fs::write(d.path().join("bun.lockb"), b"").unwrap();
assert_eq!(detect_npm_pkg_manager(d.path()), NpmPkgManager::Bun);
}
#[test]
fn bun_requires_installed_node_modules() {
let d = tempfile::tempdir().unwrap();
std::fs::write(d.path().join("bun.lock"), "").unwrap();
assert_eq!(detect_npm_pkg_manager(d.path()), NpmPkgManager::Unknown);
}
#[test]
fn bun_priority_over_pnpm_when_both_markers_present() {
let d = tempfile::tempdir().unwrap();
std::fs::create_dir_all(d.path().join("node_modules/.pnpm")).unwrap();
std::fs::write(d.path().join("bun.lock"), "").unwrap();
assert_eq!(detect_npm_pkg_manager(d.path()), NpmPkgManager::Bun);
}
#[test]
fn yarn_berry_pnp_priority_over_bun() {
let d = tempfile::tempdir().unwrap();
std::fs::write(d.path().join(".pnp.cjs"), "").unwrap();
std::fs::write(d.path().join("bun.lock"), "").unwrap();
std::fs::create_dir_all(d.path().join("node_modules")).unwrap();
assert_eq!(
detect_npm_pkg_manager(d.path()),
NpmPkgManager::YarnBerryPnP
);
}
}