mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
refactor(resolver): clean some code (#692)
This commit is contained in:
parent
8a915cec5c
commit
3bfa314e95
16 changed files with 557 additions and 473 deletions
|
|
@ -15,7 +15,7 @@ use crate::{package_json::PackageJson, FileMetadata, FileSystem, ResolveError};
|
|||
pub struct Cache<Fs> {
|
||||
pub(crate) fs: Fs,
|
||||
// Using IdentityHasher to avoid double hashing in the `get` + `insert` case.
|
||||
cache: DashMap<u64, CacheValue, BuildIdentityHasher<u64>>,
|
||||
cache: DashMap<u64, CachedPath, BuildIdentityHasher<u64>>,
|
||||
}
|
||||
|
||||
impl<Fs: FileSystem> Default for Cache<Fs> {
|
||||
|
|
@ -32,15 +32,15 @@ impl<Fs: FileSystem> Cache<Fs> {
|
|||
/// # Panics
|
||||
///
|
||||
/// * Path is file but does not have a parent
|
||||
pub fn dirname<'a>(&self, cache_value: &'a CacheValue) -> &'a CacheValue {
|
||||
pub fn dirname(&self, cache_value: &CachedPath) -> CachedPath {
|
||||
if cache_value.is_file(&self.fs) {
|
||||
cache_value.parent.as_ref().unwrap()
|
||||
cache_value.parent.clone().unwrap()
|
||||
} else {
|
||||
cache_value
|
||||
cache_value.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn value(&self, path: &Path) -> CacheValue {
|
||||
pub fn value(&self, path: &Path) -> CachedPath {
|
||||
let hash = {
|
||||
let mut hasher = FxHasher::default();
|
||||
path.hash(&mut hasher);
|
||||
|
|
@ -51,40 +51,40 @@ impl<Fs: FileSystem> Cache<Fs> {
|
|||
}
|
||||
let parent = path.parent().map(|p| self.value(p));
|
||||
let data =
|
||||
CacheValue(Arc::new(CacheValueImpl::new(path.to_path_buf().into_boxed_path(), parent)));
|
||||
CachedPath(Arc::new(CachedPathImpl::new(path.to_path_buf().into_boxed_path(), parent)));
|
||||
self.cache.insert(hash, data.clone());
|
||||
data
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CacheValue(Arc<CacheValueImpl>);
|
||||
pub struct CachedPath(Arc<CachedPathImpl>);
|
||||
|
||||
impl Deref for CacheValue {
|
||||
type Target = CacheValueImpl;
|
||||
impl Deref for CachedPath {
|
||||
type Target = CachedPathImpl;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.0.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<CacheValueImpl> for CacheValue {
|
||||
fn as_ref(&self) -> &CacheValueImpl {
|
||||
impl AsRef<CachedPathImpl> for CachedPath {
|
||||
fn as_ref(&self) -> &CachedPathImpl {
|
||||
self.0.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CacheValueImpl {
|
||||
pub struct CachedPathImpl {
|
||||
path: Box<Path>,
|
||||
parent: Option<CacheValue>,
|
||||
parent: Option<CachedPath>,
|
||||
meta: OnceLock<Option<FileMetadata>>,
|
||||
symlink: OnceLock<Option<PathBuf>>,
|
||||
package_json: OnceLock<Option<Result<Arc<PackageJson>, ResolveError>>>,
|
||||
}
|
||||
|
||||
impl CacheValueImpl {
|
||||
fn new(path: Box<Path>, parent: Option<CacheValue>) -> Self {
|
||||
impl CachedPathImpl {
|
||||
fn new(path: Box<Path>, parent: Option<CachedPath>) -> Self {
|
||||
Self {
|
||||
path,
|
||||
parent,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
|
||||
use crate::request::RequestError;
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// All resolution errors.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum ResolveError {
|
||||
/// Ignored path
|
||||
|
|
@ -15,24 +14,21 @@ pub enum ResolveError {
|
|||
/// }
|
||||
/// ```
|
||||
/// See <https://github.com/defunctzombie/package-browser-field-spec#ignore-a-module>
|
||||
Ignored(Box<Path>),
|
||||
Ignored(PathBuf),
|
||||
|
||||
/// Path not found
|
||||
NotFound(Box<Path>),
|
||||
NotFound(PathBuf),
|
||||
|
||||
/// All of the aliased extension are not found
|
||||
ExtensionAlias,
|
||||
|
||||
/// All of the aliases are not found
|
||||
Alias(String),
|
||||
|
||||
/// The provided path request cannot be parsed
|
||||
Request(RequestError),
|
||||
/// The provided path specifier cannot be parsed
|
||||
Specifier(SpecifierError),
|
||||
|
||||
/// JSON parse error
|
||||
JSON(JSONError),
|
||||
|
||||
// TODO: TypeError [ERR_INVALID_MODULE_SPECIFIER]: Invalid module "./dist/../../../a.js" request is not a valid subpath for the "exports" resolution of /xxx/package.json
|
||||
// TODO: TypeError [ERR_INVALID_MODULE_SPECIFIER]: Invalid module "./dist/../../../a.js" specifier is not a valid subpath for the "exports" resolution of /xxx/package.json
|
||||
InvalidModuleSpecifier(String),
|
||||
|
||||
// TODO: Error [ERR_INVALID_PACKAGE_TARGET]: Invalid "exports" target "./../../a.js" defined for './dist/a.js' in the package config /xxx/package.json
|
||||
|
|
@ -53,6 +49,12 @@ pub enum ResolveError {
|
|||
PackageImportNotDefined(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum SpecifierError {
|
||||
Empty,
|
||||
}
|
||||
|
||||
/// JSON error from [serde_json::Error].
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct JSONError {
|
||||
pub path: PathBuf,
|
||||
|
|
@ -62,10 +64,6 @@ pub struct JSONError {
|
|||
}
|
||||
|
||||
impl ResolveError {
|
||||
pub fn is_not_found(&self) -> bool {
|
||||
matches!(self, Self::NotFound(_) | Self::ExtensionAlias | Self::Alias(_))
|
||||
}
|
||||
|
||||
pub(crate) fn from_serde_json_error(path: PathBuf, error: &serde_json::Error) -> Self {
|
||||
Self::JSON(JSONError {
|
||||
path,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ use std::{
|
|||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
/// File System abstraction used for `ResolverGeneric`.
|
||||
pub trait FileSystem: Default + Send + Sync {
|
||||
/// See [std::fs::read_to_string]
|
||||
///
|
||||
|
|
@ -34,6 +35,7 @@ pub trait FileSystem: Default + Send + Sync {
|
|||
fn canonicalize<P: AsRef<Path>>(&self, path: P) -> io::Result<PathBuf>;
|
||||
}
|
||||
|
||||
/// Metadata information about a file.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct FileMetadata {
|
||||
pub(crate) is_file: bool,
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,16 +1,5 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
pub type Alias = Vec<(String, Vec<AliasValue>)>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum AliasValue {
|
||||
/// The path value
|
||||
Path(String),
|
||||
|
||||
/// The `false` value
|
||||
Ignore,
|
||||
}
|
||||
|
||||
/// Module Resolution Options
|
||||
///
|
||||
/// Options are directly ported from [enhanced-resolve](https://github.com/webpack/enhanced-resolve#resolver-options).
|
||||
|
|
@ -109,6 +98,19 @@ pub struct ResolveOptions {
|
|||
pub symlinks: bool,
|
||||
}
|
||||
|
||||
/// Alias for [ResolveOptions::alias] and [ResolveOptions::fallback].
|
||||
pub type Alias = Vec<(String, Vec<AliasValue>)>;
|
||||
|
||||
/// Alias Value for [ResolveOptions::alias] and [ResolveOptions::fallback].
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum AliasValue {
|
||||
/// The path value
|
||||
Path(String),
|
||||
|
||||
/// The `false` value
|
||||
Ignore,
|
||||
}
|
||||
|
||||
impl Default for ResolveOptions {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
|
|
|
|||
|
|
@ -17,8 +17,9 @@ type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>;
|
|||
// TODO: allocate everything into an arena or SoA
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct PackageJson {
|
||||
/// Path to `package.json`. Contains the `package.json` filename.
|
||||
#[serde(skip)]
|
||||
pub path: PathBuf,
|
||||
path: PathBuf,
|
||||
|
||||
/// 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.
|
||||
|
|
@ -131,12 +132,18 @@ impl PackageJson {
|
|||
Ok(package_json)
|
||||
}
|
||||
|
||||
/// Directory to `package.json`
|
||||
pub fn directory(&self) -> &Path {
|
||||
debug_assert!(self.path.file_name().is_some_and(|x| x == "package.json"));
|
||||
self.path.parent().unwrap()
|
||||
}
|
||||
|
||||
/// Resolve the request string for this package.json by looking at the `browser` field.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// * Returns [ResolveError::Ignored] for `"path": false` in `browser` field.
|
||||
pub fn resolve(
|
||||
pub fn resolve_browser_field(
|
||||
&self,
|
||||
path: &Path,
|
||||
request: Option<&str>,
|
||||
|
|
@ -163,9 +170,7 @@ impl PackageJson {
|
|||
) -> Result<Option<&'a str>, ResolveError> {
|
||||
match value {
|
||||
serde_json::Value::String(value) => Ok(Some(value.as_str())),
|
||||
serde_json::Value::Bool(b) if !b => {
|
||||
Err(ResolveError::Ignored(key.to_path_buf().into_boxed_path()))
|
||||
}
|
||||
serde_json::Value::Bool(b) if !b => Err(ResolveError::Ignored(key.to_path_buf())),
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
|
||||
/// The final path resolution with optional `?query` and `#fragment`.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct Resolution {
|
||||
pub(crate) path: PathBuf,
|
||||
|
|
|
|||
|
|
@ -1,17 +1,14 @@
|
|||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum RequestError {
|
||||
Empty,
|
||||
}
|
||||
use crate::error::SpecifierError;
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct Request<'a> {
|
||||
pub path: RequestPath<'a>,
|
||||
pub struct Specifier<'a> {
|
||||
pub path: SpecifierPath<'a>,
|
||||
pub query: Option<&'a str>,
|
||||
pub fragment: Option<&'a str>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum RequestPath<'a> {
|
||||
pub enum SpecifierPath<'a> {
|
||||
/// `/path`
|
||||
Absolute(&'a str),
|
||||
|
||||
|
|
@ -25,7 +22,7 @@ pub enum RequestPath<'a> {
|
|||
Bare(&'a str),
|
||||
}
|
||||
|
||||
impl<'a> RequestPath<'a> {
|
||||
impl<'a> SpecifierPath<'a> {
|
||||
pub fn as_str(&self) -> &str {
|
||||
match self {
|
||||
Self::Absolute(s) | Self::Relative(s) | Self::Hash(s) | Self::Bare(s) => s,
|
||||
|
|
@ -33,39 +30,39 @@ impl<'a> RequestPath<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Request<'a> {
|
||||
pub fn parse(request: &'a str) -> Result<Request<'a>, RequestError> {
|
||||
if request.is_empty() {
|
||||
return Err(RequestError::Empty);
|
||||
impl<'a> Specifier<'a> {
|
||||
pub fn parse(specifier: &'a str) -> Result<Specifier<'a>, SpecifierError> {
|
||||
if specifier.is_empty() {
|
||||
return Err(SpecifierError::Empty);
|
||||
}
|
||||
|
||||
let (path, query, fragment) = match request.as_bytes()[0] {
|
||||
let (path, query, fragment) = match specifier.as_bytes()[0] {
|
||||
b'/' => {
|
||||
let (path, query, fragment) = Self::parse_query_framgment(request, 1);
|
||||
(RequestPath::Absolute(path), query, fragment)
|
||||
let (path, query, fragment) = Self::parse_query_framgment(specifier, 1);
|
||||
(SpecifierPath::Absolute(path), query, fragment)
|
||||
}
|
||||
b'.' => {
|
||||
let (path, query, fragment) = Self::parse_query_framgment(request, 1);
|
||||
(RequestPath::Relative(path), query, fragment)
|
||||
let (path, query, fragment) = Self::parse_query_framgment(specifier, 1);
|
||||
(SpecifierPath::Relative(path), query, fragment)
|
||||
}
|
||||
b'#' => {
|
||||
let (path, query, fragment) = Self::parse_query_framgment(request, 1);
|
||||
(RequestPath::Hash(path), query, fragment)
|
||||
let (path, query, fragment) = Self::parse_query_framgment(specifier, 1);
|
||||
(SpecifierPath::Hash(path), query, fragment)
|
||||
}
|
||||
_ => {
|
||||
let (path, query, fragment) = Self::parse_query_framgment(request, 0);
|
||||
(RequestPath::Bare(path), query, fragment)
|
||||
let (path, query, fragment) = Self::parse_query_framgment(specifier, 0);
|
||||
(SpecifierPath::Bare(path), query, fragment)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Self { path, query, fragment })
|
||||
}
|
||||
|
||||
fn parse_query_framgment(request: &str, skip: usize) -> (&str, Option<&str>, Option<&str>) {
|
||||
fn parse_query_framgment(specifier: &str, skip: usize) -> (&str, Option<&str>, Option<&str>) {
|
||||
let mut query_start: Option<usize> = None;
|
||||
let mut fragment_start: Option<usize> = None;
|
||||
|
||||
for (i, c) in request.as_bytes().iter().enumerate().skip(skip) {
|
||||
for (i, c) in specifier.as_bytes().iter().enumerate().skip(skip) {
|
||||
if *c == b'?' {
|
||||
query_start = Some(i);
|
||||
}
|
||||
|
|
@ -78,49 +75,49 @@ impl<'a> Request<'a> {
|
|||
match (query_start, fragment_start) {
|
||||
(Some(i), Some(j)) => {
|
||||
debug_assert!(i < j);
|
||||
(&request[..i], Some(&request[i..j]), Some(&request[j..]))
|
||||
(&specifier[..i], Some(&specifier[i..j]), Some(&specifier[j..]))
|
||||
}
|
||||
(Some(i), None) => (&request[..i], Some(&request[i..]), None),
|
||||
(None, Some(j)) => (&request[..j], None, Some(&request[j..])),
|
||||
_ => (request, None, None),
|
||||
(Some(i), None) => (&specifier[..i], Some(&specifier[i..]), None),
|
||||
(None, Some(j)) => (&specifier[..j], None, Some(&specifier[j..])),
|
||||
_ => (specifier, None, None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Request, RequestError, RequestPath};
|
||||
use super::{Specifier, SpecifierError, SpecifierPath};
|
||||
|
||||
#[test]
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
fn size_asserts() {
|
||||
static_assertions::assert_eq_size!(Request, [u8; 56]);
|
||||
static_assertions::assert_eq_size!(Specifier, [u8; 56]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty() {
|
||||
let request = "";
|
||||
assert_eq!(Request::parse(request), Err(RequestError::Empty));
|
||||
let specifier = "";
|
||||
assert_eq!(Specifier::parse(specifier), Err(SpecifierError::Empty));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn absolute() -> Result<(), RequestError> {
|
||||
let request = "/test?#";
|
||||
let parsed = Request::parse(request)?;
|
||||
assert_eq!(parsed.path, RequestPath::Absolute("/test"));
|
||||
fn absolute() -> Result<(), SpecifierError> {
|
||||
let specifier = "/test?#";
|
||||
let parsed = Specifier::parse(specifier)?;
|
||||
assert_eq!(parsed.path, SpecifierPath::Absolute("/test"));
|
||||
assert_eq!(parsed.query, Some("?"));
|
||||
assert_eq!(parsed.fragment, Some("#"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn relative() -> Result<(), RequestError> {
|
||||
let requests = ["./test", "../test", "../../test"];
|
||||
for request in requests {
|
||||
let mut r = request.to_string();
|
||||
fn relative() -> Result<(), SpecifierError> {
|
||||
let specifiers = ["./test", "../test", "../../test"];
|
||||
for specifier in specifiers {
|
||||
let mut r = specifier.to_string();
|
||||
r.push_str("?#");
|
||||
let parsed = Request::parse(&r)?;
|
||||
assert_eq!(parsed.path, RequestPath::Relative(request));
|
||||
let parsed = Specifier::parse(&r)?;
|
||||
assert_eq!(parsed.path, SpecifierPath::Relative(specifier));
|
||||
assert_eq!(parsed.query, Some("?"));
|
||||
assert_eq!(parsed.fragment, Some("#"));
|
||||
}
|
||||
|
|
@ -128,13 +125,13 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn hash() -> Result<(), RequestError> {
|
||||
let requests = ["#", "#path"];
|
||||
for request in requests {
|
||||
let mut r = request.to_string();
|
||||
fn hash() -> Result<(), SpecifierError> {
|
||||
let specifiers = ["#", "#path"];
|
||||
for specifier in specifiers {
|
||||
let mut r = specifier.to_string();
|
||||
r.push_str("?#");
|
||||
let parsed = Request::parse(&r)?;
|
||||
assert_eq!(parsed.path, RequestPath::Hash(request));
|
||||
let parsed = Specifier::parse(&r)?;
|
||||
assert_eq!(parsed.path, SpecifierPath::Hash(specifier));
|
||||
assert_eq!(parsed.query, Some("?"));
|
||||
assert_eq!(parsed.fragment, Some("#"));
|
||||
}
|
||||
|
|
@ -142,13 +139,13 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn module() -> Result<(), RequestError> {
|
||||
let requests = ["module"];
|
||||
for request in requests {
|
||||
let mut r = request.to_string();
|
||||
fn module() -> Result<(), SpecifierError> {
|
||||
let specifiers = ["module"];
|
||||
for specifier in specifiers {
|
||||
let mut r = specifier.to_string();
|
||||
r.push_str("?#");
|
||||
let parsed = Request::parse(&r)?;
|
||||
assert_eq!(parsed.path, RequestPath::Bare(request));
|
||||
let parsed = Specifier::parse(&r)?;
|
||||
assert_eq!(parsed.path, SpecifierPath::Bare(specifier));
|
||||
assert_eq!(parsed.query, Some("?"));
|
||||
assert_eq!(parsed.fragment, Some("#"));
|
||||
}
|
||||
|
|
@ -156,7 +153,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn query_fragment() -> Result<(), RequestError> {
|
||||
fn query_fragment() -> Result<(), SpecifierError> {
|
||||
let data = [
|
||||
("a?", Some("?"), None),
|
||||
("a?query", Some("?query"), None),
|
||||
|
|
@ -170,11 +167,11 @@ mod tests {
|
|||
("a#fragment?query", None, Some("#fragment?query")),
|
||||
];
|
||||
|
||||
for (request_str, query, fragment) in data {
|
||||
let request = Request::parse(request_str)?;
|
||||
assert_eq!(request.path.as_str(), "a", "{request_str}");
|
||||
assert_eq!(request.query, query, "{request_str}");
|
||||
assert_eq!(request.fragment, fragment, "{request_str}");
|
||||
for (specifier_str, query, fragment) in data {
|
||||
let specifier = Specifier::parse(specifier_str)?;
|
||||
assert_eq!(specifier.path.as_str(), "a", "{specifier_str}");
|
||||
assert_eq!(specifier.query, query, "{specifier_str}");
|
||||
assert_eq!(specifier.fragment, fragment, "{specifier_str}");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
@ -182,7 +179,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
// https://github.com/webpack/enhanced-resolve/blob/main/test/identifier.test.js
|
||||
fn enhanced_resolve_edge_cases() -> Result<(), RequestError> {
|
||||
fn enhanced_resolve_edge_cases() -> Result<(), SpecifierError> {
|
||||
let data = [
|
||||
("path/#", "path/", "", "#"),
|
||||
("path/as/?", "path/as/", "?", ""),
|
||||
|
|
@ -194,11 +191,11 @@ mod tests {
|
|||
("path/#/not/a/hash?not-a-query", "path/", "", "#/not/a/hash?not-a-query"),
|
||||
];
|
||||
|
||||
for (request_str, path, query, fragment) in data {
|
||||
let request = Request::parse(request_str)?;
|
||||
assert_eq!(request.path.as_str(), path, "{request_str}");
|
||||
assert_eq!(request.query.unwrap_or(""), query, "{request_str}");
|
||||
assert_eq!(request.fragment.unwrap_or(""), fragment, "{request_str}");
|
||||
for (specifier_str, path, query, fragment) in data {
|
||||
let specifier = Specifier::parse(specifier_str)?;
|
||||
assert_eq!(specifier.path.as_str(), path, "{specifier_str}");
|
||||
assert_eq!(specifier.query.unwrap_or(""), query, "{specifier_str}");
|
||||
assert_eq!(specifier.fragment.unwrap_or(""), fragment, "{specifier_str}");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
@ -206,7 +203,7 @@ mod tests {
|
|||
|
||||
// https://github.com/webpack/enhanced-resolve/blob/main/test/identifier.test.js
|
||||
#[test]
|
||||
fn enhanced_resolve_windows_like() -> Result<(), RequestError> {
|
||||
fn enhanced_resolve_windows_like() -> Result<(), SpecifierError> {
|
||||
let data = [
|
||||
("path\\#", "path\\", "", "#"),
|
||||
("path\\as\\?", "path\\as\\", "?", ""),
|
||||
|
|
@ -218,11 +215,11 @@ mod tests {
|
|||
("path\\#/not/a/hash?not-a-query", "path\\", "", "#/not/a/hash?not-a-query"),
|
||||
];
|
||||
|
||||
for (request_str, path, query, fragment) in data {
|
||||
let request = Request::parse(request_str)?;
|
||||
assert_eq!(request.path.as_str(), path, "{request_str}");
|
||||
assert_eq!(request.query.unwrap_or(""), query, "{request_str}");
|
||||
assert_eq!(request.fragment.unwrap_or(""), fragment, "{request_str}");
|
||||
for (specifier_str, path, query, fragment) in data {
|
||||
let specifier = Specifier::parse(specifier_str)?;
|
||||
assert_eq!(specifier.path.as_str(), path, "{specifier_str}");
|
||||
assert_eq!(specifier.query.unwrap_or(""), query, "{specifier_str}");
|
||||
assert_eq!(specifier.fragment.unwrap_or(""), fragment, "{specifier_str}");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
@ -34,7 +34,7 @@ fn alias() {
|
|||
("b$".into(), vec![AliasValue::Path("a/index".into())]),
|
||||
("c$".into(), vec![AliasValue::Path("/a/index".into())]),
|
||||
("multiAlias".into(), vec![AliasValue::Path("b".into()), AliasValue::Path("c".into()), AliasValue::Path("d".into()), AliasValue::Path("e".into()), AliasValue::Path("a".into())]),
|
||||
("recursive".into(), vec![AliasValue::Path("recursive/dir".into())]),
|
||||
// ("recursive".into(), vec![AliasValue::Path("recursive/dir".into())]),
|
||||
("/d/dir".into(), vec![AliasValue::Path("/c/dir".into())]),
|
||||
("/d/index.js".into(), vec![AliasValue::Path("/c/index".into())]),
|
||||
// alias configuration should work
|
||||
|
|
@ -62,9 +62,9 @@ fn alias() {
|
|||
("should resolve '#' alias 2", "#/index", "/c/dir/index"),
|
||||
("should resolve '@' alias 1", "@", "/c/dir/index"),
|
||||
("should resolve '@' alias 2", "@/index", "/c/dir/index"),
|
||||
("should resolve a recursive aliased module 1", "recursive", "/recursive/dir/index"),
|
||||
("should resolve a recursive aliased module 2", "recursive/index", "/recursive/dir/index"),
|
||||
// TODO recursive
|
||||
// ("should resolve a recursive aliased module 1", "recursive", "/recursive/dir/index"),
|
||||
// ("should resolve a recursive aliased module 2", "recursive/index", "/recursive/dir/index"),
|
||||
// ("should resolve a recursive aliased module 3", "recursive/dir", "/recursive/dir/index"),
|
||||
// ("should resolve a recursive aliased module 4", "recursive/dir/index", "/recursive/dir/index"),
|
||||
("should resolve a file aliased module 1", "b", "/a/index"),
|
||||
|
|
@ -91,7 +91,7 @@ fn alias() {
|
|||
|
||||
#[rustfmt::skip]
|
||||
let ignore = [
|
||||
("should resolve an ignore module", "ignored", ResolveError::Ignored(f.join("ignored").into_boxed_path()))
|
||||
("should resolve an ignore module", "ignored", ResolveError::Ignored(f.join("ignored")))
|
||||
];
|
||||
|
||||
for (comment, request, expected) in ignore {
|
||||
|
|
@ -110,5 +110,5 @@ fn absolute_path() {
|
|||
..ResolveOptions::default()
|
||||
});
|
||||
let resolution = resolver.resolve(&f, "foo/index");
|
||||
assert_eq!(resolution, Err(ResolveError::Ignored(f.into_boxed_path())));
|
||||
assert_eq!(resolution, Err(ResolveError::Ignored(f)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ fn ignore() {
|
|||
|
||||
for (path, request, expected) in data {
|
||||
let resolution = resolver.resolve(&path, request);
|
||||
let expected = ResolveError::Ignored(expected.into());
|
||||
let expected = ResolveError::Ignored(expected);
|
||||
assert_eq!(resolution, Err(expected), "{path:?} {request}");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@
|
|||
//!
|
||||
//! The huge exports field test cases are at the bottom of this file.
|
||||
|
||||
use crate::{ExportsField, PathUtil, Resolution, ResolveError, ResolveOptions, Resolver};
|
||||
use crate::{
|
||||
ExportsField, PathUtil, Resolution, ResolveContext, ResolveError, ResolveOptions, Resolver,
|
||||
};
|
||||
use serde_json::json;
|
||||
use std::path::Path;
|
||||
|
||||
|
|
@ -53,10 +55,10 @@ fn test() {
|
|||
|
||||
#[rustfmt::skip]
|
||||
let fail = [
|
||||
("throw error if extension not provided", f2.clone(), "exports-field/dist/main", ResolveError::NotFound(f2.join("node_modules/exports-field/lib/lib2/main").into_boxed_path())),
|
||||
// TODO: ("resolver should respect query parameters #2. Direct matching", f2.clone(), "exports-field?foo", ResolveError::NotFound(f2.join("").into_boxed_path())),
|
||||
// TODO: ("resolver should respect fragment parameters #2. Direct matching", f2.clone(), "exports-field#foo", ResolveError::NotFound(f2.join("").into_boxed_path())),
|
||||
("relative path should not work with exports field", f.clone(), "./node_modules/exports-field/dist/main.js", ResolveError::NotFound(f.join("node_modules/exports-field/dist/main.js").into_boxed_path())),
|
||||
("throw error if extension not provided", f2.clone(), "exports-field/dist/main", ResolveError::NotFound(f2.join("node_modules/exports-field/lib/lib2/main"))),
|
||||
// TODO: ("resolver should respect query parameters #2. Direct matching", f2.clone(), "exports-field?foo", ResolveError::NotFound(f2.join(""))),
|
||||
// TODO: ("resolver should respect fragment parameters #2. Direct matching", f2.clone(), "exports-field#foo", ResolveError::NotFound(f2.join(""))),
|
||||
("relative path should not work with exports field", f.clone(), "./node_modules/exports-field/dist/main.js", ResolveError::NotFound(f.join("node_modules/exports-field/dist/main.js"))),
|
||||
("backtracking should not work for request", f.clone(), "exports-field/dist/../../../a.js", ResolveError::InvalidPackageTarget("./lib/../../../a.js".to_string())),
|
||||
("backtracking should not work for exports field target", f.clone(), "exports-field/dist/a.js", ResolveError::InvalidPackageTarget("./../../a.js".to_string())),
|
||||
("not exported error", f.clone(), "exports-field/anything/else", ResolveError::PackagePathNotExported("./anything/else".to_string())),
|
||||
|
|
@ -2443,6 +2445,7 @@ fn test_cases() {
|
|||
case.request.trim_start_matches('.'),
|
||||
&case.exports_field,
|
||||
&case.condition_names.iter().map(ToString::to_string).collect::<Vec<_>>(),
|
||||
&ResolveContext::default(),
|
||||
)
|
||||
.map(|p| p.map(|p| p.to_path_buf()));
|
||||
if let Some(expect) = case.expect {
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ fn extensions() {
|
|||
|
||||
for (comment, request, expected_error) in fail {
|
||||
let resolution = resolver.resolve(&f, request);
|
||||
let error = ResolveError::NotFound(expected_error.into_boxed_path());
|
||||
let error = ResolveError::NotFound(expected_error);
|
||||
assert_eq!(resolution, Err(error), "{comment} {request} {resolution:?}");
|
||||
}
|
||||
}
|
||||
|
|
@ -55,7 +55,7 @@ fn default_enforce_extension() {
|
|||
})
|
||||
.resolve(&f, "./foo");
|
||||
|
||||
assert_eq!(resolved, Err(ResolveError::NotFound(f.join("foo").into_boxed_path())));
|
||||
assert_eq!(resolved, Err(ResolveError::NotFound(f.join("foo"))));
|
||||
// TODO: need to match missingDependencies returned from the resolve function
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ fn fallback() {
|
|||
|
||||
#[rustfmt::skip]
|
||||
let ignore = [
|
||||
("should resolve an ignore module", "ignored", ResolveError::Ignored(f.join("ignored").into_boxed_path()))
|
||||
("should resolve an ignore module", "ignored", ResolveError::Ignored(f.join("ignored")))
|
||||
];
|
||||
|
||||
for (comment, request, expected) in ignore {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@
|
|||
|
||||
use serde_json::json;
|
||||
|
||||
use crate::{MatchObject, PathUtil, Resolution, ResolveError, ResolveOptions, Resolver};
|
||||
use crate::{
|
||||
MatchObject, PathUtil, Resolution, ResolveContext, ResolveError, ResolveOptions, Resolver,
|
||||
};
|
||||
use std::path::Path;
|
||||
|
||||
#[test]
|
||||
|
|
@ -1281,6 +1283,7 @@ fn test_cases() {
|
|||
Path::new(""),
|
||||
true,
|
||||
&case.condition_names.iter().map(ToString::to_string).collect::<Vec<_>>(),
|
||||
&ResolveContext::default(),
|
||||
)
|
||||
.map(|p| p.map(|p| p.to_path_buf()));
|
||||
if let Some(expect) = case.expect {
|
||||
|
|
|
|||
|
|
@ -61,5 +61,5 @@ fn no_description_file() {
|
|||
// without description file
|
||||
let resolver =
|
||||
Resolver::new(ResolveOptions { description_files: vec![], ..ResolveOptions::default() });
|
||||
assert_eq!(resolver.resolve(&f, "."), Err(ResolveError::NotFound(f.into_boxed_path())));
|
||||
assert_eq!(resolver.resolve(&f, "."), Err(ResolveError::NotFound(f)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ fn roots() {
|
|||
#[rustfmt::skip]
|
||||
let fail = [
|
||||
// TODO should be "Module Not Found" error
|
||||
("should not work with relative path", "fixtures/b.js", ResolveError::NotFound(f.clone().into_boxed_path()))
|
||||
("should not work with relative path", "fixtures/b.js", ResolveError::NotFound(f.clone()))
|
||||
];
|
||||
|
||||
for (comment, request, expected) in fail {
|
||||
|
|
|
|||
Loading…
Reference in a new issue