Skip to content

Commit

Permalink
feat(wasm): get nassun and node-maintainer working well in wasm (#131)
Browse files Browse the repository at this point in the history
  • Loading branch information
zkat committed Mar 10, 2023
1 parent e11ae10 commit 16ad5ba
Show file tree
Hide file tree
Showing 22 changed files with 588 additions and 223 deletions.
54 changes: 54 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Expand Up @@ -51,7 +51,6 @@ members = [
async-compression = "0.3.5"
async-process = "1.0.1"
async-std = "1.12.0"
async-tar-wasm = "0.4.2-wasm.1"
async-trait = "0.1.64"
backon = "0.4.0"
bincode = "1.3.1"
Expand Down Expand Up @@ -99,6 +98,7 @@ thiserror = "1.0.38"
tracing = "0.1.37"
tracing-appender = "0.2.2"
tracing-subscriber = "0.3.16"
tsify = "0.4.3"
url = "2.3.1"
wasm-bindgen = "0.2.84"
wasm-bindgen-futures = "0.4.34"
Expand Down
8 changes: 6 additions & 2 deletions crates/nassun/Cargo.toml
Expand Up @@ -27,10 +27,11 @@ serde_json = { workspace = true }
ssri = { workspace = true }
thiserror = { workspace = true }
tracing = { workspace = true }
tsify = { workspace = true, default-features = false, features = ["js"] }
url = { workspace = true }

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
async-tar-wasm = { workspace = true }
async-tar-wasm = "0.4.2-wasm.1"
async-process = { workspace = true }
async-std = { workspace = true, features = ["attributes", "std"] }
backon = { workspace = true }
Expand All @@ -42,7 +43,10 @@ tempfile = { workspace = true }
which = { workspace = true }

[target.'cfg(target_arch = "wasm32")'.dependencies]
async-tar-wasm = { workspace = true, default-features = false }
# NOTE: This async-tar-wasm version can't be configured at the workspace
# level, because otherwise the conditional features don't work.
async-tar-wasm = { version = "0.4.2-wasm.1", default-features = false }
console_error_panic_hook = { workspace = true }
js-sys = { workspace = true }
serde-wasm-bindgen = { workspace = true }
wasm-bindgen = { workspace = true }
Expand Down
18 changes: 16 additions & 2 deletions crates/nassun/src/client.rs
Expand Up @@ -8,19 +8,21 @@ use url::Url;

pub use oro_package_spec::{PackageSpec, VersionSpec};

use crate::entries::Entries;
use crate::error::Result;
#[cfg(not(target_arch = "wasm32"))]
use crate::fetch::DirFetcher;
#[cfg(not(target_arch = "wasm32"))]
use crate::fetch::GitFetcher;
use crate::fetch::{DummyFetcher, NpmFetcher, PackageFetcher};
use crate::package::Package;
use crate::resolver::PackageResolver;
use crate::{Entries, PackageResolution, Tarball};
use crate::resolver::{PackageResolution, PackageResolver};
use crate::tarball::Tarball;

/// Build a new Nassun instance with specified options.
#[derive(Clone, Debug, PartialEq, Eq, Default)]
pub struct NassunOpts {
#[cfg(not(target_arch = "wasm32"))]
cache: Option<PathBuf>,
base_dir: Option<PathBuf>,
default_tag: Option<String>,
Expand All @@ -32,6 +34,8 @@ impl NassunOpts {
Default::default()
}

/// Cache directory to use for requests.
#[cfg(not(target_arch = "wasm32"))]
pub fn cache(mut self, cache: impl AsRef<Path>) -> Self {
self.cache = Some(PathBuf::from(cache.as_ref()));
self
Expand All @@ -42,29 +46,37 @@ impl NassunOpts {
self
}

/// Adds a registry to use for a specific scope.
pub fn scope_registry(mut self, scope: impl AsRef<str>, registry: Url) -> Self {
self.registries
.insert(Some(scope.as_ref().into()), registry);
self
}

/// Base directory to use for resolving relative paths. Defaults to `"."`.
pub fn base_dir(mut self, base_dir: impl AsRef<Path>) -> Self {
self.base_dir = Some(PathBuf::from(base_dir.as_ref()));
self
}

/// Default tag to use when resolving package versions. Defaults to `latest`.
pub fn default_tag(mut self, default_tag: impl AsRef<str>) -> Self {
self.default_tag = Some(default_tag.as_ref().into());
self
}

/// Build a new Nassun instance from this options object.
pub fn build(self) -> Nassun {
let registry = self
.registries
.get(&None)
.cloned()
.unwrap_or_else(|| "https://registry.npmjs.org/".parse().unwrap());
#[cfg(target_arch = "wasm32")]
let client_builder = OroClient::builder().registry(registry);
#[cfg(not(target_arch = "wasm32"))]
let mut client_builder = OroClient::builder().registry(registry);
#[cfg(not(target_arch = "wasm32"))]
let cache = if let Some(cache) = self.cache {
client_builder = client_builder.cache(cache.clone());
Arc::new(Some(cache))
Expand All @@ -75,6 +87,8 @@ impl NassunOpts {
Nassun {
#[cfg(not(target_arch = "wasm32"))]
cache,
#[cfg(target_arch = "wasm32")]
cache: Arc::new(None),
resolver: PackageResolver {
#[cfg(target_arch = "wasm32")]
base_dir: PathBuf::from("."),
Expand Down
3 changes: 2 additions & 1 deletion crates/nassun/src/entries.rs
Expand Up @@ -7,7 +7,8 @@ use futures::{AsyncRead, Stream};

pub use async_tar_wasm::Header;

use crate::{error::Result, Tarball};
use crate::error::Result;
use crate::tarball::Tarball;

#[cfg(not(target_arch = "wasm32"))]
type EntriesStream = Box<dyn Stream<Item = Result<Entry>> + Unpin + Send + Sync>;
Expand Down
6 changes: 6 additions & 0 deletions crates/nassun/src/error.rs
Expand Up @@ -45,6 +45,7 @@ pub enum NassunError {
#[diagnostic(code(nassun::io::extract))]
ExtractIoError(#[source] std::io::Error, Option<PathBuf>, String),

#[cfg(not(target_arch = "wasm32"))]
#[error("Failed to extract tarball to cache. {0}{}", if let Some(path) = .1 {
format!(" (file: {})", path.to_string_lossy())
} else {
Expand Down Expand Up @@ -89,6 +90,11 @@ pub enum NassunError {
versions: Vec<String>,
},

#[cfg(target_arch = "wasm32")]
#[error(transparent)]
#[diagnostic(code(node_maintainer::serde_wasm_bindgen::error))]
SerdeWasmBindgenError(#[from] serde_wasm_bindgen::Error),

#[cfg(not(target_arch = "wasm32"))]
#[error(transparent)]
#[diagnostic(
Expand Down
19 changes: 12 additions & 7 deletions crates/nassun/src/lib.rs
Expand Up @@ -4,21 +4,26 @@
use futures::AsyncRead;
pub use oro_package_spec::{GitHost, GitInfo, PackageSpec, VersionSpec};

mod client;
mod entries;
mod error;
mod fetch;
mod package;
mod resolver;
mod tarball;
pub mod client;
pub mod entries;
pub mod error;
pub mod fetch;
pub mod package;
pub mod resolver;
pub mod tarball;
#[cfg(target_arch = "wasm32")]
mod wasm;

#[cfg(not(target_arch = "wasm32"))]
pub use client::*;
#[cfg(not(target_arch = "wasm32"))]
pub use entries::*;
#[cfg(not(target_arch = "wasm32"))]
pub use error::NassunError;
#[cfg(not(target_arch = "wasm32"))]
pub use package::*;
pub use resolver::*;
#[cfg(not(target_arch = "wasm32"))]
pub use tarball::*;
#[cfg(target_arch = "wasm32")]
pub use wasm::*;
Expand Down
9 changes: 7 additions & 2 deletions crates/nassun/src/package.rs
@@ -1,13 +1,17 @@
use std::fmt;
use std::path::{Path, PathBuf};
#[cfg(not(target_arch = "wasm32"))]
use std::path::Path;
use std::path::PathBuf;

use async_std::sync::Arc;
use oro_common::{CorgiPackument, CorgiVersionMetadata, Packument, VersionMetadata};
use oro_package_spec::PackageSpec;
use ssri::Integrity;

use crate::entries::Entries;
use crate::error::{NassunError, Result};
#[cfg(not(target_arch = "wasm32"))]
use crate::error::NassunError;
use crate::error::Result;
use crate::fetch::PackageFetcher;
use crate::resolver::PackageResolution;
use crate::tarball::Tarball;
Expand All @@ -21,6 +25,7 @@ pub struct Package {
pub(crate) resolved: PackageResolution,
pub(crate) fetcher: Arc<dyn PackageFetcher>,
pub(crate) base_dir: PathBuf,
#[cfg_attr(target_arch = "wasm32", allow(dead_code))]
pub(crate) cache: Arc<Option<PathBuf>>,
}

Expand Down
4 changes: 3 additions & 1 deletion crates/nassun/src/resolver.rs
Expand Up @@ -6,7 +6,9 @@ use oro_package_spec::{GitInfo, PackageSpec, VersionSpec};
use ssri::Integrity;
use url::Url;

use crate::{fetch::PackageFetcher, package::Package, NassunError};
use crate::error::NassunError;
use crate::fetch::PackageFetcher;
use crate::package::Package;

/// Represents a fully-resolved, specific version of a package as it would be fetched.
#[derive(Clone, PartialEq, Eq)]
Expand Down
13 changes: 12 additions & 1 deletion crates/nassun/src/tarball.rs
@@ -1,33 +1,41 @@
#[cfg(not(target_arch = "wasm32"))]
use std::io::Write;
#[cfg(not(target_arch = "wasm32"))]
use std::io::{Read, Seek};
#[cfg(not(target_arch = "wasm32"))]
use std::path::{Path, PathBuf};
use std::pin::Pin;
use std::task::{Context, Poll};
#[cfg(not(target_arch = "wasm32"))]
use std::time::Duration;

use async_compression::futures::bufread::GzipDecoder;
use async_std::io::BufReader;
use async_tar_wasm::Archive;
#[cfg(not(target_arch = "wasm32"))]
use backon::{BlockingRetryable, ConstantBuilder};
#[cfg(not(target_arch = "wasm32"))]
use cacache::WriteOpts;
use futures::{AsyncRead, AsyncReadExt, StreamExt};
#[cfg(not(target_arch = "wasm32"))]
use futures::AsyncReadExt;
use futures::{AsyncRead, StreamExt};
#[cfg(not(target_arch = "wasm32"))]
use ssri::IntegrityOpts;
use ssri::{Integrity, IntegrityChecker};
#[cfg(not(target_arch = "wasm32"))]
use tempfile::NamedTempFile;

use crate::entries::{Entries, Entry};
use crate::error::{NassunError, Result};
use crate::TarballStream;

#[cfg(not(target_arch = "wasm32"))]
const MAX_IN_MEMORY_TARBALL_SIZE: usize = 1024 * 1024 * 5;

pub struct Tarball {
checker: Option<IntegrityChecker>,
reader: TarballStream,
#[cfg(not(target_arch = "wasm32"))]
integrity: Option<Integrity>,
}

Expand All @@ -36,6 +44,7 @@ impl Tarball {
Self {
reader,
checker: Some(IntegrityChecker::new(integrity.clone())),
#[cfg(not(target_arch = "wasm32"))]
integrity: Some(integrity),
}
}
Expand All @@ -44,6 +53,7 @@ impl Tarball {
Self {
reader,
checker: None,
#[cfg(not(target_arch = "wasm32"))]
integrity: None,
}
}
Expand Down Expand Up @@ -351,6 +361,7 @@ fn strip_one(path: &Path) -> Option<&Path> {
comps.next().map(|_| comps.as_path())
}

#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn tarball_key(integrity: &Integrity) -> String {
format!("nassun::package::{integrity}")
}
Expand Down

0 comments on commit 16ad5ba

Please sign in to comment.