mirror of
https://github.com/danbulant/oxc
synced 2026-05-25 04:42:10 +00:00
feat(resolver): expose raw package_json value; improve print debug (#738)
This commit is contained in:
parent
99a7ad4319
commit
7c3e29d421
6 changed files with 97 additions and 19 deletions
|
|
@ -13,23 +13,25 @@ rust-version.workspace = true
|
|||
categories.workspace = true
|
||||
|
||||
[dependencies]
|
||||
tracing = { workspace = true }
|
||||
dashmap = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] } # derive for Deserialize from package.json
|
||||
serde_json = { workspace = true, features = ["preserve_order"] } # preserve_order: package_json.exports requires order such as `["require", "import", "default"]`
|
||||
tracing = { workspace = true }
|
||||
dashmap = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] } # derive for Deserialize from package.json
|
||||
serde_json = { workspace = true, features = [
|
||||
"preserve_order",
|
||||
] } # preserve_order: package_json.exports requires order such as `["require", "import", "default"]`
|
||||
rustc-hash = { workspace = true }
|
||||
indexmap = { workspace = true, features = ["serde"] } # serde for Deserialize from package.json
|
||||
dunce = "1.0.4"
|
||||
indexmap = { workspace = true, features = ["serde"] } # serde for Deserialize from package.json
|
||||
dunce = "1.0.4"
|
||||
# Use `std::sync::OnceLock::get_or_try_init` when it is stable.
|
||||
once_cell = "1.18.0"
|
||||
|
||||
[dev-dependencies]
|
||||
static_assertions = { workspace = true }
|
||||
criterion = { workspace = true }
|
||||
rayon = { workspace = true } # for benchmark
|
||||
nodejs-resolver = "0.0.88" # for benchmark
|
||||
vfs = "0.9.0" # for testing with in memory file system
|
||||
rayon = { workspace = true } # for benchmark
|
||||
static_assertions = { workspace = true }
|
||||
tracing-subscriber = { workspace = true, features = ["env-filter"] }
|
||||
vfs = "0.9.0" # for testing with in memory file system
|
||||
|
||||
[target.'cfg(not(target_env = "msvc"))'.dev-dependencies]
|
||||
jemallocator = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -44,6 +44,10 @@ impl<Fs: FileSystem> Cache<Fs> {
|
|||
Self { fs, ..Self::default() }
|
||||
}
|
||||
|
||||
pub fn clear(&self) {
|
||||
self.cache.clear();
|
||||
}
|
||||
|
||||
pub fn value(&self, path: &Path) -> CachedPath {
|
||||
let hash = {
|
||||
let mut hasher = FxHasher::default();
|
||||
|
|
|
|||
|
|
@ -32,14 +32,16 @@ use std::{
|
|||
cell::RefCell,
|
||||
cmp::Ordering,
|
||||
ffi::OsStr,
|
||||
fmt,
|
||||
ops::Deref,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
cache::{Cache, CachedPath},
|
||||
file_system::FileSystemOs,
|
||||
package_json::{ExportsField, ExportsKey, MatchObject, PackageJson},
|
||||
package_json::{ExportsField, ExportsKey, MatchObject},
|
||||
path::PathUtil,
|
||||
specifier::{Specifier, SpecifierKind},
|
||||
};
|
||||
|
|
@ -47,6 +49,7 @@ pub use crate::{
|
|||
error::{JSONError, ResolveError},
|
||||
file_system::{FileMetadata, FileSystem},
|
||||
options::{Alias, AliasValue, EnforceExtension, ResolveOptions, Restriction},
|
||||
package_json::PackageJson,
|
||||
resolution::Resolution,
|
||||
};
|
||||
|
||||
|
|
@ -56,7 +59,13 @@ pub type Resolver = ResolverGeneric<FileSystemOs>;
|
|||
/// Generic implementation of the resolver, can be configured by the [FileSystem] trait.
|
||||
pub struct ResolverGeneric<Fs> {
|
||||
options: ResolveOptions,
|
||||
cache: Cache<Fs>,
|
||||
cache: Arc<Cache<Fs>>,
|
||||
}
|
||||
|
||||
impl<Fs> fmt::Debug for ResolverGeneric<Fs> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.options.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
type ResolveState = Result<Option<CachedPath>, ResolveError>;
|
||||
|
|
@ -118,11 +127,24 @@ impl<Fs: FileSystem> Default for ResolverGeneric<Fs> {
|
|||
|
||||
impl<Fs: FileSystem> ResolverGeneric<Fs> {
|
||||
pub fn new(options: ResolveOptions) -> Self {
|
||||
Self { options: options.sanitize(), cache: Cache::default() }
|
||||
Self { options: options.sanitize(), cache: Arc::new(Cache::default()) }
|
||||
}
|
||||
|
||||
pub fn new_with_file_system(file_system: Fs, options: ResolveOptions) -> Self {
|
||||
Self { cache: Cache::new(file_system), ..Self::new(options) }
|
||||
Self { cache: Arc::new(Cache::new(file_system)), ..Self::new(options) }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn clone_with_options(&self, options: ResolveOptions) -> Self {
|
||||
Self { options: options.sanitize(), cache: Arc::clone(&self.cache) }
|
||||
}
|
||||
|
||||
pub fn options(&self) -> &ResolveOptions {
|
||||
&self.options
|
||||
}
|
||||
|
||||
pub fn clear_cache(&self) {
|
||||
self.cache.clear();
|
||||
}
|
||||
|
||||
/// Resolve `specifier` at `path`
|
||||
|
|
@ -156,7 +178,12 @@ impl<Fs: FileSystem> ResolverGeneric<Fs> {
|
|||
// enhanced-resolve: restrictions
|
||||
self.check_restrictions(&path)?;
|
||||
let mut ctx = ctx.borrow_mut();
|
||||
Ok(Resolution { path, query: ctx.query.take(), fragment: ctx.fragment.take() })
|
||||
Ok(Resolution {
|
||||
path,
|
||||
query: ctx.query.take(),
|
||||
fragment: ctx.fragment.take(),
|
||||
package_json: cached_path.find_package_json(&self.cache.fs, &self.options)?,
|
||||
})
|
||||
}
|
||||
|
||||
/// require(X) from module at path Y
|
||||
|
|
@ -698,7 +725,7 @@ impl<Fs: FileSystem> ResolverGeneric<Fs> {
|
|||
let new_specifier = Specifier::parse(&new_specifier)
|
||||
.map_err(ResolveError::Specifier)?;
|
||||
ctx.with_fully_specified(false);
|
||||
// Override query and fragment from the alias
|
||||
// Alias may contain `?query`, pass it along.
|
||||
ctx.with_query_fragment(specifier.query, specifier.fragment);
|
||||
match self.require(cached_path, &new_specifier, ctx) {
|
||||
Err(ResolveError::NotFound(_)) => { /* noop */ }
|
||||
|
|
@ -708,7 +735,8 @@ impl<Fs: FileSystem> ResolverGeneric<Fs> {
|
|||
}
|
||||
}
|
||||
AliasValue::Ignore => {
|
||||
return Err(ResolveError::Ignored(cached_path.path().join(alias_key)));
|
||||
let path = cached_path.path().normalize_with(alias_key);
|
||||
return Err(ResolveError::Ignored(path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ impl EnforceExtension {
|
|||
pub type Alias = Vec<(String, Vec<AliasValue>)>;
|
||||
|
||||
/// Alias Value for [ResolveOptions::alias] and [ResolveOptions::fallback].
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||
pub enum AliasValue {
|
||||
/// The path value
|
||||
Path(String),
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
use std::{
|
||||
hash::BuildHasherDefault,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use indexmap::IndexMap;
|
||||
|
|
@ -20,6 +21,10 @@ pub struct PackageJson {
|
|||
#[serde(skip)]
|
||||
path: PathBuf,
|
||||
|
||||
#[serde(skip)]
|
||||
#[serde(default)]
|
||||
raw_json: Arc<serde_json::Value>,
|
||||
|
||||
/// The "name" field defines your package's name.
|
||||
/// The "name" field can be used in addition to the "exports" field to self-reference a package using its name.
|
||||
///
|
||||
|
|
@ -166,6 +171,7 @@ impl PackageJson {
|
|||
}
|
||||
|
||||
package_json.path = path;
|
||||
package_json.raw_json = Arc::new(package_json_value);
|
||||
Ok(package_json)
|
||||
}
|
||||
|
||||
|
|
@ -188,6 +194,15 @@ impl PackageJson {
|
|||
}
|
||||
|
||||
/// Directory to `package.json`
|
||||
pub fn raw_json(&self) -> &serde_json::Value {
|
||||
self.raw_json.as_ref()
|
||||
}
|
||||
|
||||
/// Directory to `package.json`
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// * When the package.json path is misconfigured.
|
||||
pub fn directory(&self) -> &Path {
|
||||
debug_assert!(self.path.file_name().is_some_and(|x| x == "package.json"));
|
||||
self.path.parent().unwrap()
|
||||
|
|
|
|||
|
|
@ -1,7 +1,12 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
use crate::package_json::PackageJson;
|
||||
use std::{
|
||||
fmt,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
/// The final path resolution with optional `?query` and `#fragment`.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
#[derive(Clone)]
|
||||
pub struct Resolution {
|
||||
pub(crate) path: PathBuf,
|
||||
|
||||
|
|
@ -10,8 +15,27 @@ pub struct Resolution {
|
|||
|
||||
/// path fragment `#query`, contains `#`.
|
||||
pub(crate) fragment: Option<String>,
|
||||
|
||||
pub(crate) package_json: Option<Arc<PackageJson>>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for Resolution {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Resolution")
|
||||
.field("path", &self.path)
|
||||
.field("query", &self.query)
|
||||
.field("fragment", &self.fragment)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Resolution {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.path == other.path && self.query == other.query && self.fragment == other.fragment
|
||||
}
|
||||
}
|
||||
impl Eq for Resolution {}
|
||||
|
||||
impl Resolution {
|
||||
/// Returns the path without query and fragment
|
||||
pub fn path(&self) -> &Path {
|
||||
|
|
@ -33,6 +57,10 @@ impl Resolution {
|
|||
self.fragment.as_deref()
|
||||
}
|
||||
|
||||
pub fn package_json(&self) -> Option<&Arc<PackageJson>> {
|
||||
self.package_json.as_ref()
|
||||
}
|
||||
|
||||
/// Returns the full path with query and fragment
|
||||
pub fn full_path(&self) -> PathBuf {
|
||||
let mut path = self.path.clone().into_os_string();
|
||||
|
|
@ -52,6 +80,7 @@ fn test() {
|
|||
path: PathBuf::from("foo"),
|
||||
query: Some("?query".to_string()),
|
||||
fragment: Some("#fragment".to_string()),
|
||||
package_json: None,
|
||||
};
|
||||
assert_eq!(resolution.path(), Path::new("foo"));
|
||||
assert_eq!(resolution.query(), Some("?query"));
|
||||
|
|
|
|||
Loading…
Reference in a new issue