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