next_core/next_client_reference/
visit_client_reference.rs1use std::future::Future;
2
3use anyhow::Result;
4use rustc_hash::FxHashSet;
5use serde::{Deserialize, Serialize};
6use tracing::Instrument;
7use turbo_rcstr::RcStr;
8use turbo_tasks::{
9 FxIndexSet, NonLocalValue, ReadRef, ResolvedVc, TryJoinIterExt, ValueToString, Vc,
10 debug::ValueDebugFormat,
11 graph::{AdjacencyMap, GraphTraversal, Visit, VisitControlFlow},
12 trace::TraceRawVcs,
13};
14use turbopack::css::chunk::CssChunkPlaceable;
15use turbopack_core::{
16 chunk::ChunkingType, module::Module, reference::primary_chunkable_referenced_modules,
17};
18
19use crate::{
20 next_client_reference::{
21 CssClientReferenceModule,
22 ecmascript_client_reference::ecmascript_client_reference_module::EcmascriptClientReferenceModule,
23 },
24 next_server_component::server_component_module::NextServerComponentModule,
25 next_server_utility::server_utility_module::NextServerUtilityModule,
26};
27
28#[derive(
29 Copy,
30 Clone,
31 Eq,
32 PartialEq,
33 Hash,
34 Serialize,
35 Deserialize,
36 Debug,
37 ValueDebugFormat,
38 TraceRawVcs,
39 NonLocalValue,
40)]
41pub struct ClientReference {
42 pub server_component: Option<ResolvedVc<NextServerComponentModule>>,
43 pub ty: ClientReferenceType,
44}
45
46impl ClientReference {
47 pub fn server_component(&self) -> Option<ResolvedVc<NextServerComponentModule>> {
48 self.server_component
49 }
50
51 pub fn ty(&self) -> ClientReferenceType {
52 self.ty
53 }
54}
55
56#[derive(
57 Copy,
58 Clone,
59 Eq,
60 PartialEq,
61 Hash,
62 Serialize,
63 Deserialize,
64 Debug,
65 ValueDebugFormat,
66 TraceRawVcs,
67 NonLocalValue,
68)]
69pub enum ClientReferenceType {
70 EcmascriptClientReference(ResolvedVc<EcmascriptClientReferenceModule>),
71 CssClientReference(ResolvedVc<Box<dyn CssChunkPlaceable>>),
72}
73
74#[turbo_tasks::value(shared)]
75#[derive(Clone, Debug, Default)]
76pub struct ClientReferenceGraphResult {
77 pub client_references: Vec<ClientReference>,
78 pub server_component_entries: Vec<ResolvedVc<NextServerComponentModule>>,
79 pub server_utils: Vec<ResolvedVc<NextServerUtilityModule>>,
80}
81
82#[turbo_tasks::value(shared)]
83pub struct VisitedClientReferenceGraphNodes(FxHashSet<FindServerEntriesNode>);
84
85#[turbo_tasks::value_impl]
86impl VisitedClientReferenceGraphNodes {
87 #[turbo_tasks::function]
88 pub fn empty() -> Vc<Self> {
89 VisitedClientReferenceGraphNodes(Default::default()).cell()
90 }
91}
92
93#[turbo_tasks::value(transparent)]
94pub struct ClientReferenceTypes(FxIndexSet<ClientReferenceType>);
95
96#[turbo_tasks::value_impl]
97impl ClientReferenceGraphResult {
98 #[turbo_tasks::function]
99 pub fn types(&self) -> Vc<ClientReferenceTypes> {
100 Vc::cell(
101 self.client_references
102 .iter()
103 .map(|r| r.ty())
104 .collect::<FxIndexSet<_>>(),
105 )
106 }
107}
108
109#[turbo_tasks::value(shared)]
110#[derive(Clone, Debug)]
111pub struct ServerEntries {
112 pub server_component_entries: Vec<ResolvedVc<NextServerComponentModule>>,
113 pub server_utils: Vec<ResolvedVc<NextServerUtilityModule>>,
114}
115
116#[turbo_tasks::function]
119pub async fn find_server_entries(
120 entry: ResolvedVc<Box<dyn Module>>,
121 include_traced: bool,
122) -> Result<Vc<ServerEntries>> {
123 async move {
124 let graph = AdjacencyMap::new()
125 .skip_duplicates()
126 .visit(
127 vec![FindServerEntriesNode::Internal(
128 entry,
129 entry.ident().to_string().await?,
130 )],
131 FindServerEntries { include_traced },
132 )
133 .await
134 .completed()?
135 .into_inner();
136
137 let mut server_component_entries = vec![];
138 let mut server_utils = vec![];
139 for node in graph.postorder_topological() {
140 match node {
141 FindServerEntriesNode::ServerUtilEntry(server_util, _) => {
142 server_utils.push(*server_util);
143 }
144 FindServerEntriesNode::ServerComponentEntry(server_component, _) => {
145 server_component_entries.push(*server_component);
146 }
147 FindServerEntriesNode::Internal(_, _) | FindServerEntriesNode::ClientReference => {}
148 }
149 }
150
151 Ok(ServerEntries {
152 server_component_entries,
153 server_utils,
154 }
155 .cell())
156 }
157 .instrument(tracing::info_span!("find server entries"))
158 .await
159}
160
161struct FindServerEntries {
162 include_traced: bool,
164}
165
166#[derive(
167 Clone,
168 Eq,
169 PartialEq,
170 Hash,
171 Serialize,
172 Deserialize,
173 Debug,
174 ValueDebugFormat,
175 TraceRawVcs,
176 NonLocalValue,
177)]
178enum FindServerEntriesNode {
179 ClientReference,
180 ServerComponentEntry(ResolvedVc<NextServerComponentModule>, ReadRef<RcStr>),
181 ServerUtilEntry(ResolvedVc<NextServerUtilityModule>, ReadRef<RcStr>),
182 Internal(ResolvedVc<Box<dyn Module>>, ReadRef<RcStr>),
183}
184
185impl Visit<FindServerEntriesNode> for FindServerEntries {
186 type Edge = FindServerEntriesNode;
187 type EdgesIntoIter = Vec<Self::Edge>;
188 type EdgesFuture = impl Future<Output = Result<Self::EdgesIntoIter>>;
189
190 fn visit(&mut self, edge: Self::Edge) -> VisitControlFlow<FindServerEntriesNode> {
191 match edge {
192 FindServerEntriesNode::Internal(..) => VisitControlFlow::Continue(edge),
193 FindServerEntriesNode::ClientReference
194 | FindServerEntriesNode::ServerUtilEntry(..)
195 | FindServerEntriesNode::ServerComponentEntry(..) => VisitControlFlow::Skip(edge),
196 }
197 }
198
199 fn edges(&mut self, node: &FindServerEntriesNode) -> Self::EdgesFuture {
200 let include_traced = self.include_traced;
201 let parent_module = match node {
202 FindServerEntriesNode::ClientReference => {
205 unreachable!("ClientReference node should not be visited")
206 }
207 FindServerEntriesNode::Internal(module, _) => **module,
208 FindServerEntriesNode::ServerUtilEntry(module, _) => Vc::upcast(**module),
209 FindServerEntriesNode::ServerComponentEntry(module, _) => Vc::upcast(**module),
210 };
211 async move {
212 let referenced_modules =
215 primary_chunkable_referenced_modules(parent_module, include_traced).await?;
216
217 let referenced_modules = referenced_modules
218 .iter()
219 .flat_map(|(chunking_type, _, modules)| match chunking_type {
220 ChunkingType::Traced => None,
221 _ => Some(modules.iter()),
222 })
223 .flatten()
224 .map(async |module| {
225 if ResolvedVc::try_downcast_type::<EcmascriptClientReferenceModule>(*module)
226 .is_some()
227 || ResolvedVc::try_downcast_type::<CssClientReferenceModule>(*module)
228 .is_some()
229 {
230 return Ok(FindServerEntriesNode::ClientReference);
231 }
232
233 if let Some(server_component_asset) =
234 ResolvedVc::try_downcast_type::<NextServerComponentModule>(*module)
235 {
236 return Ok(FindServerEntriesNode::ServerComponentEntry(
237 server_component_asset,
238 server_component_asset.ident().to_string().await?,
239 ));
240 }
241
242 if let Some(server_util_module) =
243 ResolvedVc::try_downcast_type::<NextServerUtilityModule>(*module)
244 {
245 return Ok(FindServerEntriesNode::ServerUtilEntry(
246 server_util_module,
247 module.ident().to_string().await?,
248 ));
249 }
250
251 Ok(FindServerEntriesNode::Internal(
252 *module,
253 module.ident().to_string().await?,
254 ))
255 });
256
257 let assets = referenced_modules.try_join().await?;
258
259 Ok(assets)
260 }
261 }
262
263 fn span(&mut self, node: &FindServerEntriesNode) -> tracing::Span {
264 match node {
265 FindServerEntriesNode::ClientReference => {
266 tracing::info_span!("client reference")
267 }
268 FindServerEntriesNode::Internal(_, name) => {
269 tracing::info_span!("module", name = name.to_string())
270 }
271 FindServerEntriesNode::ServerUtilEntry(_, name) => {
272 tracing::info_span!("server util", name = name.to_string())
273 }
274 FindServerEntriesNode::ServerComponentEntry(_, name) => {
275 tracing::info_span!("layout segment", name = name.to_string())
276 }
277 }
278 }
279}