diff --git a/crates/oxc_resolver/README.md b/crates/oxc_resolver/README.md index 2ca746972..57b5c907e 100644 --- a/crates/oxc_resolver/README.md +++ b/crates/oxc_resolver/README.md @@ -51,7 +51,7 @@ Tests ported from [enhanced-resolve](https://github.com/webpack/enhanced-resolve - ~[ ] forEachBail.test.js~ - [ ] fullSpecified.test.js - [ ] getPaths.test.js -- [ ] identifier.test.js +- [x] identifier.test.js (see unit test in `crates/oxc_resolver/src/request.rs`) - [ ] importsField.test.js - [x] incorrect-description-file.test.js (need to add ctx.fileDependencies) - [ ] missing.test.js diff --git a/crates/oxc_resolver/src/request.rs b/crates/oxc_resolver/src/request.rs index 85ecbfb95..bd2fc87b4 100644 --- a/crates/oxc_resolver/src/request.rs +++ b/crates/oxc_resolver/src/request.rs @@ -63,20 +63,20 @@ impl<'a> Request<'a> { let mut fragment_start: Option = None; for (i, c) in request.as_bytes().iter().enumerate().skip(skip) { - match *c { - b'?' => query_start = Some(i), - b'#' => fragment_start = Some(i), - _ => {} + if *c == b'?' { + query_start = Some(i); + } + if *c == b'#' { + fragment_start = Some(i); + break; } } match (query_start, fragment_start) { - (Some(i), Some(j)) if i < j => { + (Some(i), Some(j)) => { + debug_assert!(i < j); (&request[..i], Some(&request[i..j]), Some(&request[j..])) } - (Some(i), Some(j)) if i > j => { - (&request[..j], Some(&request[i..]), Some(&request[j..i])) - } (Some(i), None) => (&request[..i], Some(&request[i..]), None), (None, Some(j)) => (&request[..j], None, Some(&request[j..])), _ => (request, None, None), @@ -112,7 +112,7 @@ mod tests { #[test] fn relative() -> Result<(), RequestError> { - let requests = ["./test", "../test?#", "../../test?#"]; + let requests = ["./test", "../test", "../../test"]; for request in requests { let mut r = request.to_string(); r.push_str("?#"); @@ -163,8 +163,8 @@ mod tests { ("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")), + ("a#fragment?", None, Some("#fragment?")), + ("a#fragment?query", None, Some("#fragment?query")), ]; for (request_str, query, fragment) in data { @@ -176,4 +176,52 @@ mod tests { Ok(()) } + + #[test] + // https://github.com/webpack/enhanced-resolve/blob/main/test/identifier.test.js + fn enhanced_resolve_edge_cases() -> Result<(), RequestError> { + let data = [ + ("path/#", "path/", "", "#"), + ("path/as/?", "path/as/", "?", ""), + ("path/#/?", "path/", "", "#/?"), + ("path/#repo#hash", "path/", "", "#repo#hash"), + ("path/#r#hash", "path/", "", "#r#hash"), + ("path/#repo/#repo2#hash", "path/", "", "#repo/#repo2#hash"), + ("path/#r/#r#hash", "path/", "", "#r/#r#hash"), + ("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}"); + } + + Ok(()) + } + + // https://github.com/webpack/enhanced-resolve/blob/main/test/identifier.test.js + #[test] + fn enhanced_resolve_windows_like() -> Result<(), RequestError> { + let data = [ + ("path\\#", "path\\", "", "#"), + ("path\\as\\?", "path\\as\\", "?", ""), + ("path\\#\\?", "path\\", "", "#\\?"), + ("path\\#repo#hash", "path\\", "", "#repo#hash"), + ("path\\#r#hash", "path\\", "", "#r#hash"), + ("path\\#repo\\#repo2#hash", "path\\", "", "#repo\\#repo2#hash"), + ("path\\#r\\#r#hash", "path\\", "", "#r\\#r#hash"), + ("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}"); + } + + Ok(()) + } } diff --git a/crates/oxc_resolver/tests/enhanced_resolve/test/resolve.rs b/crates/oxc_resolver/tests/enhanced_resolve/test/resolve.rs index d40cf10fc..74549e9e0 100644 --- a/crates/oxc_resolver/tests/enhanced_resolve/test/resolve.rs +++ b/crates/oxc_resolver/tests/enhanced_resolve/test/resolve.rs @@ -25,11 +25,11 @@ fn resolve() { ("from nested directory to not overwritten file in module", f.join("multiple_modules"), "m1/b.js", f.join("node_modules/m1/b.js")), ("file with query", f.clone(), "./main1.js?query", f.join("main1.js?query")), ("file with fragment", f.clone(), "./main1.js#fragment", f.join("main1.js#fragment")), - ("file with fragment and query", f.clone(), "./main1.js#fragment?query", f.join("main1.js?query#fragment")), + ("file with fragment and query", f.clone(), "./main1.js#fragment?query", f.join("main1.js#fragment?query")), ("file with query and fragment", f.clone(), "./main1.js?#fragment", f.join("main1.js?#fragment")), ("file in module with query", f.clone(), "m1/a?query", f.join("node_modules/m1/a.js?query")), ("file in module with fragment", f.clone(), "m1/a#fragment", f.join("node_modules/m1/a.js#fragment")), - ("file in module with fragment and query", f.clone(), "m1/a#fragment?query", f.join("node_modules/m1/a.js?query#fragment")), + ("file in module with fragment and query", f.clone(), "m1/a#fragment?query", f.join("node_modules/m1/a.js#fragment?query")), ("file in module with query and fragment", f.clone(), "m1/a?#fragment", f.join("node_modules/m1/a.js?#fragment")), ("file in module with query and fragment", f.clone(), "m1/a?#fragment", f.join("node_modules/m1/a.js?#fragment")), ("differ between directory and file, resolve file", f.clone(), "./dirOrFile", f.join("dirOrFile.js")),