next_core/next_app/app_client_references_chunks.rs
1use anyhow::Result;
2use tracing::Instrument;
3use turbo_rcstr::rcstr;
4use turbo_tasks::{FxIndexMap, ResolvedVc, TryFlatJoinIterExt, TryJoinIterExt, Vc};
5use turbopack_core::{
6 chunk::{ChunkGroupResult, ChunkingContext, availability_info::AvailabilityInfo},
7 module::Module,
8 module_graph::{ModuleGraph, chunk_group_info::ChunkGroup},
9 output::OutputAssetsWithReferenced,
10};
11
12use crate::{
13 next_client_reference::{
14 ClientReferenceType,
15 ecmascript_client_reference::ecmascript_client_reference_module::{
16 ecmascript_client_reference_merge_tag, ecmascript_client_reference_merge_tag_ssr,
17 },
18 visit_client_reference::ClientReferenceGraphResult,
19 },
20 next_server_component::server_component_module::NextServerComponentModule,
21};
22
23#[turbo_tasks::value]
24pub struct ClientReferencesChunks {
25 #[bincode(with = "turbo_bincode::indexmap")]
26 pub client_component_client_chunks:
27 FxIndexMap<ClientReferenceType, ResolvedVc<ChunkGroupResult>>,
28 #[bincode(with = "turbo_bincode::indexmap")]
29 pub client_component_ssr_chunks:
30 FxIndexMap<ClientReferenceType, ResolvedVc<OutputAssetsWithReferenced>>,
31 #[bincode(with = "turbo_bincode::indexmap")]
32 pub layout_segment_client_chunks:
33 FxIndexMap<ResolvedVc<NextServerComponentModule>, ResolvedVc<OutputAssetsWithReferenced>>,
34}
35
36/// Computes all client references chunks.
37///
38/// This returns a map from client reference type to the chunks that the reference
39/// type needs to load.
40#[turbo_tasks::function]
41pub async fn get_app_client_references_chunks(
42 app_client_references: Vc<ClientReferenceGraphResult>,
43 module_graph: Vc<ModuleGraph>,
44 client_chunking_context: Vc<Box<dyn ChunkingContext>>,
45 client_availability_info: AvailabilityInfo,
46 ssr_chunking_context: Option<Vc<Box<dyn ChunkingContext>>>,
47) -> Result<Vc<ClientReferencesChunks>> {
48 async move {
49 // TODO Reconsider this. Maybe it need to be true in production.
50 let separate_chunk_group_per_client_reference = false;
51 let app_client_references = app_client_references.await?;
52 if separate_chunk_group_per_client_reference {
53 todo!();
54 // let app_client_references_chunks: Vec<(_, (_, Option<_>))> = app_client_references
55 // .client_references
56 // .iter()
57 // .map(|client_reference| async move {
58 // Ok((
59 // client_reference.ty,
60 // match client_reference.ty {
61 // ClientReferenceType::EcmascriptClientReference(
62 // ecmascript_client_reference,
63 // ) => {
64 // let ecmascript_client_reference_ref =
65 // ecmascript_client_reference.await?;
66
67 // let client_chunk_group = client_chunking_context
68 // .root_chunk_group(
69 // module_graph,
70 // *ResolvedVc::upcast(
71 // ecmascript_client_reference_ref.client_module,
72 // ),
73 // )
74 // .await?;
75
76 // (
77 // (
78 // client_chunk_group.assets,
79 // client_chunk_group.availability_info,
80 // ),
81 // if let Some(ssr_chunking_context) = ssr_chunking_context {
82 // let ssr_chunk_group = ssr_chunking_context
83 // .root_chunk_group(
84 // *ResolvedVc::upcast(
85 // ecmascript_client_reference_ref.ssr_module,
86 // ),
87 // module_graph,
88 // )
89 // .await?;
90
91 // Some((
92 // ssr_chunk_group.assets,
93 // ssr_chunk_group.availability_info,
94 // ))
95 // } else {
96 // None
97 // },
98 // )
99 // }
100 // ClientReferenceType::CssClientReference(css_client_reference) => {
101 // let client_chunk_group = client_chunking_context
102 // .root_chunk_group(
103 // *ResolvedVc::upcast(css_client_reference),
104 // module_graph,
105 // )
106 // .await?;
107
108 // (
109 // (
110 // client_chunk_group.assets,
111 // client_chunk_group.availability_info,
112 // ),
113 // None,
114 // )
115 // }
116 // },
117 // ))
118 // })
119 // .try_join()
120 // .await?;
121
122 // Ok(ClientReferencesChunks {
123 // client_component_client_chunks: app_client_references_chunks
124 // .iter()
125 // .map(|&(client_reference_ty, (client_chunks, _))| {
126 // (client_reference_ty, client_chunks)
127 // })
128 // .collect(),
129 // client_component_ssr_chunks: app_client_references_chunks
130 // .iter()
131 // .flat_map(|&(client_reference_ty, (_, ssr_chunks))| {
132 // ssr_chunks.map(|ssr_chunks| (client_reference_ty, ssr_chunks))
133 // })
134 // .collect(),
135 // layout_segment_client_chunks: FxIndexMap::default(),
136 // }
137 // .cell())
138 } else {
139 let mut client_references_by_server_component: FxIndexMap<_, Vec<_>> =
140 FxIndexMap::default();
141 let mut framework_reference_types = Vec::new();
142 for &server_component in app_client_references.server_component_entries.iter() {
143 client_references_by_server_component
144 .entry(server_component)
145 .or_default();
146 }
147 for client_reference in app_client_references.client_references.iter() {
148 if let Some(server_component) = client_reference.server_component {
149 client_references_by_server_component
150 .entry(server_component)
151 .or_default()
152 .push(client_reference.ty);
153 } else {
154 framework_reference_types.push(client_reference.ty);
155 }
156 }
157 // Framework components need to go into first layout segment
158 if let Some((_, list)) = client_references_by_server_component.first_mut() {
159 list.extend(framework_reference_types);
160 }
161
162 let chunk_group_info = module_graph.chunk_group_info();
163
164 let mut current_client_chunk_group = ChunkGroupResult {
165 assets: ResolvedVc::cell(vec![]),
166 referenced_assets: ResolvedVc::cell(vec![]),
167 references: ResolvedVc::cell(vec![]),
168 availability_info: client_availability_info,
169 }
170 .resolved_cell();
171 let mut current_ssr_chunk_group = ChunkGroupResult::empty_resolved();
172
173 let mut layout_segment_client_chunks = FxIndexMap::default();
174 let mut client_component_ssr_chunks = FxIndexMap::default();
175 let mut client_component_client_chunks = FxIndexMap::default();
176
177 for (server_component, client_reference_types) in
178 client_references_by_server_component.into_iter()
179 {
180 let parent_chunk_group = *chunk_group_info
181 .get_index_of(ChunkGroup::Shared(ResolvedVc::upcast(
182 server_component.await?.module,
183 )))
184 .await?;
185
186 let base_ident = server_component.ident();
187
188 let server_path = server_component.server_path().owned().await?;
189 let is_layout = server_path.file_stem() == Some("layout");
190 let server_component_path = server_path.value_to_string().await?;
191
192 let ssr_modules = client_reference_types
193 .iter()
194 .map(|client_reference_ty| async move {
195 Ok(match client_reference_ty {
196 ClientReferenceType::EcmascriptClientReference(
197 ecmascript_client_reference,
198 ) => {
199 let ecmascript_client_reference_ref =
200 ecmascript_client_reference.await?;
201
202 Some(ResolvedVc::upcast(
203 ecmascript_client_reference_ref.ssr_module,
204 ))
205 }
206 _ => None,
207 })
208 })
209 .try_flat_join()
210 .await?;
211
212 let ssr_chunk_group = if !ssr_modules.is_empty()
213 && let Some(ssr_chunking_context) = ssr_chunking_context
214 {
215 let availability_info = current_ssr_chunk_group.await?.availability_info;
216 let _span = tracing::info_span!(
217 "server side rendering",
218 layout_segment = display(&server_component_path),
219 )
220 .entered();
221
222 Some(ssr_chunking_context.chunk_group(
223 base_ident.with_modifier(rcstr!("ssr modules")),
224 ChunkGroup::IsolatedMerged {
225 parent: parent_chunk_group,
226 merge_tag: ecmascript_client_reference_merge_tag_ssr(),
227 entries: ssr_modules,
228 },
229 module_graph,
230 availability_info,
231 ))
232 } else {
233 None
234 };
235
236 let client_modules = client_reference_types
237 .iter()
238 .map(|client_reference_ty| async move {
239 Ok(match client_reference_ty {
240 ClientReferenceType::EcmascriptClientReference(
241 ecmascript_client_reference,
242 ) => {
243 ResolvedVc::upcast(ecmascript_client_reference.await?.client_module)
244 }
245 ClientReferenceType::CssClientReference(css_client_reference) => {
246 ResolvedVc::upcast(*css_client_reference)
247 }
248 })
249 })
250 .try_join()
251 .await?;
252 let client_chunk_group = if !client_modules.is_empty() {
253 let availability_info = current_client_chunk_group.await?.availability_info;
254 let _span = tracing::info_span!(
255 "client side rendering",
256 layout_segment = display(&server_component_path),
257 )
258 .entered();
259
260 Some(client_chunking_context.chunk_group(
261 base_ident.with_modifier(rcstr!("client modules")),
262 ChunkGroup::IsolatedMerged {
263 parent: parent_chunk_group,
264 merge_tag: ecmascript_client_reference_merge_tag(),
265 entries: client_modules,
266 },
267 module_graph,
268 availability_info,
269 ))
270 } else {
271 None
272 };
273
274 if let Some(client_chunk_group) = client_chunk_group {
275 let client_chunk_group = current_client_chunk_group
276 .concatenate(client_chunk_group)
277 .to_resolved()
278 .await?;
279
280 if is_layout {
281 current_client_chunk_group = client_chunk_group;
282 }
283
284 let assets = client_chunk_group
285 .output_assets_with_referenced()
286 .to_resolved()
287 .await?;
288 layout_segment_client_chunks.insert(server_component, assets);
289
290 for &client_reference_ty in client_reference_types.iter() {
291 if let ClientReferenceType::EcmascriptClientReference(_) =
292 client_reference_ty
293 {
294 client_component_client_chunks
295 .insert(client_reference_ty, client_chunk_group);
296 }
297 }
298 }
299
300 if let Some(ssr_chunk_group) = ssr_chunk_group {
301 let ssr_chunk_group = current_ssr_chunk_group
302 .concatenate(ssr_chunk_group)
303 .to_resolved()
304 .await?;
305
306 if is_layout {
307 current_ssr_chunk_group = ssr_chunk_group;
308 }
309
310 let assets = ssr_chunk_group
311 .output_assets_with_referenced()
312 .to_resolved()
313 .await?;
314 for &client_reference_ty in client_reference_types.iter() {
315 if let ClientReferenceType::EcmascriptClientReference(_) =
316 client_reference_ty
317 {
318 client_component_ssr_chunks.insert(client_reference_ty, assets);
319 }
320 }
321 }
322 }
323
324 Ok(ClientReferencesChunks {
325 client_component_client_chunks,
326 client_component_ssr_chunks,
327 layout_segment_client_chunks,
328 }
329 .cell())
330 }
331 }
332 .instrument(tracing::info_span!("process client references"))
333 .await
334}