mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
feat(resolver): add preferRelative and preferAbsolute (#577)
This commit is contained in:
parent
beb0ae9d8e
commit
30b9731843
5 changed files with 95 additions and 50 deletions
|
|
@ -28,8 +28,8 @@
|
|||
| | 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 |
|
||||
| | 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 |
|
||||
| ✅ | 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 |
|
||||
| | symlinks | true | Whether to resolve symlinks to their symlinked location |
|
||||
|
|
@ -61,7 +61,7 @@ Tests ported from [enhanced-resolve](https://github.com/webpack/enhanced-resolve
|
|||
- [ ] pr-53.test.js
|
||||
- [x] resolve.test.js (need to add resolveToContext)
|
||||
- [ ] restrictions.test.js
|
||||
- [x] roots.test.js (need to add resolveToContext, resolverPreferAbsolute)
|
||||
- [x] roots.test.js (need to add resolveToContext)
|
||||
- [x] scoped-packages.test.js
|
||||
- [x] simple.test.js
|
||||
- [ ] symlink.test.js
|
||||
|
|
|
|||
|
|
@ -96,13 +96,10 @@ impl<Fs: FileSystem> ResolverGeneric<Fs> {
|
|||
})
|
||||
}
|
||||
|
||||
/// require(X) from module at path Y
|
||||
/// X: request
|
||||
/// Y: path
|
||||
fn require(&self, path: &Path, request: &Request) -> Result<PathBuf, ResolveError> {
|
||||
// X: request
|
||||
// Y: path
|
||||
// require(X) from module at path Y
|
||||
let mut path = path;
|
||||
let request_str;
|
||||
|
||||
match request.path {
|
||||
// 1. If X is a core module,
|
||||
// a. return the core module
|
||||
|
|
@ -110,47 +107,20 @@ impl<Fs: FileSystem> ResolverGeneric<Fs> {
|
|||
// 2. If X begins with '/'
|
||||
// a. set Y to be the file system root
|
||||
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);
|
||||
}
|
||||
if !self.options.prefer_relative && self.options.prefer_absolute {
|
||||
if let Ok(path) = self.require_path(path, absolute_path) {
|
||||
return Ok(path);
|
||||
}
|
||||
return Err(ResolveError::NotFound(
|
||||
Path::new(absolute_path).to_path_buf().into_boxed_path(),
|
||||
));
|
||||
}
|
||||
self.load_roots(path, absolute_path)
|
||||
}
|
||||
// 3. If X begins with './' or '/' or '../'
|
||||
RequestPath::Relative(relative_path) => {
|
||||
return self.require_relative(path, relative_path);
|
||||
}
|
||||
RequestPath::Relative(relative_path) => self.require_relative(path, relative_path),
|
||||
// 4. If X begins with '#'
|
||||
RequestPath::Hash(hash_path) => {
|
||||
request_str = hash_path;
|
||||
}
|
||||
RequestPath::Hash(hash_path) => self.require_path(path, hash_path),
|
||||
// a. LOAD_PACKAGE_IMPORTS(X, dirname(Y))
|
||||
RequestPath::Module(module_path) => {
|
||||
request_str = module_path;
|
||||
}
|
||||
RequestPath::Module(module_path) => self.require_path(path, module_path),
|
||||
}
|
||||
// 5. LOAD_PACKAGE_SELF(X, dirname(Y))
|
||||
if let Some(path) = self.load_package_self(path, request_str)? {
|
||||
return Ok(path);
|
||||
}
|
||||
// 6. LOAD_NODE_MODULES(X, dirname(Y))
|
||||
if let Some(path) = self.load_node_modules(path, request_str)? {
|
||||
return Ok(path);
|
||||
}
|
||||
if let Some(path) = self.load_as_file(&path.join(request_str))? {
|
||||
return Ok(path);
|
||||
}
|
||||
// 7. THROW "not found"
|
||||
Err(ResolveError::NotFound(path.to_path_buf().into_boxed_path()))
|
||||
}
|
||||
|
||||
// 3. If X begins with './' or '/' or '../'
|
||||
|
|
@ -173,6 +143,22 @@ impl<Fs: FileSystem> ResolverGeneric<Fs> {
|
|||
Err(ResolveError::NotFound(path.into_boxed_path()))
|
||||
}
|
||||
|
||||
fn require_path(&self, path: &Path, request_str: &str) -> Result<PathBuf, ResolveError> {
|
||||
// 5. LOAD_PACKAGE_SELF(X, dirname(Y))
|
||||
if let Some(path) = self.load_package_self(path, request_str)? {
|
||||
return Ok(path);
|
||||
}
|
||||
// 6. LOAD_NODE_MODULES(X, dirname(Y))
|
||||
if let Some(path) = self.load_node_modules(path, request_str)? {
|
||||
return Ok(path);
|
||||
}
|
||||
if let Some(path) = self.load_as_file(&path.join(request_str))? {
|
||||
return Ok(path);
|
||||
}
|
||||
// 7. THROW "not found"
|
||||
Err(ResolveError::NotFound(path.to_path_buf().into_boxed_path()))
|
||||
}
|
||||
|
||||
#[allow(clippy::unnecessary_wraps)]
|
||||
fn load_as_file(&self, path: &Path) -> ResolveState {
|
||||
// enhanced-resolve feature: extension_alias
|
||||
|
|
@ -345,4 +331,17 @@ impl<Fs: FileSystem> ResolverGeneric<Fs> {
|
|||
}
|
||||
Err(ResolveError::ExtensionAlias)
|
||||
}
|
||||
|
||||
fn load_roots(&self, path: &Path, request_str: &str) -> Result<PathBuf, ResolveError> {
|
||||
debug_assert!(request_str.starts_with('/'));
|
||||
if self.options.roots.is_empty() {
|
||||
return self.require_path(Path::new("/"), request_str);
|
||||
}
|
||||
for root in &self.options.roots {
|
||||
if let Ok(path) = self.require_relative(root, request_str.trim_start_matches('/')) {
|
||||
return Ok(path);
|
||||
}
|
||||
}
|
||||
Err(ResolveError::NotFound(path.to_path_buf().into_boxed_path()))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,6 +61,16 @@ pub struct ResolveOptions {
|
|||
/// Default `["node_modules"]`
|
||||
pub modules: Vec<String>,
|
||||
|
||||
/// prefer to resolve module requests as relative requests instead of using modules from node_modules directories.
|
||||
///
|
||||
/// Default `false`
|
||||
pub prefer_relative: bool,
|
||||
|
||||
/// Prefer to resolve server-relative urls as absolute paths before falling back to resolve in ResolveOptions::roots.
|
||||
///
|
||||
/// Default `false`
|
||||
pub prefer_absolute: bool,
|
||||
|
||||
/// 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.
|
||||
///
|
||||
|
|
@ -80,6 +90,8 @@ impl Default for ResolveOptions {
|
|||
fallback: vec![],
|
||||
main_files: vec!["index".into()],
|
||||
modules: vec!["node_modules".into()],
|
||||
prefer_relative: false,
|
||||
prefer_absolute: false,
|
||||
roots: vec![],
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
//! <https://github.com/webpack/enhanced-resolve/blob/main/test/resolve.test.js>
|
||||
|
||||
use oxc_resolver::{Resolution, Resolver};
|
||||
use oxc_resolver::{Resolution, ResolveOptions, Resolver};
|
||||
|
||||
#[test]
|
||||
fn resolve() {
|
||||
|
|
@ -11,7 +11,7 @@ fn resolve() {
|
|||
let main1_js_path = f.join("main1.js").to_string_lossy().to_string();
|
||||
|
||||
#[rustfmt::skip]
|
||||
let data = [
|
||||
let pass = [
|
||||
("absolute path", f.clone(), main1_js_path.as_str(), f.join("main1.js")),
|
||||
("file with .js", f.clone(), "./main1.js", f.join("main1.js")),
|
||||
("file without extension", f.clone(), "./main1", f.join("main1.js")),
|
||||
|
|
@ -42,7 +42,7 @@ fn resolve() {
|
|||
// ("handle fragment escaping", f.clone(), "./no\0#fragment/\0#/\0##fragment", f.join("no\0#fragment/\0#\0#.js#fragment")),
|
||||
];
|
||||
|
||||
for (comment, path, request, expected) in data {
|
||||
for (comment, path, request, expected) in pass {
|
||||
let resolved_path = resolver.resolve(&path, request).map(Resolution::full_path);
|
||||
assert_eq!(resolved_path, Ok(expected), "{comment} {path:?} {request}");
|
||||
}
|
||||
|
|
@ -53,8 +53,23 @@ fn resolve() {
|
|||
fn issue238_resolve() {}
|
||||
|
||||
#[test]
|
||||
#[ignore = "preferRelativeResolve"]
|
||||
fn prefer_relative_resolve() {}
|
||||
fn prefer_relative() {
|
||||
let f = super::fixture();
|
||||
|
||||
let resolver =
|
||||
Resolver::new(ResolveOptions { prefer_relative: true, ..ResolveOptions::default() });
|
||||
|
||||
#[rustfmt::skip]
|
||||
let pass = [
|
||||
("should correctly resolve with preferRelative 1", "main1.js", f.join("main1.js")),
|
||||
("should correctly resolve with preferRelative 2", "m1/a.js", f.join("node_modules/m1/a.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}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "add resolveToContext option"]
|
||||
|
|
|
|||
|
|
@ -45,5 +45,24 @@ fn roots() {
|
|||
fn resolve_to_context() {}
|
||||
|
||||
#[test]
|
||||
#[ignore = "resolver_prefer_absolute"]
|
||||
fn resolver_prefer_absolute() {}
|
||||
fn prefer_absolute() {
|
||||
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()],
|
||||
prefer_absolute: true,
|
||||
..ResolveOptions::default()
|
||||
});
|
||||
|
||||
#[rustfmt::skip]
|
||||
let pass = [
|
||||
("should resolve an absolute path (prefer absolute)", f.join("b.js").to_string_lossy().to_string(), 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}");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue