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::{ChunkingContext, availability_info::AvailabilityInfo},
7 module::Module,
8 module_graph::{ModuleGraph, chunk_group_info::ChunkGroup},
9 output::OutputAssets,
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<OutputAssets>, AvailabilityInfo)>,
27 pub client_component_ssr_chunks:
28 FxIndexMap<ClientReferenceType, (ResolvedVc<OutputAssets>, AvailabilityInfo)>,
29 pub layout_segment_client_chunks:
30 FxIndexMap<ResolvedVc<NextServerComponentModule>, ResolvedVc<OutputAssets>>,
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 // let client_reference_ty = client_reference.ty();
56 // Ok((
57 // client_reference_ty,
58 // match client_reference_ty {
59 // ClientReferenceType::EcmascriptClientReference(
60 // ecmascript_client_reference,
61 // ) => {
62 // let ecmascript_client_reference_ref =
63 // ecmascript_client_reference.await?;
64
65 // let client_chunk_group = client_chunking_context
66 // .root_chunk_group(
67 // module_graph,
68 // *ResolvedVc::upcast(
69 // ecmascript_client_reference_ref.client_module,
70 // ),
71 // )
72 // .await?;
73
74 // (
75 // (
76 // client_chunk_group.assets,
77 // client_chunk_group.availability_info,
78 // ),
79 // if let Some(ssr_chunking_context) = ssr_chunking_context {
80 // let ssr_chunk_group = ssr_chunking_context
81 // .root_chunk_group(
82 // *ResolvedVc::upcast(
83 // ecmascript_client_reference_ref.ssr_module,
84 // ),
85 // module_graph,
86 // )
87 // .await?;
88
89 // Some((
90 // ssr_chunk_group.assets,
91 // ssr_chunk_group.availability_info,
92 // ))
93 // } else {
94 // None
95 // },
96 // )
97 // }
98 // ClientReferenceType::CssClientReference(css_client_reference) => {
99 // let client_chunk_group = client_chunking_context
100 // .root_chunk_group(
101 // *ResolvedVc::upcast(css_client_reference),
102 // module_graph,
103 // )
104 // .await?;
105
106 // (
107 // (
108 // client_chunk_group.assets,
109 // client_chunk_group.availability_info,
110 // ),
111 // None,
112 // )
113 // }
114 // },
115 // ))
116 // })
117 // .try_join()
118 // .await?;
119
120 // Ok(ClientReferencesChunks {
121 // client_component_client_chunks: app_client_references_chunks
122 // .iter()
123 // .map(|&(client_reference_ty, (client_chunks, _))| {
124 // (client_reference_ty, client_chunks)
125 // })
126 // .collect(),
127 // client_component_ssr_chunks: app_client_references_chunks
128 // .iter()
129 // .flat_map(|&(client_reference_ty, (_, ssr_chunks))| {
130 // ssr_chunks.map(|ssr_chunks| (client_reference_ty, ssr_chunks))
131 // })
132 // .collect(),
133 // layout_segment_client_chunks: FxIndexMap::default(),
134 // }
135 // .cell())
136 } else {
137 let mut client_references_by_server_component: FxIndexMap<_, Vec<_>> =
138 FxIndexMap::default();
139 let mut framework_reference_types = Vec::new();
140 for &server_component in app_client_references.server_component_entries.iter() {
141 client_references_by_server_component
142 .entry(server_component)
143 .or_default();
144 }
145 for client_reference in app_client_references.client_references.iter() {
146 if let Some(server_component) = client_reference.server_component() {
147 client_references_by_server_component
148 .entry(server_component)
149 .or_default()
150 .push(client_reference.ty());
151 } else {
152 framework_reference_types.push(client_reference.ty());
153 }
154 }
155 // Framework components need to go into first layout segment
156 if let Some((_, list)) = client_references_by_server_component.first_mut() {
157 list.extend(framework_reference_types);
158 }
159
160 let chunk_group_info = module_graph.chunk_group_info();
161
162 let mut current_client_availability_info = client_availability_info;
163 let mut current_client_chunks = OutputAssets::empty().to_resolved().await?;
164 let mut current_ssr_availability_info = AvailabilityInfo::Root;
165 let mut current_ssr_chunks = 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().await?.clone_value();
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 client_chunk_group = client_chunk_group.await?;
268
269 let client_chunks =
270 current_client_chunks.concatenate(*client_chunk_group.assets);
271 let client_chunks = client_chunks.to_resolved().await?;
272
273 if is_layout {
274 current_client_availability_info = client_chunk_group.availability_info;
275 current_client_chunks = client_chunks;
276 }
277
278 layout_segment_client_chunks.insert(server_component, client_chunks);
279
280 for &client_reference_ty in client_reference_types.iter() {
281 if let ClientReferenceType::EcmascriptClientReference(_) =
282 client_reference_ty
283 {
284 client_component_client_chunks.insert(
285 client_reference_ty,
286 (client_chunks, client_chunk_group.availability_info),
287 );
288 }
289 }
290 }
291
292 if let Some(ssr_chunk_group) = ssr_chunk_group {
293 let ssr_chunk_group = ssr_chunk_group.await?;
294
295 let ssr_chunks = current_ssr_chunks.concatenate(*ssr_chunk_group.assets);
296 let ssr_chunks = ssr_chunks.to_resolved().await?;
297
298 if is_layout {
299 current_ssr_availability_info = ssr_chunk_group.availability_info;
300 current_ssr_chunks = ssr_chunks;
301 }
302
303 for &client_reference_ty in client_reference_types.iter() {
304 if let ClientReferenceType::EcmascriptClientReference(_) =
305 client_reference_ty
306 {
307 client_component_ssr_chunks.insert(
308 client_reference_ty,
309 (ssr_chunks, ssr_chunk_group.availability_info),
310 );
311 }
312 }
313 }
314 }
315
316 Ok(ClientReferencesChunks {
317 client_component_client_chunks,
318 client_component_ssr_chunks,
319 layout_segment_client_chunks,
320 }
321 .cell())
322 }
323 }
324 .instrument(tracing::info_span!("process client references"))
325 .await
326}