diff --git a/Cargo.lock b/Cargo.lock index e68b282..c765d88 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,38 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 + +[[package]] +name = "boxed_error" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17d4f95e880cfd28c4ca5a006cf7f6af52b4bcb7b5866f573b2faa126fb7affb" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "capacity_builder" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f2d24a6dcf0cd402a21b65d35340f3a49ff3475dc5fdac91d22d2733e6641c6" +dependencies = [ + "capacity_builder_macros", + "ecow", + "hipstr", + "itoa", +] + +[[package]] +name = "capacity_builder_macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b4a6cae9efc04cc6cbb8faf338d2c497c165c83e74509cf4dbedea948bbf6e5" +dependencies = [ + "quote", + "syn", +] [[package]] name = "deno_error" @@ -29,6 +61,7 @@ dependencies = [ name = "deno_package_json" version = "0.2.1" dependencies = [ + "boxed_error", "deno_error", "deno_path_util", "deno_semver", @@ -36,32 +69,36 @@ dependencies = [ "pretty_assertions", "serde", "serde_json", - "thiserror 2.0.3", + "thiserror", "url", ] [[package]] name = "deno_path_util" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4889646c1ce8437a6fde3acb057fd7e2d039e62c61f5063fc125ed1ede114dc6" +checksum = "b02c7d341e1b2cf089daff0f4fb2b4be8f3b5511b1d96040b3f7ed63a66c737b" dependencies = [ + "deno_error", "percent-encoding", - "thiserror 1.0.61", + "thiserror", "url", ] [[package]] name = "deno_semver" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4756be7351289726087408984db18b9eb5e0186907673f39f858d119d0162071" +checksum = "a21aae482ce4a88e8a9f4746f4f93a3069709d297a7a5593bf984f221792d012" dependencies = [ + "capacity_builder", "deno_error", + "ecow", + "hipstr", "monch", "once_cell", "serde", - "thiserror 2.0.3", + "thiserror", "url", ] @@ -82,6 +119,15 @@ dependencies = [ "syn", ] +[[package]] +name = "ecow" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42fc0a93992b20c58b99e59d61eaf1635a25bfbe49e4275c34ba0aee98119ba" +dependencies = [ + "serde", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -103,6 +149,17 @@ version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +[[package]] +name = "hipstr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97971ffc85d4c98de12e2608e992a43f5294ebb625fdb045b27c731b64c4c6d6" +dependencies = [ + "serde", + "serde_bytes", + "sptr", +] + [[package]] name = "icu_collections" version = "1.5.0" @@ -255,9 +312,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "libc" @@ -310,9 +367,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.35" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -332,6 +389,15 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde_bytes" +version = "0.11.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" +dependencies = [ + "serde", +] + [[package]] name = "serde_derive" version = "1.0.197" @@ -360,6 +426,12 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +[[package]] +name = "sptr" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a" + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -388,33 +460,13 @@ dependencies = [ "syn", ] -[[package]] -name = "thiserror" -version = "1.0.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" -dependencies = [ - "thiserror-impl 1.0.61", -] - [[package]] name = "thiserror" version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa" dependencies = [ - "thiserror-impl 2.0.3", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "thiserror-impl", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index b789d3f..57e7e2a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,9 +16,10 @@ serde = { version = "1.0.149", features = ["derive"] } serde_json = "1.0.85" url = { version = "2.5.1" } thiserror = "2" -deno_semver = "0.6.0" -deno_path_util = "0.2.0" +deno_semver = "0.7.0" +deno_path_util = "0.2.2" deno_error = { version = "0.5.2", features = ["serde", "serde_json"] } +boxed_error = "0.2.3" [dev-dependencies] pretty_assertions = "1.4.0" diff --git a/clippy.toml b/clippy.toml index 2344e59..ad21fa8 100644 --- a/clippy.toml +++ b/clippy.toml @@ -40,4 +40,5 @@ disallowed-methods = [ ] disallowed-types = [ { path = "std::sync::Arc", reason = "use crate::sync::MaybeArc instead" }, + { path = "std::sync::OnceLock", reason = "use crate::sync::MaybeOnceLock instead" }, ] diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 8749391..80afd2d 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "1.79.0" +channel = "1.83.0" components = ["rustfmt", "clippy"] diff --git a/src/fs.rs b/src/fs.rs index d0def5e..227e2c4 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -10,7 +10,7 @@ pub trait DenoPkgJsonFs { ) -> Result, std::io::Error>; } -impl<'a> Default for &'a dyn DenoPkgJsonFs { +impl Default for &dyn DenoPkgJsonFs { fn default() -> Self { &RealDenoPkgJsonFs } diff --git a/src/lib.rs b/src/lib.rs index 05d60f4..7e48cd1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,16 +5,19 @@ #![deny(clippy::unused_async)] #![deny(clippy::unnecessary_wraps)] +use std::path::Path; +use std::path::PathBuf; + +use boxed_error::Boxed; use deno_error::JsError; use deno_semver::npm::NpmVersionReqParseError; use deno_semver::package::PackageReq; +use deno_semver::StackString; use deno_semver::VersionReq; use indexmap::IndexMap; use serde::Serialize; use serde_json::Map; use serde_json::Value; -use std::path::Path; -use std::path::PathBuf; use thiserror::Error; use url::Url; @@ -23,14 +26,23 @@ mod sync; #[allow(clippy::disallowed_types)] pub type PackageJsonRc = crate::sync::MaybeArc; +#[allow(clippy::disallowed_types)] +pub type PackageJsonDepsRc = crate::sync::MaybeArc; +#[allow(clippy::disallowed_types)] +type PackageJsonDepsRcCell = crate::sync::MaybeOnceLock; pub trait PackageJsonCache { fn get(&self, path: &Path) -> Option; fn set(&self, path: PathBuf, package_json: PackageJsonRc); } -#[derive(Debug, Error, Clone, JsError)] -pub enum PackageJsonDepValueParseError { +#[derive(Debug, Clone, JsError, PartialEq, Eq, Boxed)] +pub struct PackageJsonDepValueParseError( + pub Box, +); + +#[derive(Debug, Error, Clone, JsError, PartialEq, Eq)] +pub enum PackageJsonDepValueParseErrorKind { #[class(inherit)] #[error(transparent)] VersionReq(#[from] NpmVersionReqParseError), @@ -57,8 +69,10 @@ pub enum PackageJsonDepValue { Workspace(PackageJsonDepWorkspaceReq), } -pub type PackageJsonDepsMap = - IndexMap>; +pub type PackageJsonDepsMap = IndexMap< + StackString, + Result, +>; #[derive(Debug, Clone)] pub struct PackageJsonDeps { @@ -124,6 +138,8 @@ pub struct PackageJson { pub dev_dependencies: Option>, pub scripts: Option>, pub workspaces: Option>, + #[serde(skip_serializing)] + resolved_deps: PackageJsonDepsRcCell, } impl PackageJson { @@ -173,6 +189,7 @@ impl PackageJson { dev_dependencies: None, scripts: None, workspaces: None, + resolved_deps: Default::default(), }); } @@ -314,6 +331,7 @@ impl PackageJson { dev_dependencies, scripts, workspaces, + resolved_deps: Default::default(), } } @@ -335,7 +353,7 @@ impl PackageJson { } /// Resolve the package.json's dependencies. - pub fn resolve_local_package_json_deps(&self) -> PackageJsonDeps { + pub fn resolve_local_package_json_deps(&self) -> &PackageJsonDepsRc { /// Gets the name and raw version constraint for a registry info or /// package.json dependency entry taking into account npm package aliases. fn parse_dep_entry_name_and_raw_version<'a>( @@ -377,19 +395,24 @@ impl PackageJson { || value.starts_with("http:") || value.starts_with("https:") { - return Err(PackageJsonDepValueParseError::Unsupported { - scheme: value.split(':').next().unwrap().to_string(), - }); + return Err( + PackageJsonDepValueParseErrorKind::Unsupported { + scheme: value.split(':').next().unwrap().to_string(), + } + .into_box(), + ); } let (name, version_req) = parse_dep_entry_name_and_raw_version(key, value); let result = VersionReq::parse_from_npm(version_req); match result { Ok(version_req) => Ok(PackageJsonDepValue::Req(PackageReq { - name: name.to_string(), + name: name.into(), version_req, })), - Err(err) => Err(PackageJsonDepValueParseError::VersionReq(err)), + Err(err) => { + Err(PackageJsonDepValueParseErrorKind::VersionReq(err).into_box()) + } } } @@ -400,16 +423,18 @@ impl PackageJson { let mut result = IndexMap::with_capacity(deps.len()); for (key, value) in deps { result - .entry(key.to_string()) + .entry(StackString::from(key.as_str())) .or_insert_with(|| parse_entry(key, value)); } result } - PackageJsonDeps { - dependencies: get_map(self.dependencies.as_ref()), - dev_dependencies: get_map(self.dev_dependencies.as_ref()), - } + self.resolved_deps.get_or_init(|| { + PackageJsonDepsRc::new(PackageJsonDeps { + dependencies: get_map(self.dependencies.as_ref()), + dev_dependencies: get_map(self.dev_dependencies.as_ref()), + }) + }) } } @@ -460,21 +485,22 @@ mod test { fn get_local_package_json_version_reqs_for_tests( package_json: &PackageJson, - ) -> IndexMap> { + ) -> IndexMap< + String, + Result, + > { let deps = package_json.resolve_local_package_json_deps(); deps .dependencies + .clone() .into_iter() - .chain(deps.dev_dependencies) + .chain(deps.dev_dependencies.clone()) .map(|(k, v)| { ( - k, + k.to_string(), match v { Ok(v) => Ok(v), - Err(err) => Err(format!( - "{err}{}", - err.source().map(|e| format!("\n {e}")).unwrap_or_default() - )), + Err(err) => Err(err.into_kind()), }, ) }) @@ -498,16 +524,17 @@ mod test { assert_eq!( deps .dependencies + .clone() .into_iter() .map(|d| (d.0, d.1.unwrap())) .collect::>(), Vec::from([ ( - "test".to_string(), + "test".into(), PackageJsonDepValue::Req(PackageReq::from_str("test@^1.2").unwrap()) ), ( - "other".to_string(), + "other".into(), PackageJsonDepValue::Req( PackageReq::from_str("package@~1.3").unwrap() ) @@ -517,18 +544,19 @@ mod test { assert_eq!( deps .dev_dependencies + .clone() .into_iter() .map(|d| (d.0, d.1.unwrap())) .collect::>(), Vec::from([ ( - "package_b".to_string(), + "package_b".into(), PackageJsonDepValue::Req( PackageReq::from_str("package_b@~2.2").unwrap() ) ), ( - "other".to_string(), + "other".into(), PackageJsonDepValue::Req(PackageReq::from_str("other@^3.2").unwrap()) ), ]) @@ -545,12 +573,12 @@ mod test { "%*(#$%()".to_string(), )])); let map = get_local_package_json_version_reqs_for_tests(&package_json); + assert_eq!(map.len(), 1); + let err = map.get("test").unwrap().as_ref().unwrap_err(); + assert_eq!(format!("{}", err), "Invalid version requirement"); assert_eq!( - map, - IndexMap::from([( - "test".to_string(), - Err("Invalid version requirement\n Unexpected character.\n %*(#$%()\n ~".to_string()) - )]) + format!("{}", err.source().unwrap()), + concat!("Unexpected character.\n", " %*(#$%()\n", " ~") ); } @@ -569,7 +597,7 @@ mod test { IndexMap::from([( "test".to_string(), Ok(PackageJsonDepValue::Req(PackageReq { - name: "test".to_string(), + name: "test".into(), version_req: VersionReq::parse_from_npm("1.x - 1.3").unwrap() })) )]) @@ -599,22 +627,6 @@ mod test { assert_eq!( result, IndexMap::from([ - ( - "file-test".to_string(), - Err("Not implemented scheme 'file'".to_string()), - ), - ( - "git-test".to_string(), - Err("Not implemented scheme 'git'".to_string()), - ), - ( - "http-test".to_string(), - Err("Not implemented scheme 'http'".to_string()), - ), - ( - "https-test".to_string(), - Err("Not implemented scheme 'https'".to_string()), - ), ( "test".to_string(), Ok(PackageJsonDepValue::Req( @@ -622,18 +634,18 @@ mod test { )) ), ( - "work-test-version-req".to_string(), + "work-test-star".to_string(), Ok(PackageJsonDepValue::Workspace( PackageJsonDepWorkspaceReq::VersionReq( - VersionReq::parse_from_npm("1.1.1").unwrap() + VersionReq::parse_from_npm("*").unwrap() ) )) ), ( - "work-test-star".to_string(), + "work-test-version-req".to_string(), Ok(PackageJsonDepValue::Workspace( PackageJsonDepWorkspaceReq::VersionReq( - VersionReq::parse_from_npm("*").unwrap() + VersionReq::parse_from_npm("1.1.1").unwrap() ) )) ), @@ -649,6 +661,30 @@ mod test { PackageJsonDepWorkspaceReq::Caret )) ), + ( + "file-test".to_string(), + Err(PackageJsonDepValueParseErrorKind::Unsupported { + scheme: "file".to_string() + }), + ), + ( + "git-test".to_string(), + Err(PackageJsonDepValueParseErrorKind::Unsupported { + scheme: "git".to_string() + }), + ), + ( + "http-test".to_string(), + Err(PackageJsonDepValueParseErrorKind::Unsupported { + scheme: "http".to_string() + }), + ), + ( + "https-test".to_string(), + Err(PackageJsonDepValueParseErrorKind::Unsupported { + scheme: "https".to_string() + }), + ), ]) ); } diff --git a/src/sync.rs b/src/sync.rs index 87091e3..c80136d 100644 --- a/src/sync.rs +++ b/src/sync.rs @@ -6,10 +6,12 @@ pub use inner::*; mod inner { #![allow(clippy::disallowed_types)] pub use std::sync::Arc as MaybeArc; + pub use std::sync::OnceLock as MaybeOnceLock; } #[cfg(not(feature = "sync"))] mod inner { + pub use std::cell::OnceCell as MaybeOnceLock; pub use std::rc::Rc as MaybeArc; }