diff --git a/crates/next-api/src/app.rs b/crates/next-api/src/app.rs index 273c7c544855b..cf3f5c0aa4a00 100644 --- a/crates/next-api/src/app.rs +++ b/crates/next-api/src/app.rs @@ -10,7 +10,6 @@ use next_core::{ }, get_edge_resolve_options_context, get_next_package, next_app::{ - app_client_references_chunks::get_app_server_reference_modules, get_app_client_references_chunks, get_app_client_shared_chunk_group, get_app_page_entry, get_app_route_entry, include_modules_module::IncludeModulesModule, metadata::route::get_app_metadata_route_entry, AppEntry, AppPage, @@ -910,256 +909,254 @@ impl AppEndpoint { None }; - let ( - app_server_reference_modules, - client_dynamic_imports, - client_references, - client_references_chunks, - ) = if process_client_components { - let client_shared_chunk_group = get_app_client_shared_chunk_group( - AssetIdent::from_path(this.app_project.project().project_path()) - .with_modifier(client_shared_chunks_modifier()), - this.app_project.client_runtime_entries(), - client_chunking_context, - ) - .await?; + let (client_dynamic_imports, client_references, client_references_chunks) = + if process_client_components { + let client_shared_chunk_group = get_app_client_shared_chunk_group( + AssetIdent::from_path(this.app_project.project().project_path()) + .with_modifier(client_shared_chunks_modifier()), + this.app_project.client_runtime_entries(), + client_chunking_context, + ) + .await?; - let mut client_shared_chunks = vec![]; - for chunk in client_shared_chunk_group.assets.await?.iter().copied() { - client_assets.insert(chunk); + let mut client_shared_chunks = vec![]; + for chunk in client_shared_chunk_group.assets.await?.iter().copied() { + client_assets.insert(chunk); - let chunk_path = chunk.ident().path().await?; - if chunk_path.extension_ref() == Some("js") { - client_shared_chunks.push(chunk); + let chunk_path = chunk.ident().path().await?; + if chunk_path.extension_ref() == Some("js") { + client_shared_chunks.push(chunk); + } } - } - let client_shared_availability_info = client_shared_chunk_group.availability_info; + let client_shared_availability_info = client_shared_chunk_group.availability_info; - let client_references = { - let ServerEntries { - server_component_entries, - server_utils, - } = &*find_server_entries(*rsc_entry).await?; + let client_references = { + let ServerEntries { + server_component_entries, + server_utils, + } = &*find_server_entries(*rsc_entry).await?; - let mut client_references = client_reference_graph( - server_utils.clone(), - VisitedClientReferenceGraphNodes::empty(), - ) - .await? - .clone_value(); + let mut client_references = client_reference_graph( + server_utils.clone(), + VisitedClientReferenceGraphNodes::empty(), + ) + .await? + .clone_value(); - for module in server_component_entries - .iter() - .map(|m| Vc::upcast::>(*m)) - .chain(std::iter::once(*rsc_entry)) - { - let current_client_references = - client_reference_graph(vec![module], client_references.visited_nodes) - .await?; + for module in server_component_entries + .iter() + .map(|m| Vc::upcast::>(*m)) + .chain(std::iter::once(*rsc_entry)) + { + let current_client_references = + client_reference_graph(vec![module], client_references.visited_nodes) + .await?; - client_references.extend(¤t_client_references); - } - client_references - }; - let client_references_cell = client_references.clone().cell(); + client_references.extend(¤t_client_references); + } + client_references + }; + let client_references_cell = client_references.clone().cell(); + + let client_dynamic_imports = { + let mut client_dynamic_imports = FxIndexMap::default(); + let mut visited_modules = VisitedDynamicImportModules::empty(); + + for refs in client_references + .client_references_by_server_component + .values() + { + let result = collect_next_dynamic_imports( + refs.clone(), + Vc::upcast(this.app_project.client_module_context()), + visited_modules, + ) + .await?; + client_dynamic_imports.extend( + result + .client_dynamic_imports + .iter() + .map(|(k, v)| (*k, v.clone())), + ); + visited_modules = result.visited_modules; + } + + client_dynamic_imports + }; - let client_dynamic_imports = { - let mut client_dynamic_imports = FxIndexMap::default(); - let mut visited_modules = VisitedDynamicImportModules::empty(); + let client_references_chunks = get_app_client_references_chunks( + client_references_cell, + client_chunking_context, + Value::new(client_shared_availability_info), + ssr_chunking_context, + ); + let client_references_chunks_ref = client_references_chunks.await?; - for refs in client_references - .client_references_by_server_component + let mut entry_client_chunks = FxIndexSet::default(); + // TODO(alexkirsz) In which manifest does this go? + let mut entry_ssr_chunks = FxIndexSet::default(); + for chunks in client_references_chunks_ref + .layout_segment_client_chunks .values() { - let result = collect_next_dynamic_imports( - refs.clone(), - Vc::upcast(this.app_project.client_module_context()), - visited_modules, - ) - .await?; - client_dynamic_imports.extend( - result - .client_dynamic_imports - .iter() - .map(|(k, v)| (*k, v.clone())), - ); - visited_modules = result.visited_modules; + entry_client_chunks.extend(chunks.await?.iter().copied()); + } + for (chunks, _) in client_references_chunks_ref + .client_component_client_chunks + .values() + { + client_assets.extend(chunks.await?.iter().copied()); + } + for (chunks, _) in client_references_chunks_ref + .client_component_ssr_chunks + .values() + { + entry_ssr_chunks.extend(chunks.await?.iter().copied()); } - client_dynamic_imports - }; - - let client_references_chunks = get_app_client_references_chunks( - client_references_cell, - client_chunking_context, - Value::new(client_shared_availability_info), - ssr_chunking_context, - ); - let client_references_chunks_ref = client_references_chunks.await?; - - let mut entry_client_chunks = FxIndexSet::default(); - // TODO(alexkirsz) In which manifest does this go? - let mut entry_ssr_chunks = FxIndexSet::default(); - for chunks in client_references_chunks_ref - .layout_segment_client_chunks - .values() - { - entry_client_chunks.extend(chunks.await?.iter().copied()); - } - for (chunks, _) in client_references_chunks_ref - .client_component_client_chunks - .values() - { - client_assets.extend(chunks.await?.iter().copied()); - } - for (chunks, _) in client_references_chunks_ref - .client_component_ssr_chunks - .values() - { - entry_ssr_chunks.extend(chunks.await?.iter().copied()); - } - - client_assets.extend(entry_client_chunks.iter().copied()); - server_assets.extend(entry_ssr_chunks.iter().copied()); + client_assets.extend(entry_client_chunks.iter().copied()); + server_assets.extend(entry_ssr_chunks.iter().copied()); - let manifest_path_prefix = &app_entry.original_name; + let manifest_path_prefix = &app_entry.original_name; - if emit_manifests { - let app_build_manifest = AppBuildManifest { - pages: fxindexmap!( - app_entry.original_name.clone() => Vc::cell(entry_client_chunks - .iter() - .chain(client_shared_chunks.iter()) - .copied() - .collect()) - ), - }; - let app_build_manifest_output = app_build_manifest - .build_output( - node_root.join( - format!("server/app{manifest_path_prefix}/app-build-manifest.json",) - .into(), + if emit_manifests { + let app_build_manifest = AppBuildManifest { + pages: fxindexmap!( + app_entry.original_name.clone() => Vc::cell(entry_client_chunks + .iter() + .chain(client_shared_chunks.iter()) + .copied() + .collect()) ), - client_relative_path, - ) - .await? - .to_resolved() - .await?; - - server_assets.insert(app_build_manifest_output); - } - - // polyfill-nomodule.js is a pre-compiled asset distributed as part of next, - // load it as a RawModule. - let next_package = get_next_package(this.app_project.project().project_path()); - let polyfill_source = FileSource::new( - next_package.join("dist/build/polyfills/polyfill-nomodule.js".into()), - ); - let polyfill_output_path = - client_chunking_context.chunk_path(polyfill_source.ident(), ".js".into()); - let polyfill_output_asset = ResolvedVc::upcast( - RawOutput::new(polyfill_output_path, Vc::upcast(polyfill_source)) - .to_resolved() - .await?, - ); - client_assets.insert(polyfill_output_asset); - - if emit_manifests { - if *this - .app_project - .project() - .should_create_webpack_stats() - .await? - { - let webpack_stats = - generate_webpack_stats(app_entry.original_name.clone(), &client_assets) + }; + let app_build_manifest_output = + app_build_manifest + .build_output( + node_root.join( + format!( + "server/app{manifest_path_prefix}/app-build-manifest.json", + ) + .into(), + ), + client_relative_path, + ) + .await? + .to_resolved() .await?; - let stats_output = VirtualOutputAsset::new( - node_root.join( - format!("server/app{manifest_path_prefix}/webpack-stats.json",).into(), - ), - AssetContent::file( - File::from(serde_json::to_string_pretty(&webpack_stats)?).into(), - ), - ) - .to_resolved() - .await?; - server_assets.insert(ResolvedVc::upcast(stats_output)); + + server_assets.insert(app_build_manifest_output); } - let build_manifest = BuildManifest { - root_main_files: client_shared_chunks, - polyfill_files: vec![polyfill_output_asset], - ..Default::default() - }; - let build_manifest_output = ResolvedVc::upcast( - build_manifest - .build_output( + // polyfill-nomodule.js is a pre-compiled asset distributed as part of next, + // load it as a RawModule. + let next_package = get_next_package(this.app_project.project().project_path()); + let polyfill_source = FileSource::new( + next_package.join("dist/build/polyfills/polyfill-nomodule.js".into()), + ); + let polyfill_output_path = + client_chunking_context.chunk_path(polyfill_source.ident(), ".js".into()); + let polyfill_output_asset = ResolvedVc::upcast( + RawOutput::new(polyfill_output_path, Vc::upcast(polyfill_source)) + .to_resolved() + .await?, + ); + client_assets.insert(polyfill_output_asset); + + if emit_manifests { + if *this + .app_project + .project() + .should_create_webpack_stats() + .await? + { + let webpack_stats = + generate_webpack_stats(app_entry.original_name.clone(), &client_assets) + .await?; + let stats_output = VirtualOutputAsset::new( node_root.join( - format!("server/app{manifest_path_prefix}/build-manifest.json",) + format!("server/app{manifest_path_prefix}/webpack-stats.json",) .into(), ), - client_relative_path, + AssetContent::file( + File::from(serde_json::to_string_pretty(&webpack_stats)?).into(), + ), ) - .await? .to_resolved() - .await?, - ); - server_assets.insert(build_manifest_output); - } - - if runtime == NextRuntime::Edge { - // as the edge runtime doesn't support chunk loading we need to add all client - // references to the middleware manifest so they get loaded during runtime - // initialization - let client_references_chunks = &*client_references_chunks.await?; - - for (ssr_chunks, _) in client_references_chunks - .client_component_ssr_chunks - .values() - { - let ssr_chunks = ssr_chunks.await?; + .await?; + server_assets.insert(ResolvedVc::upcast(stats_output)); + } - middleware_assets.extend(ssr_chunks); + let build_manifest = BuildManifest { + root_main_files: client_shared_chunks, + polyfill_files: vec![polyfill_output_asset], + ..Default::default() + }; + let build_manifest_output = + ResolvedVc::upcast( + build_manifest + .build_output( + node_root.join( + format!( + "server/app{manifest_path_prefix}/build-manifest.json", + ) + .into(), + ), + client_relative_path, + ) + .await? + .to_resolved() + .await?, + ); + server_assets.insert(build_manifest_output); } - } - ( - Some(get_app_server_reference_modules( - client_references_cell.types(), - )), - Some(client_dynamic_imports), - Some(client_references_cell), - Some(client_references_chunks), - ) - } else { - (None, None, None, None) - }; + if runtime == NextRuntime::Edge { + // as the edge runtime doesn't support chunk loading we need to add all client + // references to the middleware manifest so they get loaded during runtime + // initialization + let client_references_chunks = &*client_references_chunks.await?; + + for (ssr_chunks, _) in client_references_chunks + .client_component_ssr_chunks + .values() + { + let ssr_chunks = ssr_chunks.await?; + + middleware_assets.extend(ssr_chunks); + } + } - let server_action_manifest_loader = - if let Some(app_server_reference_modules) = app_server_reference_modules { - let server_action_manifest = create_server_actions_manifest( - *ResolvedVc::upcast(app_entry.rsc_entry), - app_server_reference_modules, - this.app_project.project().project_path(), - node_root, - app_entry.original_name.clone(), - runtime, - match runtime { - NextRuntime::Edge => Vc::upcast(this.app_project.edge_rsc_module_context()), - NextRuntime::NodeJs => Vc::upcast(this.app_project.rsc_module_context()), - }, - this.app_project - .project() - .runtime_chunking_context(process_client_assets, runtime), + ( + Some(client_dynamic_imports), + Some(client_references_cell), + Some(client_references_chunks), ) - .await?; - server_assets.insert(server_action_manifest.manifest); - Some(server_action_manifest.loader) } else { - None + (None, None, None) }; + let server_action_manifest_loader = if process_client_components { + let server_action_manifest = create_server_actions_manifest( + *ResolvedVc::upcast(app_entry.rsc_entry), + this.app_project.project().project_path(), + node_root, + app_entry.original_name.clone(), + runtime, + match runtime { + NextRuntime::Edge => Vc::upcast(this.app_project.edge_rsc_module_context()), + NextRuntime::NodeJs => Vc::upcast(this.app_project.rsc_module_context()), + }, + this.app_project + .project() + .runtime_chunking_context(process_client_assets, runtime), + ) + .await?; + server_assets.insert(server_action_manifest.manifest); + Some(server_action_manifest.loader) + } else { + None + }; + let (app_entry_chunks, app_entry_chunks_availability) = &*self .app_entry_chunks( client_references, diff --git a/crates/next-api/src/dynamic_imports.rs b/crates/next-api/src/dynamic_imports.rs index 422f791f05148..51a82d7099baa 100644 --- a/crates/next-api/src/dynamic_imports.rs +++ b/crates/next-api/src/dynamic_imports.rs @@ -2,6 +2,7 @@ use std::collections::{HashMap, HashSet}; use anyhow::{bail, Result}; use futures::Future; +use next_core::next_client_reference::EcmascriptClientReferenceModule; use serde::{Deserialize, Serialize}; use swc_core::ecma::{ ast::{CallExpr, Callee, Expr, Ident, Lit}, @@ -223,17 +224,26 @@ async fn get_next_dynamic_edges( module: Vc>, ) -> Result> { let dynamic_imports_map = build_dynamic_imports_map_for_module(client_asset_context, module); - let mut edges = primary_referenced_modules(module) + + let mut edges = if Vc::try_resolve_downcast_type::(module) .await? - .iter() - .map(|&referenced_module| async move { - Ok(NextDynamicVisitEntry::Module( - referenced_module.to_resolved().await?, - referenced_module.ident().to_string().await?, - )) - }) - .try_join() - .await?; + .is_some() + { + vec![] + } else { + primary_referenced_modules(module) + .await? + .iter() + .map(|&referenced_module| async move { + Ok(NextDynamicVisitEntry::Module( + referenced_module.to_resolved().await?, + referenced_module.ident().to_string().await?, + )) + }) + .try_join() + .await? + }; + if let Some(dynamic_imports_map) = *dynamic_imports_map.await? { edges.reserve_exact(1); edges.push(NextDynamicVisitEntry::DynamicImportsMap( diff --git a/crates/next-api/src/server_actions.rs b/crates/next-api/src/server_actions.rs index 08d1b24c2c9de..3762cd282d076 100644 --- a/crates/next-api/src/server_actions.rs +++ b/crates/next-api/src/server_actions.rs @@ -3,6 +3,7 @@ use std::{collections::BTreeMap, future::Future, io::Write, iter::once}; use anyhow::{bail, Context, Result}; use indexmap::map::Entry; use next_core::{ + next_client_reference::EcmascriptClientReferenceModule, next_manifests::{ ActionLayer, ActionManifestModuleId, ActionManifestWorkerEntry, ServerReferenceManifest, }, @@ -31,7 +32,7 @@ use turbopack_core::{ chunk::{ChunkItem, ChunkItemExt, ChunkableModule, ChunkingContext, EvaluatableAsset}, context::AssetContext, file_source::FileSource, - module::{Module, Modules}, + module::Module, output::OutputAsset, reference::primary_referenced_modules, reference_type::{EcmaScriptModulesReferenceSubType, ReferenceType}, @@ -60,7 +61,6 @@ pub(crate) struct ServerActionsManifest { #[turbo_tasks::function] pub(crate) async fn create_server_actions_manifest( rsc_entry: Vc>, - server_reference_modules: Vc, project_path: Vc, node_root: Vc, page_name: RcStr, @@ -68,7 +68,8 @@ pub(crate) async fn create_server_actions_manifest( asset_context: Vc>, chunking_context: Vc>, ) -> Result> { - let actions = find_actions(rsc_entry, server_reference_modules, asset_context); + let actions = find_actions(rsc_entry, asset_context); + let loader = build_server_actions_loader(project_path, page_name.clone(), actions, asset_context); let evaluable = Vc::try_resolve_sidecast::>(loader) @@ -190,7 +191,6 @@ async fn build_manifest( #[turbo_tasks::function] async fn find_actions( rsc_entry: ResolvedVc>, - server_reference_modules: Vc, asset_context: Vc>, ) -> Result> { async move { @@ -201,17 +201,7 @@ async fn find_actions( ActionLayer::Rsc, rsc_entry, rsc_entry.ident().to_string().await?, - )) - .chain( - server_reference_modules - .await? - .iter() - .map(|m| async move { - Ok((ActionLayer::ActionBrowser, *m, m.ident().to_string().await?)) - }) - .try_join() - .await?, - ), + )), FindActionsVisit {}, ) .await @@ -284,11 +274,24 @@ impl turbo_tasks::graph::Visit for FindActionsVisit { async fn get_referenced_modules( (layer, module, _): FindActionsNode, ) -> Result + Send> { + if let Some(module) = + ResolvedVc::try_downcast_type::(module).await? + { + let module: ReadRef = module.await?; + return Ok(vec![( + ActionLayer::ActionBrowser, + ResolvedVc::upcast(module.client_module), + module.client_module.ident().to_string().await?, + )] + .into_iter()); + } + let modules = primary_referenced_modules(*module).await?; Ok(modules .into_iter() - .map(move |&m| async move { Ok((layer, m, m.ident().to_string().await?)) }) + .copied() + .map(move |m| async move { Ok((layer, m, m.ident().to_string().await?)) }) .try_join() .await? .into_iter()) diff --git a/crates/next-core/src/next_app/app_client_references_chunks.rs b/crates/next-core/src/next_app/app_client_references_chunks.rs index 489231ac754b5..c8484a2a5a6d7 100644 --- a/crates/next-core/src/next_app/app_client_references_chunks.rs +++ b/crates/next-core/src/next_app/app_client_references_chunks.rs @@ -6,7 +6,7 @@ use turbo_tasks::{ }; use turbopack_core::{ chunk::{availability_info::AvailabilityInfo, ChunkingContext, ChunkingContextExt}, - module::{Module, Modules}, + module::Module, output::OutputAssets, }; @@ -14,7 +14,6 @@ use super::include_modules_module::IncludeModulesModule; use crate::{ next_client_reference::{ visit_client_reference::ClientReferenceGraphResult, ClientReferenceType, - ClientReferenceTypes, }, next_server_component::server_component_module::NextServerComponentModule, }; @@ -316,35 +315,3 @@ pub async fn get_app_client_references_chunks( .instrument(tracing::info_span!("process client references")) .await } - -/// Crawls all modules emitted in the client transition, returning a list of all -/// client JS modules. -#[turbo_tasks::function] -pub async fn get_app_server_reference_modules( - app_client_reference_types: Vc, -) -> Result> { - Ok(Vc::cell( - app_client_reference_types - .await? - .iter() - .map(|client_reference_ty| async move { - Ok(match client_reference_ty { - ClientReferenceType::EcmascriptClientReference { - module: ecmascript_client_reference, - .. - } => { - let ecmascript_client_reference_ref = ecmascript_client_reference.await?; - Some(ResolvedVc::upcast( - ecmascript_client_reference_ref - .client_module - .to_resolved() - .await?, - )) - } - _ => None, - }) - }) - .try_flat_join() - .await?, - )) -} diff --git a/crates/next-core/src/next_client_reference/ecmascript_client_reference/ecmascript_client_reference_module.rs b/crates/next-core/src/next_client_reference/ecmascript_client_reference/ecmascript_client_reference_module.rs index 906f39b0d6b96..50eece0820e73 100644 --- a/crates/next-core/src/next_client_reference/ecmascript_client_reference/ecmascript_client_reference_module.rs +++ b/crates/next-core/src/next_client_reference/ecmascript_client_reference/ecmascript_client_reference_module.rs @@ -5,7 +5,8 @@ use turbo_tasks::{ResolvedVc, Vc}; use turbopack_core::{ asset::{Asset, AssetContent}, ident::AssetIdent, - module::{Module, Modules}, + module::Module, + reference::{ModuleReferences, SingleModuleReference}, }; use turbopack_ecmascript::chunk::EcmascriptChunkPlaceable; @@ -46,7 +47,17 @@ impl EcmascriptClientReferenceModule { #[turbo_tasks::function] fn ecmascript_client_reference_modifier() -> Vc { - Vc::cell("ecmascript client reference".into()) + Vc::cell("ecmascript client reference module".into()) +} + +#[turbo_tasks::function] +fn ecmascript_client_reference_client_ref_modifier() -> Vc { + Vc::cell("ecmascript client reference to client".into()) +} + +#[turbo_tasks::function] +fn ecmascript_client_reference_ssr_ref_modifier() -> Vc { + Vc::cell("ecmascript client reference to ssr".into()) } #[turbo_tasks::value_impl] @@ -58,10 +69,19 @@ impl Module for EcmascriptClientReferenceModule { } #[turbo_tasks::function] - fn additional_layers_modules(&self) -> Vc { + fn references(&self) -> Vc { let client_module = ResolvedVc::upcast(self.client_module); let ssr_module = ResolvedVc::upcast(self.ssr_module); - Vc::cell(vec![client_module, ssr_module]) + Vc::cell(vec![ + Vc::upcast(SingleModuleReference::new( + *client_module, + ecmascript_client_reference_client_ref_modifier(), + )), + Vc::upcast(SingleModuleReference::new( + *ssr_module, + ecmascript_client_reference_ssr_ref_modifier(), + )), + ]) } }