Skip to content

Commit

Permalink
Turbopack: Emit whether server or client assets changed (#53879)
Browse files Browse the repository at this point in the history
### What?

Updates the new `entrypoint.changed()` method to signal whether the server or client assets changed. Now, a `{ changed: 'server' | 'client' | 'both' }` will value will be yielded by the subscription.

### Why?

So that client-only changes can be handled differently than server-only changes.

### How?

We just needed to track the server and client output assets separately, so that we can detect changes to either individually. It's difficult to tell what `Vc` change triggered a recomputation on the Rust side (I'd have to involve mutable `State` into the Vc), and it's also hard to re-emit a duplicate value (eg, if a client change follows a client change, we need 2 emits). Instead, I tie the two different `server_changed()` and `client_changed()` functions into a single enum on the TS side.

Depends on #53809, since I don't want to introduce merge conflicts while Tobias is on vacation.
  • Loading branch information
jridgewell authored and ijjk committed Aug 11, 2023
1 parent 9db43c7 commit 1b0db1f
Show file tree
Hide file tree
Showing 5 changed files with 226 additions and 75 deletions.
32 changes: 30 additions & 2 deletions packages/next-swc/crates/napi/src/next_api/endpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ pub async fn endpoint_write_to_disk(
}

#[napi(ts_return_type = "{ __napiType: \"RootTask\" }")]
pub fn endpoint_changed_subscribe(
pub fn endpoint_server_changed_subscribe(
#[napi(ts_arg_type = "{ __napiType: \"Endpoint\" }")] endpoint: External<ExternalEndpoint>,
func: JsFunction,
) -> napi::Result<External<RootTask>> {
Expand All @@ -103,7 +103,35 @@ pub fn endpoint_changed_subscribe(
turbo_tasks,
func,
move || async move {
let changed = endpoint.changed();
let changed = endpoint.server_changed();
let issues = get_issues(changed).await?;
let diags = get_diagnostics(changed).await?;
changed.strongly_consistent().await?;
Ok((issues, diags))
},
|ctx| {
let (issues, diags) = ctx.value;
Ok(vec![TurbopackResult {
result: (),
issues: issues.iter().map(|i| NapiIssue::from(&**i)).collect(),
diagnostics: diags.iter().map(|d| NapiDiagnostic::from(d)).collect(),
}])
},
)
}

#[napi(ts_return_type = "{ __napiType: \"RootTask\" }")]
pub fn endpoint_client_changed_subscribe(
#[napi(ts_arg_type = "{ __napiType: \"Endpoint\" }")] endpoint: External<ExternalEndpoint>,
func: JsFunction,
) -> napi::Result<External<RootTask>> {
let turbo_tasks = endpoint.turbo_tasks().clone();
let endpoint = ***endpoint;
subscribe(
turbo_tasks,
func,
move || async move {
let changed = endpoint.client_changed();
let issues = get_issues(changed).await?;
let diags = get_diagnostics(changed).await?;
changed.strongly_consistent().await?;
Expand Down
90 changes: 54 additions & 36 deletions packages/next-swc/crates/next-api/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -486,7 +486,8 @@ impl AppEndpoint {

let server_path = node_root.join("server".to_string());

let mut output_assets = vec![];
let mut server_assets = vec![];
let mut client_assets = vec![];

let client_shared_chunks = get_app_client_shared_chunks(
this.app_project.client_runtime_entries(),
Expand All @@ -495,7 +496,7 @@ impl AppEndpoint {

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

let chunk_path = chunk.ident().path().await?;
if chunk_path.extension_ref() == Some("js") {
Expand Down Expand Up @@ -561,8 +562,8 @@ impl AppEndpoint {
entry_ssr_chunks.extend(client_reference_chunks.ssr_chunks.await?.iter().copied());
}

output_assets.extend(entry_client_chunks.iter().copied());
output_assets.extend(entry_ssr_chunks.iter().copied());
client_assets.extend(entry_client_chunks.iter().copied());
server_assets.extend(entry_ssr_chunks.iter().copied());

let entry_client_chunks_paths = entry_client_chunks
.iter()
Expand Down Expand Up @@ -594,7 +595,7 @@ impl AppEndpoint {
File::from(serde_json::to_string_pretty(&app_build_manifest)?).into(),
),
));
output_assets.push(app_build_manifest_output);
server_assets.push(app_build_manifest_output);

let build_manifest = BuildManifest {
root_main_files: client_shared_chunks_paths,
Expand All @@ -607,7 +608,7 @@ impl AppEndpoint {
)),
AssetContent::file(File::from(serde_json::to_string_pretty(&build_manifest)?).into()),
));
output_assets.push(build_manifest_output);
server_assets.push(build_manifest_output);

let entry_manifest = ClientReferenceManifest::build_output(
node_root,
Expand All @@ -618,7 +619,7 @@ impl AppEndpoint {
this.app_project.project().client_chunking_context(),
Vc::upcast(this.app_project.project().ssr_chunking_context()),
);
output_assets.push(entry_manifest);
server_assets.push(entry_manifest);

fn create_app_paths_manifest(
node_root: Vc<FileSystemPath>,
Expand Down Expand Up @@ -662,7 +663,7 @@ impl AppEndpoint {
.as_root_chunk(Vc::upcast(chunking_context)),
Vc::cell(evaluatable_assets),
);
output_assets.extend(files.await?.iter().copied());
server_assets.extend(files.await?.iter().copied());

let node_root_value = node_root.await?;
let files_paths_from_root = files
Expand Down Expand Up @@ -735,16 +736,17 @@ impl AppEndpoint {
.cell(),
),
));
output_assets.push(middleware_manifest_v2);
server_assets.push(middleware_manifest_v2);

// create app paths manifest
let app_paths_manifest_output =
create_app_paths_manifest(node_root, &app_entry.original_name, base_file)?;
output_assets.push(app_paths_manifest_output);
server_assets.push(app_paths_manifest_output);

AppEndpointOutput::Edge {
files,
output_assets: Vc::cell(output_assets),
server_assets: Vc::cell(server_assets),
client_assets: Vc::cell(client_assets),
}
}
NextRuntime::NodeJs => {
Expand All @@ -760,7 +762,7 @@ impl AppEndpoint {
app_entry.rsc_entry,
this.app_project.rsc_runtime_entries(),
);
output_assets.push(rsc_chunk);
server_assets.push(rsc_chunk);

let app_paths_manifest_output = create_app_paths_manifest(
node_root,
Expand All @@ -771,11 +773,12 @@ impl AppEndpoint {
.expect("RSC chunk path should be within app paths manifest directory")
.to_string(),
)?;
output_assets.push(app_paths_manifest_output);
server_assets.push(app_paths_manifest_output);

AppEndpointOutput::NodeJs {
rsc_chunk,
output_assets: Vc::cell(output_assets),
server_assets: Vc::cell(server_assets),
client_assets: Vc::cell(client_assets),
}
}
};
Expand Down Expand Up @@ -838,20 +841,14 @@ impl Endpoint for AppEndpoint {
.clone_value();

let written_endpoint = match *output.await? {
AppEndpointOutput::NodeJs {
rsc_chunk,
output_assets: _,
} => WrittenEndpoint::NodeJs {
AppEndpointOutput::NodeJs { rsc_chunk, .. } => WrittenEndpoint::NodeJs {
server_entry_path: node_root_ref
.get_path_to(&*rsc_chunk.ident().path().await?)
.context("Node.js chunk entry path must be inside the node root")?
.to_string(),
server_paths,
},
AppEndpointOutput::Edge {
files,
output_assets: _,
} => WrittenEndpoint::Edge {
AppEndpointOutput::Edge { files, .. } => WrittenEndpoint::Edge {
files: files
.await?
.iter()
Expand All @@ -871,37 +868,58 @@ impl Endpoint for AppEndpoint {
}

#[turbo_tasks::function]
async fn changed(self: Vc<Self>) -> Result<Vc<Completion>> {
let output_assets = self.output().output_assets();
Ok(any_content_changed_of_output_assets(output_assets))
fn server_changed(self: Vc<Self>) -> Vc<Completion> {
any_content_changed_of_output_assets(self.output().server_assets())
}

#[turbo_tasks::function]
fn client_changed(self: Vc<Self>) -> Vc<Completion> {
any_content_changed_of_output_assets(self.output().client_assets())
}
}

#[turbo_tasks::value]
enum AppEndpointOutput {
NodeJs {
rsc_chunk: Vc<Box<dyn OutputAsset>>,
output_assets: Vc<OutputAssets>,
server_assets: Vc<OutputAssets>,
client_assets: Vc<OutputAssets>,
},
Edge {
files: Vc<OutputAssets>,
output_assets: Vc<OutputAssets>,
server_assets: Vc<OutputAssets>,
client_assets: Vc<OutputAssets>,
},
}

#[turbo_tasks::value_impl]
impl AppEndpointOutput {
#[turbo_tasks::function]
pub fn output_assets(&self) -> Vc<OutputAssets> {
pub async fn output_assets(self: Vc<Self>) -> Result<Vc<OutputAssets>> {
let server_assets = self.server_assets().await?;
let client_assets = self.client_assets().await?;
Ok(Vc::cell(
server_assets
.iter()
.cloned()
.chain(client_assets.iter().cloned())
.collect(),
))
}

#[turbo_tasks::function]
pub fn server_assets(&self) -> Vc<OutputAssets> {
match *self {
AppEndpointOutput::NodeJs { server_assets, .. }
| AppEndpointOutput::Edge { server_assets, .. } => server_assets,
}
}

#[turbo_tasks::function]
pub fn client_assets(&self) -> Vc<OutputAssets> {
match *self {
AppEndpointOutput::NodeJs {
rsc_chunk: _,
output_assets,
} => output_assets,
AppEndpointOutput::Edge {
files: _,
output_assets,
} => output_assets,
AppEndpointOutput::NodeJs { client_assets, .. }
| AppEndpointOutput::Edge { client_assets, .. } => client_assets,
}
}
}
79 changes: 48 additions & 31 deletions packages/next-swc/crates/next-api/src/pages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -771,13 +771,15 @@ impl PageEndpoint {
async fn output(self: Vc<Self>) -> Result<Vc<PageEndpointOutput>> {
let this = self.await?;

let mut output_assets = vec![];
let mut server_assets = vec![];
let mut client_assets = vec![];

let ssr_chunk = match this.ty {
PageEndpointType::Html => {
let client_chunks = self.client_chunks();
output_assets.extend(client_chunks.await?.iter().copied());
client_assets.extend(client_chunks.await?.iter().copied());
let build_manifest = self.build_manifest(client_chunks);
output_assets.push(build_manifest);
server_assets.push(build_manifest);
self.ssr_chunk()
}
PageEndpointType::Data => self.ssr_data_chunk(),
Expand All @@ -788,19 +790,21 @@ impl PageEndpoint {
let page_output = match *ssr_chunk.await? {
SsrChunk::NodeJs { entry } => {
let pages_manifest = self.pages_manifest(entry);
output_assets.push(pages_manifest);
output_assets.push(entry);
server_assets.push(pages_manifest);
server_assets.push(entry);

PageEndpointOutput::NodeJs {
entry_chunk: entry,
output_assets: Vc::cell(output_assets),
server_assets: Vc::cell(server_assets),
client_assets: Vc::cell(client_assets),
}
}
SsrChunk::Edge { files } => {
output_assets.extend(files.await?.iter().copied());
server_assets.extend(files.await?.iter().copied());
PageEndpointOutput::Edge {
files,
output_assets: Vc::cell(output_assets),
server_assets: Vc::cell(server_assets),
client_assets: Vc::cell(client_assets),
}
}
};
Expand Down Expand Up @@ -832,20 +836,14 @@ impl Endpoint for PageEndpoint {

let node_root = &node_root.await?;
let written_endpoint = match *output.await? {
PageEndpointOutput::NodeJs {
entry_chunk,
output_assets: _,
} => WrittenEndpoint::NodeJs {
PageEndpointOutput::NodeJs { entry_chunk, .. } => WrittenEndpoint::NodeJs {
server_entry_path: node_root
.get_path_to(&*entry_chunk.ident().path().await?)
.context("ssr chunk entry path must be inside the node root")?
.to_string(),
server_paths,
},
PageEndpointOutput::Edge {
files,
output_assets: _,
} => WrittenEndpoint::Edge {
PageEndpointOutput::Edge { files, .. } => WrittenEndpoint::Edge {
files: files
.await?
.iter()
Expand All @@ -866,39 +864,58 @@ impl Endpoint for PageEndpoint {
}

#[turbo_tasks::function]
async fn changed(self: Vc<Self>) -> Result<Vc<Completion>> {
let output = self.output();
let output_assets = output.output_assets();
fn server_changed(self: Vc<Self>) -> Vc<Completion> {
any_content_changed_of_output_assets(self.output().server_assets())
}

Ok(any_content_changed_of_output_assets(output_assets))
#[turbo_tasks::function]
fn client_changed(self: Vc<Self>) -> Vc<Completion> {
any_content_changed_of_output_assets(self.output().client_assets())
}
}

#[turbo_tasks::value]
enum PageEndpointOutput {
NodeJs {
entry_chunk: Vc<Box<dyn OutputAsset>>,
output_assets: Vc<OutputAssets>,
server_assets: Vc<OutputAssets>,
client_assets: Vc<OutputAssets>,
},
Edge {
files: Vc<OutputAssets>,
output_assets: Vc<OutputAssets>,
server_assets: Vc<OutputAssets>,
client_assets: Vc<OutputAssets>,
},
}

#[turbo_tasks::value_impl]
impl PageEndpointOutput {
#[turbo_tasks::function]
pub fn output_assets(&self) -> Vc<OutputAssets> {
pub async fn output_assets(self: Vc<Self>) -> Result<Vc<OutputAssets>> {
let server_assets = self.server_assets().await?;
let client_assets = self.client_assets().await?;
Ok(Vc::cell(
server_assets
.iter()
.cloned()
.chain(client_assets.iter().cloned())
.collect(),
))
}

#[turbo_tasks::function]
pub fn server_assets(&self) -> Vc<OutputAssets> {
match *self {
PageEndpointOutput::NodeJs { server_assets, .. }
| PageEndpointOutput::Edge { server_assets, .. } => server_assets,
}
}

#[turbo_tasks::function]
pub fn client_assets(&self) -> Vc<OutputAssets> {
match *self {
PageEndpointOutput::NodeJs {
entry_chunk: _,
output_assets,
} => output_assets,
PageEndpointOutput::Edge {
files: _,
output_assets,
} => output_assets,
PageEndpointOutput::NodeJs { client_assets, .. }
| PageEndpointOutput::Edge { client_assets, .. } => client_assets,
}
}
}
Expand Down

0 comments on commit 1b0db1f

Please sign in to comment.