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,
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 {
104 original_name,
105 has_action_manifest,
106 ..
107 } => RouteOperation::AppRoute {
108 original_name: original_name.clone(),
109 endpoint: pick_endpoint(entrypoints, EndpointSelector::RouteAppRoute(key)),
110 has_action_manifest: *has_action_manifest,
111 },
112 Route::Conflict => RouteOperation::Conflict,
113 }
114}
115
116#[turbo_tasks::task_input]
117#[derive(Debug, Clone, PartialEq, Eq, Hash, ValueDebugFormat, OperationValue, Encode, Decode)]
118enum EndpointSelector {
119 RoutePageHtml(RcStr),
120 RoutePageData(RcStr),
121 RoutePageApi(RcStr),
122 RouteAppPageHtml(RcStr, usize),
123 RouteAppPageRscHmr(RcStr, usize),
124 RouteAppRoute(RcStr),
125 InstrumentationNodeJs,
126 InstrumentationEdge,
127 Middleware,
128 PagesDocument,
129 PagesApp,
130 PagesError,
131}
132
133#[turbo_tasks::value(transparent)]
134pub struct OptionEndpoint(Option<ResolvedVc<Box<dyn Endpoint>>>);
135
136#[turbo_tasks::function(operation, root)]
140async fn pick_endpoint(
141 op: OperationVc<Entrypoints>,
142 selector: EndpointSelector,
143) -> Result<Vc<OptionEndpoint>> {
144 let endpoints = op.read_strongly_consistent().await?;
145 let endpoint = match selector {
146 EndpointSelector::InstrumentationNodeJs => {
147 endpoints.instrumentation.as_ref().map(|i| i.node_js)
148 }
149 EndpointSelector::InstrumentationEdge => endpoints.instrumentation.as_ref().map(|i| i.edge),
150 EndpointSelector::Middleware => endpoints.middleware.as_ref().map(|m| m.endpoint),
151 EndpointSelector::PagesDocument => Some(endpoints.pages_document_endpoint),
152 EndpointSelector::PagesApp => Some(endpoints.pages_app_endpoint),
153 EndpointSelector::PagesError => Some(endpoints.pages_error_endpoint),
154 EndpointSelector::RoutePageHtml(name) => {
155 if let Some(Route::Page { html_endpoint, .. }) = endpoints.routes.get(&name) {
156 Some(*html_endpoint)
157 } else {
158 None
159 }
160 }
161 EndpointSelector::RoutePageData(name) => {
162 if let Some(Route::Page { data_endpoint, .. }) = endpoints.routes.get(&name) {
163 *data_endpoint
164 } else {
165 None
166 }
167 }
168 EndpointSelector::RoutePageApi(name) => {
169 if let Some(Route::PageApi { endpoint }) = endpoints.routes.get(&name) {
170 Some(*endpoint)
171 } else {
172 None
173 }
174 }
175 EndpointSelector::RouteAppPageHtml(name, i) => {
176 if let Some(Route::AppPage(pages)) = endpoints.routes.get(&name) {
177 pages.get(i).as_ref().map(|p| p.html_endpoint)
178 } else {
179 None
180 }
181 }
182 EndpointSelector::RouteAppPageRscHmr(name, i) => {
183 if let Some(Route::AppPage(pages)) = endpoints.routes.get(&name) {
184 pages.get(i).as_ref().map(|p| p.rsc_hmr_endpoint)
185 } else {
186 None
187 }
188 }
189 EndpointSelector::RouteAppRoute(name) => {
190 if let Some(Route::AppRoute { endpoint, .. }) = endpoints.routes.get(&name) {
191 Some(*endpoint)
192 } else {
193 None
194 }
195 }
196 };
197 Ok(Vc::cell(endpoint))
198}
199
200#[derive(PartialEq, Eq, ValueDebugFormat, NonLocalValue, Encode, Decode)]
201pub struct InstrumentationOperation {
202 pub node_js: OperationVc<OptionEndpoint>,
203 pub edge: OperationVc<OptionEndpoint>,
204}
205
206#[derive(PartialEq, Eq, ValueDebugFormat, NonLocalValue, Encode, Decode)]
207pub struct MiddlewareOperation {
208 pub endpoint: OperationVc<OptionEndpoint>,
209 pub is_proxy: bool,
210}
211
212#[turbo_tasks::value(shared)]
213#[derive(Clone, Debug)]
214pub enum RouteOperation {
215 Page {
216 html_endpoint: OperationVc<OptionEndpoint>,
217 data_endpoint: OperationVc<OptionEndpoint>,
218 },
219 PageApi {
220 endpoint: OperationVc<OptionEndpoint>,
221 },
222 AppPage(Vec<AppPageRouteOperation>),
223 AppRoute {
224 original_name: RcStr,
225 endpoint: OperationVc<OptionEndpoint>,
226 has_action_manifest: bool,
227 },
228 Conflict,
229}
230
231#[derive(PartialEq, Eq, ValueDebugFormat, Clone, Debug, NonLocalValue, Encode, Decode)]
232pub struct AppPageRouteOperation {
233 pub original_name: RcStr,
234 pub html_endpoint: OperationVc<OptionEndpoint>,
235 pub rsc_hmr_endpoint: OperationVc<OptionEndpoint>,
236}