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