diff --git a/crates/next-api/src/app.rs b/crates/next-api/src/app.rs index cae78afa761b8..b36deadf57ca3 100644 --- a/crates/next-api/src/app.rs +++ b/crates/next-api/src/app.rs @@ -49,7 +49,7 @@ use turbopack_core::{ asset::AssetContent, chunk::{ availability_info::AvailabilityInfo, ChunkingContext, ChunkingContextExt, - EntryChunkGroupResult, EvaluatableAssets, + EntryChunkGroupResult, EvaluatableAsset, EvaluatableAssets, }, file_source::FileSource, ident::AssetIdent, @@ -75,7 +75,7 @@ use crate::{ }, project::Project, route::{AppPageRoute, Endpoint, Route, Routes, WrittenEndpoint}, - server_actions::create_server_actions_manifest, + server_actions::{build_server_actions_loader, create_server_actions_manifest, get_actions}, }; #[turbo_tasks::value] @@ -764,6 +764,42 @@ impl AppEndpoint { Ok(app_entry) } + #[turbo_tasks::function] + async fn server_actions_loader(self: Vc) -> Result>> { + let this = self.await?; + + let app_entry = self.app_endpoint_entry().await?; + let rsc_entry = app_entry.rsc_entry; + + let runtime = app_entry.config.await?.runtime.unwrap_or_default(); + + let rsc_entry_asset = Vc::upcast(rsc_entry); + let client_references = client_reference_graph(Vc::cell(vec![rsc_entry_asset])); + let client_reference_types = client_references.types(); + let app_server_reference_modules = get_app_server_reference_modules(client_reference_types); + + let asset_context = match runtime { + NextRuntime::Edge => Vc::upcast(this.app_project.edge_rsc_module_context()), + NextRuntime::NodeJs => Vc::upcast(this.app_project.rsc_module_context()), + }; + + let actions = get_actions(rsc_entry, app_server_reference_modules, asset_context); + + let server_actions_loader = build_server_actions_loader( + this.app_project.project().project_path(), + app_entry.original_name.clone(), + actions, + asset_context, + ); + + let evaluatable_server_actions_loader = + Vc::try_resolve_sidecast::>(server_actions_loader) + .await? + .context("loader module must be evaluatable")?; + + Ok(evaluatable_server_actions_loader) + } + #[turbo_tasks::function] fn output_assets(self: Vc) -> Vc { self.output().output_assets() @@ -1048,11 +1084,12 @@ impl AppEndpoint { .context("Entry module must be evaluatable")?; evaluatable_assets.push(evaluatable); + let loader = self.server_actions_loader(); if let Some(app_server_reference_modules) = app_server_reference_modules { - let (loader, manifest) = create_server_actions_manifest( + let manifest = create_server_actions_manifest( Vc::upcast(app_entry.rsc_entry), + loader, app_server_reference_modules, - this.app_project.project().project_path(), node_root, &app_entry.original_name, NextRuntime::Edge, @@ -1192,11 +1229,12 @@ impl AppEndpoint { .project() .server_chunking_context(process_client); + let loader = self.server_actions_loader(); if let Some(app_server_reference_modules) = app_server_reference_modules { - let (loader, manifest) = create_server_actions_manifest( + let manifest = create_server_actions_manifest( Vc::upcast(app_entry.rsc_entry), + loader, app_server_reference_modules, - this.app_project.project().project_path(), node_root, &app_entry.original_name, NextRuntime::NodeJs, @@ -1371,8 +1409,17 @@ impl Endpoint for AppEndpoint { #[turbo_tasks::function] async fn root_modules(self: Vc) -> Result> { + let this = self.await?; + let rsc_entry = self.app_endpoint_entry().await?.rsc_entry; - Ok(Vc::cell(vec![rsc_entry])) + let mut modules = vec![rsc_entry]; + + if matches!(this.ty, AppEndpointType::Page { .. }) { + let server_actions_loader_module = Vc::upcast(self.server_actions_loader()); + modules.push(server_actions_loader_module); + } + + Ok(Vc::cell(modules)) } } diff --git a/crates/next-api/src/server_actions.rs b/crates/next-api/src/server_actions.rs index 6f68e8fe4daf6..c98c111e4a30b 100644 --- a/crates/next-api/src/server_actions.rs +++ b/crates/next-api/src/server_actions.rs @@ -1,6 +1,6 @@ use std::{collections::BTreeMap, io::Write, iter::once}; -use anyhow::{bail, Context, Result}; +use anyhow::{bail, Result}; use indexmap::{map::Entry, IndexMap}; use next_core::{ next_manifests::{ActionLayer, ActionManifestWorkerEntry, ServerReferenceManifest}, @@ -40,27 +40,22 @@ use turbopack_ecmascript::{ /// loader. pub(crate) async fn create_server_actions_manifest( rsc_entry: Vc>, + loader: Vc>, server_reference_modules: Vc>>>, - project_path: Vc, node_root: Vc, page_name: &str, runtime: NextRuntime, asset_context: Vc>, chunking_context: Vc>, -) -> Result<(Vc>, Vc>)> { +) -> Result>> { let actions = get_actions(rsc_entry, server_reference_modules, asset_context); - let loader = - build_server_actions_loader(project_path, page_name.into(), actions, asset_context); - let evaluable = Vc::try_resolve_sidecast::>(loader) - .await? - .context("loader module must be evaluatable")?; let loader_id = loader .as_chunk_item(Vc::upcast(chunking_context)) .id() .to_string(); let manifest = build_manifest(node_root, page_name, runtime, actions, loader_id).await?; - Ok((evaluable, manifest)) + Ok(manifest) } /// Builds the "action loader" entry point, which reexports every found action @@ -70,7 +65,7 @@ pub(crate) async fn create_server_actions_manifest( /// file's name and the action name). This hash matches the id sent to the /// client and present inside the paired manifest. #[turbo_tasks::function] -async fn build_server_actions_loader( +pub async fn build_server_actions_loader( project_path: Vc, page_name: RcStr, actions: Vc, @@ -167,7 +162,7 @@ fn action_modifier() -> Vc { /// comment which identifies server actions. Every found server action will be /// returned along with the module which exports that action. #[turbo_tasks::function] -async fn get_actions( +pub async fn get_actions( rsc_entry: Vc>, server_reference_modules: Vc>>>, asset_context: Vc>, @@ -332,7 +327,7 @@ type HashToLayerNameModule = IndexMap