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 fn output(self: Vc<Self>) -> Vc<EndpointOutput>;
53 fn server_changed(self: Vc<Self>) -> Vc<Completion>;
55 fn client_changed(self: Vc<Self>) -> Vc<Completion>;
56 fn entries(self: Vc<Self>) -> Vc<GraphEntries>;
58 fn additional_entries(self: Vc<Self>, _graph: Vc<ModuleGraph>) -> Vc<GraphEntries> {
61 GraphEntries::empty()
62 }
63}
64
65#[turbo_tasks::value(transparent)]
66pub struct Endpoints(Vec<ResolvedVc<Box<dyn Endpoint>>>);
67
68#[turbo_tasks::function]
69pub async fn endpoint_write_to_disk(
70 endpoint: ResolvedVc<Box<dyn Endpoint>>,
71) -> Result<Vc<EndpointOutputPaths>> {
72 let output_op = output_assets_operation(endpoint);
73 let EndpointOutput {
74 project,
75 output_paths,
76 ..
77 } = *output_op.connect().await?;
78
79 let _ = project
80 .emit_all_output_assets(endpoint_output_assets_operation(output_op))
81 .resolve()
82 .await?;
83
84 Ok(*output_paths)
85}
86
87#[turbo_tasks::function(operation)]
88fn output_assets_operation(endpoint: ResolvedVc<Box<dyn Endpoint>>) -> Vc<EndpointOutput> {
89 endpoint.output()
90}
91
92#[turbo_tasks::function(operation)]
93async fn endpoint_output_assets_operation(
94 output: OperationVc<EndpointOutput>,
95) -> Result<Vc<OutputAssets>> {
96 Ok(*output.connect().await?.output_assets)
97}
98
99#[turbo_tasks::function(operation)]
100pub async fn endpoint_write_to_disk_operation(
101 endpoint: OperationVc<OptionEndpoint>,
102) -> Result<Vc<EndpointOutputPaths>> {
103 Ok(if let Some(endpoint) = *endpoint.connect().await? {
104 endpoint_write_to_disk(*endpoint)
105 } else {
106 EndpointOutputPaths::NotFound.cell()
107 })
108}
109
110#[turbo_tasks::function(operation)]
111pub async fn endpoint_server_changed_operation(
112 endpoint: OperationVc<OptionEndpoint>,
113) -> Result<Vc<Completion>> {
114 Ok(if let Some(endpoint) = *endpoint.connect().await? {
115 endpoint.server_changed()
116 } else {
117 Completion::new()
118 })
119}
120
121#[turbo_tasks::function(operation)]
122pub async fn endpoint_client_changed_operation(
123 endpoint: OperationVc<OptionEndpoint>,
124) -> Result<Vc<Completion>> {
125 Ok(if let Some(endpoint) = *endpoint.connect().await? {
126 endpoint.client_changed()
127 } else {
128 Completion::new()
129 })
130}
131
132#[turbo_tasks::value(shared)]
133#[derive(Debug, Clone)]
134pub struct EndpointOutput {
135 pub output_assets: ResolvedVc<OutputAssets>,
136 pub output_paths: ResolvedVc<EndpointOutputPaths>,
137 pub project: ResolvedVc<Project>,
138}
139
140#[turbo_tasks::value(shared)]
141#[derive(Debug, Clone)]
142pub enum EndpointOutputPaths {
143 NodeJs {
144 server_entry_path: String,
146 server_paths: Vec<ServerPath>,
147 client_paths: Vec<RcStr>,
148 },
149 Edge {
150 server_paths: Vec<ServerPath>,
151 client_paths: Vec<RcStr>,
152 },
153 NotFound,
154}
155
156#[turbo_tasks::value(transparent)]
159pub struct Routes(FxIndexMap<RcStr, Route>);