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