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    pub client_component_client_chunks:
26        FxIndexMap<ClientReferenceType, ResolvedVc<ChunkGroupResult>>,
27    pub client_component_ssr_chunks:
28        FxIndexMap<ClientReferenceType, ResolvedVc<OutputAssetsWithReferenced>>,
29    pub layout_segment_client_chunks:
30        FxIndexMap<ResolvedVc<NextServerComponentModule>, ResolvedVc<OutputAssetsWithReferenced>>,
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            //         Ok((
56            //             client_reference.ty,
57            //             match client_reference.ty {
58            //                 ClientReferenceType::EcmascriptClientReference(
59            //                     ecmascript_client_reference,
60            //                 ) => {
61            //                     let ecmascript_client_reference_ref =
62            //                         ecmascript_client_reference.await?;
63
64            //                     let client_chunk_group = client_chunking_context
65            //                         .root_chunk_group(
66            //                             module_graph,
67            //                             *ResolvedVc::upcast(
68            //                                 ecmascript_client_reference_ref.client_module,
69            //                             ),
70            //                         )
71            //                         .await?;
72
73            //                     (
74            //                         (
75            //                             client_chunk_group.assets,
76            //                             client_chunk_group.availability_info,
77            //                         ),
78            //                         if let Some(ssr_chunking_context) = ssr_chunking_context {
79            //                             let ssr_chunk_group = ssr_chunking_context
80            //                                 .root_chunk_group(
81            //                                     *ResolvedVc::upcast(
82            //                                         ecmascript_client_reference_ref.ssr_module,
83            //                                     ),
84            //                                     module_graph,
85            //                                 )
86            //                                 .await?;
87
88            //                             Some((
89            //                                 ssr_chunk_group.assets,
90            //                                 ssr_chunk_group.availability_info,
91            //                             ))
92            //                         } else {
93            //                             None
94            //                         },
95            //                     )
96            //                 }
97            //                 ClientReferenceType::CssClientReference(css_client_reference) => {
98            //                     let client_chunk_group = client_chunking_context
99            //                         .root_chunk_group(
100            //                             *ResolvedVc::upcast(css_client_reference),
101            //                             module_graph,
102            //                         )
103            //                         .await?;
104
105            //                     (
106            //                         (
107            //                             client_chunk_group.assets,
108            //                             client_chunk_group.availability_info,
109            //                         ),
110            //                         None,
111            //                     )
112            //                 }
113            //             },
114            //         ))
115            //     })
116            //     .try_join()
117            //     .await?;
118
119            // Ok(ClientReferencesChunks {
120            //     client_component_client_chunks: app_client_references_chunks
121            //         .iter()
122            //         .map(|&(client_reference_ty, (client_chunks, _))| {
123            //             (client_reference_ty, client_chunks)
124            //         })
125            //         .collect(),
126            //     client_component_ssr_chunks: app_client_references_chunks
127            //         .iter()
128            //         .flat_map(|&(client_reference_ty, (_, ssr_chunks))| {
129            //             ssr_chunks.map(|ssr_chunks| (client_reference_ty, ssr_chunks))
130            //         })
131            //         .collect(),
132            //     layout_segment_client_chunks: FxIndexMap::default(),
133            // }
134            // .cell())
135        } else {
136            let mut client_references_by_server_component: FxIndexMap<_, Vec<_>> =
137                FxIndexMap::default();
138            let mut framework_reference_types = Vec::new();
139            for &server_component in app_client_references.server_component_entries.iter() {
140                client_references_by_server_component
141                    .entry(server_component)
142                    .or_default();
143            }
144            for client_reference in app_client_references.client_references.iter() {
145                if let Some(server_component) = client_reference.server_component {
146                    client_references_by_server_component
147                        .entry(server_component)
148                        .or_default()
149                        .push(client_reference.ty);
150                } else {
151                    framework_reference_types.push(client_reference.ty);
152                }
153            }
154            // Framework components need to go into first layout segment
155            if let Some((_, list)) = client_references_by_server_component.first_mut() {
156                list.extend(framework_reference_types);
157            }
158
159            let chunk_group_info = module_graph.chunk_group_info();
160
161            let mut current_client_chunk_group = ChunkGroupResult {
162                assets: ResolvedVc::cell(vec![]),
163                referenced_assets: ResolvedVc::cell(vec![]),
164                references: ResolvedVc::cell(vec![]),
165                availability_info: client_availability_info,
166            }
167            .resolved_cell();
168            let mut current_ssr_chunk_group = ChunkGroupResult::empty_resolved();
169
170            let mut layout_segment_client_chunks = FxIndexMap::default();
171            let mut client_component_ssr_chunks = FxIndexMap::default();
172            let mut client_component_client_chunks = FxIndexMap::default();
173
174            for (server_component, client_reference_types) in
175                client_references_by_server_component.into_iter()
176            {
177                let parent_chunk_group = *chunk_group_info
178                    .get_index_of(ChunkGroup::Shared(ResolvedVc::upcast(
179                        server_component.await?.module,
180                    )))
181                    .await?;
182
183                let base_ident = server_component.ident();
184
185                let server_path = server_component.server_path().owned().await?;
186                let is_layout = server_path.file_stem() == Some("layout");
187                let server_component_path = server_path.value_to_string().await?;
188
189                let ssr_modules = client_reference_types
190                    .iter()
191                    .map(|client_reference_ty| async move {
192                        Ok(match client_reference_ty {
193                            ClientReferenceType::EcmascriptClientReference(
194                                ecmascript_client_reference,
195                            ) => {
196                                let ecmascript_client_reference_ref =
197                                    ecmascript_client_reference.await?;
198
199                                Some(ResolvedVc::upcast(
200                                    ecmascript_client_reference_ref.ssr_module,
201                                ))
202                            }
203                            _ => None,
204                        })
205                    })
206                    .try_flat_join()
207                    .await?;
208
209                let ssr_chunk_group = if !ssr_modules.is_empty()
210                    && let Some(ssr_chunking_context) = ssr_chunking_context
211                {
212                    let availability_info = current_ssr_chunk_group.await?.availability_info;
213                    let _span = tracing::info_span!(
214                        "server side rendering",
215                        layout_segment = display(&server_component_path),
216                    )
217                    .entered();
218
219                    Some(ssr_chunking_context.chunk_group(
220                        base_ident.with_modifier(rcstr!("ssr modules")),
221                        ChunkGroup::IsolatedMerged {
222                            parent: parent_chunk_group,
223                            merge_tag: ecmascript_client_reference_merge_tag_ssr(),
224                            entries: ssr_modules,
225                        },
226                        module_graph,
227                        availability_info,
228                    ))
229                } else {
230                    None
231                };
232
233                let client_modules = client_reference_types
234                    .iter()
235                    .map(|client_reference_ty| async move {
236                        Ok(match client_reference_ty {
237                            ClientReferenceType::EcmascriptClientReference(
238                                ecmascript_client_reference,
239                            ) => {
240                                ResolvedVc::upcast(ecmascript_client_reference.await?.client_module)
241                            }
242                            ClientReferenceType::CssClientReference(css_client_reference) => {
243                                ResolvedVc::upcast(*css_client_reference)
244                            }
245                        })
246                    })
247                    .try_join()
248                    .await?;
249                let client_chunk_group = if !client_modules.is_empty() {
250                    let availability_info = current_client_chunk_group.await?.availability_info;
251                    let _span = tracing::info_span!(
252                        "client side rendering",
253                        layout_segment = display(&server_component_path),
254                    )
255                    .entered();
256
257                    Some(client_chunking_context.chunk_group(
258                        base_ident.with_modifier(rcstr!("client modules")),
259                        ChunkGroup::IsolatedMerged {
260                            parent: parent_chunk_group,
261                            merge_tag: ecmascript_client_reference_merge_tag(),
262                            entries: client_modules,
263                        },
264                        module_graph,
265                        availability_info,
266                    ))
267                } else {
268                    None
269                };
270
271                if let Some(client_chunk_group) = client_chunk_group {
272                    let client_chunk_group = current_client_chunk_group
273                        .concatenate(client_chunk_group)
274                        .to_resolved()
275                        .await?;
276
277                    if is_layout {
278                        current_client_chunk_group = client_chunk_group;
279                    }
280
281                    let assets = client_chunk_group
282                        .output_assets_with_referenced()
283                        .to_resolved()
284                        .await?;
285                    layout_segment_client_chunks.insert(server_component, assets);
286
287                    for &client_reference_ty in client_reference_types.iter() {
288                        if let ClientReferenceType::EcmascriptClientReference(_) =
289                            client_reference_ty
290                        {
291                            client_component_client_chunks
292                                .insert(client_reference_ty, client_chunk_group);
293                        }
294                    }
295                }
296
297                if let Some(ssr_chunk_group) = ssr_chunk_group {
298                    let ssr_chunk_group = current_ssr_chunk_group
299                        .concatenate(ssr_chunk_group)
300                        .to_resolved()
301                        .await?;
302
303                    if is_layout {
304                        current_ssr_chunk_group = ssr_chunk_group;
305                    }
306
307                    let assets = ssr_chunk_group
308                        .output_assets_with_referenced()
309                        .to_resolved()
310                        .await?;
311                    for &client_reference_ty in client_reference_types.iter() {
312                        if let ClientReferenceType::EcmascriptClientReference(_) =
313                            client_reference_ty
314                        {
315                            client_component_ssr_chunks.insert(client_reference_ty, assets);
316                        }
317                    }
318                }
319            }
320
321            Ok(ClientReferencesChunks {
322                client_component_client_chunks,
323                client_component_ssr_chunks,
324                layout_segment_client_chunks,
325            }
326            .cell())
327        }
328    }
329    .instrument(tracing::info_span!("process client references"))
330    .await
331}