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(server_component)))
182                    .await?;
183
184                let base_ident = server_component.ident();
185
186                let server_path = server_component.server_path().owned().await?;
187                let is_layout = server_path.file_stem() == Some("layout");
188                let server_component_path = server_path.value_to_string().await?;
189
190                let ssr_modules = client_reference_types
191                    .iter()
192                    .map(|client_reference_ty| async move {
193                        Ok(match client_reference_ty {
194                            ClientReferenceType::EcmascriptClientReference(
195                                ecmascript_client_reference,
196                            ) => {
197                                let ecmascript_client_reference_ref =
198                                    ecmascript_client_reference.await?;
199
200                                Some(ResolvedVc::upcast(
201                                    ecmascript_client_reference_ref.ssr_module,
202                                ))
203                            }
204                            _ => None,
205                        })
206                    })
207                    .try_flat_join()
208                    .await?;
209
210                let ssr_chunk_group = if !ssr_modules.is_empty()
211                    && let Some(ssr_chunking_context) = ssr_chunking_context
212                {
213                    let availability_info = current_ssr_chunk_group.await?.availability_info;
214                    let _span = tracing::info_span!(
215                        "server side rendering",
216                        layout_segment = display(&server_component_path),
217                    )
218                    .entered();
219
220                    Some(ssr_chunking_context.chunk_group(
221                        base_ident.with_modifier(rcstr!("ssr modules")),
222                        ChunkGroup::IsolatedMerged {
223                            parent: parent_chunk_group,
224                            merge_tag: ecmascript_client_reference_merge_tag_ssr(),
225                            entries: ssr_modules,
226                        },
227                        module_graph,
228                        availability_info,
229                    ))
230                } else {
231                    None
232                };
233
234                let client_modules = client_reference_types
235                    .iter()
236                    .map(|client_reference_ty| async move {
237                        Ok(match client_reference_ty {
238                            ClientReferenceType::EcmascriptClientReference(
239                                ecmascript_client_reference,
240                            ) => {
241                                ResolvedVc::upcast(ecmascript_client_reference.await?.client_module)
242                            }
243                            ClientReferenceType::CssClientReference(css_client_reference) => {
244                                ResolvedVc::upcast(*css_client_reference)
245                            }
246                        })
247                    })
248                    .try_join()
249                    .await?;
250                let client_chunk_group = if !client_modules.is_empty() {
251                    let availability_info = current_client_chunk_group.await?.availability_info;
252                    let _span = tracing::info_span!(
253                        "client side rendering",
254                        layout_segment = display(&server_component_path),
255                    )
256                    .entered();
257
258                    Some(client_chunking_context.chunk_group(
259                        base_ident.with_modifier(rcstr!("client modules")),
260                        ChunkGroup::IsolatedMerged {
261                            parent: parent_chunk_group,
262                            merge_tag: ecmascript_client_reference_merge_tag(),
263                            entries: client_modules,
264                        },
265                        module_graph,
266                        availability_info,
267                    ))
268                } else {
269                    None
270                };
271
272                if let Some(client_chunk_group) = client_chunk_group {
273                    let client_chunk_group = current_client_chunk_group
274                        .concatenate(client_chunk_group)
275                        .to_resolved()
276                        .await?;
277
278                    if is_layout {
279                        current_client_chunk_group = client_chunk_group;
280                    }
281
282                    let assets = client_chunk_group
283                        .output_assets_with_referenced()
284                        .to_resolved()
285                        .await?;
286                    layout_segment_client_chunks.insert(server_component, assets);
287
288                    for &client_reference_ty in client_reference_types.iter() {
289                        if let ClientReferenceType::EcmascriptClientReference(_) =
290                            client_reference_ty
291                        {
292                            client_component_client_chunks
293                                .insert(client_reference_ty, client_chunk_group);
294                        }
295                    }
296                }
297
298                if let Some(ssr_chunk_group) = ssr_chunk_group {
299                    let ssr_chunk_group = current_ssr_chunk_group
300                        .concatenate(ssr_chunk_group)
301                        .to_resolved()
302                        .await?;
303
304                    if is_layout {
305                        current_ssr_chunk_group = ssr_chunk_group;
306                    }
307
308                    let assets = ssr_chunk_group
309                        .output_assets_with_referenced()
310                        .to_resolved()
311                        .await?;
312                    for &client_reference_ty in client_reference_types.iter() {
313                        if let ClientReferenceType::EcmascriptClientReference(_) =
314                            client_reference_ty
315                        {
316                            client_component_ssr_chunks.insert(client_reference_ty, assets);
317                        }
318                    }
319                }
320            }
321
322            Ok(ClientReferencesChunks {
323                client_component_client_chunks,
324                client_component_ssr_chunks,
325                layout_segment_client_chunks,
326            }
327            .cell())
328        }
329    }
330    .instrument(tracing::info_span!("process client references"))
331    .await
332}