mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
feat(resolver): implement roots (#576)
This commit is contained in:
parent
6089898bf0
commit
beb0ae9d8e
5 changed files with 95 additions and 19 deletions
|
|
@ -31,7 +31,7 @@
|
|||
| | preferRelative | false | Prefer to resolve module requests as relative request and fallback to resolving as module |
|
||||
| | preferAbsolute | false | Prefer to resolve server-relative urls as absolute paths before falling back to resolve in roots |
|
||||
| | restrictions | [] | A list of resolve restrictions |
|
||||
| | roots | [] | A list of root paths |
|
||||
| ✅ | roots | [] | A list of root paths |
|
||||
| | symlinks | true | Whether to resolve symlinks to their symlinked location |
|
||||
| | unsafeCache | false | Use this cache object to unsafely cache the successful requests
|
||||
|
||||
|
|
@ -59,9 +59,9 @@ Tests ported from [enhanced-resolve](https://github.com/webpack/enhanced-resolve
|
|||
- [ ] plugins.test.js
|
||||
- [ ] pnp.test.js
|
||||
- [ ] pr-53.test.js
|
||||
- [x] resolve.test.js (partially done)
|
||||
- [x] resolve.test.js (need to add resolveToContext)
|
||||
- [ ] restrictions.test.js
|
||||
- [ ] roots.test.js
|
||||
- [x] roots.test.js (need to add resolveToContext, resolverPreferAbsolute)
|
||||
- [x] scoped-packages.test.js
|
||||
- [x] simple.test.js
|
||||
- [ ] symlink.test.js
|
||||
|
|
|
|||
|
|
@ -112,25 +112,22 @@ impl<Fs: FileSystem> ResolverGeneric<Fs> {
|
|||
RequestPath::Absolute(absolute_path) => {
|
||||
path = Path::new("/");
|
||||
request_str = absolute_path;
|
||||
if !self.options.roots.is_empty() {
|
||||
for root in &self.options.roots {
|
||||
if let Ok(path) =
|
||||
self.require_relative(root, absolute_path.trim_start_matches('/'))
|
||||
{
|
||||
return Ok(path);
|
||||
}
|
||||
}
|
||||
return Err(ResolveError::NotFound(
|
||||
Path::new(absolute_path).to_path_buf().into_boxed_path(),
|
||||
));
|
||||
}
|
||||
}
|
||||
// 3. If X begins with './' or '/' or '../'
|
||||
RequestPath::Relative(relative_path) => {
|
||||
if let Some(path) = self.load_package_self(path, relative_path)? {
|
||||
return Ok(path);
|
||||
}
|
||||
let path = path.normalize_with(relative_path);
|
||||
// a. LOAD_AS_FILE(Y + X)
|
||||
if !relative_path.ends_with('/') {
|
||||
if let Some(path) = self.load_as_file(&path)? {
|
||||
return Ok(path);
|
||||
}
|
||||
}
|
||||
// b. LOAD_AS_DIRECTORY(Y + X)
|
||||
if let Some(path) = self.load_as_directory(&path)? {
|
||||
return Ok(path);
|
||||
}
|
||||
// c. THROW "not found"
|
||||
return Err(ResolveError::NotFound(path.into_boxed_path()));
|
||||
return self.require_relative(path, relative_path);
|
||||
}
|
||||
// 4. If X begins with '#'
|
||||
RequestPath::Hash(hash_path) => {
|
||||
|
|
@ -156,6 +153,26 @@ impl<Fs: FileSystem> ResolverGeneric<Fs> {
|
|||
Err(ResolveError::NotFound(path.to_path_buf().into_boxed_path()))
|
||||
}
|
||||
|
||||
// 3. If X begins with './' or '/' or '../'
|
||||
fn require_relative(&self, path: &Path, request_str: &str) -> Result<PathBuf, ResolveError> {
|
||||
if let Some(path) = self.load_package_self(path, request_str)? {
|
||||
return Ok(path);
|
||||
}
|
||||
let path = path.normalize_with(request_str);
|
||||
// a. LOAD_AS_FILE(Y + X)
|
||||
if !request_str.ends_with('/') {
|
||||
if let Some(path) = self.load_as_file(&path)? {
|
||||
return Ok(path);
|
||||
}
|
||||
}
|
||||
// b. LOAD_AS_DIRECTORY(Y + X)
|
||||
if let Some(path) = self.load_as_directory(&path)? {
|
||||
return Ok(path);
|
||||
}
|
||||
// c. THROW "not found"
|
||||
Err(ResolveError::NotFound(path.into_boxed_path()))
|
||||
}
|
||||
|
||||
#[allow(clippy::unnecessary_wraps)]
|
||||
fn load_as_file(&self, path: &Path) -> ResolveState {
|
||||
// enhanced-resolve feature: extension_alias
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
pub type Alias = Vec<(String, Vec<AliasValue>)>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
@ -58,6 +60,12 @@ pub struct ResolveOptions {
|
|||
///
|
||||
/// Default `["node_modules"]`
|
||||
pub modules: Vec<String>,
|
||||
|
||||
/// A list of directories where requests of server-relative URLs (starting with '/') are resolved.
|
||||
/// On non-Windows systems these requests are resolved as an absolute path first.
|
||||
///
|
||||
/// Default `[]`
|
||||
pub roots: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
impl Default for ResolveOptions {
|
||||
|
|
@ -72,6 +80,7 @@ impl Default for ResolveOptions {
|
|||
fallback: vec![],
|
||||
main_files: vec!["index".into()],
|
||||
modules: vec!["node_modules".into()],
|
||||
roots: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ mod extensions;
|
|||
mod fallback;
|
||||
mod incorrect_description_file;
|
||||
mod resolve;
|
||||
mod roots;
|
||||
mod scoped_packages;
|
||||
mod simple;
|
||||
|
||||
|
|
|
|||
49
crates/oxc_resolver/tests/enhanced_resolve/test/roots.rs
Normal file
49
crates/oxc_resolver/tests/enhanced_resolve/test/roots.rs
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
//! <https://github.com/webpack/enhanced-resolve/blob/main/test/roots.test.js>
|
||||
|
||||
use std::env;
|
||||
|
||||
use oxc_resolver::{AliasValue, Resolution, ResolveError, ResolveOptions, Resolver};
|
||||
|
||||
#[test]
|
||||
fn roots() {
|
||||
let f = super::fixture();
|
||||
|
||||
let resolver = Resolver::new(ResolveOptions {
|
||||
extensions: vec![".js".into()],
|
||||
alias: vec![("foo".into(), vec![AliasValue::Path("/fixtures".into())])],
|
||||
roots: vec![env::current_dir().unwrap().join("tests/enhanced_resolve/test"), f.clone()],
|
||||
..ResolveOptions::default()
|
||||
});
|
||||
|
||||
#[rustfmt::skip]
|
||||
let pass = [
|
||||
("should respect roots option", "/fixtures/b.js", f.join("b.js")),
|
||||
("should try another root option, if it exists", "/b.js", f.join("b.js")),
|
||||
("should respect extension", "/fixtures/b", f.join("b.js")),
|
||||
("should resolve in directory", "/fixtures/extensions/dir", f.join("extensions/dir/index.js")),
|
||||
("should respect aliases", "foo/b", f.join("b.js")),
|
||||
];
|
||||
|
||||
for (comment, request, expected) in pass {
|
||||
let resolved_path = resolver.resolve(&f, request).map(Resolution::full_path);
|
||||
assert_eq!(resolved_path, Ok(expected), "{comment} {request}");
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
let fail = [
|
||||
("should not work with relative path", "fixtures/b.js", ResolveError::NotFound(f.clone().into_boxed_path()))
|
||||
];
|
||||
|
||||
for (comment, request, expected) in fail {
|
||||
let resolution = resolver.resolve(&f, request);
|
||||
assert_eq!(resolution, Err(expected), "{comment} {request}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "resolve_to_context"]
|
||||
fn resolve_to_context() {}
|
||||
|
||||
#[test]
|
||||
#[ignore = "resolver_prefer_absolute"]
|
||||
fn resolver_prefer_absolute() {}
|
||||
Loading…
Reference in a new issue