From fa1438ef264cb1f99a4b821483ab49a9e1e5b993 Mon Sep 17 00:00:00 2001 From: devKagan Date: Wed, 13 May 2026 17:33:47 +0900 Subject: [PATCH] fix(ccusage): resolve nvm node bin path from alias/default --- src-tauri/src/plugin_engine/host_api.rs | 68 +++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/src-tauri/src/plugin_engine/host_api.rs b/src-tauri/src/plugin_engine/host_api.rs index a39ac09d..78a8129d 100644 --- a/src-tauri/src/plugin_engine/host_api.rs +++ b/src-tauri/src/plugin_engine/host_api.rs @@ -1666,12 +1666,30 @@ fn ccusage_runner_candidates(kind: CcusageRunnerKind) -> Vec { unique } +fn nvm_default_bin_path(home: &Path) -> Option { + let alias_path = home.join(".nvm/alias/default"); + let version = std::fs::read_to_string(&alias_path).ok()?; + let version = version.trim(); + if version.is_empty() { + return None; + } + let version = if version.starts_with('v') { + version.to_string() + } else { + format!("v{version}") + }; + Some(home.join(".nvm/versions/node").join(version).join("bin")) +} + fn ccusage_path_entries_with(home: Option<&Path>, existing_path: Option<&OsStr>) -> Vec { let mut entries: Vec = Vec::new(); if let Some(home) = home { entries.push(home.join(".bun/bin")); entries.push(home.join(".nvm/current/bin")); + if let Some(nvm_bin) = nvm_default_bin_path(home) { + entries.push(nvm_bin); + } entries.push(home.join(".local/bin")); } @@ -3572,6 +3590,56 @@ mod tests { ); } + #[test] + fn nvm_default_bin_path_resolves_version_with_v_prefix() { + let home = std::env::temp_dir().join("openusage-test-nvm-v-prefix"); + let alias_dir = home.join(".nvm/alias"); + std::fs::create_dir_all(&alias_dir).expect("create alias dir"); + std::fs::write(alias_dir.join("default"), "v22.16.0").expect("write alias"); + let result = nvm_default_bin_path(&home); + let _ = std::fs::remove_dir_all(&home); + assert_eq!( + result, + Some(home.join(".nvm/versions/node/v22.16.0/bin")) + ); + } + + #[test] + fn nvm_default_bin_path_resolves_version_without_v_prefix() { + let home = std::env::temp_dir().join("openusage-test-nvm-no-v-prefix"); + let alias_dir = home.join(".nvm/alias"); + std::fs::create_dir_all(&alias_dir).expect("create alias dir"); + std::fs::write(alias_dir.join("default"), "22.16.0").expect("write alias"); + let result = nvm_default_bin_path(&home); + let _ = std::fs::remove_dir_all(&home); + assert_eq!( + result, + Some(home.join(".nvm/versions/node/v22.16.0/bin")) + ); + } + + #[test] + fn nvm_default_bin_path_returns_none_when_alias_missing() { + let home = std::env::temp_dir().join("openusage-test-nvm-no-alias"); + let _ = std::fs::remove_dir_all(&home); + let result = nvm_default_bin_path(&home); + assert_eq!(result, None); + } + + #[test] + fn ccusage_path_entries_with_includes_nvm_default_version() { + let home = std::env::temp_dir().join("openusage-test-nvm-entries"); + let alias_dir = home.join(".nvm/alias"); + std::fs::create_dir_all(&alias_dir).expect("create alias dir"); + std::fs::write(alias_dir.join("default"), "22.16.0").expect("write alias"); + let entries = ccusage_path_entries_with(Some(&home), None); + let _ = std::fs::remove_dir_all(&home); + assert!( + entries.contains(&home.join(".nvm/versions/node/v22.16.0/bin")), + "expected nvm default version bin in entries" + ); + } + #[test] fn configure_ccusage_command_sets_path_override() { let mut command = std::process::Command::new("echo");