Skip to content

Commit 6c6388e

Browse files
Merge branch 'feat/mf-layers' into cursor/manifest-rsc-client-data-d493
2 parents 41ec155 + 2fde130 commit 6c6388e

File tree

39 files changed

+662
-351
lines changed

39 files changed

+662
-351
lines changed

crates/rspack/src/builder/mod.rs

Lines changed: 41 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,18 @@ macro_rules! expect {
2929
};
3030
}
3131

32-
use std::{borrow::Cow, future::ready, sync::Arc};
32+
use std::{
33+
borrow::Cow,
34+
future::ready,
35+
sync::{Arc, LazyLock},
36+
};
3337

3438
use builder_context::BuiltinPluginOptions;
3539
use derive_more::Debug;
3640
use devtool::DevtoolFlags;
3741
use externals::ExternalsPresets;
3842
use indexmap::IndexMap;
43+
use regex::Regex;
3944
use rspack_core::{
4045
AssetParserDataUrl, AssetParserDataUrlOptions, AssetParserOptions, BoxPlugin, ByDependency,
4146
CacheOptions, ChunkLoading, ChunkLoadingType, CleanOptions, Compiler, CompilerOptions,
@@ -2694,6 +2699,10 @@ impl OutputOptionsBuilder {
26942699
}
26952700
});
26962701

2702+
static CHUNK_FILENAME_BASENAME_RE: LazyLock<Regex> = LazyLock::new(|| {
2703+
Regex::new(r"(^|\/)([^/]*(?:\?|$))").expect("should initialize chunk filename basename regex")
2704+
});
2705+
26972706
let chunk_filename = f!(self.chunk_filename.take(), || {
26982707
// Get template string from filename if it's not a function
26992708
if let Some(template) = filename.template() {
@@ -2707,8 +2716,7 @@ impl OutputOptionsBuilder {
27072716
filename.clone()
27082717
} else {
27092718
// Otherwise prefix "[id]." in front of the basename to make it changing
2710-
let new_template = regex::Regex::new(r"(^|\/)([^/]*(?:\?|$))")
2711-
.expect("should initialize `Regex`")
2719+
let new_template = CHUNK_FILENAME_BASENAME_RE
27122720
.replace(template, "$1[id].$2")
27132721
.into_owned();
27142722
Filename::from(new_template)
@@ -2724,10 +2732,13 @@ impl OutputOptionsBuilder {
27242732
CrossOriginLoading::Disable
27252733
);
27262734

2735+
static JS_EXTENSION_IN_FILENAME_RE: LazyLock<Regex> = LazyLock::new(|| {
2736+
Regex::new(r"\.[mc]?js(\?|$)").expect("should initialize js extension in filename regex")
2737+
});
2738+
27272739
let css_filename = f!(self.css_filename.take(), || {
27282740
if let Some(template) = filename.template() {
2729-
let new_template = regex::Regex::new(r"\.[mc]?js(\?|$)")
2730-
.expect("should initialize `Regex`")
2741+
let new_template = JS_EXTENSION_IN_FILENAME_RE
27312742
.replace(template, ".css$1")
27322743
.into_owned();
27332744
Filename::from(new_template)
@@ -2738,8 +2749,7 @@ impl OutputOptionsBuilder {
27382749

27392750
let css_chunk_filename = f!(self.css_chunk_filename.take(), || {
27402751
if let Some(template) = chunk_filename.template() {
2741-
let new_template = regex::Regex::new(r"\.[mc]?js(\?|$)")
2742-
.expect("should initialize `Regex`")
2752+
let new_template = JS_EXTENSION_IN_FILENAME_RE
27432753
.replace(template, ".css$1")
27442754
.into_owned();
27452755
Filename::from(new_template)
@@ -2761,6 +2771,11 @@ impl OutputOptionsBuilder {
27612771
});
27622772

27632773
// Generate unique name from library name or package.json
2774+
static LIBRARY_NAME_PLACEHOLDER_RE: LazyLock<Regex> = LazyLock::new(|| {
2775+
Regex::new(r"^\[(\\*[\w:]+\\*)\](\.)|(\.)\[(\\*[\w:]+\\*)\](\.|\z)|\[(\\*[\w:]+\\*)\]")
2776+
.expect("failed to create library name placeholder regex")
2777+
});
2778+
27642779
let unique_name = f!(self.unique_name.take(), || {
27652780
if let Some(library) = &self.library
27662781
&& let Some(name) = &library.name
@@ -2772,29 +2787,25 @@ impl OutputOptionsBuilder {
27722787
};
27732788

27742789
// Clean up library name using regex
2775-
let re = regex::Regex::new(
2776-
r"^\[(\\*[\w:]+\\*)\](\.)|(\.)\[(\\*[\w:]+\\*)\](\.|\z)|\[(\\*[\w:]+\\*)\]",
2777-
)
2778-
.expect("failed to create regex");
2779-
2780-
let cleaned_name = re.replace_all(&library_name, |caps: &regex::Captures| {
2781-
let content = caps
2782-
.get(1)
2783-
.or_else(|| caps.get(4))
2784-
.or_else(|| caps.get(6))
2785-
.map_or("", |m| m.as_str());
2786-
2787-
if content.starts_with('\\') && content.ends_with('\\') {
2788-
format!(
2789-
"{}{}{}",
2790-
caps.get(3).map_or("", |_| "."),
2791-
format_args!("[{}]", &content[1..content.len() - 1]),
2792-
caps.get(2).map_or("", |_| ".")
2793-
)
2794-
} else {
2795-
String::new()
2796-
}
2797-
});
2790+
let cleaned_name =
2791+
LIBRARY_NAME_PLACEHOLDER_RE.replace_all(&library_name, |caps: &regex::Captures| {
2792+
let content = caps
2793+
.get(1)
2794+
.or_else(|| caps.get(4))
2795+
.or_else(|| caps.get(6))
2796+
.map_or("", |m| m.as_str());
2797+
2798+
if content.starts_with('\\') && content.ends_with('\\') {
2799+
format!(
2800+
"{}{}{}",
2801+
caps.get(3).map_or("", |_| "."),
2802+
format_args!("[{}]", &content[1..content.len() - 1]),
2803+
caps.get(2).map_or("", |_| ".")
2804+
)
2805+
} else {
2806+
String::new()
2807+
}
2808+
});
27982809

27992810
if !cleaned_name.is_empty() {
28002811
return cleaned_name.into_owned();

crates/rspack/src/builder/target.rs

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
use std::sync::LazyLock;
2+
3+
use regex::Regex;
14
use rspack_browserslist::load_browserslist;
25
use rspack_core::{CompilerPlatform, Context};
36

@@ -143,6 +146,25 @@ impl From<&TargetProperties> for CompilerPlatform {
143146
}
144147
}
145148

149+
static NODE_TARGET_RE: LazyLock<Regex> = LazyLock::new(|| {
150+
Regex::new(r"^(async-)?node((\d+)(?:\.(\d+))?)?$").expect("should initialize `Regex`")
151+
});
152+
153+
static BROWSERSLIST_TARGET_RE: LazyLock<Regex> =
154+
LazyLock::new(|| Regex::new(r"^browserslist(?::(.+))?$").expect("should initialize `Regex`"));
155+
156+
static ELECTRON_TARGET_RE: LazyLock<Regex> = LazyLock::new(|| {
157+
Regex::new(r"^electron((\d+)(?:\.(\d+))?)?-(main|preload|renderer)$")
158+
.expect("should initialize `Regex`")
159+
});
160+
161+
static NWJS_TARGET_RE: LazyLock<Regex> = LazyLock::new(|| {
162+
Regex::new(r"^(?:nwjs|node-webkit)((\d+)(?:\.(\d+))?)?$").expect("should initialize `Regex`")
163+
});
164+
165+
static ES_TARGET_RE: LazyLock<Regex> =
166+
LazyLock::new(|| Regex::new(r"^es(\d+)$").expect("should initialize `Regex`"));
167+
146168
fn version_dependent(
147169
major: u32,
148170
minor: Option<u32>,
@@ -217,10 +239,7 @@ fn merge_target_properties(target_properties: &[TargetProperties]) -> TargetProp
217239

218240
fn get_target_properties(target: &str, context: &Context) -> TargetProperties {
219241
// Parse target string
220-
if let Some(captures) = regex::Regex::new(r"^(async-)?node((\d+)(?:\.(\d+))?)?$")
221-
.expect("should initialize `Regex`")
222-
.captures(target)
223-
{
242+
if let Some(captures) = NODE_TARGET_RE.captures(target) {
224243
let async_flag = captures.get(1).is_some();
225244
let major = captures.get(3).map(|m| {
226245
m.as_str()
@@ -269,10 +288,7 @@ fn get_target_properties(target: &str, context: &Context) -> TargetProperties {
269288
};
270289
}
271290

272-
if let Some(captures) = regex::Regex::new(r"^browserslist(?::(.+))?$")
273-
.expect("should initialize `Regex`")
274-
.captures(target)
275-
{
291+
if let Some(captures) = BROWSERSLIST_TARGET_RE.captures(target) {
276292
let rest = captures.get(1).map(|m| m.as_str());
277293

278294
let browsers = load_browserslist(rest.map(|r| r.trim()), context).unwrap_or_default();
@@ -325,11 +341,7 @@ fn get_target_properties(target: &str, context: &Context) -> TargetProperties {
325341
}
326342

327343
// Electron target
328-
if let Some(captures) =
329-
regex::Regex::new(r"^electron((\d+)(?:\.(\d+))?)?-(main|preload|renderer)$")
330-
.expect("should initialize `Regex`")
331-
.captures(target)
332-
{
344+
if let Some(captures) = ELECTRON_TARGET_RE.captures(target) {
333345
let major = captures.get(2).map(|m| {
334346
m.as_str()
335347
.parse::<u32>()
@@ -381,10 +393,7 @@ fn get_target_properties(target: &str, context: &Context) -> TargetProperties {
381393
}
382394

383395
// NW.js target
384-
if let Some(captures) = regex::Regex::new(r"^(?:nwjs|node-webkit)((\d+)(?:\.(\d+))?)?$")
385-
.expect("should initialize `Regex`")
386-
.captures(target)
387-
{
396+
if let Some(captures) = NWJS_TARGET_RE.captures(target) {
388397
let major = captures.get(2).map(|m| {
389398
m.as_str()
390399
.parse::<u32>()
@@ -428,10 +437,7 @@ fn get_target_properties(target: &str, context: &Context) -> TargetProperties {
428437
}
429438

430439
// ES version target
431-
if let Some(captures) = regex::Regex::new(r"^es(\d+)$")
432-
.expect("should initialize `Regex`")
433-
.captures(target)
434-
{
440+
if let Some(captures) = ES_TARGET_RE.captures(target) {
435441
let mut version = captures
436442
.get(1)
437443
.expect("should initialize `Regex`")

crates/rspack_browserslist/src/load_config.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
use std::sync::LazyLock;
2+
13
use browserslist::Opts;
4+
use regex::Regex;
25

36
/// Configuration parsed from input string and context directory
47
#[derive(Debug, Default)]
@@ -38,10 +41,12 @@ pub fn parse<'a>(input: Option<&str>, context: &'a str) -> BrowserslistHandlerCo
3841
// group 1: absolute path (optionally Windows drive letter)
3942
// group 2: optional env suffix after colon
4043
// same as JS: /^(?:((?:[A-Z]:)?[/\\].*?))?(?::(.+?))?$/i
41-
let re = regex::Regex::new(r"^(?:((?:[A-Z]:)?[/\\].*?))?(?::(.+?))?$")
42-
.expect("Should initialize browserlist regex");
44+
static BROWSERSLIST_INPUT_RE: LazyLock<Regex> = LazyLock::new(|| {
45+
Regex::new(r"^(?:((?:[A-Z]:)?[/\\].*?))?(?::(.+?))?$")
46+
.expect("Should initialize browserslist input regex")
47+
});
4348

44-
if let Some(caps) = re.captures(input) {
49+
if let Some(caps) = BROWSERSLIST_INPUT_RE.captures(input) {
4550
let config_path = caps.get(1).map(|m| m.as_str().to_string());
4651
let env = caps.get(2).map(|m| m.as_str().to_string());
4752

crates/rspack_loader_swc/src/rsc_transforms/react_server_components.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ use swc_core::{
2323

2424
use super::{cjs_finder::contains_cjs, import_analyzer::ImportMap};
2525

26+
static NODE_MODULES_PATH_REGEX: Lazy<Regex> = Lazy::new(|| {
27+
#[allow(clippy::unwrap_used)]
28+
Regex::new(r"node_modules[\\/]").unwrap()
29+
});
30+
2631
#[derive(Clone, Debug, Deserialize)]
2732
#[serde(untagged)]
2833
pub enum Config {
@@ -431,11 +436,7 @@ impl ReactServerComponentValidator {
431436
}
432437

433438
fn is_from_node_modules(&self, filepath: &str) -> bool {
434-
static RE: Lazy<Regex> = Lazy::new(|| {
435-
#[allow(clippy::unwrap_used)]
436-
Regex::new(r"node_modules[\\/]").unwrap()
437-
});
438-
RE.is_match(filepath)
439+
NODE_MODULES_PATH_REGEX.is_match(filepath)
439440
}
440441

441442
// Asserts the server lib apis

crates/rspack_plugin_esm_library/src/dependency/dyn_import.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use atomic_refcell::AtomicRefCell;
44
use rspack_collections::IdentifierMap;
55
use rspack_core::{
66
Dependency, DependencyId, DependencyTemplate, ExportsType, FakeNamespaceObjectMode, ModuleGraph,
7-
RuntimeGlobals, TemplateContext, get_exports_type,
7+
ModuleReferenceOptions, RuntimeGlobals, TemplateContext, get_exports_type,
88
};
99
use rspack_plugin_javascript::dependency::ImportDependency;
1010
use rspack_plugin_rslib::dyn_import_external::render_dyn_import_external_module;
@@ -221,7 +221,31 @@ impl DependencyTemplate for DynamicImportDependencyTemplate {
221221
return;
222222
}
223223

224-
// Check if the module needs namespace remapping (exports were renamed or namespace access)
224+
if already_in_chunk {
225+
// Same chunk + scope hoisted: the module's variables are already in scope.
226+
// Use a namespace module reference so the link phase resolves it to the
227+
// module's namespace object (e.g., `Promise.resolve(dynamic_namespaceObject)`).
228+
let ns_ref = concatenation_scope.create_module_reference(
229+
&ref_module.identifier(),
230+
&ModuleReferenceOptions {
231+
ids: vec![],
232+
call: false,
233+
direct_import: true,
234+
deferred_import: false,
235+
asi_safe: Some(true),
236+
..Default::default()
237+
},
238+
);
239+
source.replace(
240+
import_dep.range.start,
241+
import_dep.range.end,
242+
&format!("Promise.resolve({ns_ref})"),
243+
None,
244+
);
245+
return;
246+
}
247+
248+
// Cross-chunk: check if the module needs namespace remapping (exports were renamed or namespace access)
225249
let ns_name = {
226250
let ns_map = self.dyn_import_ns_map.borrow();
227251
ns_map.get(&ref_module.identifier()).cloned()

0 commit comments

Comments
 (0)