use std::path::Path;
use std::slice;
use super::npm::Globals;
use crate::manager::{Installed, Manager, Package, Step, capture};
pub struct Pnpm;
impl Manager for Pnpm {
fn id(&self) -> &'static str {
"pnpm"
}
fn name(&self) -> &'static str {
"pnpm (global)"
}
fn binary(&self) -> &'static str {
"pnpm"
}
fn outdated(&self, bin: &Path) -> Result<Vec<Package>, String> {
let json = capture(bin, ["outdated", "--global", "--format", "json"])?.expect(&[0, 1])?;
super::npm::parse_outdated(&json)
}
fn upgrade_steps(&self, _bin: &Path, packages: &[Package]) -> Vec<Step> {
let (own, others): (Vec<Package>, Vec<Package>) =
packages.iter().cloned().partition(|p| p.name == "pnpm");
let mut steps = Vec::new();
if !others.is_empty() {
steps.push(super::npm::install("add", &others));
}
for p in own {
steps.push(Step::new(["self-update", &p.latest], slice::from_ref(&p)));
}
steps
}
fn incompatible(&self, bin: &Path, packages: &[Package]) -> Vec<(Package, String)> {
super::engines::incompatible(bin, packages)
}
fn installed(&self, bin: &Path) -> Result<Vec<Installed>, String> {
let json = capture(bin, ["ls", "--global", "--depth=0", "--json"])?.expect(&[0])?;
let roots: Vec<Globals> =
serde_json::from_str(&json).map_err(|e| format!("unexpected pnpm output: {e}"))?;
Ok(roots.into_iter().flat_map(Globals::packages).collect())
}
fn install_steps(&self, _bin: &Path, packages: &[Installed]) -> Vec<Step> {
Step::install_all(&["add", "--global"], packages)
}
fn config_files(&self) -> &'static [&'static str] {
&[".config/pnpm/rc", "Library/Preferences/pnpm/rc"]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn updates_itself_with_self_update() {
let package = |name: &str, latest: &str| Package {
name: name.into(),
current: "1.0.0".into(),
latest: latest.into(),
};
let steps = Pnpm.upgrade_steps(
Path::new("pnpm"),
&[package("pnpm", "12.15.0"), package("eslint", "10.13.0")],
);
let args: Vec<_> = steps.iter().map(|s| s.commands[0].join(" ")).collect();
assert_eq!(args, ["add --global eslint@10.13.0", "self-update 12.15.0"]);
}
}