next_core/next_client_reference/
visit_client_reference.rs1use std::future::Future;
2
3use anyhow::Result;
4use bincode::{Decode, Encode};
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_core::{
14 chunk::ChunkingType, module::Module, reference::primary_chunkable_referenced_modules,
15};
16use turbopack_css::chunk::CssChunkPlaceable;
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 Debug,
34 ValueDebugFormat,
35 TraceRawVcs,
36 NonLocalValue,
37 Encode,
38 Decode,
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 Debug,
52 ValueDebugFormat,
53 TraceRawVcs,
54 NonLocalValue,
55 Encode,
56 Decode,
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 include_binding_usage: bool,
85) -> Result<Vc<ServerEntries>> {
86 async move {
87 let emit_spans = tracing::enabled!(Level::INFO);
88 let graph = AdjacencyMap::new()
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 emit_spans,
101 include_traced,
102 include_binding_usage,
103 },
104 )
105 .await
106 .completed()?;
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 emit_spans: bool,
134 include_traced: bool,
136 include_binding_usage: bool,
138}
139
140#[derive(Clone, Eq, PartialEq, Hash, Debug, ValueDebugFormat, TraceRawVcs, NonLocalValue)]
141enum FindServerEntriesNode {
142 ClientReference,
143 ServerComponentEntry(
144 ResolvedVc<NextServerComponentModule>,
145 Option<ReadRef<RcStr>>,
146 ),
147 ServerUtilEntry(ResolvedVc<NextServerUtilityModule>, Option<ReadRef<RcStr>>),
148 Internal(ResolvedVc<Box<dyn Module>>, Option<ReadRef<RcStr>>),
149}
150
151impl Visit<FindServerEntriesNode> for FindServerEntries {
152 type EdgesIntoIter = Vec<(FindServerEntriesNode, ())>;
153 type EdgesFuture = impl Future<Output = Result<Self::EdgesIntoIter>>;
154
155 fn visit(&mut self, node: &FindServerEntriesNode, _edge: Option<&()>) -> VisitControlFlow {
156 match node {
157 FindServerEntriesNode::Internal(..) => VisitControlFlow::Continue,
158 FindServerEntriesNode::ClientReference
159 | FindServerEntriesNode::ServerUtilEntry(..)
160 | FindServerEntriesNode::ServerComponentEntry(..) => VisitControlFlow::Skip,
161 }
162 }
163
164 fn edges(&mut self, node: &FindServerEntriesNode) -> Self::EdgesFuture {
165 let include_traced = self.include_traced;
166 let include_binding_usage = self.include_binding_usage;
167 let parent_module = match node {
168 FindServerEntriesNode::ClientReference => {
171 unreachable!("ClientReference node should not be visited")
172 }
173 FindServerEntriesNode::Internal(module, _) => **module,
174 FindServerEntriesNode::ServerUtilEntry(module, _) => Vc::upcast(**module),
175 FindServerEntriesNode::ServerComponentEntry(module, _) => Vc::upcast(**module),
176 };
177 let emit_spans = self.emit_spans;
178 async move {
179 let referenced_modules = primary_chunkable_referenced_modules(
183 parent_module,
184 include_traced,
185 include_binding_usage,
186 )
187 .await?;
188
189 let referenced_modules = referenced_modules
190 .iter()
191 .flat_map(|(_, resolved)| match resolved.chunking_type {
192 ChunkingType::Traced => None,
193 _ => Some(resolved.modules.iter()),
194 })
195 .flatten()
196 .map(async |module| {
197 if ResolvedVc::try_downcast_type::<EcmascriptClientReferenceModule>(*module)
198 .is_some()
199 || ResolvedVc::try_downcast_type::<CssClientReferenceModule>(*module)
200 .is_some()
201 {
202 return Ok((FindServerEntriesNode::ClientReference, ()));
203 }
204
205 if let Some(server_component_asset) =
206 ResolvedVc::try_downcast_type::<NextServerComponentModule>(*module)
207 {
208 return Ok((
209 FindServerEntriesNode::ServerComponentEntry(
210 server_component_asset,
211 if emit_spans {
212 Some(server_component_asset.ident_string().untracked().await?)
215 } else {
216 None
217 },
218 ),
219 (),
220 ));
221 }
222
223 if let Some(server_util_module) =
224 ResolvedVc::try_downcast_type::<NextServerUtilityModule>(*module)
225 {
226 return Ok((
227 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 }
240
241 Ok((
242 FindServerEntriesNode::Internal(
243 *module,
244 if emit_spans {
245 Some(module.ident_string().untracked().await?)
248 } else {
249 None
250 },
251 ),
252 (),
253 ))
254 });
255
256 let assets = referenced_modules.try_join().await?;
257
258 Ok(assets)
259 }
260 }
261
262 fn span(&mut self, node: &FindServerEntriesNode, _edge: Option<&()>) -> tracing::Span {
263 if !self.emit_spans {
264 return Span::none();
265 }
266 match node {
267 FindServerEntriesNode::ClientReference => {
268 tracing::info_span!("client reference")
269 }
270 FindServerEntriesNode::Internal(_, name) => {
271 tracing::info_span!("module", name = display(name.as_ref().unwrap()))
272 }
273 FindServerEntriesNode::ServerUtilEntry(_, name) => {
274 tracing::info_span!("server util", name = display(name.as_ref().unwrap()))
275 }
276 FindServerEntriesNode::ServerComponentEntry(_, name) => {
277 tracing::info_span!("layout segment", name = display(name.as_ref().unwrap()))
278 }
279 }
280 }
281}