Skip to content

Commit b3a916c

Browse files
authored
fix server-side changes events (#55437)
### What? fix the problems that caused sideSideChanges to be emitted for client side changes ### Why? ### How? Closes WEB-1578
1 parent b02946c commit b3a916c

File tree

5 files changed

+170
-98
lines changed

5 files changed

+170
-98
lines changed

packages/next-swc/crates/next-api/src/app.rs

Lines changed: 98 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ use turbopack_binding::{
4040
turbopack::{
4141
core::{
4242
asset::{Asset, AssetContent},
43-
changed::any_content_changed_of_output_assets,
4443
chunk::{ChunkableModule, ChunkingContext, EvaluatableAssets},
4544
file_source::FileSource,
4645
output::{OutputAsset, OutputAssets},
@@ -487,15 +486,19 @@ impl AppEndpoint {
487486
async fn output(self: Vc<Self>) -> Result<Vc<AppEndpointOutput>> {
488487
let this = self.await?;
489488

490-
let (app_entry, ty) = match this.ty {
491-
AppEndpointType::Page { ty: _, loader_tree } => {
492-
(self.app_page_entry(loader_tree), "page")
493-
}
489+
let (app_entry, ty, ssr_and_client) = match this.ty {
490+
AppEndpointType::Page { ty, loader_tree } => (
491+
self.app_page_entry(loader_tree),
492+
"page",
493+
matches!(ty, AppPageEndpointType::Html),
494+
),
494495
// NOTE(alexkirsz) For routes, technically, a lot of the following code is not needed,
495496
// as we know we won't have any client references. However, for now, for simplicity's
496497
// sake, we just do the same thing as for pages.
497-
AppEndpointType::Route { path } => (self.app_route_entry(path), "route"),
498-
AppEndpointType::Metadata { metadata } => (self.app_metadata_entry(metadata), "route"),
498+
AppEndpointType::Route { path } => (self.app_route_entry(path), "route", false),
499+
AppEndpointType::Metadata { metadata } => {
500+
(self.app_metadata_entry(metadata), "route", false)
501+
}
499502
};
500503

501504
let node_root = this.app_project.project().node_root();
@@ -562,82 +565,86 @@ impl AppEndpoint {
562565
.entry(Vc::upcast(app_entry.rsc_entry))
563566
.await?;
564567

565-
let client_references_chunks = get_app_client_references_chunks(
566-
client_reference_types,
567-
this.app_project.project().client_chunking_context(),
568-
this.app_project.project().ssr_chunking_context(),
569-
);
570-
let client_references_chunks_ref = client_references_chunks.await?;
571-
572-
let mut entry_client_chunks = vec![];
573-
// TODO(alexkirsz) In which manifest does this go?
574-
let mut entry_ssr_chunks = vec![];
575-
for client_reference in app_entry_client_references.iter() {
576-
let client_reference_chunks = client_references_chunks_ref
577-
.get(client_reference.ty())
578-
.expect("client reference should have corresponding chunks");
579-
entry_client_chunks
580-
.extend(client_reference_chunks.client_chunks.await?.iter().copied());
581-
entry_ssr_chunks.extend(client_reference_chunks.ssr_chunks.await?.iter().copied());
582-
}
568+
if ssr_and_client {
569+
let client_references_chunks = get_app_client_references_chunks(
570+
client_reference_types,
571+
this.app_project.project().client_chunking_context(),
572+
this.app_project.project().ssr_chunking_context(),
573+
);
574+
let client_references_chunks_ref = client_references_chunks.await?;
575+
576+
let mut entry_client_chunks = vec![];
577+
// TODO(alexkirsz) In which manifest does this go?
578+
let mut entry_ssr_chunks = vec![];
579+
for client_reference in app_entry_client_references.iter() {
580+
let client_reference_chunks = client_references_chunks_ref
581+
.get(client_reference.ty())
582+
.expect("client reference should have corresponding chunks");
583+
entry_client_chunks
584+
.extend(client_reference_chunks.client_chunks.await?.iter().copied());
585+
entry_ssr_chunks.extend(client_reference_chunks.ssr_chunks.await?.iter().copied());
586+
}
583587

584-
client_assets.extend(entry_client_chunks.iter().copied());
585-
server_assets.extend(entry_ssr_chunks.iter().copied());
588+
client_assets.extend(entry_client_chunks.iter().copied());
589+
server_assets.extend(entry_ssr_chunks.iter().copied());
586590

587-
let entry_client_chunks_paths = entry_client_chunks
588-
.iter()
589-
.map(|chunk| chunk.ident().path())
590-
.try_join()
591-
.await?;
592-
let mut entry_client_chunks_paths: Vec<_> = entry_client_chunks_paths
593-
.iter()
594-
.map(|path| {
595-
client_relative_path_ref
596-
.get_path_to(path)
597-
.expect("asset path should be inside client root")
598-
.to_string()
599-
})
600-
.collect();
601-
entry_client_chunks_paths.extend(client_shared_chunks_paths.iter().cloned());
591+
let entry_client_chunks_paths = entry_client_chunks
592+
.iter()
593+
.map(|chunk| chunk.ident().path())
594+
.try_join()
595+
.await?;
596+
let mut entry_client_chunks_paths: Vec<_> = entry_client_chunks_paths
597+
.iter()
598+
.map(|path| {
599+
client_relative_path_ref
600+
.get_path_to(path)
601+
.expect("asset path should be inside client root")
602+
.to_string()
603+
})
604+
.collect();
605+
entry_client_chunks_paths.extend(client_shared_chunks_paths.iter().cloned());
602606

603-
let app_build_manifest = AppBuildManifest {
604-
pages: [(app_entry.original_name.clone(), entry_client_chunks_paths)]
605-
.into_iter()
606-
.collect(),
607-
};
608-
let manifest_path_prefix = get_asset_prefix_from_pathname(&app_entry.pathname);
609-
let app_build_manifest_output = Vc::upcast(VirtualOutputAsset::new(
610-
node_root.join(format!(
611-
"server/app{manifest_path_prefix}/{ty}/app-build-manifest.json",
612-
)),
613-
AssetContent::file(
614-
File::from(serde_json::to_string_pretty(&app_build_manifest)?).into(),
615-
),
616-
));
617-
server_assets.push(app_build_manifest_output);
607+
let app_build_manifest = AppBuildManifest {
608+
pages: [(app_entry.original_name.clone(), entry_client_chunks_paths)]
609+
.into_iter()
610+
.collect(),
611+
};
612+
let manifest_path_prefix = get_asset_prefix_from_pathname(&app_entry.pathname);
613+
let app_build_manifest_output = Vc::upcast(VirtualOutputAsset::new(
614+
node_root.join(format!(
615+
"server/app{manifest_path_prefix}/{ty}/app-build-manifest.json",
616+
)),
617+
AssetContent::file(
618+
File::from(serde_json::to_string_pretty(&app_build_manifest)?).into(),
619+
),
620+
));
621+
server_assets.push(app_build_manifest_output);
618622

619-
let build_manifest = BuildManifest {
620-
root_main_files: client_shared_chunks_paths,
621-
..Default::default()
622-
};
623-
let build_manifest_output = Vc::upcast(VirtualOutputAsset::new(
624-
node_root.join(format!(
625-
"server/app{manifest_path_prefix}/{ty}/build-manifest.json",
626-
)),
627-
AssetContent::file(File::from(serde_json::to_string_pretty(&build_manifest)?).into()),
628-
));
629-
server_assets.push(build_manifest_output);
630-
631-
let entry_manifest = ClientReferenceManifest::build_output(
632-
node_root,
633-
client_relative_path,
634-
app_entry.original_name.clone(),
635-
client_references,
636-
client_references_chunks,
637-
this.app_project.project().client_chunking_context(),
638-
Vc::upcast(this.app_project.project().ssr_chunking_context()),
639-
);
640-
server_assets.push(entry_manifest);
623+
let build_manifest = BuildManifest {
624+
root_main_files: client_shared_chunks_paths,
625+
..Default::default()
626+
};
627+
let build_manifest_output = Vc::upcast(VirtualOutputAsset::new(
628+
node_root.join(format!(
629+
"server/app{manifest_path_prefix}/{ty}/build-manifest.json",
630+
)),
631+
AssetContent::file(
632+
File::from(serde_json::to_string_pretty(&build_manifest)?).into(),
633+
),
634+
));
635+
server_assets.push(build_manifest_output);
636+
637+
let entry_manifest = ClientReferenceManifest::build_output(
638+
node_root,
639+
client_relative_path,
640+
app_entry.original_name.clone(),
641+
client_references,
642+
client_references_chunks,
643+
this.app_project.project().client_chunking_context(),
644+
Vc::upcast(this.app_project.project().ssr_chunking_context()),
645+
);
646+
server_assets.push(entry_manifest);
647+
}
641648

642649
fn create_app_paths_manifest(
643650
node_root: Vc<FileSystemPath>,
@@ -897,13 +904,21 @@ impl Endpoint for AppEndpoint {
897904
}
898905

899906
#[turbo_tasks::function]
900-
fn server_changed(self: Vc<Self>) -> Vc<Completion> {
901-
any_content_changed_of_output_assets(self.output().server_assets())
907+
async fn server_changed(self: Vc<Self>) -> Result<Vc<Completion>> {
908+
Ok(self
909+
.await?
910+
.app_project
911+
.project()
912+
.server_changed(self.output().server_assets()))
902913
}
903914

904915
#[turbo_tasks::function]
905-
fn client_changed(self: Vc<Self>) -> Vc<Completion> {
906-
any_content_changed_of_output_assets(self.output().client_assets())
916+
async fn client_changed(self: Vc<Self>) -> Result<Vc<Completion>> {
917+
Ok(self
918+
.await?
919+
.app_project
920+
.project()
921+
.client_changed(self.output().client_assets()))
907922
}
908923
}
909924

packages/next-swc/crates/next-api/src/middleware.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ use turbopack_binding::{
1414
turbopack::{
1515
core::{
1616
asset::AssetContent,
17-
changed::any_content_changed_of_output_assets,
1817
chunk::{ChunkableModule, ChunkingContext},
1918
context::AssetContext,
2019
module::Module,
@@ -209,8 +208,8 @@ impl Endpoint for MiddlewareEndpoint {
209208
}
210209

211210
#[turbo_tasks::function]
212-
fn server_changed(self: Vc<Self>) -> Vc<Completion> {
213-
any_content_changed_of_output_assets(self.output_assets())
211+
async fn server_changed(self: Vc<Self>) -> Result<Vc<Completion>> {
212+
Ok(self.await?.project.server_changed(self.output_assets()))
214213
}
215214

216215
#[turbo_tasks::function]

packages/next-swc/crates/next-api/src/pages.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ use turbopack_binding::{
3737
build::BuildChunkingContext,
3838
core::{
3939
asset::AssetContent,
40-
changed::any_content_changed_of_output_assets,
4140
chunk::{ChunkableModule, ChunkingContext, EvaluatableAssets},
4241
context::AssetContext,
4342
file_source::FileSource,
@@ -944,13 +943,21 @@ impl Endpoint for PageEndpoint {
944943
}
945944

946945
#[turbo_tasks::function]
947-
fn server_changed(self: Vc<Self>) -> Vc<Completion> {
948-
any_content_changed_of_output_assets(self.output().server_assets())
946+
async fn server_changed(self: Vc<Self>) -> Result<Vc<Completion>> {
947+
Ok(self
948+
.await?
949+
.pages_project
950+
.project()
951+
.server_changed(self.output().server_assets()))
949952
}
950953

951954
#[turbo_tasks::function]
952-
fn client_changed(self: Vc<Self>) -> Vc<Completion> {
953-
any_content_changed_of_output_assets(self.output().client_assets())
955+
async fn client_changed(self: Vc<Self>) -> Result<Vc<Completion>> {
956+
Ok(self
957+
.await?
958+
.pages_project
959+
.project()
960+
.client_changed(self.output().client_assets()))
954961
}
955962
}
956963

packages/next-swc/crates/next-api/src/project.rs

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,11 @@ use next_core::{
1919
};
2020
use serde::{Deserialize, Serialize};
2121
use turbo_tasks::{
22-
debug::ValueDebugFormat, trace::TraceRawVcs, Completion, IntoTraitRef, State, TaskInput,
23-
TransientInstance, Value, Vc,
22+
debug::ValueDebugFormat,
23+
graph::{AdjacencyMap, GraphTraversal},
24+
trace::TraceRawVcs,
25+
Completion, Completions, IntoTraitRef, State, TaskInput, TransientInstance, TryFlatJoinIterExt,
26+
Value, Vc,
2427
};
2528
use turbopack_binding::{
2629
turbo::{
@@ -30,13 +33,14 @@ use turbopack_binding::{
3033
turbopack::{
3134
build::BuildChunkingContext,
3235
core::{
36+
changed::content_changed,
3337
chunk::ChunkingContext,
3438
compile_time_info::CompileTimeInfo,
3539
context::AssetContext,
3640
diagnostics::DiagnosticExt,
3741
environment::ServerAddr,
3842
file_source::FileSource,
39-
output::OutputAssets,
43+
output::{OutputAsset, OutputAssets},
4044
reference_type::{EntryReferenceSubType, ReferenceType},
4145
resolve::{find_context_file, FindContextFileResult},
4246
source::Source,
@@ -657,6 +661,55 @@ impl Project {
657661
.versioned_content_map
658662
.keys_in_path(self.client_relative_path()))
659663
}
664+
665+
/// Completion when server side changes are detected in output assets
666+
/// referenced from the roots
667+
#[turbo_tasks::function]
668+
pub fn server_changed(self: Vc<Self>, roots: Vc<OutputAssets>) -> Vc<Completion> {
669+
let path = self.node_root();
670+
any_output_changed(roots, path)
671+
}
672+
673+
/// Completion when client side changes are detected in output assets
674+
/// referenced from the roots
675+
#[turbo_tasks::function]
676+
pub fn client_changed(self: Vc<Self>, roots: Vc<OutputAssets>) -> Vc<Completion> {
677+
let path = self.client_root();
678+
any_output_changed(roots, path)
679+
}
680+
}
681+
682+
#[turbo_tasks::function]
683+
async fn any_output_changed(
684+
roots: Vc<OutputAssets>,
685+
path: Vc<FileSystemPath>,
686+
) -> Result<Vc<Completion>> {
687+
let path = &path.await?;
688+
let completions = AdjacencyMap::new()
689+
.skip_duplicates()
690+
.visit(roots.await?.iter().copied(), get_referenced_output_assets)
691+
.await
692+
.completed()?
693+
.into_inner()
694+
.into_reverse_topological()
695+
.map(|m| async move {
696+
let asset_path = m.ident().path().await?;
697+
if !asset_path.path.ends_with(".map") && asset_path.is_inside_ref(path) {
698+
Ok(Some(content_changed(Vc::upcast(m))))
699+
} else {
700+
Ok(None)
701+
}
702+
})
703+
.try_flat_join()
704+
.await?;
705+
706+
Ok(Vc::<Completions>::cell(completions).completed())
707+
}
708+
709+
async fn get_referenced_output_assets(
710+
parent: Vc<Box<dyn OutputAsset>>,
711+
) -> Result<impl Iterator<Item = Vc<Box<dyn OutputAsset>>> + Send> {
712+
Ok(parent.references().await?.clone_value().into_iter())
660713
}
661714

662715
#[turbo_tasks::function]

test/development/basic/next-rs-api.test.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -353,8 +353,7 @@ describe('next.rs api', () => {
353353
file: 'pages/index.js',
354354
content: pagesIndexCode('hello world2'),
355355
expectedUpdate: '/pages/index.js',
356-
// TODO(sokra) this should be false, but source maps change on server side
357-
expectedServerSideChange: true,
356+
expectedServerSideChange: false,
358357
},
359358
{
360359
name: 'server-side change on a page',
@@ -381,8 +380,7 @@ describe('next.rs api', () => {
381380
file: 'app/app/client.ts',
382381
content: '"use client";\nexport default () => <div>hello world2</div>',
383382
expectedUpdate: '/app/app/client.ts',
384-
// TODO(sokra) this should be false, not sure why it's true
385-
expectedServerSideChange: true,
383+
expectedServerSideChange: false,
386384
},
387385
{
388386
name: 'server-side change on a app page',

0 commit comments

Comments
 (0)