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}