增强node环境检查
This commit is contained in:
@@ -34,7 +34,10 @@ fn get_folder_id_from_url(window: &tauri::Window) -> Option<i32> {
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
let _ = fix_path_env::fix();
|
||||
if let Err(err) = fix_path_env::fix() {
|
||||
eprintln!("[PATH] fix_path_env failed: {err}");
|
||||
}
|
||||
process::ensure_node_in_path();
|
||||
|
||||
tauri::Builder::default()
|
||||
.plugin(tauri_plugin_window_state::Builder::new().build())
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
|
||||
#[cfg(windows)]
|
||||
@@ -80,3 +81,105 @@ where
|
||||
configure_tokio_command(&mut command);
|
||||
command
|
||||
}
|
||||
|
||||
/// If `node` is not already in PATH, detect common Node.js version manager
|
||||
/// installations (nvm, fnm, volta) and prepend the best matching bin directory
|
||||
/// to the process PATH so that **all** downstream code (`which`, `Command`,
|
||||
/// child processes) can find node/npm/npx without any special handling.
|
||||
///
|
||||
/// Call once at startup, after `fix_path_env::fix()`.
|
||||
pub fn ensure_node_in_path() {
|
||||
// Already reachable — nothing to do.
|
||||
if which::which("node").is_ok() {
|
||||
return;
|
||||
}
|
||||
|
||||
let home = match dirs::home_dir() {
|
||||
Some(h) => h,
|
||||
None => return,
|
||||
};
|
||||
|
||||
if let Some(bin_dir) = find_node_bin_dir(&home) {
|
||||
prepend_to_path(&bin_dir);
|
||||
eprintln!("[PATH] node not in PATH, prepended {}", bin_dir.display());
|
||||
}
|
||||
}
|
||||
|
||||
/// Search common Node.js version manager directories for a `node` binary and
|
||||
/// return the containing bin directory.
|
||||
fn find_node_bin_dir(home: &std::path::Path) -> Option<PathBuf> {
|
||||
let mut candidates: Vec<PathBuf> = Vec::new();
|
||||
|
||||
// ── nvm ──────────────────────────────────────────────────────────────
|
||||
let nvm_dir = std::env::var("NVM_DIR")
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|_| home.join(".nvm"));
|
||||
if nvm_dir.is_dir() {
|
||||
let versions_dir = nvm_dir.join("versions").join("node");
|
||||
|
||||
// Prefer the version pointed to by the "default" alias.
|
||||
let default_alias = nvm_dir.join("alias").join("default");
|
||||
if let Ok(alias) = std::fs::read_to_string(&default_alias) {
|
||||
let alias = alias.trim().to_string();
|
||||
if let Ok(entries) = std::fs::read_dir(&versions_dir) {
|
||||
for entry in entries.flatten() {
|
||||
let name = entry.file_name().to_string_lossy().to_string();
|
||||
let stripped = name.trim_start_matches('v');
|
||||
if stripped.starts_with(&alias) || name.starts_with(&alias) {
|
||||
candidates.push(entry.path().join("bin"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back: all installed versions, newest first.
|
||||
if let Ok(mut entries) = std::fs::read_dir(&versions_dir)
|
||||
.map(|rd| rd.flatten().map(|e| e.path()).collect::<Vec<_>>())
|
||||
{
|
||||
entries.sort();
|
||||
entries.reverse();
|
||||
for entry in entries {
|
||||
candidates.push(entry.join("bin"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── fnm ──────────────────────────────────────────────────────────────
|
||||
let fnm_dir = std::env::var("FNM_DIR")
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|_| home.join(".local").join("share").join("fnm"));
|
||||
let fnm_versions = fnm_dir.join("node-versions");
|
||||
if fnm_versions.is_dir() {
|
||||
if let Ok(mut entries) = std::fs::read_dir(&fnm_versions)
|
||||
.map(|rd| rd.flatten().map(|e| e.path()).collect::<Vec<_>>())
|
||||
{
|
||||
entries.sort();
|
||||
entries.reverse();
|
||||
for entry in entries {
|
||||
candidates.push(entry.join("installation").join("bin"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── volta ────────────────────────────────────────────────────────────
|
||||
let volta_home = std::env::var("VOLTA_HOME")
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|_| home.join(".volta"));
|
||||
let volta_bin = volta_home.join("bin");
|
||||
if volta_bin.is_dir() {
|
||||
candidates.push(volta_bin);
|
||||
}
|
||||
|
||||
// Return the first candidate that actually contains a `node` binary.
|
||||
candidates.into_iter().find(|dir| dir.join("node").is_file())
|
||||
}
|
||||
|
||||
/// Prepend a directory to the process `PATH` environment variable.
|
||||
fn prepend_to_path(dir: &std::path::Path) {
|
||||
let sep = if cfg!(windows) { ";" } else { ":" };
|
||||
let current = std::env::var_os("PATH").unwrap_or_default();
|
||||
let mut new_path = OsString::from(dir);
|
||||
new_path.push(sep);
|
||||
new_path.push(current);
|
||||
std::env::set_var("PATH", new_path);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user