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