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    fn output(self: Vc<Self>) -> Vc<EndpointOutput>;
53    // fn write_to_disk(self: Vc<Self>) -> Vc<EndpointOutputPaths>;
54    fn server_changed(self: Vc<Self>) -> Vc<Completion>;
55    fn client_changed(self: Vc<Self>) -> Vc<Completion>;
56    /// The entry modules for the modules graph.
57    fn entries(self: Vc<Self>) -> Vc<GraphEntries>;
58    /// Additional entry modules for the module graph.
59    /// This may read the module graph and return additional modules.
60    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        /// Relative to the root_path
145        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/// The routes as map from pathname to route. (pathname includes the leading
157/// slash)
158#[turbo_tasks::value(transparent)]
159pub struct Routes(FxIndexMap<RcStr, Route>);