Skip to main content

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}