From ef2e4725efbf1c2c9c3e138c0b49c9cb11546333 Mon Sep 17 00:00:00 2001 From: shulaoda Date: Sun, 15 Sep 2024 20:57:55 +0800 Subject: [PATCH 1/7] perf(rspack_core): replace HASH_PLACEHOLDER_REGEX with simple string parsing --- crates/rspack_core/src/options/filename.rs | 56 +++++++++++----------- crates/rspack_core/src/utils/runtime.rs | 55 ++++++++++----------- 2 files changed, 57 insertions(+), 54 deletions(-) diff --git a/crates/rspack_core/src/options/filename.rs b/crates/rspack_core/src/options/filename.rs index 2da9cda6760..8b7f8831a73 100644 --- a/crates/rspack_core/src/options/filename.rs +++ b/crates/rspack_core/src/options/filename.rs @@ -6,13 +6,15 @@ use std::sync::Arc; use std::sync::LazyLock; use std::{borrow::Cow, convert::Infallible, ptr}; -use regex::{Captures, NoExpand, Regex}; +use cow_utils::CowUtils; +use regex::{NoExpand, Regex}; use rspack_error::error; use rspack_macros::MergeFrom; use rspack_util::atom::Atom; use rspack_util::ext::CowExt; use rspack_util::MergeFrom; +use crate::extract_hash_pattern; use crate::{parse_resource, AssetInfo, PathData, ResourceParsedData}; pub static FILE_PLACEHOLDER: LazyLock = @@ -35,14 +37,6 @@ pub static RUNTIME_PLACEHOLDER: LazyLock = LazyLock::new(|| Regex::new(r"\[runtime\]").expect("Should generate regex")); pub static URL_PLACEHOLDER: LazyLock = LazyLock::new(|| Regex::new(r"\[url\]").expect("Should generate regex")); -pub static HASH_PLACEHOLDER: LazyLock = - LazyLock::new(|| Regex::new(r"\[hash(:(\d*))?]").expect("Invalid regex")); -pub static CHUNK_HASH_PLACEHOLDER: LazyLock = - LazyLock::new(|| Regex::new(r"\[chunkhash(:(\d*))?]").expect("Invalid regex")); -pub static CONTENT_HASH_PLACEHOLDER: LazyLock = - LazyLock::new(|| Regex::new(r"\[contenthash(:(\d*))?]").expect("Invalid regex")); -pub static FULL_HASH_PLACEHOLDER: LazyLock = - LazyLock::new(|| Regex::new(r"\[fullhash(:(\d*))?]").expect("Invalid regex")); static DATA_URI_REGEX: LazyLock = LazyLock::new(|| Regex::new(r"^data:([^;,]+)").expect("Invalid regex")); @@ -177,17 +171,21 @@ impl FromStr for Filename { } } -fn hash_len(hash: &str, caps: &Captures) -> usize { +#[inline] +fn hash_len(hash: &str, len: Option) -> usize { let hash_len = hash.len(); - caps - .get(2) - .and_then(|m| m.as_str().parse().ok()) - .unwrap_or(hash_len) - .min(hash_len) + len.unwrap_or(hash_len).min(hash_len) } pub fn has_hash_placeholder(template: &str) -> bool { - HASH_PLACEHOLDER.is_match(template) || FULL_HASH_PLACEHOLDER.is_match(template) + for key in ["[hash]", "[fullhash]"] { + if let Some(start) = template.find(&key[..key.len() - 1]) { + if template[start + 5..].find(']').is_some() { + return true; + } + } + } + false } impl Filename { @@ -290,18 +288,20 @@ fn render_template( asset_info.version = content_hash.to_string(); } t = t.map(|t| { - CONTENT_HASH_PLACEHOLDER.replace_all(t, |caps: &Captures| { - let content_hash = &content_hash[..hash_len(content_hash, caps)]; + if let Some(p) = extract_hash_pattern(t, "[contenthash]") { + let hash: &str = &content_hash[..hash_len(content_hash, p.len)]; if let Some(asset_info) = asset_info.as_mut() { asset_info.set_immutable(Some(true)); - asset_info.set_content_hash(content_hash.to_owned()); + asset_info.set_content_hash(hash.to_owned()); } - content_hash - }) + t.cow_replace(&p.pattern, hash) + } else { + Cow::Borrowed(t) + } }); } if let Some(hash) = options.hash { - for reg in [&HASH_PLACEHOLDER, &FULL_HASH_PLACEHOLDER] { + for key in ["[hash]", "[fullhash]"] { t = t.map(|t| { reg.replace_all(t, |caps: &Captures| { let hash = &hash[..hash_len(hash, caps)]; @@ -325,15 +325,17 @@ fn render_template( } if let Some(d) = chunk.rendered_hash.as_ref() { t = t.map(|t| { - CHUNK_HASH_PLACEHOLDER.replace_all(t, |caps: &Captures| { - let hash = &**d; - let hash = &hash[..hash_len(hash, caps)]; + let hash = &**d; + if let Some(p) = extract_hash_pattern(t, "[chunkhash]") { + let hash: &str = &hash[..hash_len(hash, p.len)]; if let Some(asset_info) = asset_info.as_mut() { asset_info.set_immutable(Some(true)); asset_info.set_chunk_hash(hash.to_owned()); } - hash - }) + t.cow_replace(&p.pattern, hash) + } else { + Cow::Borrowed(t) + } }); } } diff --git a/crates/rspack_core/src/utils/runtime.rs b/crates/rspack_core/src/utils/runtime.rs index 5c4b1233045..d580c2a0e81 100644 --- a/crates/rspack_core/src/utils/runtime.rs +++ b/crates/rspack_core/src/utils/runtime.rs @@ -1,14 +1,8 @@ -use std::sync::LazyLock; - use indexmap::IndexMap; -use regex::{Captures, Regex}; use rustc_hash::FxHashMap as HashMap; use rustc_hash::FxHashSet as HashSet; -use crate::{ - merge_runtime, EntryData, EntryOptions, Filename, RuntimeSpec, CHUNK_HASH_PLACEHOLDER, - CONTENT_HASH_PLACEHOLDER, FULL_HASH_PLACEHOLDER, HASH_PLACEHOLDER, -}; +use crate::{merge_runtime, EntryData, EntryOptions, Filename, RuntimeSpec}; pub fn get_entry_runtime( name: &str, @@ -48,14 +42,26 @@ pub fn get_entry_runtime( } } -static HASH_REPLACERS: LazyLock, &str)>> = LazyLock::new(|| { - vec![ - (&HASH_PLACEHOLDER, "[hash]"), - (&FULL_HASH_PLACEHOLDER, "[fullhash]"), - (&CHUNK_HASH_PLACEHOLDER, "[chunkhash]"), - (&CONTENT_HASH_PLACEHOLDER, "[contenthash]"), - ] -}); +pub struct ExtractedHashPattern { + pub pattern: String, + pub len: Option, +} + +/// Extract `[hash]` or `[hash:8]` in the template +pub fn extract_hash_pattern(pattern: &str, key: &str) -> Option { + let start = pattern.find(&key[..key.len() - 1])?; + let end = pattern[start + 5..].find(']')?; + let len = if let Some(n) = pattern[start + 5..start + 5 + end].strip_prefix(':') { + Some(n.parse::().ok()?) + } else { + None + }; + let pattern = &pattern[start..=start + 5 + end]; + Some(ExtractedHashPattern { + pattern: pattern.to_string(), + len, + }) +} pub fn get_filename_without_hash_length( filename: &Filename, @@ -65,18 +71,13 @@ pub fn get_filename_without_hash_length( return (filename.clone(), hash_len_map); }; let mut template = template.to_string(); - for (reg, key) in HASH_REPLACERS.iter() { - template = reg - .replace_all(&template, |caps: &Captures| { - if let Some(hash_len) = match caps.get(2) { - Some(m) => m.as_str().parse().ok(), - None => None, - } { - hash_len_map.insert((*key).to_string(), hash_len); - } - key - }) - .into_owned(); + for key in ["[hash]", "[fullhash]", "[chunkhash]", "[contenthash]"] { + if let Some(p) = extract_hash_pattern(&template, key) { + if let Some(hash_len) = p.len { + hash_len_map.insert((*key).to_string(), hash_len); + } + template = template.replace(&p.pattern, key); + } } (Filename::from(template), hash_len_map) } From ba31e45ba1d295c2a9781443532ff806ce174380 Mon Sep 17 00:00:00 2001 From: shulaoda Date: Sun, 15 Sep 2024 23:11:31 +0800 Subject: [PATCH 2/7] feat: add replace_all_hash_pattern --- crates/rspack_core/src/utils/runtime.rs | 68 +++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 4 deletions(-) diff --git a/crates/rspack_core/src/utils/runtime.rs b/crates/rspack_core/src/utils/runtime.rs index d580c2a0e81..2c7eb7e7843 100644 --- a/crates/rspack_core/src/utils/runtime.rs +++ b/crates/rspack_core/src/utils/runtime.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + use indexmap::IndexMap; use rustc_hash::FxHashMap as HashMap; use rustc_hash::FxHashSet as HashSet; @@ -49,20 +51,78 @@ pub struct ExtractedHashPattern { /// Extract `[hash]` or `[hash:8]` in the template pub fn extract_hash_pattern(pattern: &str, key: &str) -> Option { - let start = pattern.find(&key[..key.len() - 1])?; - let end = pattern[start + 5..].find(']')?; - let len = if let Some(n) = pattern[start + 5..start + 5 + end].strip_prefix(':') { + let key_offset = key.len() - 1; + let start = pattern.find(&key[..key_offset])?; + let end = pattern[start + key_offset..].find(']')?; + let len = if let Some(n) = pattern[start + key_offset..start + key_offset + end].strip_prefix(':') + { Some(n.parse::().ok()?) } else { None }; - let pattern = &pattern[start..=start + 5 + end]; + let pattern = &pattern[start..=start + key_offset + end]; Some(ExtractedHashPattern { pattern: pattern.to_string(), len, }) } +pub fn replace_all_hash_pattern<'a>(pattern: &'a str, key: &'a str, hash: &'a str) -> Cow<'a, str> { + let key_offset = key.len() - 1; + let key = &key[..key_offset]; + + let Some(start) = pattern.find(key) else { + return Cow::Borrowed(pattern); + }; + + let Some(end) = pattern[start + key_offset..].find(']') else { + return Cow::Borrowed(pattern); + }; + + let hash_len = hash.len(); + let mut result = String::with_capacity(pattern.len()); + + result.push_str(&pattern[..start]); + result.push_str( + &hash[..pattern[start + key_offset..start + key_offset + end] + .strip_prefix(':') + .map(|n| n.parse::().ok().unwrap_or(hash_len).min(hash_len)) + .unwrap_or(hash_len)], + ); + + let mut offset = start + key_offset + end; + + while let Some(start) = pattern[offset..].find(key) { + let start = offset + start; + if let Some(end) = pattern[start + key_offset..].find(']') { + let hash_len = pattern[start + key_offset..start + key_offset + end] + .strip_prefix(':') + .map(|n| n.parse::().ok().unwrap_or(hash_len).min(hash_len)) + .unwrap_or(hash_len); + + result.push_str(&pattern[offset + 1..start]); + result.push_str(&hash[..hash_len]); + + offset = start + key_offset + end; + } else { + result.push_str(&pattern[offset + 1..]); + return Cow::Owned(result); + } + } + + result.push_str(&pattern[offset + 1..]); + + Cow::Owned(result) +} + +#[test] +fn test_replace_all_hash_pattern() { + let result = replace_all_hash_pattern("hello-[hash].js", "[hash]", "abc"); + assert_eq!(result.as_ref(), "hello-abc.js"); + let result = replace_all_hash_pattern("hello-[hash]-[hash:5].js", "[hash]", "abcdefgh"); + assert_eq!(result.as_ref(), "hello-abcdefgh-abcde.js"); +} + pub fn get_filename_without_hash_length( filename: &Filename, ) -> (Filename, HashMap) { From 15e7f84cf9301fb8a7dddb114cac8bef0df25629 Mon Sep 17 00:00:00 2001 From: shulaoda Date: Mon, 16 Sep 2024 12:18:18 +0800 Subject: [PATCH 3/7] refactor: replace_all_hash_pattern --- Cargo.lock | 7 -- Cargo.toml | 1 - crates/rspack_core/Cargo.toml | 1 - crates/rspack_core/src/options/filename.rs | 25 +++---- crates/rspack_core/src/utils/runtime.rs | 84 +++++++++++----------- 5 files changed, 51 insertions(+), 67 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d792d1078fa..a778e782271 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -674,12 +674,6 @@ dependencies = [ "windows-sys 0.33.0", ] -[[package]] -name = "cow-utils" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "417bef24afe1460300965a25ff4a24b8b45ad011948302ec221e8a0a81eb2c79" - [[package]] name = "cpufeatures" version = "0.2.9" @@ -3440,7 +3434,6 @@ dependencies = [ "async-recursion", "async-trait", "bitflags 2.5.0", - "cow-utils", "dashmap 5.5.3", "derivative", "dyn-clone", diff --git a/Cargo.toml b/Cargo.toml index c643b87415e..a061c41f592 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,6 @@ async-trait = { version = "0.1.79" } bitflags = { version = "2.5.0" } camino = { version = "1.1.8" } concat-string = { version = "1.0.1" } -cow-utils = { version = "0.1.3" } css-module-lexer = { version = "0.0.14" } dashmap = { version = "5.5.3" } derivative = { version = "2.2.0" } diff --git a/crates/rspack_core/Cargo.toml b/crates/rspack_core/Cargo.toml index ffd45eb7683..3d71e4ff397 100644 --- a/crates/rspack_core/Cargo.toml +++ b/crates/rspack_core/Cargo.toml @@ -10,7 +10,6 @@ anymap = { workspace = true } async-recursion = { workspace = true } async-trait = { workspace = true } bitflags = { workspace = true } -cow-utils = { workspace = true } dashmap = { workspace = true, features = ["rayon"] } derivative = { workspace = true } dyn-clone = "1.0.17" diff --git a/crates/rspack_core/src/options/filename.rs b/crates/rspack_core/src/options/filename.rs index 8b7f8831a73..2749406fab4 100644 --- a/crates/rspack_core/src/options/filename.rs +++ b/crates/rspack_core/src/options/filename.rs @@ -6,7 +6,6 @@ use std::sync::Arc; use std::sync::LazyLock; use std::{borrow::Cow, convert::Infallible, ptr}; -use cow_utils::CowUtils; use regex::{NoExpand, Regex}; use rspack_error::error; use rspack_macros::MergeFrom; @@ -14,7 +13,7 @@ use rspack_util::atom::Atom; use rspack_util::ext::CowExt; use rspack_util::MergeFrom; -use crate::extract_hash_pattern; +use crate::replace_all_hash_pattern; use crate::{parse_resource, AssetInfo, PathData, ResourceParsedData}; pub static FILE_PLACEHOLDER: LazyLock = @@ -288,16 +287,15 @@ fn render_template( asset_info.version = content_hash.to_string(); } t = t.map(|t| { - if let Some(p) = extract_hash_pattern(t, "[contenthash]") { - let hash: &str = &content_hash[..hash_len(content_hash, p.len)]; + replace_all_hash_pattern(t, "[contenthash]", |len| { + let hash: &str = &content_hash[..hash_len(content_hash, len)]; if let Some(asset_info) = asset_info.as_mut() { asset_info.set_immutable(Some(true)); asset_info.set_content_hash(hash.to_owned()); } - t.cow_replace(&p.pattern, hash) - } else { - Cow::Borrowed(t) - } + hash + }) + .map_or(Cow::Borrowed(t), Cow::Owned) }); } if let Some(hash) = options.hash { @@ -326,16 +324,15 @@ fn render_template( if let Some(d) = chunk.rendered_hash.as_ref() { t = t.map(|t| { let hash = &**d; - if let Some(p) = extract_hash_pattern(t, "[chunkhash]") { - let hash: &str = &hash[..hash_len(hash, p.len)]; + replace_all_hash_pattern(t, "[chunkhash]", |len| { + let hash: &str = &hash[..hash_len(hash, len)]; if let Some(asset_info) = asset_info.as_mut() { asset_info.set_immutable(Some(true)); asset_info.set_chunk_hash(hash.to_owned()); } - t.cow_replace(&p.pattern, hash) - } else { - Cow::Borrowed(t) - } + hash + }) + .map_or(Cow::Borrowed(t), Cow::Owned) }); } } diff --git a/crates/rspack_core/src/utils/runtime.rs b/crates/rspack_core/src/utils/runtime.rs index 2c7eb7e7843..2c615f45526 100644 --- a/crates/rspack_core/src/utils/runtime.rs +++ b/crates/rspack_core/src/utils/runtime.rs @@ -1,5 +1,3 @@ -use std::borrow::Cow; - use indexmap::IndexMap; use rustc_hash::FxHashMap as HashMap; use rustc_hash::FxHashSet as HashSet; @@ -67,60 +65,58 @@ pub fn extract_hash_pattern(pattern: &str, key: &str) -> Option(pattern: &'a str, key: &'a str, hash: &'a str) -> Cow<'a, str> { - let key_offset = key.len() - 1; - let key = &key[..key_offset]; +pub fn replace_all_hash_pattern<'a, F, S>( + pattern: &'a str, + key: &'a str, + mut hash: F, +) -> Option +where + F: FnMut(Option) -> S, + S: AsRef, +{ + let offset = key.len() - 1; + let mut iter = pattern.match_indices(&key[..offset]).peekable(); + + iter.peek()?; + + let mut ending = 0; + let mut result = String::with_capacity(pattern.len()); - let Some(start) = pattern.find(key) else { - return Cow::Borrowed(pattern); - }; + for (start, _) in iter { + if start < ending { + continue; + } - let Some(end) = pattern[start + key_offset..].find(']') else { - return Cow::Borrowed(pattern); - }; + let start_offset = start + offset; + if let Some(end) = pattern[start_offset..].find(']') { + let end = start_offset + end; - let hash_len = hash.len(); - let mut result = String::with_capacity(pattern.len()); + let hash = hash( + pattern[start_offset..end] + .strip_prefix(':') + .and_then(|n| n.parse::().ok()), + ); - result.push_str(&pattern[..start]); - result.push_str( - &hash[..pattern[start + key_offset..start + key_offset + end] - .strip_prefix(':') - .map(|n| n.parse::().ok().unwrap_or(hash_len).min(hash_len)) - .unwrap_or(hash_len)], - ); - - let mut offset = start + key_offset + end; - - while let Some(start) = pattern[offset..].find(key) { - let start = offset + start; - if let Some(end) = pattern[start + key_offset..].find(']') { - let hash_len = pattern[start + key_offset..start + key_offset + end] - .strip_prefix(':') - .map(|n| n.parse::().ok().unwrap_or(hash_len).min(hash_len)) - .unwrap_or(hash_len); - - result.push_str(&pattern[offset + 1..start]); - result.push_str(&hash[..hash_len]); - - offset = start + key_offset + end; - } else { - result.push_str(&pattern[offset + 1..]); - return Cow::Owned(result); + result.push_str(&pattern[ending..start]); + result.push_str(hash.as_ref()); + + ending = end + 1; } } - result.push_str(&pattern[offset + 1..]); + if ending < pattern.len() { + result.push_str(&pattern[ending..]); + } - Cow::Owned(result) + Some(result) } #[test] fn test_replace_all_hash_pattern() { - let result = replace_all_hash_pattern("hello-[hash].js", "[hash]", "abc"); - assert_eq!(result.as_ref(), "hello-abc.js"); - let result = replace_all_hash_pattern("hello-[hash]-[hash:5].js", "[hash]", "abcdefgh"); - assert_eq!(result.as_ref(), "hello-abcdefgh-abcde.js"); + let result = replace_all_hash_pattern("hello-[hash].js", "[hash]", |_| "abc"); + assert_eq!(result, Some("hello-abc.js".to_string())); + let result = replace_all_hash_pattern("hello-[hash]-[hash:5].js", "[hash]", |_| "abcde"); + assert_eq!(result, Some("hello-abcdefgh-abcde.js".to_string())); } pub fn get_filename_without_hash_length( From 9f3f15d91b07679daca454e779754ccf66dac906 Mon Sep 17 00:00:00 2001 From: shulaoda Date: Mon, 16 Sep 2024 12:49:29 +0800 Subject: [PATCH 4/7] improve --- crates/rspack_core/src/options/filename.rs | 5 +++-- crates/rspack_core/src/utils/runtime.rs | 14 +++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/crates/rspack_core/src/options/filename.rs b/crates/rspack_core/src/options/filename.rs index 2749406fab4..9af716591f8 100644 --- a/crates/rspack_core/src/options/filename.rs +++ b/crates/rspack_core/src/options/filename.rs @@ -178,8 +178,9 @@ fn hash_len(hash: &str, len: Option) -> usize { pub fn has_hash_placeholder(template: &str) -> bool { for key in ["[hash]", "[fullhash]"] { - if let Some(start) = template.find(&key[..key.len() - 1]) { - if template[start + 5..].find(']').is_some() { + let offset = key.len() - 1; + if let Some(start) = template.find(&key[..offset]) { + if template[start + offset..].find(']').is_some() { return true; } } diff --git a/crates/rspack_core/src/utils/runtime.rs b/crates/rspack_core/src/utils/runtime.rs index 2c615f45526..240395eaf3c 100644 --- a/crates/rspack_core/src/utils/runtime.rs +++ b/crates/rspack_core/src/utils/runtime.rs @@ -52,12 +52,10 @@ pub fn extract_hash_pattern(pattern: &str, key: &str) -> Option().ok()?) - } else { - None - }; + let len = pattern[start + key_offset..start + key_offset + end] + .strip_prefix(':') + .and_then(|n| n.parse::().ok()); + let pattern = &pattern[start..=start + key_offset + end]; Some(ExtractedHashPattern { pattern: pattern.to_string(), @@ -115,7 +113,9 @@ where fn test_replace_all_hash_pattern() { let result = replace_all_hash_pattern("hello-[hash].js", "[hash]", |_| "abc"); assert_eq!(result, Some("hello-abc.js".to_string())); - let result = replace_all_hash_pattern("hello-[hash]-[hash:5].js", "[hash]", |_| "abcde"); + let result = replace_all_hash_pattern("hello-[hash]-[hash:5].js", "[hash]", |n| { + &"abcdefgh"[..n.unwrap_or(8)] + }); assert_eq!(result, Some("hello-abcdefgh-abcde.js".to_string())); } From 19bf7437ec76c6e1175cdf5a4f1503c38cba120c Mon Sep 17 00:00:00 2001 From: shulaoda Date: Mon, 16 Sep 2024 13:18:59 +0800 Subject: [PATCH 5/7] fix --- crates/rspack_core/src/options/filename.rs | 5 +++-- crates/rspack_plugin_library/Cargo.toml | 23 +++++++++++----------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/crates/rspack_core/src/options/filename.rs b/crates/rspack_core/src/options/filename.rs index 9af716591f8..7a9afa1cf04 100644 --- a/crates/rspack_core/src/options/filename.rs +++ b/crates/rspack_core/src/options/filename.rs @@ -302,14 +302,15 @@ fn render_template( if let Some(hash) = options.hash { for key in ["[hash]", "[fullhash]"] { t = t.map(|t| { - reg.replace_all(t, |caps: &Captures| { - let hash = &hash[..hash_len(hash, caps)]; + replace_all_hash_pattern(t, key, |len| { + let hash = &hash[..hash_len(hash, len)]; if let Some(asset_info) = asset_info.as_mut() { asset_info.set_immutable(Some(true)); asset_info.set_full_hash(hash.to_owned()); } hash }) + .map_or(Cow::Borrowed(t), Cow::Owned) }); } } diff --git a/crates/rspack_plugin_library/Cargo.toml b/crates/rspack_plugin_library/Cargo.toml index 21ee69f4c2f..0368d37eb2c 100644 --- a/crates/rspack_plugin_library/Cargo.toml +++ b/crates/rspack_plugin_library/Cargo.toml @@ -10,17 +10,19 @@ version = "0.1.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -async-trait = { workspace = true } -regex = { workspace = true } -rspack_collections = { version = "0.1.0", path = "../rspack_collections" } -rspack_core = { version = "0.1.0", path = "../rspack_core" } -rspack_error = { version = "0.1.0", path = "../rspack_error" } -rspack_hash = { version = "0.1.0", path = "../rspack_hash" } -rspack_hook = { version = "0.1.0", path = "../rspack_hook" } +async-trait = { workspace = true } +regex = { workspace = true } +rspack_collections = { version = "0.1.0", path = "../rspack_collections" } +rspack_core = { version = "0.1.0", path = "../rspack_core" } +rspack_error = { version = "0.1.0", path = "../rspack_error" } +rspack_hash = { version = "0.1.0", path = "../rspack_hash" } +rspack_hook = { version = "0.1.0", path = "../rspack_hook" } rspack_plugin_javascript = { version = "0.1.0", path = "../rspack_plugin_javascript" } -rspack_util = { version = "0.1.0", path = "../rspack_util" } -rustc-hash = { workspace = true } -serde_json = { workspace = true } +rspack_util = { version = "0.1.0", path = "../rspack_util" } +rustc-hash = { workspace = true } +serde_json = { workspace = true } +tracing = { workspace = true } + swc_core = { workspace = true, features = [ "__parser", "__utils", @@ -33,7 +35,6 @@ swc_core = { workspace = true, features = [ "base", "ecma_quote", ] } -tracing = { workspace = true } [package.metadata.cargo-shear] ignored = ["tracing"] From b9dab7a68d41228db760bbd7218de7aac6ed1793 Mon Sep 17 00:00:00 2001 From: shulaoda Date: Mon, 16 Sep 2024 13:50:53 +0800 Subject: [PATCH 6/7] improve --- crates/rspack_core/src/options/filename.rs | 13 +++++++++---- crates/rspack_core/src/utils/runtime.rs | 11 ++++++++++- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/crates/rspack_core/src/options/filename.rs b/crates/rspack_core/src/options/filename.rs index 7a9afa1cf04..91b249f34e1 100644 --- a/crates/rspack_core/src/options/filename.rs +++ b/crates/rspack_core/src/options/filename.rs @@ -37,6 +37,11 @@ pub static RUNTIME_PLACEHOLDER: LazyLock = pub static URL_PLACEHOLDER: LazyLock = LazyLock::new(|| Regex::new(r"\[url\]").expect("Should generate regex")); +pub static HASH_PLACEHOLDER: &str = "[hash]"; +pub static FULL_HASH_PLACEHOLDER: &str = "[fullhash]"; +pub static CHUNK_HASH_PLACEHOLDER: &str = "[chunkhash]"; +pub static CONTENT_HASH_PLACEHOLDER: &str = "[contenthash]"; + static DATA_URI_REGEX: LazyLock = LazyLock::new(|| Regex::new(r"^data:([^;,]+)").expect("Invalid regex")); @@ -177,7 +182,7 @@ fn hash_len(hash: &str, len: Option) -> usize { } pub fn has_hash_placeholder(template: &str) -> bool { - for key in ["[hash]", "[fullhash]"] { + for key in [HASH_PLACEHOLDER, FULL_HASH_PLACEHOLDER] { let offset = key.len() - 1; if let Some(start) = template.find(&key[..offset]) { if template[start + offset..].find(']').is_some() { @@ -288,7 +293,7 @@ fn render_template( asset_info.version = content_hash.to_string(); } t = t.map(|t| { - replace_all_hash_pattern(t, "[contenthash]", |len| { + replace_all_hash_pattern(t, CONTENT_HASH_PLACEHOLDER, |len| { let hash: &str = &content_hash[..hash_len(content_hash, len)]; if let Some(asset_info) = asset_info.as_mut() { asset_info.set_immutable(Some(true)); @@ -300,7 +305,7 @@ fn render_template( }); } if let Some(hash) = options.hash { - for key in ["[hash]", "[fullhash]"] { + for key in [HASH_PLACEHOLDER, FULL_HASH_PLACEHOLDER] { t = t.map(|t| { replace_all_hash_pattern(t, key, |len| { let hash = &hash[..hash_len(hash, len)]; @@ -326,7 +331,7 @@ fn render_template( if let Some(d) = chunk.rendered_hash.as_ref() { t = t.map(|t| { let hash = &**d; - replace_all_hash_pattern(t, "[chunkhash]", |len| { + replace_all_hash_pattern(t, CHUNK_HASH_PLACEHOLDER, |len| { let hash: &str = &hash[..hash_len(hash, len)]; if let Some(asset_info) = asset_info.as_mut() { asset_info.set_immutable(Some(true)); diff --git a/crates/rspack_core/src/utils/runtime.rs b/crates/rspack_core/src/utils/runtime.rs index 240395eaf3c..d9d9c8354e7 100644 --- a/crates/rspack_core/src/utils/runtime.rs +++ b/crates/rspack_core/src/utils/runtime.rs @@ -3,6 +3,9 @@ use rustc_hash::FxHashMap as HashMap; use rustc_hash::FxHashSet as HashSet; use crate::{merge_runtime, EntryData, EntryOptions, Filename, RuntimeSpec}; +use crate::{ + CHUNK_HASH_PLACEHOLDER, CONTENT_HASH_PLACEHOLDER, FULL_HASH_PLACEHOLDER, HASH_PLACEHOLDER, +}; pub fn get_entry_runtime( name: &str, @@ -63,6 +66,7 @@ pub fn extract_hash_pattern(pattern: &str, key: &str) -> Option( pattern: &'a str, key: &'a str, @@ -127,7 +131,12 @@ pub fn get_filename_without_hash_length( return (filename.clone(), hash_len_map); }; let mut template = template.to_string(); - for key in ["[hash]", "[fullhash]", "[chunkhash]", "[contenthash]"] { + for key in [ + HASH_PLACEHOLDER, + FULL_HASH_PLACEHOLDER, + CHUNK_HASH_PLACEHOLDER, + CONTENT_HASH_PLACEHOLDER, + ] { if let Some(p) = extract_hash_pattern(&template, key) { if let Some(hash_len) = p.len { hash_len_map.insert((*key).to_string(), hash_len); From a06f078ee77c66f45eeb4a6ac35ee7b57e94eca8 Mon Sep 17 00:00:00 2001 From: shulaoda Date: Wed, 9 Oct 2024 07:22:45 +0800 Subject: [PATCH 7/7] solve conflicts --- Cargo.lock | 7 +++++++ Cargo.toml | 1 + crates/rspack_core/Cargo.toml | 1 + crates/rspack_core/src/utils/runtime.rs | 9 ++++++--- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a778e782271..d792d1078fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -674,6 +674,12 @@ dependencies = [ "windows-sys 0.33.0", ] +[[package]] +name = "cow-utils" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "417bef24afe1460300965a25ff4a24b8b45ad011948302ec221e8a0a81eb2c79" + [[package]] name = "cpufeatures" version = "0.2.9" @@ -3434,6 +3440,7 @@ dependencies = [ "async-recursion", "async-trait", "bitflags 2.5.0", + "cow-utils", "dashmap 5.5.3", "derivative", "dyn-clone", diff --git a/Cargo.toml b/Cargo.toml index a061c41f592..c643b87415e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ async-trait = { version = "0.1.79" } bitflags = { version = "2.5.0" } camino = { version = "1.1.8" } concat-string = { version = "1.0.1" } +cow-utils = { version = "0.1.3" } css-module-lexer = { version = "0.0.14" } dashmap = { version = "5.5.3" } derivative = { version = "2.2.0" } diff --git a/crates/rspack_core/Cargo.toml b/crates/rspack_core/Cargo.toml index 3d71e4ff397..ffd45eb7683 100644 --- a/crates/rspack_core/Cargo.toml +++ b/crates/rspack_core/Cargo.toml @@ -10,6 +10,7 @@ anymap = { workspace = true } async-recursion = { workspace = true } async-trait = { workspace = true } bitflags = { workspace = true } +cow-utils = { workspace = true } dashmap = { workspace = true, features = ["rayon"] } derivative = { workspace = true } dyn-clone = "1.0.17" diff --git a/crates/rspack_core/src/utils/runtime.rs b/crates/rspack_core/src/utils/runtime.rs index d9d9c8354e7..eda7d8f06dd 100644 --- a/crates/rspack_core/src/utils/runtime.rs +++ b/crates/rspack_core/src/utils/runtime.rs @@ -1,3 +1,6 @@ +use std::borrow::Cow; + +use cow_utils::CowUtils; use indexmap::IndexMap; use rustc_hash::FxHashMap as HashMap; use rustc_hash::FxHashSet as HashSet; @@ -130,7 +133,7 @@ pub fn get_filename_without_hash_length( let Some(template) = filename.template() else { return (filename.clone(), hash_len_map); }; - let mut template = template.to_string(); + let mut template = Cow::Borrowed(template); for key in [ HASH_PLACEHOLDER, FULL_HASH_PLACEHOLDER, @@ -141,8 +144,8 @@ pub fn get_filename_without_hash_length( if let Some(hash_len) = p.len { hash_len_map.insert((*key).to_string(), hash_len); } - template = template.replace(&p.pattern, key); + template = Cow::Owned(template.cow_replace(&p.pattern, key).into_owned()); } } - (Filename::from(template), hash_len_map) + (Filename::from(template.into_owned()), hash_len_map) }