refactor: better node binary traversal

This commit is contained in:
zhom
2025-08-10 02:25:59 +04:00
parent 05746cf9b0
commit c0589d1278
2 changed files with 62 additions and 18 deletions
+13 -13
View File
@@ -103,20 +103,20 @@ pub async fn bundle_project(
let node_executable = node_downloader
.ensure_node_binary_with_progress(Some(&pb_prepare))
.await?;
let node_root_buf = if Platform::current().is_windows() {
// On Windows, node.exe lives directly under the platform directory
node_executable
let node_root_buf = {
// The extraction target_dir is what we passed to NodeDownloader::download_and_extract_node
// which is cache_dir/node/<version>/<platform> on all platforms. We want to bundle that
// entire directory under "node/" so the runtime can find binaries consistently.
// Derive the root by walking up from the executable until we find the directory named
// the platform triplet (win32-*, darwin-*, linux-*).
let mut cur = node_executable
.parent()
.expect("node executable must have a parent")
.to_path_buf()
} else {
// On Unix, node is under <platform>/bin/node
node_executable
.parent()
.expect("node executable must have a parent")
.parent()
.unwrap_or_else(|| panic!("Unexpected node layout for {}", node_executable.display()))
.to_path_buf()
.expect("node executable must have a parent");
// If on Unix and we are at .../<platform>/bin, step up to <platform>
if cur.file_name().is_some_and(|n| n == "bin") {
cur = cur.parent().unwrap_or(cur);
}
cur.to_path_buf()
};
let node_root: &Path = &node_root_buf;
pb_prepare.finish_and_clear();
+49 -5
View File
@@ -94,7 +94,7 @@ impl NodeDownloader {
.join(&self.node_version)
.join(self.platform.to_string());
let node_executable = node_dir.join(self.platform.node_executable_path());
let mut node_executable = node_dir.join(self.platform.node_executable_path());
if node_executable.exists() {
// Update in-memory cache
@@ -118,11 +118,55 @@ impl NodeDownloader {
// Download and extract Node.js
self.download_and_extract_node(&node_dir, progress).await?;
// Validate presence; if not in expected location, search recursively as a fallback
if !node_executable.exists() {
anyhow::bail!(
"Node executable not found after extraction: {}",
node_executable.display()
);
// Platform-specific name to search for
let expected_name = self.platform.node_executable_path();
let expected_name_str = expected_name
.file_name()
.and_then(|s| s.to_str())
.unwrap_or("node.exe");
let mut found_path: Option<PathBuf> = None;
if std::fs::read_dir(&node_dir).is_ok() {
for e in walkdir::WalkDir::new(&node_dir)
.follow_links(true)
.into_iter()
.flatten()
{
let p = e.path();
if p.is_file() {
if let Some(name) = p.file_name().and_then(|n| n.to_str()) {
// On Windows search for node.exe case-insensitively, else match exact
if (self.platform.is_windows()
&& name.eq_ignore_ascii_case(expected_name_str))
|| (!self.platform.is_windows() && name == expected_name_str)
{
found_path = Some(p.to_path_buf());
break;
}
}
}
}
}
if let Some(found) = found_path {
node_executable = found;
} else {
// Provide additional diagnostics
let dir_list: Vec<String> = std::fs::read_dir(&node_dir)
.map(|it| {
it.filter_map(|e| e.ok())
.map(|entry| entry.file_name().to_string_lossy().to_string())
.collect()
})
.unwrap_or_default();
anyhow::bail!(
"Node executable not found after extraction: {}\nDirectory: {}\nEntries: {:?}",
node_executable.display(),
node_dir.display(),
dir_list
);
}
}
// Make executable on Unix systems