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::{OutputAssets, 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: FxIndexMap<ClientReferenceType, ChunkGroupResult>,
26    pub client_component_ssr_chunks: FxIndexMap<ClientReferenceType, ChunkGroupResult>,
27    pub layout_segment_client_chunks:
28        FxIndexMap<ResolvedVc<NextServerComponentModule>, OutputAssetsWithReferenced>,
29}
30
31/// Computes all client references chunks.
32///
33/// This returns a map from client reference type to the chunks that the reference
34/// type needs to load.
35#[turbo_tasks::function]
36pub async fn get_app_client_references_chunks(
37    app_client_references: Vc<ClientReferenceGraphResult>,
38    module_graph: Vc<ModuleGraph>,
39    client_chunking_context: Vc<Box<dyn ChunkingContext>>,
40    client_availability_info: AvailabilityInfo,
41    ssr_chunking_context: Option<Vc<Box<dyn ChunkingContext>>>,
42) -> Result<Vc<ClientReferencesChunks>> {
43    async move {
44        // TODO Reconsider this. Maybe it need to be true in production.
45        let separate_chunk_group_per_client_reference = false;
46        let app_client_references = app_client_references.await?;
47        if separate_chunk_group_per_client_reference {
48            todo!();
49            // let app_client_references_chunks: Vec<(_, (_, Option<_>))> = app_client_references
50            //     .client_references
51            //     .iter()
52            //     .map(|client_reference| async move {
53            //         let client_reference_ty = client_reference.ty();
54            //         Ok((
55            //             client_reference_ty,
56            //             match client_reference_ty {
57            //                 ClientReferenceType::EcmascriptClientReference(
58            //                     ecmascript_client_reference,
59            //                 ) => {
60            //                     let ecmascript_client_reference_ref =
61            //                         ecmascript_client_reference.await?;
62
63            //                     let client_chunk_group = client_chunking_context
64            //                         .root_chunk_group(
65            //                             module_graph,
66            //                             *ResolvedVc::upcast(
67            //                                 ecmascript_client_reference_ref.client_module,
68            //                             ),
69            //                         )
70            //                         .await?;
71
72            //                     (
73            //                         (
74            //                             client_chunk_group.assets,
75            //                             client_chunk_group.availability_info,
76            //                         ),
77            //                         if let Some(ssr_chunking_context) = ssr_chunking_context {
78            //                             let ssr_chunk_group = ssr_chunking_context
79            //                                 .root_chunk_group(
80            //                                     *ResolvedVc::upcast(
81            //                                         ecmascript_client_reference_ref.ssr_module,
82            //                                     ),
83            //                                     module_graph,
84            //                                 )
85            //                                 .await?;
86
87            //                             Some((
88            //                                 ssr_chunk_group.assets,
89            //                                 ssr_chunk_group.availability_info,
90            //                             ))
91            //                         } else {
92            //                             None
93            //                         },
94            //                     )
95            //                 }
96            //                 ClientReferenceType::CssClientReference(css_client_reference) => {
97            //                     let client_chunk_group = client_chunking_context
98            //                         .root_chunk_group(
99            //                             *ResolvedVc::upcast(css_client_reference),
100            //                             module_graph,
101            //                         )
102            //                         .await?;
103
104            //                     (
105            //                         (
106            //                             client_chunk_group.assets,
107            //                             client_chunk_group.availability_info,
108            //                         ),
109            //                         None,
110            //                     )
111            //                 }
112            //             },
113            //         ))
114            //     })
115            //     .try_join()
116            //     .await?;
117
118            // Ok(ClientReferencesChunks {
119            //     client_component_client_chunks: app_client_references_chunks
120            //         .iter()
121            //         .map(|&(client_reference_ty, (client_chunks, _))| {
122            //             (client_reference_ty, client_chunks)
123            //         })
124            //         .collect(),
125            //     client_component_ssr_chunks: app_client_references_chunks
126            //         .iter()
127            //         .flat_map(|&(client_reference_ty, (_, ssr_chunks))| {
128            //             ssr_chunks.map(|ssr_chunks| (client_reference_ty, ssr_chunks))
129            //         })
130            //         .collect(),
131            //     layout_segment_client_chunks: FxIndexMap::default(),
132            // }
133            // .cell())
134        } else {
135            let mut client_references_by_server_component: FxIndexMap<_, Vec<_>> =
136                FxIndexMap::default();
137            let mut framework_reference_types = Vec::new();
138            for &server_component in app_client_references.server_component_entries.iter() {
139                client_references_by_server_component
140                    .entry(server_component)
141                    .or_default();
142            }
143            for client_reference in app_client_references.client_references.iter() {
144                if let Some(server_component) = client_reference.server_component() {
145                    client_references_by_server_component
146                        .entry(server_component)
147                        .or_default()
148                        .push(client_reference.ty());
149                } else {
150                    framework_reference_types.push(client_reference.ty());
151                }
152            }
153            // Framework components need to go into first layout segment
154            if let Some((_, list)) = client_references_by_server_component.first_mut() {
155                list.extend(framework_reference_types);
156            }
157
158            let chunk_group_info = module_graph.chunk_group_info();
159
160            let mut current_client_availability_info = client_availability_info;
161            let mut current_client_chunks = OutputAssets::empty().to_resolved().await?;
162            let mut current_client_referenced_assets = OutputAssets::empty().to_resolved().await?;
163            let mut current_ssr_availability_info = AvailabilityInfo::Root;
164            let mut current_ssr_chunks = OutputAssets::empty().to_resolved().await?;
165            let mut current_ssr_referenced_assets = 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().owned().await?;
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 ChunkGroupResult {
268                        assets,
269                        referenced_assets,
270                        availability_info,
271                    } = *client_chunk_group.await?;
272
273                    let client_chunks = current_client_chunks
274                        .concatenate(*assets)
275                        .to_resolved()
276                        .await?;
277                    let client_referenced_assets = current_client_referenced_assets
278                        .concatenate(*referenced_assets)
279                        .to_resolved()
280                        .await?;
281
282                    if is_layout {
283                        current_client_availability_info = availability_info;
284                        current_client_chunks = client_chunks;
285                        current_client_referenced_assets = client_referenced_assets;
286                    }
287
288                    layout_segment_client_chunks.insert(
289                        server_component,
290                        OutputAssetsWithReferenced {
291                            assets: client_chunks,
292                            referenced_assets: client_referenced_assets,
293                        },
294                    );
295
296                    for &client_reference_ty in client_reference_types.iter() {
297                        if let ClientReferenceType::EcmascriptClientReference(_) =
298                            client_reference_ty
299                        {
300                            client_component_client_chunks.insert(
301                                client_reference_ty,
302                                ChunkGroupResult {
303                                    assets: client_chunks,
304                                    referenced_assets: client_referenced_assets,
305                                    availability_info,
306                                },
307                            );
308                        }
309                    }
310                }
311
312                if let Some(ssr_chunk_group) = ssr_chunk_group {
313                    let ssr_chunk_group = ssr_chunk_group.await?;
314
315                    let ssr_chunks = current_ssr_chunks
316                        .concatenate(*ssr_chunk_group.assets)
317                        .to_resolved()
318                        .await?;
319                    let ssr_referenced_assets = current_ssr_referenced_assets
320                        .concatenate(*ssr_chunk_group.referenced_assets)
321                        .to_resolved()
322                        .await?;
323
324                    if is_layout {
325                        current_ssr_availability_info = ssr_chunk_group.availability_info;
326                        current_ssr_chunks = ssr_chunks;
327                        current_ssr_referenced_assets = ssr_referenced_assets;
328                    }
329
330                    for &client_reference_ty in client_reference_types.iter() {
331                        if let ClientReferenceType::EcmascriptClientReference(_) =
332                            client_reference_ty
333                        {
334                            client_component_ssr_chunks.insert(
335                                client_reference_ty,
336                                ChunkGroupResult {
337                                    assets: ssr_chunks,
338                                    referenced_assets: ssr_referenced_assets,
339                                    availability_info: ssr_chunk_group.availability_info,
340                                },
341                            );
342                        }
343                    }
344                }
345            }
346
347            Ok(ClientReferencesChunks {
348                client_component_client_chunks,
349                client_component_ssr_chunks,
350                layout_segment_client_chunks,
351            }
352            .cell())
353        }
354    }
355    .instrument(tracing::info_span!("process client references"))
356    .await
357}