mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
feat(resolver): implement the basics of path alias (#564)
This commit is contained in:
parent
3cbec3459a
commit
0c17f4f783
8 changed files with 279 additions and 67 deletions
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
| Done | Field | Default | Description |
|
||||
|------|------------------|-----------------------------| --------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| | alias | [] | A list of module alias configurations or an object which maps key to value |
|
||||
| ✅ | alias | [] | A list of module alias configurations or an object which maps key to value |
|
||||
| ✅ | aliasFields | [] | A list of alias fields in description files |
|
||||
| ✅ | extensionAlias | {} | An object which maps extension to extension aliases |
|
||||
| | cachePredicate | function() { return true }; | A function which decides whether a request should be cached or not. An object is passed to the function with `path` and `request` properties. |
|
||||
|
|
@ -24,7 +24,7 @@
|
|||
| | fullySpecified | false | Request passed to resolve is already fully specified and extensions or main files are not resolved for it (they are still resolved for internal requests) |
|
||||
| | mainFields | ["main"] | A list of main fields in description files |
|
||||
| ✅ | mainFiles | ["index"] | A list of main files in directories |
|
||||
| | modules | ["node_modules"] | A list of directories to resolve modules from, can be absolute path or folder name |
|
||||
| ✅ | modules | ["node_modules"] | A list of directories to resolve modules from, can be absolute path or folder name |
|
||||
| | plugins | [] | A list of additional resolve plugins which should be applied |
|
||||
| | resolver | undefined | A prepared Resolver to which the plugins are attached |
|
||||
| | resolveToContext | false | Resolve to a context instead of a file |
|
||||
|
|
@ -41,7 +41,7 @@ Tests ported from [enhanced-resolve](https://github.com/webpack/enhanced-resolve
|
|||
|
||||
- [ ] CachedInputFileSystem.test.js
|
||||
- [ ] SyncAsyncFileSystemDecorator.test.js
|
||||
- [ ] alias.test.js
|
||||
- [x] alias.test.js (partially done)
|
||||
- [x] browserField.test.js (reading the browser field is currently static - not read from the `browserField` option)
|
||||
- [ ] dependencies.test.js
|
||||
- [ ] exportsField.test.js
|
||||
|
|
|
|||
|
|
@ -26,6 +26,9 @@ pub enum ResolveError {
|
|||
/// All of the aliased extension are not found
|
||||
ExtensionAlias,
|
||||
|
||||
/// All of the aliases are not found
|
||||
Alias(String),
|
||||
|
||||
/// JSON parse error
|
||||
JSON(JSONError),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ mod request;
|
|||
mod resolution;
|
||||
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
ffi::OsStr,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
|
@ -24,7 +25,7 @@ pub use crate::{
|
|||
cache::Cache,
|
||||
error::{JSONError, ResolveError},
|
||||
file_system::{FileMetadata, FileSystem, FileSystemOs},
|
||||
options::ResolveOptions,
|
||||
options::{AliasValue, ResolveOptions},
|
||||
resolution::Resolution,
|
||||
};
|
||||
use crate::{
|
||||
|
|
@ -66,10 +67,15 @@ impl<Fs: FileSystem> ResolverGeneric<Fs> {
|
|||
pub fn resolve<P: AsRef<Path>>(
|
||||
&self,
|
||||
path: P,
|
||||
request: &str,
|
||||
request_str: &str,
|
||||
) -> Result<Resolution, ResolveError> {
|
||||
let request = Request::parse(request).map_err(ResolveError::Request)?;
|
||||
let path = self.require(path.as_ref(), &request)?;
|
||||
let path = path.as_ref();
|
||||
let request = Request::parse(request_str).map_err(ResolveError::Request)?;
|
||||
let path = if let Some(path) = self.load_alias(path, request.path.as_str())? {
|
||||
path
|
||||
} else {
|
||||
self.require(path, &request)?
|
||||
};
|
||||
Ok(Resolution {
|
||||
path,
|
||||
query: request.query.map(ToString::to_string),
|
||||
|
|
@ -114,6 +120,9 @@ impl<Fs: FileSystem> ResolverGeneric<Fs> {
|
|||
return Err(ResolveError::NotFound(path.into_boxed_path()));
|
||||
}
|
||||
// 4. If X begins with '#'
|
||||
RequestPath::Hash(hash_path) => {
|
||||
request_str = hash_path;
|
||||
}
|
||||
// a. LOAD_PACKAGE_IMPORTS(X, dirname(Y))
|
||||
RequestPath::Module(module_path) => {
|
||||
request_str = module_path;
|
||||
|
|
@ -159,13 +168,19 @@ impl<Fs: FileSystem> ResolverGeneric<Fs> {
|
|||
|
||||
#[allow(clippy::unnecessary_wraps)]
|
||||
fn load_index(&self, path: &Path) -> ResolveState {
|
||||
// 1. If X/index.js is a file, load X/index.js as JavaScript text. STOP
|
||||
// 2. If X/index.json is a file, parse X/index.json to a JavaScript object. STOP
|
||||
// 3. If X/index.node is a file, load X/index.node as binary addon. STOP
|
||||
for extension in &self.options.extensions {
|
||||
let index_path = path.join("index").with_extension(extension);
|
||||
if self.cache.is_file(&index_path) {
|
||||
return Ok(Some(index_path));
|
||||
for main_field in &self.options.main_files {
|
||||
let main_path = path.join(main_field);
|
||||
if !self.options.enforce_extension && self.cache.is_file(&main_path) {
|
||||
return Ok(Some(main_path));
|
||||
}
|
||||
// 1. If X/index.js is a file, load X/index.js as JavaScript text. STOP
|
||||
// 2. If X/index.json is a file, parse X/index.json to a JavaScript object. STOP
|
||||
// 3. If X/index.node is a file, load X/index.node as binary addon. STOP
|
||||
for extension in &self.options.extensions {
|
||||
let main_path_with_extension = main_path.with_extension(extension);
|
||||
if self.cache.is_file(&main_path_with_extension) {
|
||||
return Ok(Some(main_path_with_extension));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
|
|
@ -200,7 +215,7 @@ impl<Fs: FileSystem> ResolverGeneric<Fs> {
|
|||
|
||||
fn load_node_modules(&self, start: &Path, request_str: &str) -> ResolveState {
|
||||
// 1. let DIRS = NODE_MODULES_PATHS(START)
|
||||
let dirs = Self::node_module_paths(start);
|
||||
let dirs = self.node_module_paths(start);
|
||||
// 2. for each DIR in DIRS:
|
||||
for node_module_path in dirs {
|
||||
let node_module_path = node_module_path.join(request_str);
|
||||
|
|
@ -224,10 +239,9 @@ impl<Fs: FileSystem> ResolverGeneric<Fs> {
|
|||
Ok(None)
|
||||
}
|
||||
|
||||
fn node_module_paths(path: &Path) -> impl Iterator<Item = PathBuf> + '_ {
|
||||
fn node_module_paths<'a>(&'a self, path: &'a Path) -> impl Iterator<Item = PathBuf> + 'a {
|
||||
path.ancestors()
|
||||
.filter(|path| path.file_name().is_some_and(|name| name != "node_modules"))
|
||||
.map(|path| path.join("node_modules"))
|
||||
.flat_map(|path| self.options.modules.iter().map(|module| path.join(module)))
|
||||
}
|
||||
|
||||
fn load_package_self(&self, path: &Path, request_str: &str) -> ResolveState {
|
||||
|
|
@ -247,6 +261,37 @@ impl<Fs: FileSystem> ResolverGeneric<Fs> {
|
|||
Ok(None)
|
||||
}
|
||||
|
||||
fn load_alias(&self, path: &Path, request_str: &str) -> ResolveState {
|
||||
for (alias, requests) in &self.options.alias {
|
||||
let exact_match = alias.strip_prefix(request_str).is_some_and(|c| c == "$");
|
||||
if request_str.starts_with(alias) || exact_match {
|
||||
for request in requests {
|
||||
match request {
|
||||
AliasValue::Path(new_request) => {
|
||||
let new_request = if exact_match {
|
||||
Cow::Borrowed(new_request)
|
||||
} else {
|
||||
Cow::Owned(request_str.replacen(alias, new_request, 1))
|
||||
};
|
||||
let new_request =
|
||||
Request::parse(&new_request).map_err(ResolveError::Request)?;
|
||||
match self.require(path, &new_request) {
|
||||
Err(ResolveError::NotFound(_)) => { /* noop */ }
|
||||
Ok(path) => return Ok(Some(path)),
|
||||
Err(err) => return Err(err),
|
||||
}
|
||||
}
|
||||
AliasValue::Ignore => {
|
||||
return Err(ResolveError::Ignored(path.join(alias).into_boxed_path()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return Err(ResolveError::Alias(alias.clone()));
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Given an extension alias map `{".js": [".ts", "js"]}`,
|
||||
/// load the mapping instead of the provided extension
|
||||
///
|
||||
|
|
|
|||
|
|
@ -1,5 +1,17 @@
|
|||
#[derive(Debug, Clone)]
|
||||
pub enum AliasValue {
|
||||
/// The path value
|
||||
Path(String),
|
||||
|
||||
/// The `false` value
|
||||
Ignore,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ResolveOptions {
|
||||
/// A list of module alias configurations or an object which maps key to value
|
||||
pub alias: Vec<(String, Vec<AliasValue>)>,
|
||||
|
||||
/// A list of alias fields in description files.
|
||||
/// Specify a field, such as `browser`, to be parsed according to [this specification](https://github.com/defunctzombie/package-browser-field-spec).
|
||||
///
|
||||
|
|
@ -25,16 +37,23 @@ pub struct ResolveOptions {
|
|||
///
|
||||
/// Default `["index"]`
|
||||
pub main_files: Vec<String>,
|
||||
|
||||
/// A list of directories to resolve modules from, can be absolute path or folder name
|
||||
///
|
||||
/// Default `["node_modules"]`
|
||||
pub modules: Vec<String>,
|
||||
}
|
||||
|
||||
impl Default for ResolveOptions {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
alias: vec![],
|
||||
alias_fields: vec![],
|
||||
extension_alias: vec![],
|
||||
enforce_extension: false,
|
||||
extensions: vec![".js".into(), ".json".into(), ".node".into()],
|
||||
main_files: vec!["index".into()],
|
||||
modules: vec!["node_modules".into()],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,21 +16,53 @@ pub enum RequestPath<'a> {
|
|||
Absolute(&'a str),
|
||||
/// `./path`, `../path`
|
||||
Relative(&'a str),
|
||||
/// `#path`
|
||||
Hash(&'a str),
|
||||
/// `path`, `@scope/path`
|
||||
Module(&'a str),
|
||||
}
|
||||
|
||||
impl<'a> RequestPath<'a> {
|
||||
pub fn as_str(&self) -> &str {
|
||||
match self {
|
||||
Self::Absolute(s) | Self::Relative(s) | Self::Hash(s) | Self::Module(s) => s,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Request<'a> {
|
||||
pub fn parse(request: &'a str) -> Result<Request<'a>, RequestError> {
|
||||
let (request, query, fragment) = Self::parse_query_framgment(request);
|
||||
RequestPath::parse(request).map(|path| Self { path, query, fragment })
|
||||
if request.is_empty() {
|
||||
return Err(RequestError::Empty);
|
||||
}
|
||||
|
||||
let (path, query, fragment) = match request.as_bytes()[0] {
|
||||
b'/' => {
|
||||
let (path, query, fragment) = Self::parse_query_framgment(request, 1);
|
||||
(RequestPath::Absolute(path), query, fragment)
|
||||
}
|
||||
b'.' => {
|
||||
let (path, query, fragment) = Self::parse_query_framgment(request, 1);
|
||||
(RequestPath::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(request, 0);
|
||||
(RequestPath::Module(path), query, fragment)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Self { path, query, fragment })
|
||||
}
|
||||
|
||||
fn parse_query_framgment(request: &str) -> (&str, Option<&str>, Option<&str>) {
|
||||
fn parse_query_framgment(request: &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() {
|
||||
for (i, c) in request.as_bytes().iter().enumerate().skip(skip) {
|
||||
match *c {
|
||||
b'?' => query_start = Some(i),
|
||||
b'#' => fragment_start = Some(i),
|
||||
|
|
@ -52,17 +84,6 @@ impl<'a> Request<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> RequestPath<'a> {
|
||||
fn parse(request: &'a str) -> Result<Self, RequestError> {
|
||||
match request.chars().next() {
|
||||
Some('/') => Ok(Self::Absolute(request)),
|
||||
Some('.') => Ok(Self::Relative(request)),
|
||||
Some(_) => Ok(Self::Module(request)),
|
||||
_ => Err(RequestError::Empty),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Request, RequestError, RequestPath};
|
||||
|
|
@ -81,18 +102,38 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn absolute() -> Result<(), RequestError> {
|
||||
let request = "/test";
|
||||
let request = "/test?#";
|
||||
let parsed = Request::parse(request)?;
|
||||
assert_eq!(parsed.path, RequestPath::Absolute(request));
|
||||
assert_eq!(parsed.path, RequestPath::Absolute("/test"));
|
||||
assert_eq!(parsed.query, Some("?"));
|
||||
assert_eq!(parsed.fragment, Some("#"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_relative() -> Result<(), RequestError> {
|
||||
let requests = ["./test", "../test", "../../test"];
|
||||
fn relative() -> Result<(), RequestError> {
|
||||
let requests = ["./test", "../test?#", "../../test?#"];
|
||||
for request in requests {
|
||||
let parsed = Request::parse(request)?;
|
||||
let mut r = request.to_string();
|
||||
r.push_str("?#");
|
||||
let parsed = Request::parse(&r)?;
|
||||
assert_eq!(parsed.path, RequestPath::Relative(request));
|
||||
assert_eq!(parsed.query, Some("?"));
|
||||
assert_eq!(parsed.fragment, Some("#"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hash() -> Result<(), RequestError> {
|
||||
let requests = ["#", "#path"];
|
||||
for request in requests {
|
||||
let mut r = request.to_string();
|
||||
r.push_str("?#");
|
||||
let parsed = Request::parse(&r)?;
|
||||
assert_eq!(parsed.path, RequestPath::Hash(request));
|
||||
assert_eq!(parsed.query, Some("?"));
|
||||
assert_eq!(parsed.fragment, Some("#"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -101,31 +142,38 @@ mod tests {
|
|||
fn module() -> Result<(), RequestError> {
|
||||
let requests = ["module"];
|
||||
for request in requests {
|
||||
let parsed = Request::parse(request)?;
|
||||
let mut r = request.to_string();
|
||||
r.push_str("?#");
|
||||
let parsed = Request::parse(&r)?;
|
||||
assert_eq!(parsed.path, RequestPath::Module(request));
|
||||
assert_eq!(parsed.query, Some("?"));
|
||||
assert_eq!(parsed.fragment, Some("#"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn query_fragment() -> Result<(), RequestError> {
|
||||
assert_eq!(Request::parse("a?")?.query, Some("?"));
|
||||
assert_eq!(Request::parse("a?b")?.query, Some("?b"));
|
||||
let data = [
|
||||
("a?", Some("?"), None),
|
||||
("a?query", Some("?query"), None),
|
||||
("a#", None, Some("#")),
|
||||
("a#fragment", None, Some("#fragment")),
|
||||
("a?#", Some("?"), Some("#")),
|
||||
("a?#fragment", Some("?"), Some("#fragment")),
|
||||
("a?query#", Some("?query"), Some("#")),
|
||||
("a?query#fragment", Some("?query"), Some("#fragment")),
|
||||
("a#fragment?", Some("?"), Some("#fragment")),
|
||||
("a#fragment?query", Some("?query"), Some("#fragment")),
|
||||
];
|
||||
|
||||
assert_eq!(Request::parse("a#")?.fragment, Some("#"));
|
||||
assert_eq!(Request::parse("a#b")?.fragment, Some("#b"));
|
||||
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}");
|
||||
}
|
||||
|
||||
let request = Request::parse("a?#")?;
|
||||
assert_eq!(request.query, Some("?"));
|
||||
assert_eq!(request.fragment, Some("#"));
|
||||
|
||||
let request = Request::parse("a?b#c")?;
|
||||
assert_eq!(request.query, Some("?b"));
|
||||
assert_eq!(request.fragment, Some("#c"));
|
||||
|
||||
let request = Request::parse("a#b?c")?;
|
||||
assert_eq!(request.query, Some("?c"));
|
||||
assert_eq!(request.fragment, Some("#b"));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,19 +12,35 @@ pub struct Resolution {
|
|||
}
|
||||
|
||||
impl Resolution {
|
||||
/// Returns the path without query and fragment
|
||||
pub fn path(&self) -> &Path {
|
||||
&self.path
|
||||
}
|
||||
|
||||
/// Returns the path without query and fragment
|
||||
pub fn into_path_buf(self) -> PathBuf {
|
||||
self.path
|
||||
}
|
||||
|
||||
/// Returns the path query `?query`, contains the leading `?`
|
||||
pub fn query(&self) -> Option<&str> {
|
||||
self.query.as_deref()
|
||||
}
|
||||
|
||||
/// Returns the path fragment `#fragment`, contains the leading `#`
|
||||
pub fn fragment(&self) -> Option<&str> {
|
||||
self.fragment.as_deref()
|
||||
}
|
||||
|
||||
/// Returns the full path with query and fragment
|
||||
pub fn full_path(self) -> PathBuf {
|
||||
let mut path = self.path.into_os_string();
|
||||
if let Some(query) = &self.query {
|
||||
path.push(query);
|
||||
}
|
||||
if let Some(fragment) = &self.fragment {
|
||||
path.push(fragment);
|
||||
}
|
||||
PathBuf::from(path)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,16 @@
|
|||
//! <https://github.com/webpack/enhanced-resolve/blob/main/test/alias.test.js>
|
||||
|
||||
use oxc_resolver::{ResolveOptions, ResolverGeneric};
|
||||
use std::path::Path;
|
||||
|
||||
use oxc_resolver::{AliasValue, ResolveError, ResolveOptions, Resolver, ResolverGeneric};
|
||||
|
||||
use crate::MemoryFS;
|
||||
|
||||
#[test]
|
||||
fn alias() {
|
||||
#[cfg(not(target_os = "windows"))] // MemoryFS's path separator is always `/` so the test will not pass in windows.
|
||||
fn alias() -> Result<(), ResolveError> {
|
||||
let f = Path::new("/");
|
||||
|
||||
let file_system = MemoryFS::new(&[
|
||||
("/a/index", ""),
|
||||
("/a/dir/index", ""),
|
||||
|
|
@ -21,7 +26,91 @@ fn alias() {
|
|||
("/e/anotherDir/index", ""),
|
||||
("/e/dir/file", ""),
|
||||
]);
|
||||
let options = ResolveOptions::default();
|
||||
|
||||
#[rustfmt::skip]
|
||||
let options = ResolveOptions {
|
||||
alias: vec![
|
||||
("aliasA".into(), vec![AliasValue::Path("a".into())]),
|
||||
("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())]),
|
||||
("/d/dir".into(), vec![AliasValue::Path("/c/dir".into())]),
|
||||
("/d/index.js".into(), vec![AliasValue::Path("/c/index".into())]),
|
||||
// alias configuration should work
|
||||
("#".into(), vec![AliasValue::Path("/c/dir".into())]),
|
||||
("@".into(), vec![AliasValue::Path("/c/dir".into())]),
|
||||
("ignored".into(), vec![AliasValue::Ignore]),
|
||||
],
|
||||
modules: vec!["/".into()],
|
||||
..ResolveOptions::default()
|
||||
};
|
||||
|
||||
let resolver = ResolverGeneric::<MemoryFS>::new_with_file_system(options, file_system);
|
||||
assert!(resolver.resolve("/a/index", ".").is_ok());
|
||||
|
||||
#[rustfmt::skip]
|
||||
let pass = [
|
||||
("should resolve a not aliased module 1", "a", "/a/index"),
|
||||
("should resolve a not aliased module 2", "a/index", "/a/index"),
|
||||
("should resolve a not aliased module 3", "a/dir", "/a/dir/index"),
|
||||
("should resolve a not aliased module 4", "a/dir/index", "/a/dir/index"),
|
||||
("should resolve an aliased module 1", "aliasA", "/a/index"),
|
||||
("should resolve an aliased module 2", "aliasA/index", "/a/index"),
|
||||
("should resolve an aliased module 3", "aliasA/dir", "/a/dir/index"),
|
||||
("should resolve an aliased module 4", "aliasA/dir/index", "/a/dir/index"),
|
||||
("should resolve '#' alias 1", "#", "/c/dir/index"),
|
||||
("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 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"),
|
||||
("should resolve a file aliased module 2", "c", "/a/index"),
|
||||
("should resolve a file aliased module with a query 1", "b?query", "/a/index?query"),
|
||||
("should resolve a file aliased module with a query 1", "c?query", "/a/index?query"),
|
||||
("should resolve a path in a file aliased module 1", "b/index", "/b/index"),
|
||||
("should resolve a path in a file aliased module 2", "b/dir", "/b/dir/index"),
|
||||
("should resolve a path in a file aliased module 3", "b/dir/index", "/b/dir/index"),
|
||||
("should resolve a path in a file aliased module 4", "c/index", "/c/index"),
|
||||
("should resolve a path in a file aliased module 5", "c/dir", "/c/dir/index"),
|
||||
("should resolve a path in a file aliased module 6", "c/dir/index", "/c/dir/index"),
|
||||
// TODO aliased file
|
||||
// ("should resolve a file aliased file 1", "d", "/c/index"),
|
||||
// ("should resolve a file aliased file 2", "d/dir/index", "/c/dir/index"),
|
||||
("should resolve a file in multiple aliased dirs 1", "multiAlias/dir/file", "/e/dir/file"),
|
||||
("should resolve a file in multiple aliased dirs 2", "multiAlias/anotherDir", "/e/anotherDir/index"),
|
||||
];
|
||||
|
||||
for (comment, request, expected) in pass {
|
||||
let resolution = resolver.resolve(f, request)?;
|
||||
assert_eq!(resolution.full_path(), Path::new(expected), "{comment} {request}");
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
let ignore = [
|
||||
("should resolve an ignore module", "ignored", ResolveError::Ignored(f.join("ignored").into_boxed_path()))
|
||||
];
|
||||
|
||||
for (comment, request, expected) in ignore {
|
||||
let resolution = resolver.resolve(f, request);
|
||||
assert_eq!(resolution, Err(expected), "{comment} {request}");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "TODO: absolute path"]
|
||||
fn absolute_path() {
|
||||
let f = super::fixture();
|
||||
let resolver = Resolver::new(ResolveOptions {
|
||||
alias: vec![(f.join("foo").to_str().unwrap().to_string(), vec![AliasValue::Ignore])],
|
||||
modules: vec![f.clone().to_str().unwrap().to_string()],
|
||||
..ResolveOptions::default()
|
||||
});
|
||||
let resolution = resolver.resolve(&f, "foo/index");
|
||||
assert_eq!(resolution, Err(ResolveError::Ignored(f.into_boxed_path())));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,15 +44,7 @@ fn resolve() -> Result<(), ResolveError> {
|
|||
|
||||
for (comment, path, request, expected) in data {
|
||||
let resolution = resolver.resolve(&path, request)?;
|
||||
let mut file_name = resolution.path().file_name().unwrap().to_string_lossy().to_string();
|
||||
if let Some(query) = resolution.query() {
|
||||
file_name.push_str(query);
|
||||
}
|
||||
if let Some(fragment) = resolution.fragment() {
|
||||
file_name.push_str(fragment);
|
||||
}
|
||||
let resolved_path = resolution.path().with_file_name(file_name);
|
||||
assert_eq!(resolved_path, expected, "{comment} {path:?} {request}");
|
||||
assert_eq!(resolution.full_path(), expected, "{comment} {path:?} {request}");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
Loading…
Reference in a new issue