1use anyhow::Result;
2use serde::{Deserialize, Serialize};
3use turbo_rcstr::RcStr;
4use turbo_tasks::{
5 Completion, FxIndexMap, NonLocalValue, OperationVc, ResolvedVc, Vc, debug::ValueDebugFormat,
6 trace::TraceRawVcs,
7};
8use turbopack_core::{
9 module_graph::{GraphEntries, ModuleGraph},
10 output::OutputAssets,
11};
12
13use crate::{operation::OptionEndpoint, paths::ServerPath, project::Project};
14
15#[derive(
16 TraceRawVcs,
17 Serialize,
18 Deserialize,
19 PartialEq,
20 Eq,
21 ValueDebugFormat,
22 Clone,
23 Debug,
24 NonLocalValue,
25)]
26pub struct AppPageRoute {
27 pub original_name: RcStr,
28 pub html_endpoint: ResolvedVc<Box<dyn Endpoint>>,
29 pub rsc_endpoint: ResolvedVc<Box<dyn Endpoint>>,
30}
31
32#[turbo_tasks::value(shared)]
33#[derive(Clone, Debug)]
34pub enum Route {
35 Page {
36 html_endpoint: ResolvedVc<Box<dyn Endpoint>>,
37 data_endpoint: ResolvedVc<Box<dyn Endpoint>>,
38 },
39 PageApi {
40 endpoint: ResolvedVc<Box<dyn Endpoint>>,
41 },
42 AppPage(Vec<AppPageRoute>),
43 AppRoute {
44 original_name: RcStr,
45 endpoint: ResolvedVc<Box<dyn Endpoint>>,
46 },
47 Conflict,
48}
49
50#[turbo_tasks::value_trait]
51pub trait Endpoint {
52 #[turbo_tasks::function]
53 fn output(self: Vc<Self>) -> Vc<EndpointOutput>;
54 #[turbo_tasks::function]
56 fn server_changed(self: Vc<Self>) -> Vc<Completion>;
57 #[turbo_tasks::function]
58 fn client_changed(self: Vc<Self>) -> Vc<Completion>;
59 #[turbo_tasks::function]
61 fn entries(self: Vc<Self>) -> Vc<GraphEntries>;
62 #[turbo_tasks::function]
65 fn additional_entries(self: Vc<Self>, _graph: Vc<ModuleGraph>) -> Vc<GraphEntries> {
66 GraphEntries::empty()
67 }
68}
69
70#[turbo_tasks::value(transparent)]
71pub struct Endpoints(Vec<ResolvedVc<Box<dyn Endpoint>>>);
72
73#[turbo_tasks::function]
74pub async fn endpoint_write_to_disk(
75 endpoint: ResolvedVc<Box<dyn Endpoint>>,
76) -> Result<Vc<EndpointOutputPaths>> {
77 let output_op = output_assets_operation(endpoint);
78 let EndpointOutput {
79 project,
80 output_paths,
81 ..
82 } = *output_op.connect().await?;
83
84 let _ = project
85 .emit_all_output_assets(endpoint_output_assets_operation(output_op))
86 .resolve()
87 .await?;
88
89 Ok(*output_paths)
90}
91
92#[turbo_tasks::function(operation)]
93fn output_assets_operation(endpoint: ResolvedVc<Box<dyn Endpoint>>) -> Vc<EndpointOutput> {
94 endpoint.output()
95}
96
97#[turbo_tasks::function(operation)]
98async fn endpoint_output_assets_operation(
99 output: OperationVc<EndpointOutput>,
100) -> Result<Vc<OutputAssets>> {
101 Ok(*output.connect().await?.output_assets)
102}
103
104#[turbo_tasks::function(operation)]
105pub async fn endpoint_write_to_disk_operation(
106 endpoint: OperationVc<OptionEndpoint>,
107) -> Result<Vc<EndpointOutputPaths>> {
108 Ok(if let Some(endpoint) = *endpoint.connect().await? {
109 endpoint_write_to_disk(*endpoint)
110 } else {
111 EndpointOutputPaths::NotFound.cell()
112 })
113}
114
115#[turbo_tasks::function(operation)]
116pub async fn endpoint_server_changed_operation(
117 endpoint: OperationVc<OptionEndpoint>,
118) -> Result<Vc<Completion>> {
119 Ok(if let Some(endpoint) = *endpoint.connect().await? {
120 endpoint.server_changed()
121 } else {
122 Completion::new()
123 })
124}
125
126#[turbo_tasks::function(operation)]
127pub async fn endpoint_client_changed_operation(
128 endpoint: OperationVc<OptionEndpoint>,
129) -> Result<Vc<Completion>> {
130 Ok(if let Some(endpoint) = *endpoint.connect().await? {
131 endpoint.client_changed()
132 } else {
133 Completion::new()
134 })
135}
136
137#[turbo_tasks::value(shared)]
138#[derive(Debug, Clone)]
139pub struct EndpointOutput {
140 pub output_assets: ResolvedVc<OutputAssets>,
141 pub output_paths: ResolvedVc<EndpointOutputPaths>,
142 pub project: ResolvedVc<Project>,
143}
144
145#[turbo_tasks::value(shared)]
146#[derive(Debug, Clone)]
147pub enum EndpointOutputPaths {
148 NodeJs {
149 server_entry_path: String,
151 server_paths: Vec<ServerPath>,
152 client_paths: Vec<RcStr>,
153 },
154 Edge {
155 server_paths: Vec<ServerPath>,
156 client_paths: Vec<RcStr>,
157 },
158 NotFound,
159}
160
161#[turbo_tasks::value(transparent)]
164pub struct Routes(FxIndexMap<RcStr, Route>);