Skip to content

Commit

Permalink
avoid processing client components and server actions in route handle…
Browse files Browse the repository at this point in the history
…rs (#60985)

### What?

app api routes doesn't need to handle client components and server
actions


Closes PACK-2271
  • Loading branch information
sokra authored Feb 5, 2024
1 parent c9c6ff6 commit 2ac1468
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 123 deletions.
138 changes: 76 additions & 62 deletions packages/next-swc/crates/next-api/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -527,18 +527,19 @@ impl AppEndpoint {
async fn output(self: Vc<Self>) -> Result<Vc<AppEndpointOutput>> {
let this = self.await?;

let (app_entry, ty, ssr_and_client) = match this.ty {
let (app_entry, ty, process_client, process_ssr) = match this.ty {
AppEndpointType::Page { ty, loader_tree } => (
self.app_page_entry(loader_tree),
"page",
true,
matches!(ty, AppPageEndpointType::Html),
),
// NOTE(alexkirsz) For routes, technically, a lot of the following code is not needed,
// as we know we won't have any client references. However, for now, for simplicity's
// sake, we just do the same thing as for pages.
AppEndpointType::Route { path } => (self.app_route_entry(path), "route", false),
AppEndpointType::Route { path } => (self.app_route_entry(path), "route", false, false),
AppEndpointType::Metadata { metadata } => {
(self.app_metadata_entry(metadata), "route", false)
(self.app_metadata_entry(metadata), "route", false, false)
}
};

Expand All @@ -556,33 +557,11 @@ impl AppEndpoint {

let app_entry = app_entry.await?;

let client_shared_chunks = get_app_client_shared_chunks(
app_entry
.rsc_entry
.ident()
.with_modifier(Vc::cell("client_shared_chunks".to_string())),
this.app_project.client_runtime_entries(),
this.app_project.project().client_chunking_context(),
);

let mut client_shared_chunks_paths = vec![];
for chunk in client_shared_chunks.await?.iter().copied() {
client_assets.push(chunk);

let chunk_path = chunk.ident().path().await?;
if chunk_path.extension_ref() == Some("js") {
if let Some(chunk_path) = client_relative_path_ref.get_path_to(&chunk_path) {
client_shared_chunks_paths.push(chunk_path.to_string());
}
}
}
let runtime = app_entry.config.await?.runtime.unwrap_or_default();

let rsc_entry = app_entry.rsc_entry;

let rsc_entry_asset = Vc::upcast(rsc_entry);
let client_reference_graph = ClientReferenceGraph::new(Vc::cell(vec![rsc_entry_asset]));
let client_reference_types = client_reference_graph.types();
let client_references = client_reference_graph.entry(rsc_entry_asset);

// TODO(alexkirsz) Handle dynamic entries and dynamic chunks.
// let app_ssr_entries: Vec<_> = client_reference_types
Expand Down Expand Up @@ -610,14 +589,41 @@ impl AppEndpoint {
// ))
// .await?;

let runtime = app_entry.config.await?.runtime.unwrap_or_default();
let app_server_reference_modules = if process_client {
let client_shared_chunks = get_app_client_shared_chunks(
app_entry
.rsc_entry
.ident()
.with_modifier(Vc::cell("client_shared_chunks".to_string())),
this.app_project.client_runtime_entries(),
this.app_project.project().client_chunking_context(),
);

let mut client_shared_chunks_paths = vec![];
for chunk in client_shared_chunks.await?.iter().copied() {
client_assets.push(chunk);

if ssr_and_client {
let ssr_chunking_context = match runtime {
NextRuntime::NodeJs => {
Vc::upcast(this.app_project.project().server_chunking_context())
let chunk_path = chunk.ident().path().await?;
if chunk_path.extension_ref() == Some("js") {
if let Some(chunk_path) = client_relative_path_ref.get_path_to(&chunk_path) {
client_shared_chunks_paths.push(chunk_path.to_string());
}
}
NextRuntime::Edge => this.app_project.project().edge_chunking_context(),
}

let client_reference_graph = ClientReferenceGraph::new(Vc::cell(vec![rsc_entry_asset]));
let client_reference_types = client_reference_graph.types();
let client_references = client_reference_graph.entry(rsc_entry_asset);

let ssr_chunking_context = if process_ssr {
Some(match runtime {
NextRuntime::NodeJs => {
Vc::upcast(this.app_project.project().server_chunking_context())
}
NextRuntime::Edge => this.app_project.project().edge_chunking_context(),
})
} else {
None
};

let client_references_chunks = get_app_client_references_chunks(
Expand Down Expand Up @@ -728,7 +734,11 @@ impl AppEndpoint {
middleware_assets.extend(ssr_chunks);
}
}
}

Some(get_app_server_reference_modules(client_reference_types))
} else {
None
};

fn create_app_paths_manifest(
node_root: Vc<FileSystemPath>,
Expand Down Expand Up @@ -833,7 +843,7 @@ impl AppEndpoint {
.await?;
server_assets.push(next_font_manifest_output);

let endpoint_output = match app_entry.config.await?.runtime.unwrap_or_default() {
let endpoint_output = match runtime {
NextRuntime::Edge => {
// create edge chunks
let chunking_context = this.app_project.project().edge_chunking_context();
Expand All @@ -847,20 +857,22 @@ impl AppEndpoint {
.context("Entry module must be evaluatable")?;
evaluatable_assets.push(evaluatable);

let (loader, manifest) = create_server_actions_manifest(
Vc::upcast(app_entry.rsc_entry),
get_app_server_reference_modules(client_reference_types),
this.app_project.project().project_path(),
node_root,
&app_entry.pathname,
&app_entry.original_name,
NextRuntime::Edge,
Vc::upcast(this.app_project.edge_rsc_module_context()),
Vc::upcast(chunking_context),
)
.await?;
server_assets.push(manifest);
evaluatable_assets.push(loader);
if let Some(app_server_reference_modules) = app_server_reference_modules {
let (loader, manifest) = create_server_actions_manifest(
Vc::upcast(app_entry.rsc_entry),
app_server_reference_modules,
this.app_project.project().project_path(),
node_root,
&app_entry.pathname,
&app_entry.original_name,
NextRuntime::Edge,
Vc::upcast(this.app_project.edge_rsc_module_context()),
Vc::upcast(chunking_context),
)
.await?;
server_assets.push(manifest);
evaluatable_assets.push(loader);
}

let files = chunking_context.evaluated_chunk_group_assets(
app_entry.rsc_entry.ident(),
Expand Down Expand Up @@ -980,20 +992,22 @@ impl AppEndpoint {
let mut evaluatable_assets =
this.app_project.rsc_runtime_entries().await?.clone_value();

let (loader, manifest) = create_server_actions_manifest(
Vc::upcast(app_entry.rsc_entry),
get_app_server_reference_modules(client_reference_types),
this.app_project.project().project_path(),
node_root,
&app_entry.pathname,
&app_entry.original_name,
NextRuntime::NodeJs,
Vc::upcast(this.app_project.rsc_module_context()),
Vc::upcast(this.app_project.project().server_chunking_context()),
)
.await?;
server_assets.push(manifest);
evaluatable_assets.push(loader);
if let Some(app_server_reference_modules) = app_server_reference_modules {
let (loader, manifest) = create_server_actions_manifest(
Vc::upcast(app_entry.rsc_entry),
app_server_reference_modules,
this.app_project.project().project_path(),
node_root,
&app_entry.pathname,
&app_entry.original_name,
NextRuntime::NodeJs,
Vc::upcast(this.app_project.rsc_module_context()),
Vc::upcast(this.app_project.project().server_chunking_context()),
)
.await?;
server_assets.push(manifest);
evaluatable_assets.push(loader);
}

let EntryChunkGroupResult {
asset: rsc_chunk, ..
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ pub struct ClientReferencesChunks {
pub async fn get_app_client_references_chunks(
app_client_references: Vc<ClientReferenceGraphResult>,
client_chunking_context: Vc<Box<dyn EcmascriptChunkingContext>>,
ssr_chunking_context: Vc<Box<dyn EcmascriptChunkingContext>>,
ssr_chunking_context: Option<Vc<Box<dyn EcmascriptChunkingContext>>>,
) -> Result<Vc<ClientReferencesChunks>> {
async move {
// TODO Reconsider this. Maybe it need to be true in production.
Expand All @@ -69,9 +69,11 @@ pub async fn get_app_client_references_chunks(
client_chunking_context.root_chunk_group_assets(Vc::upcast(
ecmascript_client_reference_ref.client_module,
)),
Some(ssr_chunking_context.root_chunk_group_assets(Vc::upcast(
ecmascript_client_reference_ref.ssr_module,
))),
ssr_chunking_context.map(|ssr_chunking_context| {
ssr_chunking_context.root_chunk_group_assets(Vc::upcast(
ecmascript_client_reference_ref.ssr_module,
))
}),
)
}
ClientReferenceType::CssClientReference(css_client_reference) => {
Expand Down Expand Up @@ -172,10 +174,12 @@ pub async fn get_app_client_references_chunks(
layout_segment = display(&server_component_path),
)
.entered();
Some(ssr_chunking_context.chunk_group(
Vc::upcast(ssr_entry_module),
Value::new(current_ssr_availability_info),
))
ssr_chunking_context.map(|ssr_chunking_context| {
ssr_chunking_context.chunk_group(
Vc::upcast(ssr_entry_module),
Value::new(current_ssr_availability_info),
)
})
} else {
None
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ impl ClientReferenceManifest {
client_references: Vc<ClientReferenceGraphResult>,
client_references_chunks: Vc<ClientReferencesChunks>,
client_chunking_context: Vc<Box<dyn EcmascriptChunkingContext>>,
ssr_chunking_context: Vc<Box<dyn EcmascriptChunkingContext>>,
ssr_chunking_context: Option<Vc<Box<dyn EcmascriptChunkingContext>>>,
next_config: Vc<NextConfig>,
runtime: NextRuntime,
) -> Result<Vc<Box<dyn OutputAsset>>> {
Expand Down Expand Up @@ -105,60 +105,62 @@ impl ClientReferenceManifest {
},
);

let ssr_module_id = ecmascript_client_reference
.ssr_module
.as_chunk_item(Vc::upcast(ssr_chunking_context))
.id()
.await?;

let ssr_chunks_paths = if runtime == NextRuntime::Edge {
// the chunks get added to the middleware-manifest.json instead
// of this file because the
// edge runtime doesn't support dynamically
// loading chunks.
Vec::new()
} else if let Some(ssr_chunks) = client_references_chunks
.client_component_ssr_chunks
.get(&app_client_reference_ty)
{
let ssr_chunks = ssr_chunks.await?;

let ssr_chunks_paths = ssr_chunks
.iter()
.map(|chunk| chunk.ident().path())
.try_join()
if let Some(ssr_chunking_context) = ssr_chunking_context {
let ssr_module_id = ecmascript_client_reference
.ssr_module
.as_chunk_item(Vc::upcast(ssr_chunking_context))
.id()
.await?;

ssr_chunks_paths
.iter()
.filter_map(|chunk_path| node_root_ref.get_path_to(chunk_path))
.map(ToString::to_string)
.collect::<Vec<_>>()
} else {
Vec::new()
};
let mut ssr_manifest_node = ManifestNode::default();
ssr_manifest_node.module_exports.insert(
"*".to_string(),
ManifestNodeEntry {
name: "*".to_string(),
id: (&*ssr_module_id).into(),
chunks: ssr_chunks_paths,
// TODO(WEB-434)
r#async: false,
},
);

match runtime {
NextRuntime::NodeJs => {
entry_manifest
.ssr_module_mapping
.insert((&*client_module_id).into(), ssr_manifest_node);
}
NextRuntime::Edge => {
entry_manifest
.edge_ssr_module_mapping
.insert((&*client_module_id).into(), ssr_manifest_node);
let ssr_chunks_paths = if runtime == NextRuntime::Edge {
// the chunks get added to the middleware-manifest.json instead
// of this file because the
// edge runtime doesn't support dynamically
// loading chunks.
Vec::new()
} else if let Some(ssr_chunks) = client_references_chunks
.client_component_ssr_chunks
.get(&app_client_reference_ty)
{
let ssr_chunks = ssr_chunks.await?;

let ssr_chunks_paths = ssr_chunks
.iter()
.map(|chunk| chunk.ident().path())
.try_join()
.await?;

ssr_chunks_paths
.iter()
.filter_map(|chunk_path| node_root_ref.get_path_to(chunk_path))
.map(ToString::to_string)
.collect::<Vec<_>>()
} else {
Vec::new()
};
let mut ssr_manifest_node = ManifestNode::default();
ssr_manifest_node.module_exports.insert(
"*".to_string(),
ManifestNodeEntry {
name: "*".to_string(),
id: (&*ssr_module_id).into(),
chunks: ssr_chunks_paths,
// TODO(WEB-434)
r#async: false,
},
);

match runtime {
NextRuntime::NodeJs => {
entry_manifest
.ssr_module_mapping
.insert((&*client_module_id).into(), ssr_manifest_node);
}
NextRuntime::Edge => {
entry_manifest
.edge_ssr_module_mapping
.insert((&*client_module_id).into(), ssr_manifest_node);
}
}
}
}
Expand Down

0 comments on commit 2ac1468

Please sign in to comment.