Skip to content

Commit e681001

Browse files
committed
fix(rsc): group client chunks by server-entry ownership
1 parent 9760a3a commit e681001

33 files changed

Lines changed: 717 additions & 67 deletions

crates/rspack_binding_api/src/plugins/rsc.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@ impl From<&JsRscClientPluginOptions<'_>> for RscClientPluginOptions {
7777
#[napi(object, object_to_js = false)]
7878
pub struct JsRscServerPluginOptions<'a> {
7979
pub coordinator: ClassInstance<'a, JsCoordinator>,
80-
pub on_server_component_changes: Option<Either3<Function<'static, (), ()>, Undefined, Null>>,
80+
pub on_server_component_changes:
81+
Option<Either3<Function<'static, (), Promise<()>>, Undefined, Null>>,
8182
pub on_manifest: Option<Either3<Function<'static, String, Promise<()>>, Undefined, Null>>,
8283
}
8384

@@ -100,7 +101,14 @@ impl TryFrom<&JsRscServerPluginOptions<'_>> for RscServerPluginOptions {
100101
Some(Box::new(
101102
move || -> BoxFuture<'static, rspack_error::Result<()>> {
102103
let ts_fn = ts_fn.clone();
103-
Box::pin(async move { ts_fn.call_async(()).await.to_rspack_result() })
104+
Box::pin(async move {
105+
ts_fn
106+
.call_async(())
107+
.await
108+
.to_rspack_result()?
109+
.await
110+
.to_rspack_result()
111+
})
104112
},
105113
))
106114
}

crates/rspack_plugin_rsc/src/client_plugin.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,9 @@ async fn make(&self, compilation: &mut Compilation) -> Result<()> {
570570
if !client_modules.is_empty() || entry_state.has_css_imports_by_server_entry() {
571571
let dependency = Box::new(RscEntryDependency::new(
572572
entry_name.clone(),
573-
client_modules.clone(),
573+
entry_state.isolated_client_entries.clone(),
574+
entry_state.root_client_entries.clone(),
575+
entry_state.client_entries_by_server_entry.clone(),
574576
entry_state.css_imports_by_server_entry(),
575577
false,
576578
));

crates/rspack_plugin_rsc/src/component_info.rs

Lines changed: 70 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,17 @@ use crate::{
1919

2020
// { [request to inject into client compilation]: [exported names] }
2121
pub type ClientComponentImports = FxIndexMap<String, FxIndexSet<Atom>>;
22+
pub type ClientComponentImportsByServerEntry = FxIndexMap<String, ClientComponentImports>;
2223

2324
#[derive(Debug, Default)]
2425
pub struct ComponentInfo {
2526
pub should_inject_ssr_modules: bool,
27+
/// All client component imports reached from this RSC entry.
2628
pub client_component_imports: ClientComponentImports,
29+
/// Client component imports reached without a `use server-entry` owner.
30+
pub root_client_component_imports: ClientComponentImports,
31+
/// Client component imports reached under each `use server-entry` owner.
32+
pub client_component_imports_by_server_entry: ClientComponentImportsByServerEntry,
2733
pub css_imports_by_server_entry: CssImportsByServerEntry,
2834
pub root_css_imports: RootCssImports,
2935
pub action_imports: Vec<(String, Vec<ActionIdNamePair>)>,
@@ -155,12 +161,12 @@ fn filter_client_components(
155161
.client_component_imports
156162
.contains_key(resource.as_ref())
157163
{
158-
add_client_import(
164+
add_client_import_for_owners(
159165
module,
160-
&resource,
166+
resource.as_ref(),
161167
imported_identifiers,
162-
false,
163-
&mut component_info.client_component_imports,
168+
server_entries,
169+
component_info,
164170
);
165171
}
166172
return;
@@ -188,20 +194,12 @@ fn filter_client_components(
188194

189195
let module_graph = compilation.get_module_graph();
190196
if is_client_component_entry_module(module) {
191-
if !component_info
192-
.client_component_imports
193-
.contains_key(resource.as_ref())
194-
{
195-
component_info
196-
.client_component_imports
197-
.insert(resource.to_string(), Default::default());
198-
}
199-
add_client_import(
197+
add_client_import_for_owners(
200198
module,
201199
resource.as_ref(),
202200
imported_identifiers,
203-
true,
204-
&mut component_info.client_component_imports,
201+
server_entries,
202+
component_info,
205203
);
206204
return;
207205
}
@@ -251,6 +249,63 @@ fn filter_client_components(
251249
}
252250
}
253251

252+
fn add_client_import_for_owners(
253+
module: &dyn Module,
254+
resource: &str,
255+
imported_identifiers: &[Atom],
256+
server_entries: &[String],
257+
component_info: &mut ComponentInfo,
258+
) {
259+
add_client_import_to_map(
260+
module,
261+
resource,
262+
imported_identifiers,
263+
&mut component_info.client_component_imports,
264+
);
265+
266+
if server_entries.is_empty() {
267+
add_client_import_to_map(
268+
module,
269+
resource,
270+
imported_identifiers,
271+
&mut component_info.root_client_component_imports,
272+
);
273+
return;
274+
}
275+
276+
for server_entry in server_entries {
277+
let client_component_imports = component_info
278+
.client_component_imports_by_server_entry
279+
.entry(server_entry.clone())
280+
.or_default();
281+
add_client_import_to_map(
282+
module,
283+
resource,
284+
imported_identifiers,
285+
client_component_imports,
286+
);
287+
}
288+
}
289+
290+
fn add_client_import_to_map(
291+
module: &dyn Module,
292+
mod_request: &str,
293+
imported_identifiers: &[Atom],
294+
client_component_imports: &mut ClientComponentImports,
295+
) {
296+
let is_first_visit_module = !client_component_imports.contains_key(mod_request);
297+
if is_first_visit_module {
298+
client_component_imports.insert(mod_request.to_string(), Default::default());
299+
}
300+
add_client_import(
301+
module,
302+
mod_request,
303+
imported_identifiers,
304+
is_first_visit_module,
305+
client_component_imports,
306+
);
307+
}
308+
254309
fn add_client_import(
255310
module: &dyn Module,
256311
mod_request: &str,

crates/rspack_plugin_rsc/src/plugin_state.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use crate::reference_manifest::{
1616

1717
pub type ActionIdNamePair = (Atom, Atom);
1818
pub type CssImportsByServerEntry = FxHashMap<String, FxIndexSet<String>>;
19+
pub type ClientModulesByServerEntry = FxHashMap<String, Vec<ClientModuleImport>>;
1920
pub type RootCssImports = FxIndexSet<String>;
2021

2122
/// Structured info about a client module to inject into the client compiler.
@@ -39,7 +40,14 @@ pub struct ServerEntryState {
3940
#[derive(Debug, Default)]
4041
pub struct EntryState {
4142
pub server_entries: FxHashMap<String, ServerEntryState>,
43+
/// All client modules discovered from the RSC graph for this entry.
4244
pub injected_client_entries: Vec<ClientModuleImport>,
45+
/// Client modules used by multiple owners and kept as individual async chunks.
46+
pub isolated_client_entries: Vec<ClientModuleImport>,
47+
/// Client modules used only by the root RSC tree.
48+
pub root_client_entries: Vec<ClientModuleImport>,
49+
/// Client modules used only by one `use server-entry` subtree.
50+
pub client_entries_by_server_entry: ClientModulesByServerEntry,
4351
pub client_modules: FxHashMap<String, ManifestExport>,
4452
/// Root CSS import paths reached through a parent chain without `use server-entry`.
4553
/// These are attached directly to the matching client compiler entry.

crates/rspack_plugin_rsc/src/rsc_entry_dependency.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,21 @@ use rspack_core::{
99
DependencyType, FactorizeInfo, ModuleDependency, ResourceIdentifier,
1010
};
1111

12-
use crate::plugin_state::{ClientModuleImport, CssImportsByServerEntry};
12+
use crate::plugin_state::{
13+
ClientModuleImport, ClientModulesByServerEntry, CssImportsByServerEntry,
14+
};
1315

1416
#[cacheable]
1517
#[derive(Debug, Clone)]
1618
pub struct RscEntryDependency {
1719
id: DependencyId,
1820
pub name: Arc<str>,
21+
/// Client modules that should keep the existing one-module-per-async-block behavior.
1922
pub client_modules: Vec<ClientModuleImport>,
23+
/// Client modules owned by the root RSC entry.
24+
pub root_client_modules: Vec<ClientModuleImport>,
25+
#[cacheable(with=AsMap<AsCacheable, AsVec>)]
26+
pub client_modules_by_server_entry: ClientModulesByServerEntry,
2027
#[cacheable(with=AsMap<AsCacheable, AsVec>)]
2128
pub css_imports_by_server_entry: CssImportsByServerEntry,
2229
/// When true, client modules are loaded eagerly (not as code-split points).
@@ -30,6 +37,8 @@ impl RscEntryDependency {
3037
pub fn new(
3138
name: Arc<str>,
3239
client_modules: Vec<ClientModuleImport>,
40+
root_client_modules: Vec<ClientModuleImport>,
41+
client_modules_by_server_entry: ClientModulesByServerEntry,
3342
css_imports_by_server_entry: CssImportsByServerEntry,
3443
is_server_side_rendering: bool,
3544
) -> Self {
@@ -38,6 +47,8 @@ impl RscEntryDependency {
3847
id: DependencyId::new(),
3948
name,
4049
client_modules,
50+
root_client_modules,
51+
client_modules_by_server_entry,
4152
css_imports_by_server_entry,
4253
is_server_side_rendering,
4354
resource_identifier,

0 commit comments

Comments
 (0)