diff --git a/crates/node-maintainer/Cargo.toml b/crates/node-maintainer/Cargo.toml index 840ee6a9..3e885d40 100644 --- a/crates/node-maintainer/Cargo.toml +++ b/crates/node-maintainer/Cargo.toml @@ -13,8 +13,6 @@ readme = "README.md" nassun = { version = "=0.3.15", path = "../nassun" } oro-common = { version = "=0.3.15", path = "../oro-common" } oro-package-spec = { version = "=0.3.15", path = "../oro-package-spec" } -oro-script = { version = "=0.3.15", path = "../oro-script" } -oro-shim-bin = { version = "=0.3.15", path = "../oro-shim-bin" } async-std = { workspace = true } colored = { workspace = true } @@ -32,6 +30,9 @@ unicase = "2.6.0" url = { workspace = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] +oro-script = { version = "=0.3.15", path = "../oro-script" } +oro-shim-bin = { version = "=0.3.15", path = "../oro-shim-bin" } + reflink = { workspace = true } indicatif = { workspace = true } pathdiff = { workspace = true } diff --git a/crates/node-maintainer/src/graph.rs b/crates/node-maintainer/src/graph.rs index 395ac138..b36a2884 100644 --- a/crates/node-maintainer/src/graph.rs +++ b/crates/node-maintainer/src/graph.rs @@ -154,17 +154,31 @@ impl Graph { pub(crate) fn node_at_path(&self, path: &Path) -> Option<&Node> { let mut current = Some(self.root); let mut in_nm = true; + let mut scope = None; let slash = OsStr::new("/"); let backslash = OsStr::new("\\"); let nm = UniCase::new("node_modules".to_owned()); for raw_segment in path { - let segment = UniCase::new(raw_segment.to_string_lossy().into()); - if segment == nm || slash == raw_segment || backslash == raw_segment { + let str_segment = raw_segment.to_string_lossy().to_string(); + let segment = UniCase::new(str_segment.clone()); + if (segment == nm && scope.is_none()) + || slash == raw_segment + || backslash == raw_segment + { in_nm = true; continue; } else if let Some(curr_idx) = current { if !in_nm { break; + } else if segment.starts_with('@') { + scope = Some(segment.to_string()); + } else if let Some(curr_scope) = scope.as_deref() { + let scoped_seg = UniCase::new(format!("{curr_scope}/{segment}")); + if let Some(child) = self.inner[curr_idx].children.get(&scoped_seg) { + current = Some(*child); + } + in_nm = false; + scope = None; } else if let Some(child) = self.inner[curr_idx].children.get(&segment) { current = Some(*child); in_nm = false; diff --git a/crates/node-maintainer/src/maintainer.rs b/crates/node-maintainer/src/maintainer.rs index 27957d37..c367aef8 100644 --- a/crates/node-maintainer/src/maintainer.rs +++ b/crates/node-maintainer/src/maintainer.rs @@ -1,6 +1,8 @@ use std::cmp::Ordering; use std::collections::{BTreeMap, HashSet, VecDeque}; +#[cfg(not(target_arch = "wasm32"))] use std::ffi::OsStr; +#[cfg(not(target_arch = "wasm32"))] use std::io::{BufRead, BufReader}; use std::path::{Path, PathBuf}; #[cfg(not(target_arch = "wasm32"))] @@ -18,7 +20,10 @@ use futures::{StreamExt, TryFutureExt}; use nassun::client::{Nassun, NassunOpts}; use nassun::package::Package; use nassun::PackageSpec; -use oro_common::{BuildManifest, CorgiManifest, CorgiVersionMetadata}; +#[cfg(not(target_arch = "wasm32"))] +use oro_common::BuildManifest; +use oro_common::{CorgiManifest, CorgiVersionMetadata}; +#[cfg(not(target_arch = "wasm32"))] use oro_script::OroScript; use petgraph::stable_graph::NodeIndex; use petgraph::visit::EdgeRef; @@ -332,7 +337,7 @@ struct NodeDependency { pub struct NodeMaintainer { nassun: Nassun, - graph: Graph, + pub(crate) graph: Graph, concurrency: usize, #[allow(dead_code)] cache: Option, @@ -1328,6 +1333,7 @@ fn supports_reflink(src_dir: &Path, dest_dir: &Path) -> bool { supports_reflink } +#[cfg(not(target_arch = "wasm32"))] fn link_bin(from: &Path, to: &Path) -> Result<(), NodeMaintainerError> { #[cfg(windows)] oro_shim_bin::shim_bin(from, to)?; diff --git a/crates/node-maintainer/src/wasm.rs b/crates/node-maintainer/src/wasm.rs index 37d55a86..26a5927d 100644 --- a/crates/node-maintainer/src/wasm.rs +++ b/crates/node-maintainer/src/wasm.rs @@ -1,10 +1,13 @@ use std::{collections::HashMap, path::Path}; +use futures::{StreamExt, TryStreamExt}; +use js_sys::Promise; use miette::Diagnostic; use nassun::Package; use serde::Deserialize; use tsify::Tsify; use wasm_bindgen::prelude::*; +use wasm_bindgen_futures::JsFuture; use crate::error::NodeMaintainerError; @@ -160,6 +163,42 @@ impl NodeMaintainer { .package_at_path(Path::new(path)) .map(Package::from_core_package) } + + /// Concurrently over all packages in the tree, calling `f` on each. + #[wasm_bindgen(js_name = "forEachPackage")] + pub async fn for_each_package(&self, f: &js_sys::Function) -> std::result::Result<(), JsValue> { + futures::stream::iter(self.inner.graph.inner.node_indices()) + .map(Ok) + .try_for_each_concurrent(10, move |idx| async move { + if idx == self.inner.graph.root { + return Ok(()); + } + + let node = &self.inner.graph.inner[idx]; + let pkg = &node.package; + let path = self + .inner + .graph + .node_path(idx) + .iter() + .map(|x| x.to_string()) + .collect::>() + .join("/node_modules/"); + let promise: Option = f + .call2( + &JsValue::NULL, + &Package::from_core_package(pkg.clone()).into(), + &(&path).into(), + )? + .dyn_into() + .ok(); + if let Some(promise) = promise { + JsFuture::from(promise).await?; + } + Ok::<_, JsValue>(()) + }) + .await + } } /// Resolves a dependency tree using `spec` as the root package.