1use anyhow::Result;
2use bincode::{Decode, Encode};
3use turbo_rcstr::RcStr;
4use turbo_tasks::{
5 FxIndexMap, NonLocalValue, OperationValue, OperationVc, ResolvedVc, Vc,
6 debug::ValueDebugFormat, take_effects, trace::TraceRawVcs,
7};
8use turbopack_core::issue::CollectibleIssuesExt;
9
10use crate::{
11 entrypoints::Entrypoints,
12 route::{Endpoint, Route},
13};
14
15#[turbo_tasks::value(shared)]
24pub struct EntrypointsOperation {
25 #[bincode(with = "turbo_bincode::indexmap")]
26 pub routes: FxIndexMap<RcStr, RouteOperation>,
27 pub middleware: Option<MiddlewareOperation>,
28 pub instrumentation: Option<InstrumentationOperation>,
29 pub pages_document_endpoint: OperationVc<OptionEndpoint>,
30 pub pages_app_endpoint: OperationVc<OptionEndpoint>,
31 pub pages_error_endpoint: OperationVc<OptionEndpoint>,
32}
33
34#[turbo_tasks::function(operation, root)]
37async fn entrypoints_without_collectibles_operation(
38 entrypoints: OperationVc<Entrypoints>,
39) -> Result<Vc<Entrypoints>> {
40 let _ = entrypoints.resolve().strongly_consistent().await?;
41 entrypoints.drop_issues();
42 let _ = take_effects(entrypoints).await?;
43 Ok(entrypoints.connect())
44}
45
46#[turbo_tasks::value_impl]
47impl EntrypointsOperation {
48 #[turbo_tasks::function(operation, root)]
49 pub async fn new(entrypoints: OperationVc<Entrypoints>) -> Result<Vc<Self>> {
50 let e = entrypoints.connect().await?;
51 let entrypoints = entrypoints_without_collectibles_operation(entrypoints);
52 Ok(Self {
53 routes: e
54 .routes
55 .iter()
56 .map(|(k, v)| (k.clone(), pick_route(entrypoints, k.clone(), v)))
57 .collect(),
58 middleware: e.middleware.as_ref().map(|m| MiddlewareOperation {
59 endpoint: pick_endpoint(entrypoints, EndpointSelector::Middleware),
60 is_proxy: m.is_proxy,
61 }),
62 instrumentation: e
63 .instrumentation
64 .as_ref()
65 .map(|_| InstrumentationOperation {
66 node_js: pick_endpoint(entrypoints, EndpointSelector::InstrumentationNodeJs),
67 edge: pick_endpoint(entrypoints, EndpointSelector::InstrumentationEdge),
68 }),
69 pages_document_endpoint: pick_endpoint(entrypoints, EndpointSelector::PagesDocument),
70 pages_app_endpoint: pick_endpoint(entrypoints, EndpointSelector::PagesApp),
71 pages_error_endpoint: pick_endpoint(entrypoints, EndpointSelector::PagesError),
72 }
73 .cell())
74 }
75}
76
77fn pick_route(entrypoints: OperationVc<Entrypoints>, key: RcStr, route: &Route) -> RouteOperation {
78 match route {
79 Route::Page { .. } => RouteOperation::Page {
80 html_endpoint: pick_endpoint(entrypoints, EndpointSelector::RoutePageHtml(key.clone())),
81 data_endpoint: pick_endpoint(entrypoints, EndpointSelector::RoutePageData(key)),
82 },
83 Route::PageApi { .. } => RouteOperation::PageApi {
84 endpoint: pick_endpoint(entrypoints, EndpointSelector::RoutePageApi(key)),
85 },
86 Route::AppPage(pages) => RouteOperation::AppPage(
87 pages
88 .iter()
89 .enumerate()
90 .map(|(i, p)| AppPageRouteOperation {
91 original_name: p.original_name.clone(),
92 html_endpoint: pick_endpoint(
93 entrypoints,
94 EndpointSelector::RouteAppPageHtml(key.clone(), i),
95 ),
96 rsc_hmr_endpoint: pick_endpoint(
97 entrypoints,
98 EndpointSelector::RouteAppPageRscHmr(key.clone(), i),
99 ),
100 })
101 .collect(),
102 ),
103 Route::AppRoute { original_name, .. } => RouteOperation::AppRoute {
104 original_name: original_name.clone(),
105 endpoint: pick_endpoint(entrypoints, EndpointSelector::RouteAppRoute(key)),
106 },
107 Route::Conflict => RouteOperation::Conflict,
108 }
109}
110
111#[turbo_tasks::task_input]
112#[derive(
113 Debug, Clone, TraceRawVcs, PartialEq, Eq, Hash, ValueDebugFormat, OperationValue, Encode, Decode,
114)]
115enum EndpointSelector {
116 RoutePageHtml(RcStr),
117 RoutePageData(RcStr),
118 RoutePageApi(RcStr),
119 RouteAppPageHtml(RcStr, usize),
120 RouteAppPageRscHmr(RcStr, usize),
121 RouteAppRoute(RcStr),
122 InstrumentationNodeJs,
123 InstrumentationEdge,
124 Middleware,
125 PagesDocument,
126 PagesApp,
127 PagesError,
128}
129
130#[turbo_tasks::value(transparent)]
131pub struct OptionEndpoint(Option<ResolvedVc<Box<dyn Endpoint>>>);
132
133#[turbo_tasks::function(operation, root)]
137async fn pick_endpoint(
138 op: OperationVc<Entrypoints>,
139 selector: EndpointSelector,
140) -> Result<Vc<OptionEndpoint>> {
141 let endpoints = op.read_strongly_consistent().await?;
142 let endpoint = match selector {
143 EndpointSelector::InstrumentationNodeJs => {
144 endpoints.instrumentation.as_ref().map(|i| i.node_js)
145 }
146 EndpointSelector::InstrumentationEdge => endpoints.instrumentation.as_ref().map(|i| i.edge),
147 EndpointSelector::Middleware => endpoints.middleware.as_ref().map(|m| m.endpoint),
148 EndpointSelector::PagesDocument => Some(endpoints.pages_document_endpoint),
149 EndpointSelector::PagesApp => Some(endpoints.pages_app_endpoint),
150 EndpointSelector::PagesError => Some(endpoints.pages_error_endpoint),
151 EndpointSelector::RoutePageHtml(name) => {
152 if let Some(Route::Page { html_endpoint, .. }) = endpoints.routes.get(&name) {
153 Some(*html_endpoint)
154 } else {
155 None
156 }
157 }
158 EndpointSelector::RoutePageData(name) => {
159 if let Some(Route::Page { data_endpoint, .. }) = endpoints.routes.get(&name) {
160 *data_endpoint
161 } else {
162 None
163 }
164 }
165 EndpointSelector::RoutePageApi(name) => {
166 if let Some(Route::PageApi { endpoint }) = endpoints.routes.get(&name) {
167 Some(*endpoint)
168 } else {
169 None
170 }
171 }
172 EndpointSelector::RouteAppPageHtml(name, i) => {
173 if let Some(Route::AppPage(pages)) = endpoints.routes.get(&name) {
174 pages.get(i).as_ref().map(|p| p.html_endpoint)
175 } else {
176 None
177 }
178 }
179 EndpointSelector::RouteAppPageRscHmr(name, i) => {
180 if let Some(Route::AppPage(pages)) = endpoints.routes.get(&name) {
181 pages.get(i).as_ref().map(|p| p.rsc_hmr_endpoint)
182 } else {
183 None
184 }
185 }
186 EndpointSelector::RouteAppRoute(name) => {
187 if let Some(Route::AppRoute { endpoint, .. }) = endpoints.routes.get(&name) {
188 Some(*endpoint)
189 } else {
190 None
191 }
192 }
193 };
194 Ok(Vc::cell(endpoint))
195}
196
197#[derive(TraceRawVcs, PartialEq, Eq, ValueDebugFormat, NonLocalValue, Encode, Decode)]
198pub struct InstrumentationOperation {
199 pub node_js: OperationVc<OptionEndpoint>,
200 pub edge: OperationVc<OptionEndpoint>,
201}
202
203#[derive(TraceRawVcs, PartialEq, Eq, ValueDebugFormat, NonLocalValue, Encode, Decode)]
204pub struct MiddlewareOperation {
205 pub endpoint: OperationVc<OptionEndpoint>,
206 pub is_proxy: bool,
207}
208
209#[turbo_tasks::value(shared)]
210#[derive(Clone, Debug)]
211pub enum RouteOperation {
212 Page {
213 html_endpoint: OperationVc<OptionEndpoint>,
214 data_endpoint: OperationVc<OptionEndpoint>,
215 },
216 PageApi {
217 endpoint: OperationVc<OptionEndpoint>,
218 },
219 AppPage(Vec<AppPageRouteOperation>),
220 AppRoute {
221 original_name: RcStr,
222 endpoint: OperationVc<OptionEndpoint>,
223 },
224 Conflict,
225}
226
227#[derive(
228 TraceRawVcs, PartialEq, Eq, ValueDebugFormat, Clone, Debug, NonLocalValue, Encode, Decode,
229)]
230pub struct AppPageRouteOperation {
231 pub original_name: RcStr,
232 pub html_endpoint: OperationVc<OptionEndpoint>,
233 pub rsc_hmr_endpoint: OperationVc<OptionEndpoint>,
234}