diff --git a/build.rs b/build.rs index 1bd20216c6..f4e1502f50 100644 --- a/build.rs +++ b/build.rs @@ -308,6 +308,7 @@ pub struct Settings {"# .to_string(), ]; + println!("cargo:rerun-if-changed=settings.toml"); let settings_toml = fs::read_to_string("settings.toml").expect("Failed to read settings.toml"); let settings: toml::Table = toml::de::from_str(&settings_toml).expect("Failed to parse settings.toml"); diff --git a/schema/mise.json b/schema/mise.json index 2c316dfcdf..ec5445289a 100644 --- a/schema/mise.json +++ b/schema/mise.json @@ -919,7 +919,8 @@ "package_manager": { "default": "npm", "description": "Package manager to use for installing npm packages.", - "type": "string" + "type": "string", + "enum": ["npm", "bun", "pnpm"] } } }, diff --git a/settings.toml b/settings.toml index e648319b8b..b5b245a4a7 100644 --- a/settings.toml +++ b/settings.toml @@ -1030,7 +1030,9 @@ Can be one of: - `bun` - `pnpm` """ +enum = ["npm", "bun", "pnpm"] env = "MISE_NPM_PACKAGE_MANAGER" +rust_type = "NpmPackageManager" type = "String" [offline] diff --git a/src/backend/npm.rs b/src/backend/npm.rs index 6af1e01c97..2dab5eea10 100644 --- a/src/backend/npm.rs +++ b/src/backend/npm.rs @@ -5,6 +5,7 @@ use crate::backend::backend_type::BackendType; use crate::cache::{CacheManager, CacheManagerBuilder}; use crate::cli::args::BackendArg; use crate::cmd::CmdLineRunner; +use crate::config::settings::NpmPackageManager; use crate::config::{Config, Settings}; use crate::install_context::InstallContext; use crate::timeout; @@ -39,22 +40,22 @@ impl Backend for NPMBackend { // package manager for installation. We avoid listing all package managers to // prevent incorrect dependency edges. let settings = Settings::get(); - let package_manager = settings.npm.package_manager.as_str(); + let package_manager = settings.npm.package_manager; let tool_name = self.tool_name(); // Avoid circular dependency when installing npm itself // But we still need the configured package manager for installation if tool_name == "npm" { return match package_manager { - "bun" => Ok(vec!["node", "bun"]), - "pnpm" => Ok(vec!["node", "pnpm"]), - _ => Ok(vec!["node"]), + NpmPackageManager::Bun => Ok(vec!["node", "bun"]), + NpmPackageManager::Pnpm => Ok(vec!["node", "pnpm"]), + NpmPackageManager::Npm => Ok(vec!["node"]), }; } // Avoid circular dependency when installing the configured package manager // e.g., npm:bun with bun configured, or npm:pnpm with pnpm configured - if tool_name == package_manager { + if tool_name == package_manager.to_string() { // Still need npm for version queries return Ok(vec!["node", "npm"]); } @@ -62,10 +63,9 @@ impl Backend for NPMBackend { // For regular packages: need npm (for version queries) + configured package manager let mut deps = vec!["node", "npm"]; match package_manager { - "bun" => deps.push("bun"), - "pnpm" => deps.push("pnpm"), - // npm is already in deps - _ => {} + NpmPackageManager::Bun => deps.push("bun"), + NpmPackageManager::Pnpm => deps.push("pnpm"), + NpmPackageManager::Npm => {} // npm is already in deps } Ok(deps) } @@ -149,8 +149,8 @@ impl Backend for NPMBackend { async fn install_version_(&self, ctx: &InstallContext, tv: ToolVersion) -> Result { self.check_install_deps(&ctx.config).await; - match Settings::get().npm.package_manager.as_str() { - "bun" => { + match Settings::get().npm.package_manager { + NpmPackageManager::Bun => { CmdLineRunner::new("bun") .arg("install") .arg(format!("{}@{}", self.tool_name(), tv.version)) @@ -174,7 +174,7 @@ impl Backend for NPMBackend { .current_dir(tv.install_path()) .execute()?; } - "pnpm" => { + NpmPackageManager::Pnpm => { let bin_dir = tv.install_path().join("bin"); crate::file::create_dir_all(&bin_dir)?; CmdLineRunner::new("pnpm") @@ -228,7 +228,7 @@ impl Backend for NPMBackend { _config: &Arc, tv: &crate::toolset::ToolVersion, ) -> eyre::Result> { - if Settings::get().npm.package_manager == "npm" { + if Settings::get().npm.package_manager == NpmPackageManager::Npm { Ok(vec![tv.install_path()]) } else { Ok(vec![tv.install_path().join("bin")]) @@ -264,8 +264,8 @@ impl NPMBackend { /// Check dependencies for package installation (npm or bun based on settings) async fn check_install_deps(&self, config: &Arc) { - match Settings::get().npm.package_manager.as_str() { - "bun" => { + match Settings::get().npm.package_manager { + NpmPackageManager::Bun => { self.warn_if_dependency_missing( config, "bun", @@ -276,7 +276,7 @@ impl NPMBackend { ) .await } - "pnpm" => { + NpmPackageManager::Pnpm => { self.warn_if_dependency_missing( config, "pnpm", diff --git a/src/config/settings.rs b/src/config/settings.rs index 2612b4db0a..88e8894737 100644 --- a/src/config/settings.rs +++ b/src/config/settings.rs @@ -73,6 +73,27 @@ pub enum SettingsStatusMissingTools { Always, } +#[derive( + Debug, + Clone, + Copy, + Serialize, + Deserialize, + Default, + strum::EnumString, + strum::Display, + PartialEq, + Eq, +)] +#[serde(rename_all = "snake_case")] +#[strum(serialize_all = "snake_case")] +pub enum NpmPackageManager { + #[default] + Npm, + Bun, + Pnpm, +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub enum PythonUvVenvAuto { #[default] @@ -359,7 +380,7 @@ impl Settings { self.python.venv_auto_create = python_venv_auto_create; } if self.npm.bun { - self.npm.package_manager = "bun".to_string(); + self.npm.package_manager = NpmPackageManager::Bun; } }