增强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)]
|
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||||
pub fn run() {
|
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()
|
tauri::Builder::default()
|
||||||
.plugin(tauri_plugin_window_state::Builder::new().build())
|
.plugin(tauri_plugin_window_state::Builder::new().build())
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use std::ffi::{OsStr, OsString};
|
use std::ffi::{OsStr, OsString};
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
@@ -80,3 +81,105 @@ where
|
|||||||
configure_tokio_command(&mut command);
|
configure_tokio_command(&mut command);
|
||||||
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