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