next_api/
route.rs

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    // fn write_to_disk(self: Vc<Self>) -> Vc<EndpointOutputPaths>;
55    #[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    /// The entry modules for the modules graph.
60    #[turbo_tasks::function]
61    fn entries(self: Vc<Self>) -> Vc<GraphEntries>;
62    /// Additional entry modules for the module graph.
63    /// This may read the module graph and return additional modules.
64    #[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        /// Relative to the root_path
150        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/// The routes as map from pathname to route. (pathname includes the leading
162/// slash)
163#[turbo_tasks::value(transparent)]
164pub struct Routes(FxIndexMap<RcStr, Route>);