From 708df5dd442eba4c8739f1807ab951df6d69b81b Mon Sep 17 00:00:00 2001 From: sorrycc Date: Thu, 5 Sep 2024 22:34:53 +0800 Subject: [PATCH 1/3] fix: parse_path failed under windows --- crates/mako/src/ast/file.rs | 51 +++++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/crates/mako/src/ast/file.rs b/crates/mako/src/ast/file.rs index f84f82e1d..5d3d14070 100644 --- a/crates/mako/src/ast/file.rs +++ b/crates/mako/src/ast/file.rs @@ -1,3 +1,4 @@ +use std::env; use std::hash::{Hash, Hasher}; use std::io::{BufRead, BufReader}; use std::path::PathBuf; @@ -331,20 +332,32 @@ type Params = Vec<(String, String)>; type Fragment = Option; pub fn parse_path(path: &str) -> Result<(PathName, Search, Params, Fragment)> { - let base = "http://a.com/"; - let base_url = Url::parse(base)?; - let full_url = base_url.join(path)?; - let path = full_url.path().to_string(); - let fragment = full_url.fragment().map(|s| s.to_string()); - let search = full_url.query().unwrap_or("").to_string(); - let query_vec = full_url - .query_pairs() - .map(|(k, v)| (k.to_string(), v.to_string())) - .collect(); - // dir or filename may contains space or other special characters - // so we need to decode it, e.g. "a%20b" -> "a b" - let path = percent_decode_str(&path).decode_utf8()?; - Ok((path.to_string(), search, query_vec, fragment)) + let path = if env::consts::OS == "windows" { + let prefix = "\\\\?\\"; + let path = path.trim_start_matches(prefix); + path.replace('\\', "/") + } else { + path.to_string() + }; + if path.contains('?') { + let (path, search) = path.split_once('?').unwrap(); + let base = "http://a.com/"; + let base_url = Url::parse(base)?; + let full_url = base_url.join(format!("?{}", search).as_str())?; + let fragment = full_url.fragment().map(|s| s.to_string()); + let search = full_url.query().unwrap_or("").to_string(); + let query_vec = full_url + .query_pairs() + .map(|(k, v)| (k.to_string(), v.to_string())) + .collect(); + // dir or filename may contains space or other special characters + // so we need to decode it, e.g. "a%20b" -> "a b" + let path = percent_decode_str(path).decode_utf8()?; + Ok((path.to_string(), search.to_string(), query_vec, fragment)) + } else { + let path = percent_decode_str(&path).decode_utf8()?; + Ok((path.to_string(), "".to_string(), vec![], None)) + } } #[cfg(test)] @@ -371,4 +384,14 @@ mod tests { ); assert_eq!(f.path(), Some("/root/d.js".to_string())); } + + #[test] + fn test_parse_path_support_windows() { + let path = "C:\\a\\b\\c?foo"; + let (path, search, params, fragment) = parse_path(path).unwrap(); + assert_eq!(path, "C:\\a\\b\\c"); + assert_eq!(search, "foo"); + assert_eq!(params, vec![("foo".to_string(), "".to_string())]); + assert_eq!(fragment, None); + } } From a24245f02c42cf38c3c2b7225f79a023732f7c2c Mon Sep 17 00:00:00 2001 From: sorrycc Date: Fri, 6 Sep 2024 10:19:34 +0800 Subject: [PATCH 2/3] chore: code style --- crates/mako/src/ast/file.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/mako/src/ast/file.rs b/crates/mako/src/ast/file.rs index 5d3d14070..1d9a6d449 100644 --- a/crates/mako/src/ast/file.rs +++ b/crates/mako/src/ast/file.rs @@ -1,4 +1,3 @@ -use std::env; use std::hash::{Hash, Hasher}; use std::io::{BufRead, BufReader}; use std::path::PathBuf; @@ -332,13 +331,14 @@ type Params = Vec<(String, String)>; type Fragment = Option; pub fn parse_path(path: &str) -> Result<(PathName, Search, Params, Fragment)> { - let path = if env::consts::OS == "windows" { + #[cfg(target_os = "windows")] + let path = { let prefix = "\\\\?\\"; let path = path.trim_start_matches(prefix); path.replace('\\', "/") - } else { - path.to_string() }; + #[cfg(not(target_os = "windows"))] + let path = path.to_string(); if path.contains('?') { let (path, search) = path.split_once('?').unwrap(); let base = "http://a.com/"; From fd8aa3c5ea98300c1c3b479ffbcc574df716b3d8 Mon Sep 17 00:00:00 2001 From: sorrycc Date: Fri, 6 Sep 2024 11:19:04 +0800 Subject: [PATCH 3/3] fix: path with fragment --- crates/mako/src/ast/file.rs | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/crates/mako/src/ast/file.rs b/crates/mako/src/ast/file.rs index 1d9a6d449..981a39826 100644 --- a/crates/mako/src/ast/file.rs +++ b/crates/mako/src/ast/file.rs @@ -330,6 +330,15 @@ type Search = String; type Params = Vec<(String, String)>; type Fragment = Option; +fn has_hash_without_dot(input: &str) -> bool { + if let Some(pos) = input.find('#') { + let after_hash = &input[pos + 1..]; + !after_hash.contains('.') + } else { + false + } +} + pub fn parse_path(path: &str) -> Result<(PathName, Search, Params, Fragment)> { #[cfg(target_os = "windows")] let path = { @@ -339,8 +348,12 @@ pub fn parse_path(path: &str) -> Result<(PathName, Search, Params, Fragment)> { }; #[cfg(not(target_os = "windows"))] let path = path.to_string(); - if path.contains('?') { - let (path, search) = path.split_once('?').unwrap(); + if path.contains('?') || has_hash_without_dot(path.as_str()) { + let (path, search) = if path.contains('?') { + path.split_once('?').unwrap_or((path.as_str(), "")) + } else { + path.split_once('#').unwrap_or((path.as_str(), "")) + }; let base = "http://a.com/"; let base_url = Url::parse(base)?; let full_url = base_url.join(format!("?{}", search).as_str())?; @@ -394,4 +407,19 @@ mod tests { assert_eq!(params, vec![("foo".to_string(), "".to_string())]); assert_eq!(fragment, None); } + + #[test] + fn test_parse_path_with_fragment() { + assert_eq!(parse_path("foo.ts#bar").unwrap().0, "foo.ts"); + assert_eq!(parse_path("foo#bar.ts").unwrap().0, "foo#bar.ts"); + } + + #[test] + fn test_has_hash_without_dot() { + assert_eq!(has_hash_without_dot("foo.ts#world"), true); + assert_eq!(has_hash_without_dot("foo#bar.ts"), false); + assert_eq!(has_hash_without_dot("#no_dot"), true); + assert_eq!(has_hash_without_dot("no_hash"), false); + assert_eq!(has_hash_without_dot("#.dot_after_hash"), false); + } }