diff --git a/crates/napi/src/next_api/endpoint.rs b/crates/napi/src/next_api/endpoint.rs index b953f7d2c7811..2ad38ad50fea5 100644 --- a/crates/napi/src/next_api/endpoint.rs +++ b/crates/napi/src/next_api/endpoint.rs @@ -7,7 +7,7 @@ use next_api::{ route::{Endpoint, WrittenEndpoint}, }; use tracing::Instrument; -use turbo_tasks::{Completion, ReadRef, Vc, VcValueType}; +use turbo_tasks::{Completion, ReadRef, Vc, VcOperation, VcValueType}; use turbopack_core::{ diagnostics::PlainDiagnostic, error::PrettyPrintError, @@ -86,10 +86,10 @@ impl From> for NapiWrittenEndpoint { // some async functions (in this case `endpoint_write_to_disk`) can cause // higher-ranked lifetime errors. See https://github.com/rust-lang/rust/issues/102211 // 2. the type_complexity clippy lint. -pub struct ExternalEndpoint(pub VcArc>>); +pub struct ExternalEndpoint(pub VcArc>); impl Deref for ExternalEndpoint { - type Target = VcArc>>; + type Target = VcArc>; fn deref(&self) -> &Self::Target { &self.0 @@ -99,13 +99,13 @@ impl Deref for ExternalEndpoint { // Await the source and return fatal issues if there are any, otherwise // propagate any actual error results. async fn strongly_consistent_catch_collectables( - source: Vc, + source: VcOperation, ) -> Result<( Option>, Arc>>, Arc>>, )> { - let result = source.strongly_consistent().await; + let result = source.connect().strongly_consistent().await; let issues = get_issues(source).await?; let diagnostics = get_diagnostics(source).await?; @@ -127,11 +127,12 @@ struct WrittenEndpointWithIssues { #[turbo_tasks::function] async fn get_written_endpoint_with_issues( - endpoint: Vc>, + endpoint: VcOperation>, ) -> Result> { - let write_to_disk = endpoint.write_to_disk(); + let write_to_disk = endpoint.connect().write_to_disk(endpoint); + let write_to_disk_operation = VcOperation::new(write_to_disk); let (written, issues, diagnostics) = - strongly_consistent_catch_collectables(write_to_disk).await?; + strongly_consistent_catch_collectables(write_to_disk_operation).await?; Ok(WrittenEndpointWithIssues { written, issues, @@ -227,14 +228,15 @@ impl Eq for EndpointIssuesAndDiags {} #[turbo_tasks::function] async fn subscribe_issues_and_diags( - endpoint: Vc>, + endpoint: VcOperation>, should_include_issues: bool, ) -> Result> { - let changed = endpoint.server_changed(); + let changed = endpoint.connect().server_changed(); + let changed_operation = VcOperation::new(changed); if should_include_issues { let (changed_value, issues, diagnostics) = - strongly_consistent_catch_collectables(changed).await?; + strongly_consistent_catch_collectables(changed_operation).await?; Ok(EndpointIssuesAndDiags { changed: changed_value, issues, @@ -264,7 +266,7 @@ pub fn endpoint_client_changed_subscribe( func, move || { async move { - let changed = endpoint.client_changed(); + let changed = endpoint.connect().client_changed(); // We don't capture issues and diagonistics here since we don't want to be // notified when they change changed.strongly_consistent().await?; diff --git a/crates/napi/src/next_api/project.rs b/crates/napi/src/next_api/project.rs index cf80a01c723b6..cdd62c30e6828 100644 --- a/crates/napi/src/next_api/project.rs +++ b/crates/napi/src/next_api/project.rs @@ -7,12 +7,14 @@ use napi::{ JsFunction, Status, }; use next_api::{ - entrypoints::Entrypoints, + operation::{ + EntrypointsOperation, InstrumentationOperation, MiddlewareOperation, RouteOperation, + }, project::{ - DefineEnv, DraftModeOptions, Instrumentation, Middleware, PartialProjectOptions, Project, - ProjectContainer, ProjectOptions, + DefineEnv, DraftModeOptions, PartialProjectOptions, Project, ProjectContainer, + ProjectOptions, }, - route::{Endpoint, Route}, + route::Endpoint, }; use next_core::tracing_presets::{ TRACING_NEXT_OVERVIEW_TARGETS, TRACING_NEXT_TARGETS, TRACING_NEXT_TURBOPACK_TARGETS, @@ -22,7 +24,9 @@ use rand::Rng; use tokio::{io::AsyncWriteExt, time::Instant}; use tracing::Instrument; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Registry}; -use turbo_tasks::{Completion, RcStr, ReadRef, TransientInstance, TurboTasks, UpdateInfo, Vc}; +use turbo_tasks::{ + Completion, RcStr, ReadRef, TransientInstance, TurboTasks, UpdateInfo, Vc, VcOperation, +}; use turbo_tasks_fs::{DiskFileSystem, FileContent, FileSystem, FileSystemPath}; use turbo_tasks_memory::MemoryBackend; use turbopack_core::{ @@ -479,17 +483,17 @@ struct NapiRoute { impl NapiRoute { fn from_route( pathname: String, - value: Route, + value: RouteOperation, turbo_tasks: &Arc>, ) -> Self { - let convert_endpoint = |endpoint: Vc>| { + let convert_endpoint = |endpoint: VcOperation>| { Some(External::new(ExternalEndpoint(VcArc::new( turbo_tasks.clone(), endpoint, )))) }; match value { - Route::Page { + RouteOperation::Page { html_endpoint, data_endpoint, } => NapiRoute { @@ -499,13 +503,13 @@ impl NapiRoute { data_endpoint: convert_endpoint(data_endpoint), ..Default::default() }, - Route::PageApi { endpoint } => NapiRoute { + RouteOperation::PageApi { endpoint } => NapiRoute { pathname, r#type: "page-api", endpoint: convert_endpoint(endpoint), ..Default::default() }, - Route::AppPage(pages) => NapiRoute { + RouteOperation::AppPage(pages) => NapiRoute { pathname, r#type: "app-page", pages: Some( @@ -520,7 +524,7 @@ impl NapiRoute { ), ..Default::default() }, - Route::AppRoute { + RouteOperation::AppRoute { original_name, endpoint, } => NapiRoute { @@ -530,7 +534,7 @@ impl NapiRoute { endpoint: convert_endpoint(endpoint), ..Default::default() }, - Route::Conflict => NapiRoute { + RouteOperation::Conflict => NapiRoute { pathname, r#type: "conflict", ..Default::default() @@ -546,7 +550,7 @@ struct NapiMiddleware { impl NapiMiddleware { fn from_middleware( - value: &Middleware, + value: &MiddlewareOperation, turbo_tasks: &Arc>, ) -> Result { Ok(NapiMiddleware { @@ -566,7 +570,7 @@ struct NapiInstrumentation { impl NapiInstrumentation { fn from_instrumentation( - value: &Instrumentation, + value: &InstrumentationOperation, turbo_tasks: &Arc>, ) -> Result { Ok(NapiInstrumentation { @@ -594,7 +598,7 @@ struct NapiEntrypoints { #[turbo_tasks::value(serialization = "none")] struct EntrypointsWithIssues { - entrypoints: ReadRef, + entrypoints: ReadRef, issues: Arc>>, diagnostics: Arc>>, } @@ -603,8 +607,9 @@ struct EntrypointsWithIssues { async fn get_entrypoints_with_issues( container: Vc, ) -> Result> { - let entrypoints_operation = container.entrypoints(); - let entrypoints = entrypoints_operation.strongly_consistent().await?; + let entrypoints = container.entrypoints_operation(); + let entrypoints_operation = VcOperation::new(entrypoints); + let entrypoints = entrypoints.strongly_consistent().await?; let issues = get_issues(entrypoints_operation).await?; let diagnostics = get_diagnostics(entrypoints_operation).await?; Ok(EntrypointsWithIssues { @@ -700,8 +705,9 @@ async fn hmr_update( identifier: RcStr, state: Vc, ) -> Result> { - let update_operation = project.hmr_update(identifier, state); - let update = update_operation.strongly_consistent().await?; + let update = project.hmr_update(identifier, state); + let update_operation = VcOperation::new(update); + let update = update.strongly_consistent().await?; let issues = get_issues(update_operation).await?; let diagnostics = get_diagnostics(update_operation).await?; Ok(HmrUpdateWithIssues { @@ -812,8 +818,9 @@ struct HmrIdentifiersWithIssues { async fn get_hmr_identifiers_with_issues( container: Vc, ) -> Result> { - let hmr_identifiers_operation = container.hmr_identifiers(); - let hmr_identifiers = hmr_identifiers_operation.strongly_consistent().await?; + let hmr_identifiers = container.hmr_identifiers(); + let hmr_identifiers_operation = VcOperation::new(hmr_identifiers); + let hmr_identifiers = hmr_identifiers.strongly_consistent().await?; let issues = get_issues(hmr_identifiers_operation).await?; let diagnostics = get_diagnostics(hmr_identifiers_operation).await?; Ok(HmrIdentifiersWithIssues { diff --git a/crates/napi/src/next_api/utils.rs b/crates/napi/src/next_api/utils.rs index 62cefa34b7af5..9e5e2689327aa 100644 --- a/crates/napi/src/next_api/utils.rs +++ b/crates/napi/src/next_api/utils.rs @@ -7,7 +7,7 @@ use napi::{ JsFunction, JsObject, JsUnknown, NapiRaw, NapiValue, Status, }; use serde::Serialize; -use turbo_tasks::{ReadRef, TaskId, TryJoinIterExt, TurboTasks, Vc}; +use turbo_tasks::{ReadRef, TaskId, TryJoinIterExt, TurboTasks, Vc, VcOperation}; use turbo_tasks_fs::FileContent; use turbo_tasks_memory::MemoryBackend; use turbopack_core::{ @@ -20,17 +20,24 @@ use turbopack_core::{ use crate::util::log_internal_error_and_inform; /// A helper type to hold both a Vc operation and the TurboTasks root process. -/// Without this, we'd need to pass both individually all over the place +/// Without this, we'd need to pass both individually all over the place. +/// Make sure to Vc::connect the `vc` when using it. #[derive(Clone)] -pub struct VcArc { +pub struct VcArc +where + T: Send, +{ turbo_tasks: Arc>, - /// The Vc. Must be resolved, otherwise you are referencing an inactive - /// operation. - vc: T, + // TODO: this should be a VcOperation + /// The Vc. + vc: VcOperation, } -impl VcArc { - pub fn new(turbo_tasks: Arc>, vc: T) -> Self { +impl VcArc +where + T: Send, +{ + pub fn new(turbo_tasks: Arc>, vc: VcOperation) -> Self { Self { turbo_tasks, vc } } @@ -39,8 +46,11 @@ impl VcArc { } } -impl Deref for VcArc { - type Target = T; +impl Deref for VcArc +where + T: Send, +{ + type Target = VcOperation; fn deref(&self) -> &Self::Target { &self.vc @@ -78,7 +88,7 @@ pub fn root_task_dispose( Ok(()) } -pub async fn get_issues(source: Vc) -> Result>>> { +pub async fn get_issues(source: VcOperation) -> Result>>> { let issues = source.peek_issues_with_path().await?; Ok(Arc::new(issues.get_plain_issues().await?)) } @@ -87,7 +97,9 @@ pub async fn get_issues(source: Vc) -> Result(source: Vc) -> Result>>> { +pub async fn get_diagnostics( + source: VcOperation, +) -> Result>>> { let captured_diags = source.peek_diagnostics().await?; let mut diags = captured_diags .diagnostics diff --git a/crates/next-api/src/app.rs b/crates/next-api/src/app.rs index 9f05b2740559e..0a58a2ea9875c 100644 --- a/crates/next-api/src/app.rs +++ b/crates/next-api/src/app.rs @@ -36,7 +36,7 @@ use next_core::{ }; use serde::{Deserialize, Serialize}; use tracing::Instrument; -use turbo_tasks::{trace::TraceRawVcs, Completion, RcStr, TryJoinIterExt, Value, Vc}; +use turbo_tasks::{trace::TraceRawVcs, Completion, RcStr, TryJoinIterExt, Value, Vc, VcOperation}; use turbo_tasks_env::{CustomProcessEnv, ProcessEnv}; use turbo_tasks_fs::{File, FileContent, FileSystemPath}; use turbopack::{ @@ -796,8 +796,10 @@ impl AppEndpoint { Ok(app_entry) } + /// This is used to wrap output assets of an endpoint into a single operation (VcOperation) #[turbo_tasks::function] - fn output_assets(self: Vc) -> Vc { + fn output_assets(self: Vc, endpoint: VcOperation>) -> Vc { + let _ = endpoint.connect(); self.output().output_assets() } @@ -1317,7 +1319,10 @@ impl AppEndpoint { #[turbo_tasks::value_impl] impl Endpoint for AppEndpoint { #[turbo_tasks::function] - async fn write_to_disk(self: Vc) -> Result> { + async fn write_to_disk( + self: Vc, + self_op: VcOperation>, + ) -> Result> { let this = self.await?; let page_name = this.page.to_string(); let span = match this.ty { @@ -1342,9 +1347,7 @@ impl Endpoint for AppEndpoint { }; async move { let output = self.output().await?; - // Must use self.output_assets() instead of output.output_assets() to make it a - // single operation - let output_assets = self.output_assets(); + let output_assets = self.output_assets(self_op); let node_root = this.app_project.project().node_root(); @@ -1352,7 +1355,7 @@ impl Endpoint for AppEndpoint { this.app_project .project() - .emit_all_output_assets(Vc::cell(output_assets)) + .emit_all_output_assets(VcOperation::new(output_assets)) .await?; let node_root = this.app_project.project().node_root(); diff --git a/crates/next-api/src/empty.rs b/crates/next-api/src/empty.rs index 2207de424ddc2..abef9f4e934d6 100644 --- a/crates/next-api/src/empty.rs +++ b/crates/next-api/src/empty.rs @@ -1,5 +1,5 @@ use anyhow::{bail, Result}; -use turbo_tasks::{Completion, Vc}; +use turbo_tasks::{Completion, Vc, VcOperation}; use turbopack_core::module::Modules; use crate::route::{Endpoint, WrittenEndpoint}; @@ -18,7 +18,10 @@ impl EmptyEndpoint { #[turbo_tasks::value_impl] impl Endpoint for EmptyEndpoint { #[turbo_tasks::function] - fn write_to_disk(self: Vc) -> Result> { + fn write_to_disk( + self: Vc, + _self_op: VcOperation>, + ) -> Result> { bail!("Empty endpoint can't be written to disk") } diff --git a/crates/next-api/src/instrumentation.rs b/crates/next-api/src/instrumentation.rs index 52faa547b9362..1ec5fbe051f0a 100644 --- a/crates/next-api/src/instrumentation.rs +++ b/crates/next-api/src/instrumentation.rs @@ -6,7 +6,7 @@ use next_core::{ next_server::{get_server_runtime_entries, ServerContextType}, }; use tracing::Instrument; -use turbo_tasks::{Completion, RcStr, Value, Vc}; +use turbo_tasks::{Completion, RcStr, Value, Vc, VcOperation}; use turbo_tasks_fs::{File, FileContent, FileSystemPath}; use turbopack_core::{ asset::AssetContent, @@ -161,6 +161,16 @@ impl InstrumentationEndpoint { Ok(chunk) } + /// This is used to wrap output assets of an endpoint into a single operation (VcOperation) + #[turbo_tasks::function] + fn output_assets_operation( + self: Vc, + endpoint: VcOperation>, + ) -> Vc { + let _ = endpoint.connect(); + self.output_assets() + } + #[turbo_tasks::function] async fn output_assets(self: Vc) -> Result> { let this = self.await?; @@ -217,14 +227,17 @@ struct InstrumentationCoreModules { #[turbo_tasks::value_impl] impl Endpoint for InstrumentationEndpoint { #[turbo_tasks::function] - async fn write_to_disk(self: Vc) -> Result> { + async fn write_to_disk( + self: Vc, + self_op: VcOperation>, + ) -> Result> { let span = tracing::info_span!("instrumentation endpoint"); async move { let this = self.await?; - let output_assets = self.output_assets(); + let output_assets = self.output_assets_operation(self_op); let _ = output_assets.resolve().await?; this.project - .emit_all_output_assets(Vc::cell(output_assets)) + .emit_all_output_assets(VcOperation::new(output_assets)) .await?; let node_root = this.project.node_root(); diff --git a/crates/next-api/src/lib.rs b/crates/next-api/src/lib.rs index 1673bbdf166d3..5250f8a62c806 100644 --- a/crates/next-api/src/lib.rs +++ b/crates/next-api/src/lib.rs @@ -11,6 +11,7 @@ pub mod global_module_id_strategy; mod instrumentation; mod loadable_manifest; mod middleware; +pub mod operation; mod pages; pub mod paths; pub mod project; diff --git a/crates/next-api/src/middleware.rs b/crates/next-api/src/middleware.rs index 720881f9aae34..25ecd42b7d5a8 100644 --- a/crates/next-api/src/middleware.rs +++ b/crates/next-api/src/middleware.rs @@ -8,7 +8,7 @@ use next_core::{ util::{parse_config_from_source, MiddlewareMatcherKind}, }; use tracing::Instrument; -use turbo_tasks::{Completion, RcStr, Value, Vc}; +use turbo_tasks::{Completion, RcStr, Value, Vc, VcOperation}; use turbo_tasks_fs::{self, File, FileContent, FileSystemPath}; use turbopack_core::{ asset::AssetContent, @@ -31,6 +31,12 @@ use crate::{ route::{Endpoint, WrittenEndpoint}, }; +/// This is used to wrap output assets of an endpoint into a single operation (VcOperation) +#[turbo_tasks::function] +fn output_assets(endpoint: VcOperation) -> Vc { + endpoint.connect().output_assets() +} + #[turbo_tasks::value] pub struct MiddlewareEndpoint { project: Vc, @@ -117,6 +123,16 @@ impl MiddlewareEndpoint { Ok(edge_files) } + /// This is used to wrap output assets of an endpoint into a single operation (VcOperation) + #[turbo_tasks::function] + fn output_assets_operation( + self: Vc, + endpoint: VcOperation>, + ) -> Vc { + let _ = endpoint.connect(); + self.output_assets() + } + #[turbo_tasks::function] async fn output_assets(self: Vc) -> Result> { let this = self.await?; @@ -271,14 +287,17 @@ impl MiddlewareEndpoint { #[turbo_tasks::value_impl] impl Endpoint for MiddlewareEndpoint { #[turbo_tasks::function] - async fn write_to_disk(self: Vc) -> Result> { + async fn write_to_disk( + self: Vc, + self_op: VcOperation>, + ) -> Result> { let span = tracing::info_span!("middleware endpoint"); async move { let this = self.await?; - let output_assets = self.output_assets(); + let output_assets = self.output_assets_operation(self_op); let _ = output_assets.resolve().await?; this.project - .emit_all_output_assets(Vc::cell(output_assets)) + .emit_all_output_assets(VcOperation::new(output_assets)) .await?; let node_root = this.project.node_root(); diff --git a/crates/next-api/src/operation.rs b/crates/next-api/src/operation.rs new file mode 100644 index 0000000000000..9e95cd001bfbb --- /dev/null +++ b/crates/next-api/src/operation.rs @@ -0,0 +1,137 @@ +use anyhow::Result; +use indexmap::IndexMap; +use serde::{Deserialize, Serialize}; +use turbo_tasks::{debug::ValueDebugFormat, trace::TraceRawVcs, RcStr, Vc, VcOperation}; + +use crate::{ + entrypoints::Entrypoints, + route::{Endpoint, Route}, +}; + +/// A derived type of Entrypoints, but with VcOperation for every endpoint. +/// This is needed to call `write_to_disk` which expects an `VcOperation`. +/// This is important as VcOperations can be stored in the VersionedContentMap and can be exposed to +/// JS via napi. +#[turbo_tasks::value(shared)] +pub struct EntrypointsOperation { + pub routes: IndexMap, + pub middleware: Option, + pub instrumentation: Option, + pub pages_document_endpoint: VcOperation>, + pub pages_app_endpoint: VcOperation>, + pub pages_error_endpoint: VcOperation>, +} + +#[turbo_tasks::value_impl] +impl EntrypointsOperation { + #[turbo_tasks::function] + pub async fn new(entrypoints: VcOperation) -> Result> { + let e = entrypoints.connect().await?; + Ok(Self { + routes: e + .routes + .iter() + .map(|(k, v)| (k.clone(), wrap_route(v, entrypoints))) + .collect(), + middleware: e.middleware.as_ref().map(|m| MiddlewareOperation { + endpoint: wrap(m.endpoint, entrypoints), + }), + instrumentation: e + .instrumentation + .as_ref() + .map(|i| InstrumentationOperation { + node_js: wrap(i.node_js, entrypoints), + edge: wrap(i.edge, entrypoints), + }), + pages_document_endpoint: wrap(e.pages_document_endpoint, entrypoints), + pages_app_endpoint: wrap(e.pages_app_endpoint, entrypoints), + pages_error_endpoint: wrap(e.pages_error_endpoint, entrypoints), + } + .cell()) + } +} + +fn wrap_route(route: &Route, entrypoints: VcOperation) -> RouteOperation { + match route { + Route::Page { + html_endpoint, + data_endpoint, + } => RouteOperation::Page { + html_endpoint: wrap(*html_endpoint, entrypoints), + data_endpoint: wrap(*data_endpoint, entrypoints), + }, + Route::PageApi { endpoint } => RouteOperation::PageApi { + endpoint: wrap(*endpoint, entrypoints), + }, + Route::AppPage(pages) => RouteOperation::AppPage( + pages + .iter() + .map(|p| AppPageRouteOperation { + original_name: p.original_name.clone(), + html_endpoint: wrap(p.html_endpoint, entrypoints), + rsc_endpoint: wrap(p.rsc_endpoint, entrypoints), + }) + .collect(), + ), + Route::AppRoute { + original_name, + endpoint, + } => RouteOperation::AppRoute { + original_name: original_name.clone(), + endpoint: wrap(*endpoint, entrypoints), + }, + Route::Conflict => RouteOperation::Conflict, + } +} + +#[turbo_tasks::function] +fn wrap_endpoint( + endpoint: Vc>, + op: VcOperation, +) -> Vc> { + let _ = op.connect(); + endpoint +} + +fn wrap( + endpoint: Vc>, + op: VcOperation, +) -> VcOperation> { + VcOperation::new(wrap_endpoint(endpoint, op)) +} + +#[derive(Serialize, Deserialize, TraceRawVcs, PartialEq, Eq, ValueDebugFormat)] +pub struct InstrumentationOperation { + pub node_js: VcOperation>, + pub edge: VcOperation>, +} + +#[derive(Serialize, Deserialize, TraceRawVcs, PartialEq, Eq, ValueDebugFormat)] +pub struct MiddlewareOperation { + pub endpoint: VcOperation>, +} + +#[turbo_tasks::value(shared)] +#[derive(Clone, Debug)] +pub enum RouteOperation { + Page { + html_endpoint: VcOperation>, + data_endpoint: VcOperation>, + }, + PageApi { + endpoint: VcOperation>, + }, + AppPage(Vec), + AppRoute { + original_name: String, + endpoint: VcOperation>, + }, + Conflict, +} + +#[derive(TraceRawVcs, Serialize, Deserialize, PartialEq, Eq, ValueDebugFormat, Clone, Debug)] +pub struct AppPageRouteOperation { + pub original_name: String, + pub html_endpoint: VcOperation>, + pub rsc_endpoint: VcOperation>, +} diff --git a/crates/next-api/src/pages.rs b/crates/next-api/src/pages.rs index 1946d1f34a6f8..b1c58a8b48604 100644 --- a/crates/next-api/src/pages.rs +++ b/crates/next-api/src/pages.rs @@ -28,7 +28,9 @@ use next_core::{ }; use serde::{Deserialize, Serialize}; use tracing::Instrument; -use turbo_tasks::{trace::TraceRawVcs, Completion, RcStr, TaskInput, TryJoinIterExt, Value, Vc}; +use turbo_tasks::{ + trace::TraceRawVcs, Completion, RcStr, TaskInput, TryJoinIterExt, Value, Vc, VcOperation, +}; use turbo_tasks_fs::{ self, File, FileContent, FileSystem, FileSystemPath, FileSystemPathOption, VirtualFileSystem, }; @@ -1029,8 +1031,13 @@ impl PageEndpoint { ))) } + /// This is used to wrap output assets of an endpoint into a single operation (VcOperation) #[turbo_tasks::function] - fn output_assets(self: Vc) -> Vc { + fn output_assets_operation( + self: Vc, + endpoint: VcOperation>, + ) -> Vc { + let _ = endpoint.connect(); self.output().output_assets() } @@ -1204,7 +1211,10 @@ pub struct ClientChunksModules { #[turbo_tasks::value_impl] impl Endpoint for PageEndpoint { #[turbo_tasks::function] - async fn write_to_disk(self: Vc) -> Result> { + async fn write_to_disk( + self: Vc, + self_op: VcOperation>, + ) -> Result> { let this = self.await?; let original_name = this.original_name.await?; let span = { @@ -1225,13 +1235,12 @@ impl Endpoint for PageEndpoint { }; async move { let output = self.output().await?; - // Must use self.output_assets() instead of output.output_assets() to make it a - // single operation - let output_assets = self.output_assets(); + + let output_assets = self.output_assets_operation(self_op); this.pages_project .project() - .emit_all_output_assets(Vc::cell(output_assets)) + .emit_all_output_assets(VcOperation::new(output_assets)) .await?; let node_root = this.pages_project.project().node_root(); diff --git a/crates/next-api/src/project.rs b/crates/next-api/src/project.rs index 65e07fe2026cf..139211f5fafe6 100644 --- a/crates/next-api/src/project.rs +++ b/crates/next-api/src/project.rs @@ -27,7 +27,7 @@ use turbo_tasks::{ graph::{AdjacencyMap, GraphTraversal}, trace::TraceRawVcs, Completion, Completions, IntoTraitRef, RcStr, ReadRef, State, TaskInput, TransientInstance, - TryFlatJoinIterExt, Value, Vc, + TryFlatJoinIterExt, Value, Vc, VcOperation, }; use turbo_tasks_env::{EnvMap, ProcessEnv}; use turbo_tasks_fs::{DiskFileSystem, FileSystem, FileSystemPath, VirtualFileSystem}; @@ -65,9 +65,10 @@ use crate::{ global_module_id_strategy::GlobalModuleIdStrategyBuilder, instrumentation::InstrumentationEndpoint, middleware::MiddlewareEndpoint, + operation::EntrypointsOperation, pages::PagesProject, route::{Endpoint, Route}, - versioned_content_map::{OutputAssetsOperation, VersionedContentMap}, + versioned_content_map::VersionedContentMap, }; #[derive(Debug, Serialize, Deserialize, Clone, TaskInput, PartialEq, Eq, Hash, TraceRawVcs)] @@ -375,6 +376,13 @@ impl ProjectContainer { self.project().entrypoints() } + /// See [Project::entrypoints]. + #[turbo_tasks::function] + pub fn entrypoints_operation(self: Vc) -> Vc { + let entrypoints = VcOperation::new(self.entrypoints()); + EntrypointsOperation::new(entrypoints) + } + /// See [Project::hmr_identifiers]. #[turbo_tasks::function] pub fn hmr_identifiers(self: Vc) -> Vc> { @@ -1119,7 +1127,7 @@ impl Project { #[turbo_tasks::function] pub async fn emit_all_output_assets( self: Vc, - output_assets: Vc, + output_assets: VcOperation, ) -> Result> { let span = tracing::info_span!("emitting"); async move { @@ -1142,7 +1150,7 @@ impl Project { Ok(Vc::cell(())) } else { let _ = emit_assets( - *all_output_assets.await?, + all_output_assets.connect(), node_root, client_relative_path, node_root, @@ -1314,17 +1322,16 @@ async fn get_referenced_output_assets( #[turbo_tasks::function] async fn all_assets_from_entries_operation_inner( - operation: Vc, + operation: VcOperation, ) -> Result> { - let assets = *operation.await?; - Vc::connect(assets); + let assets = operation.connect(); Ok(all_assets_from_entries(assets)) } fn all_assets_from_entries_operation( - operation: Vc, -) -> Vc { - Vc::cell(all_assets_from_entries_operation_inner(operation)) + operation: VcOperation, +) -> VcOperation { + VcOperation::new(all_assets_from_entries_operation_inner(operation)) } #[turbo_tasks::function] diff --git a/crates/next-api/src/route.rs b/crates/next-api/src/route.rs index 8b48b574eb786..70d7c0930dc82 100644 --- a/crates/next-api/src/route.rs +++ b/crates/next-api/src/route.rs @@ -1,7 +1,9 @@ use anyhow::Result; use indexmap::IndexMap; use serde::{Deserialize, Serialize}; -use turbo_tasks::{debug::ValueDebugFormat, trace::TraceRawVcs, Completion, RcStr, Vc}; +use turbo_tasks::{ + debug::ValueDebugFormat, trace::TraceRawVcs, Completion, RcStr, Vc, VcOperation, +}; use turbopack_core::module::Modules; use crate::paths::ServerPath; @@ -73,7 +75,10 @@ impl Route { #[turbo_tasks::value_trait] pub trait Endpoint { - fn write_to_disk(self: Vc) -> Vc; + fn write_to_disk( + self: Vc, + self_op: VcOperation>, + ) -> Vc; fn server_changed(self: Vc) -> Vc; fn client_changed(self: Vc) -> Vc; fn root_modules(self: Vc) -> Vc; diff --git a/crates/next-api/src/versioned_content_map.rs b/crates/next-api/src/versioned_content_map.rs index a16ed6fd13bb0..b33d7ac9e1552 100644 --- a/crates/next-api/src/versioned_content_map.rs +++ b/crates/next-api/src/versioned_content_map.rs @@ -6,7 +6,7 @@ use next_core::emit_assets; use serde::{Deserialize, Serialize}; use turbo_tasks::{ debug::ValueDebugFormat, trace::TraceRawVcs, Completion, RcStr, State, TryFlatJoinIterExt, - TryJoinIterExt, ValueDefault, ValueToString, Vc, + TryJoinIterExt, ValueDefault, ValueToString, Vc, VcOperation, }; use turbo_tasks_fs::FileSystemPath; use turbopack_core::{ @@ -16,16 +16,10 @@ use turbopack_core::{ version::OptionVersionedContent, }; -/// An unresolved output assets operation. We need to pass an operation here as -/// it's stored for later usage and we want to reconnect this operation when -/// it's received from the map again. -#[turbo_tasks::value(transparent)] -pub struct OutputAssetsOperation(Vc); - #[derive(Clone, TraceRawVcs, PartialEq, Eq, ValueDebugFormat, Serialize, Deserialize, Debug)] struct MapEntry { - assets_operation: Vc, - side_effects: Vc, + assets_operation: VcOperation, + side_effects_operation: VcOperation, /// Precomputed map for quick access to output asset by filepath path_to_asset: HashMap, Vc>>, } @@ -33,9 +27,9 @@ struct MapEntry { #[turbo_tasks::value(transparent)] struct OptionMapEntry(Option); -type PathToOutputOperation = HashMap, IndexSet>>; +type PathToOutputOperation = HashMap, IndexSet>>; // A precomputed map for quick access to output asset by filepath -type OutputOperationToComputeEntry = HashMap, Vc>; +type OutputOperationToComputeEntry = HashMap, VcOperation>; #[turbo_tasks::value] pub struct VersionedContentMap { @@ -70,7 +64,7 @@ impl VersionedContentMap { pub async fn insert_output_assets( self: Vc, // Output assets to emit - assets_operation: Vc, + assets_operation: VcOperation, node_root: Vc, client_relative_path: Vc, client_output_path: Vc, @@ -82,13 +76,15 @@ impl VersionedContentMap { client_relative_path, client_output_path, ); - let assets = *assets_operation.await?; - this.map_op_to_compute_entry - .update_conditionally(|map| map.insert(assets, compute_entry) != Some(compute_entry)); + let assets = assets_operation.connect(); + let compute_entry_operation = VcOperation::new(compute_entry); + this.map_op_to_compute_entry.update_conditionally(|map| { + map.insert(assets, compute_entry_operation) != Some(compute_entry_operation) + }); let Some(entry) = &*compute_entry.await? else { unreachable!("compute_entry always returns Some(MapEntry)") }; - Ok(entry.side_effects) + Ok(entry.side_effects_operation.connect()) } /// Creates a ComputEntry (a pre-computed map for optimized lookup) for an output assets @@ -96,12 +92,12 @@ impl VersionedContentMap { #[turbo_tasks::function] async fn compute_entry( self: Vc, - assets_operation: Vc, + assets_operation: VcOperation, node_root: Vc, client_relative_path: Vc, client_output_path: Vc, ) -> Result> { - let assets = *assets_operation.await?; + let assets = assets_operation.connect(); async fn get_entries( assets: Vc, ) -> Result, Vc>)>> { @@ -125,7 +121,7 @@ impl VersionedContentMap { let mut stale_assets = map.keys().copied().collect::>(); for (k, _) in entries.iter() { - let res = map.entry(*k).or_default().insert(assets); + let res = map.entry(*k).or_default().insert(assets_operation); stale_assets.remove(k); changed = changed || res; } @@ -136,17 +132,22 @@ impl VersionedContentMap { .get_mut(k) // guaranteed .unwrap() - .remove(&assets); + .remove(&assets_operation); changed = changed || res } changed }); // Make sure all written client assets are up-to-date - let side_effects = emit_assets(assets, node_root, client_relative_path, client_output_path); + let side_effects_operation = VcOperation::new(emit_assets( + assets, + node_root, + client_relative_path, + client_output_path, + )); let map_entry = Vc::cell(Some(MapEntry { - assets_operation: assets, - side_effects, + assets_operation, + side_effects_operation, path_to_asset: entries.into_iter().collect(), })); Ok(map_entry) @@ -194,11 +195,11 @@ impl VersionedContentMap { let result = self.raw_get(path).await?; if let Some(MapEntry { assets_operation: _, - side_effects, + side_effects_operation, path_to_asset, }) = &*result { - side_effects.await?; + side_effects_operation.connect().await?; if let Some(asset) = path_to_asset.get(&path) { return Ok(Vc::cell(Some(*asset))); @@ -239,7 +240,7 @@ impl VersionedContentMap { return Ok(Vc::cell(None)); }; // Need to reconnect the operation to the map - Vc::connect(assets); + let assets = assets.connect(); let compute_entry = { let map = self.map_op_to_compute_entry.get(); @@ -249,7 +250,7 @@ impl VersionedContentMap { return Ok(Vc::cell(None)); }; // Need to reconnect the operation to the map - Vc::connect(compute_entry); + let compute_entry = compute_entry.connect(); Ok(compute_entry) } diff --git a/crates/next-build-test/src/lib.rs b/crates/next-build-test/src/lib.rs index 95652e1291928..990918f7df51b 100644 --- a/crates/next-build-test/src/lib.rs +++ b/crates/next-build-test/src/lib.rs @@ -7,8 +7,9 @@ use std::{str::FromStr, time::Instant}; use anyhow::{Context, Result}; use futures_util::{StreamExt, TryStreamExt}; use next_api::{ + operation::RouteOperation, project::{ProjectContainer, ProjectOptions}, - route::{Endpoint, Route}, + route::Endpoint, }; use turbo_tasks::{RcStr, ReadConsistency, TransientInstance, TurboTasks, Vc}; use turbo_tasks_malloc::TurboMalloc; @@ -48,7 +49,7 @@ pub async fn main_inner( tracing::info!("collecting endpoints"); let entrypoints = tt - .run_once(async move { project.entrypoints().await }) + .run_once(async move { project.entrypoints_operation().await }) .await?; let mut routes = if let Some(files) = files { @@ -157,7 +158,7 @@ pub fn shuffle<'a, T: 'a>(items: impl Iterator) -> impl Iterator, - routes: impl Iterator, + routes: impl Iterator, strategy: Strategy, factor: usize, limit: usize, @@ -179,27 +180,31 @@ pub async fn render_routes( let name = name.clone(); async move { match route { - Route::Page { + RouteOperation::Page { html_endpoint, data_endpoint: _, } => { - html_endpoint.write_to_disk().await?; + html_endpoint.connect().write_to_disk(html_endpoint).await?; } - Route::PageApi { endpoint } => { - endpoint.write_to_disk().await?; + RouteOperation::PageApi { endpoint } => { + endpoint.connect().write_to_disk(endpoint).await?; } - Route::AppPage(routes) => { + RouteOperation::AppPage(routes) => { for route in routes { - route.html_endpoint.write_to_disk().await?; + route + .html_endpoint + .connect() + .write_to_disk(route.html_endpoint) + .await?; } } - Route::AppRoute { + RouteOperation::AppRoute { original_name: _, endpoint, } => { - endpoint.write_to_disk().await?; + endpoint.connect().write_to_disk(endpoint).await?; } - Route::Conflict => { + RouteOperation::Conflict => { tracing::info!("WARN: conflict {}", name); } } diff --git a/crates/next-core/src/app_structure.rs b/crates/next-core/src/app_structure.rs index 4f6bafbed457a..a005eb70fe66c 100644 --- a/crates/next-core/src/app_structure.rs +++ b/crates/next-core/src/app_structure.rs @@ -1101,12 +1101,14 @@ async fn directory_tree_to_entrypoints_internal_untraced( app_path, ) .await?; + let loader_tree = + loader_tree.context("loader tree should be created for a page/default")?; add_app_page( app_dir, &mut result, app_page.complete(PageType::Page)?, - loader_tree.context("loader tree should be created for a page/default")?, + loader_tree, ); } diff --git a/crates/next-core/src/next_app/app_page_entry.rs b/crates/next-core/src/next_app/app_page_entry.rs index e11913d12c7ee..77b720ea8881b 100644 --- a/crates/next-core/src/next_app/app_page_entry.rs +++ b/crates/next-core/src/next_app/app_page_entry.rs @@ -107,7 +107,7 @@ pub async fn get_app_page_entry( let source = VirtualSource::new_with_ident( source .ident() - .with_query(Vc::cell(query.to_string().into())), + .with_query(Vc::cell(format!("?{query}").into())), AssetContent::file(file.into()), ); diff --git a/test/development/basic/__snapshots__/next-rs-api.test.ts.snap b/test/development/basic/__snapshots__/next-rs-api.test.ts.snap index 938bfbf52a139..1cac89d2ff637 100644 --- a/test/development/basic/__snapshots__/next-rs-api.test.ts.snap +++ b/test/development/basic/__snapshots__/next-rs-api.test.ts.snap @@ -1,58 +1,1374 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`next.rs api should allow to write app Node.js page to disk: diagnostics 1`] = `[]`; +exports[`next.rs api should allow to write app Node.js page to disk: diagnostics 1`] = ` +[ + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "modularizeImports": "true", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "platform-triplet": "true", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "skipMiddlewareUrlNormalize": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "skipTrailingSlashRedirect": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcEmotion": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcExperimentalDecorators": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcImportSource": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcReactRemoveProperties": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcRelay": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcRemoveConsole": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcStyledComponents": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "transpilePackages": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "turbotrace": "false", + }, + }, +] +`; exports[`next.rs api should allow to write app Node.js page to disk: issues 1`] = `[]`; -exports[`next.rs api should allow to write app Node.js page to disk: rsc diagnostics 1`] = `[]`; +exports[`next.rs api should allow to write app Node.js page to disk: rsc diagnostics 1`] = ` +[ + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "modularizeImports": "true", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "platform-triplet": "true", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "skipMiddlewareUrlNormalize": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "skipTrailingSlashRedirect": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcEmotion": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcExperimentalDecorators": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcImportSource": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcReactRemoveProperties": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcRelay": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcRemoveConsole": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcStyledComponents": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "transpilePackages": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "turbotrace": "false", + }, + }, +] +`; exports[`next.rs api should allow to write app Node.js page to disk: rsc issues 1`] = `[]`; -exports[`next.rs api should allow to write app Node.js route to disk: diagnostics 1`] = `[]`; +exports[`next.rs api should allow to write app Node.js route to disk: diagnostics 1`] = ` +[ + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "modularizeImports": "true", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "platform-triplet": "true", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "skipMiddlewareUrlNormalize": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "skipTrailingSlashRedirect": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcEmotion": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcExperimentalDecorators": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcImportSource": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcReactRemoveProperties": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcRelay": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcRemoveConsole": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcStyledComponents": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "transpilePackages": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "turbotrace": "false", + }, + }, +] +`; exports[`next.rs api should allow to write app Node.js route to disk: issues 1`] = `[]`; -exports[`next.rs api should allow to write app edge page to disk: diagnostics 1`] = `[]`; +exports[`next.rs api should allow to write app edge page to disk: diagnostics 1`] = ` +[ + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "modularizeImports": "true", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "platform-triplet": "true", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "skipMiddlewareUrlNormalize": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "skipTrailingSlashRedirect": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcEmotion": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcExperimentalDecorators": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcImportSource": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcReactRemoveProperties": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcRelay": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcRemoveConsole": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcStyledComponents": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "transpilePackages": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "turbotrace": "false", + }, + }, +] +`; exports[`next.rs api should allow to write app edge page to disk: issues 1`] = `[]`; -exports[`next.rs api should allow to write app edge page to disk: rsc diagnostics 1`] = `[]`; +exports[`next.rs api should allow to write app edge page to disk: rsc diagnostics 1`] = ` +[ + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "modularizeImports": "true", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "platform-triplet": "true", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "skipMiddlewareUrlNormalize": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "skipTrailingSlashRedirect": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcEmotion": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcExperimentalDecorators": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcImportSource": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcReactRemoveProperties": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcRelay": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcRemoveConsole": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcStyledComponents": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "transpilePackages": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "turbotrace": "false", + }, + }, +] +`; exports[`next.rs api should allow to write app edge page to disk: rsc issues 1`] = `[]`; -exports[`next.rs api should allow to write app edge route to disk: diagnostics 1`] = `[]`; +exports[`next.rs api should allow to write app edge route to disk: diagnostics 1`] = ` +[ + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "modularizeImports": "true", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "platform-triplet": "true", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "skipMiddlewareUrlNormalize": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "skipTrailingSlashRedirect": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcEmotion": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcExperimentalDecorators": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcImportSource": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcReactRemoveProperties": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcRelay": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcRemoveConsole": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcStyledComponents": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "transpilePackages": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "turbotrace": "false", + }, + }, +] +`; exports[`next.rs api should allow to write app edge route to disk: issues 1`] = `[]`; -exports[`next.rs api should allow to write pages Node.js api to disk: diagnostics 1`] = `[]`; +exports[`next.rs api should allow to write pages Node.js api to disk: diagnostics 1`] = ` +[ + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "modularizeImports": "true", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "platform-triplet": "true", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "skipMiddlewareUrlNormalize": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "skipTrailingSlashRedirect": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcEmotion": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcExperimentalDecorators": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcImportSource": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcReactRemoveProperties": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcRelay": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcRemoveConsole": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcStyledComponents": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "transpilePackages": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "turbotrace": "false", + }, + }, +] +`; exports[`next.rs api should allow to write pages Node.js api to disk: issues 1`] = `[]`; -exports[`next.rs api should allow to write pages Node.js page to disk: data diagnostics 1`] = `[]`; +exports[`next.rs api should allow to write pages Node.js page to disk: data diagnostics 1`] = ` +[ + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "modularizeImports": "true", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "platform-triplet": "true", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "skipMiddlewareUrlNormalize": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "skipTrailingSlashRedirect": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcEmotion": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcExperimentalDecorators": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcImportSource": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcReactRemoveProperties": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcRelay": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcRemoveConsole": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcStyledComponents": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "transpilePackages": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "turbotrace": "false", + }, + }, +] +`; exports[`next.rs api should allow to write pages Node.js page to disk: data issues 1`] = `[]`; -exports[`next.rs api should allow to write pages Node.js page to disk: diagnostics 1`] = `[]`; +exports[`next.rs api should allow to write pages Node.js page to disk: diagnostics 1`] = ` +[ + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "modularizeImports": "true", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "platform-triplet": "true", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "skipMiddlewareUrlNormalize": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "skipTrailingSlashRedirect": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcEmotion": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcExperimentalDecorators": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcImportSource": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcReactRemoveProperties": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcRelay": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcRemoveConsole": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcStyledComponents": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "transpilePackages": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "turbotrace": "false", + }, + }, +] +`; exports[`next.rs api should allow to write pages Node.js page to disk: issues 1`] = `[]`; -exports[`next.rs api should allow to write pages edge api to disk: diagnostics 1`] = `[]`; +exports[`next.rs api should allow to write pages edge api to disk: diagnostics 1`] = ` +[ + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "modularizeImports": "true", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "platform-triplet": "true", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "skipMiddlewareUrlNormalize": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "skipTrailingSlashRedirect": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcEmotion": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcExperimentalDecorators": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcImportSource": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcReactRemoveProperties": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcRelay": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcRemoveConsole": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcStyledComponents": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "transpilePackages": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "turbotrace": "false", + }, + }, +] +`; exports[`next.rs api should allow to write pages edge api to disk: issues 1`] = `[]`; -exports[`next.rs api should allow to write pages edge page to disk: data diagnostics 1`] = `[]`; +exports[`next.rs api should allow to write pages edge page to disk: data diagnostics 1`] = ` +[ + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "modularizeImports": "true", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "platform-triplet": "true", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "skipMiddlewareUrlNormalize": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "skipTrailingSlashRedirect": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcEmotion": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcExperimentalDecorators": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcImportSource": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcReactRemoveProperties": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcRelay": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcRemoveConsole": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcStyledComponents": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "transpilePackages": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "turbotrace": "false", + }, + }, +] +`; exports[`next.rs api should allow to write pages edge page to disk: data issues 1`] = `[]`; -exports[`next.rs api should allow to write pages edge page to disk: diagnostics 1`] = `[]`; +exports[`next.rs api should allow to write pages edge page to disk: diagnostics 1`] = ` +[ + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "modularizeImports": "true", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "platform-triplet": "true", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "skipMiddlewareUrlNormalize": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "skipTrailingSlashRedirect": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcEmotion": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcExperimentalDecorators": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcImportSource": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcReactRemoveProperties": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcRelay": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcRemoveConsole": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcStyledComponents": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "transpilePackages": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "turbotrace": "false", + }, + }, +] +`; exports[`next.rs api should allow to write pages edge page to disk: issues 1`] = `[]`; -exports[`next.rs api should allow to write root page to disk: data diagnostics 1`] = `[]`; +exports[`next.rs api should allow to write root page to disk: data diagnostics 1`] = ` +[ + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "modularizeImports": "true", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "platform-triplet": "true", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "skipMiddlewareUrlNormalize": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "skipTrailingSlashRedirect": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcEmotion": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcExperimentalDecorators": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcImportSource": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcReactRemoveProperties": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcRelay": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcRemoveConsole": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcStyledComponents": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "transpilePackages": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "turbotrace": "false", + }, + }, +] +`; exports[`next.rs api should allow to write root page to disk: data issues 1`] = `[]`; -exports[`next.rs api should allow to write root page to disk: diagnostics 1`] = `[]`; +exports[`next.rs api should allow to write root page to disk: diagnostics 1`] = ` +[ + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "modularizeImports": "true", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "platform-triplet": "true", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "skipMiddlewareUrlNormalize": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "skipTrailingSlashRedirect": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcEmotion": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcExperimentalDecorators": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcImportSource": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcReactRemoveProperties": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcRelay": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcRemoveConsole": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "swcStyledComponents": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "transpilePackages": "false", + }, + }, + { + "category": "NextFeatureTelemetry_category_tbd", + "name": "EVENT_BUILD_FEATURE_USAGE", + "payload": { + "turbotrace": "false", + }, + }, +] +`; exports[`next.rs api should allow to write root page to disk: issues 1`] = `[]`; diff --git a/test/development/basic/next-rs-api.test.ts b/test/development/basic/next-rs-api.test.ts index 6d3f8b4d13a0d..dbd0a337aaba2 100644 --- a/test/development/basic/next-rs-api.test.ts +++ b/test/development/basic/next-rs-api.test.ts @@ -507,7 +507,7 @@ describe('next.rs api', () => { expect(result.value).toHaveProperty('issues', expect.toBeEmpty()) expect(result.value).toHaveProperty( 'diagnostics', - expect.toBeEmpty() + expect.toBeArray() ) }) ) @@ -627,7 +627,7 @@ describe('next.rs api', () => { expect(result.done).toBe(false) expect(result.value).toHaveProperty('resource', expect.toBeObject()) expect(result.value).toHaveProperty('type', 'issues') - expect(result.value).toHaveProperty('diagnostics', expect.toBeEmpty()) + expect(result.value).toHaveProperty('diagnostics', expect.toBeArray()) }) ) const merged = raceIterators(subscriptions) diff --git a/turbopack/crates/node-file-trace/src/lib.rs b/turbopack/crates/node-file-trace/src/lib.rs index d4f54341f78e8..a8eb799ad0e08 100644 --- a/turbopack/crates/node-file-trace/src/lib.rs +++ b/turbopack/crates/node-file-trace/src/lib.rs @@ -23,7 +23,7 @@ use serde::Serialize; use tokio::sync::mpsc::channel; use turbo_tasks::{ backend::Backend, util::FormatDuration, RcStr, ReadConsistency, TaskId, TransientInstance, - TransientValue, TurboTasks, UpdateInfo, Value, Vc, + TransientValue, TurboTasks, UpdateInfo, Value, Vc, VcOperation, }; use turbo_tasks_fs::{ glob::Glob, DirectoryEntry, DiskFileSystem, FileSystem, FileSystemPath, ReadGlobResult, @@ -483,10 +483,11 @@ async fn run>( module_options, resolve_options, ); + let output_op = VcOperation::new(output); let _ = output.resolve_strongly_consistent().await?; - let source = TransientValue::new(Vc::into_raw(output)); - let issues = output.peek_issues_with_path().await?; + let source = TransientValue::new(VcOperation::into_raw(output_op)); + let issues = output_op.peek_issues_with_path().await?; let console_ui = ConsoleUi::new(log_options); Vc::upcast::>(console_ui) @@ -553,9 +554,10 @@ async fn main_operation( ) .await?; for module in modules.iter() { - let set = all_modules_and_affecting_sources(*module) + let set = VcOperation::new(all_modules_and_affecting_sources(*module)) .issue_file_path(module.ident().path(), "gathering list of assets") - .await?; + .await? + .connect(); for asset in set.await?.iter() { let path = asset.ident().path().await?; result.insert(RcStr::from(&*path.path)); diff --git a/turbopack/crates/turbo-tasks-testing/tests/collectibles.rs b/turbopack/crates/turbo-tasks-testing/tests/collectibles.rs index d2f05a678a751..fc1a548c7e135 100644 --- a/turbopack/crates/turbo-tasks-testing/tests/collectibles.rs +++ b/turbopack/crates/turbo-tasks-testing/tests/collectibles.rs @@ -5,7 +5,7 @@ use std::{collections::HashSet, time::Duration}; use anyhow::Result; use auto_hash_map::AutoSet; use tokio::time::sleep; -use turbo_tasks::{emit, CollectiblesSource, RcStr, ValueToString, Vc}; +use turbo_tasks::{emit, CollectiblesSource, RcStr, ValueToString, Vc, VcOperation}; use turbo_tasks_testing::{register, run, Registration}; static REGISTRATION: Registration = register!(); @@ -15,7 +15,7 @@ async fn transitive_emitting() { run(®ISTRATION, || async { let result = my_transitive_emitting_function("".into(), "".into()); result.strongly_consistent().await?; - let list = result.peek_collectibles::>(); + let list = VcOperation::new(result).peek_collectibles::>(); assert_eq!(list.len(), 2); let mut expected = ["123", "42"].into_iter().collect::>(); for collectible in list { @@ -51,7 +51,7 @@ async fn multi_emitting() { run(®ISTRATION, || async { let result = my_multi_emitting_function(); result.strongly_consistent().await?; - let list = result.peek_collectibles::>(); + let list = VcOperation::new(result).peek_collectibles::>(); assert_eq!(list.len(), 2); let mut expected = ["123", "42"].into_iter().collect::>(); for collectible in list { @@ -68,7 +68,7 @@ async fn multi_emitting() { async fn taking_collectibles() { run(®ISTRATION, || async { let result = my_collecting_function(); - let list = result.take_collectibles::>(); + let list = VcOperation::new(result).take_collectibles::>(); // my_collecting_function already processed the collectibles so the list should // be empty assert!(list.is_empty()); @@ -84,7 +84,7 @@ async fn taking_collectibles_extra_layer() { run(®ISTRATION, || async { let result = my_collecting_function_indirect(); result.strongly_consistent().await?; - let list = result.take_collectibles::>(); + let list = VcOperation::new(result).take_collectibles::>(); // my_collecting_function already processed the collectibles so the list should // be empty assert!(list.is_empty()); @@ -100,34 +100,34 @@ async fn taking_collectibles_parallel() { run(®ISTRATION, || async { let result = my_transitive_emitting_function("".into(), "a".into()); result.strongly_consistent().await?; - let list = result.take_collectibles::>(); + let list = VcOperation::new(result).take_collectibles::>(); assert_eq!(list.len(), 2); assert_eq!(result.await?.0, 0); let result = my_transitive_emitting_function("".into(), "b".into()); result.strongly_consistent().await?; - let list = result.take_collectibles::>(); + let list = VcOperation::new(result).take_collectibles::>(); assert_eq!(list.len(), 2); assert_eq!(result.await?.0, 0); let result = my_transitive_emitting_function_with_child_scope("".into(), "b".into(), "1".into()); result.strongly_consistent().await?; - let list = result.take_collectibles::>(); + let list = VcOperation::new(result).take_collectibles::>(); assert_eq!(list.len(), 2); assert_eq!(result.await?.0, 0); let result = my_transitive_emitting_function_with_child_scope("".into(), "b".into(), "2".into()); result.strongly_consistent().await?; - let list = result.take_collectibles::>(); + let list = VcOperation::new(result).take_collectibles::>(); assert_eq!(list.len(), 2); assert_eq!(result.await?.0, 0); let result = my_transitive_emitting_function_with_child_scope("".into(), "c".into(), "3".into()); result.strongly_consistent().await?; - let list = result.take_collectibles::>(); + let list = VcOperation::new(result).take_collectibles::>(); assert_eq!(list.len(), 2); assert_eq!(result.await?.0, 0); @@ -143,7 +143,7 @@ struct Collectibles(AutoSet>>); #[turbo_tasks::function] async fn my_collecting_function() -> Result> { let result = my_transitive_emitting_function("".into(), "".into()); - result.take_collectibles::>(); + VcOperation::new(result).take_collectibles::>(); Ok(result) } @@ -151,7 +151,7 @@ async fn my_collecting_function() -> Result> { async fn my_collecting_function_indirect() -> Result> { let result = my_collecting_function(); result.strongly_consistent().await?; - let list = result.peek_collectibles::>(); + let list = VcOperation::new(result).peek_collectibles::>(); // my_collecting_function already processed the collectibles so the list should // be empty assert!(list.is_empty()); @@ -175,7 +175,7 @@ async fn my_transitive_emitting_function(key: RcStr, _key2: RcStr) -> Result Vc { let result = my_transitive_emitting_function(key, key2); - Vc::cell(result.peek_collectibles::>()) + Vc::cell(VcOperation::new(result).peek_collectibles::>()) } #[turbo_tasks::function] @@ -186,7 +186,7 @@ async fn my_transitive_emitting_function_with_child_scope( ) -> Result> { let thing = my_transitive_emitting_function(key, key2); thing.strongly_consistent().await?; - let list = thing.peek_collectibles::>(); + let list = VcOperation::new(thing).peek_collectibles::>(); assert_eq!(list.len(), 2); Ok(thing) } diff --git a/turbopack/crates/turbo-tasks-testing/tests/dirty_in_progress.rs b/turbopack/crates/turbo-tasks-testing/tests/dirty_in_progress.rs index d0b5d21c35a05..5a7629bca7cc9 100644 --- a/turbopack/crates/turbo-tasks-testing/tests/dirty_in_progress.rs +++ b/turbopack/crates/turbo-tasks-testing/tests/dirty_in_progress.rs @@ -3,7 +3,7 @@ use std::time::Duration; use anyhow::{bail, Result}; -use turbo_tasks::{emit, CollectiblesSource, RcStr, State, ValueToString, Vc}; +use turbo_tasks::{emit, CollectiblesSource, RcStr, State, ValueToString, Vc, VcOperation}; use turbo_tasks_testing::{register, run, Registration}; static REGISTRATION: Registration = register!(); @@ -89,7 +89,7 @@ async fn compute(input: Vc) -> Result> { println!("start compute"); let operation = inner_compute(input); let value = *operation.await?; - let collectibles = operation.peek_collectibles::>(); + let collectibles = VcOperation::new(operation).peek_collectibles::>(); if collectibles.len() > 1 { bail!("expected 0..1 collectible, found {}", collectibles.len()); } diff --git a/turbopack/crates/turbo-tasks-testing/tests/recompute_collectibles.rs b/turbopack/crates/turbo-tasks-testing/tests/recompute_collectibles.rs index a91c0225ac193..66288432a22ea 100644 --- a/turbopack/crates/turbo-tasks-testing/tests/recompute_collectibles.rs +++ b/turbopack/crates/turbo-tasks-testing/tests/recompute_collectibles.rs @@ -1,7 +1,7 @@ #![feature(arbitrary_self_types)] use anyhow::{bail, Result}; -use turbo_tasks::{emit, CollectiblesSource, RcStr, State, ValueToString, Vc}; +use turbo_tasks::{emit, CollectiblesSource, RcStr, State, ValueToString, Vc, VcOperation}; use turbo_tasks_testing::{register, run, Registration}; static REGISTRATION: Registration = register!(); @@ -82,7 +82,7 @@ async fn compute(input: Vc, innerness: u32) -> Result> } let operation = inner_compute(input); let value = *operation.await?; - let collectibles = operation.peek_collectibles::>(); + let collectibles = VcOperation::new(operation).peek_collectibles::>(); if collectibles.len() != 1 { bail!("expected 1 collectible, found {}", collectibles.len()); } diff --git a/turbopack/crates/turbo-tasks/src/lib.rs b/turbopack/crates/turbo-tasks/src/lib.rs index a289b5ad18aea..c34bbbac1df12 100644 --- a/turbopack/crates/turbo-tasks/src/lib.rs +++ b/turbopack/crates/turbo-tasks/src/lib.rs @@ -112,8 +112,8 @@ pub use value::{TransientInstance, TransientValue, Value}; pub use value_type::{TraitMethod, TraitType, ValueType}; pub use vc::{ Dynamic, ResolvedValue, ResolvedVc, TypedForInput, Upcast, ValueDefault, Vc, VcCast, - VcCellNewMode, VcCellSharedMode, VcDefaultRead, VcRead, VcTransparentRead, VcValueTrait, - VcValueTraitCast, VcValueType, VcValueTypeCast, + VcCellNewMode, VcCellSharedMode, VcDefaultRead, VcOperation, VcRead, VcTransparentRead, + VcValueTrait, VcValueTraitCast, VcValueType, VcValueTypeCast, }; pub use crate::rcstr::RcStr; diff --git a/turbopack/crates/turbo-tasks/src/task/function.rs b/turbopack/crates/turbo-tasks/src/task/function.rs index 12e83ab98a0f2..daea59a81e8da 100644 --- a/turbopack/crates/turbo-tasks/src/task/function.rs +++ b/turbopack/crates/turbo-tasks/src/task/function.rs @@ -26,7 +26,7 @@ use std::{future::Future, marker::PhantomData, pin::Pin}; use anyhow::Result; use super::{TaskInput, TaskOutput}; -use crate::{magic_any::MagicAny, RawVc, Vc, VcRead, VcValueType}; +use crate::{magic_any::MagicAny, RawVc, Vc, VcOperation, VcRead, VcValueType}; pub type NativeTaskFuture = Pin> + Send>>; @@ -151,6 +151,12 @@ impl TaskFnMode for FunctionMode {} pub struct AsyncFunctionMode; impl TaskFnMode for AsyncFunctionMode {} +pub struct OperationMode; +impl TaskFnMode for OperationMode {} + +pub struct AsyncOperationMode; +impl TaskFnMode for AsyncOperationMode {} + pub struct MethodMode; impl TaskFnMode for MethodMode {} @@ -273,6 +279,29 @@ macro_rules! task_fn_impl { } } + impl TaskFnInputFunctionWithThis for F + where + Recv: Sync + Send + 'static, + $($arg: TaskInput + 'static,)* + F: Fn(VcOperation, $($arg,)*) -> Output + Send + Sync + Clone + 'static, + Output: TaskOutput + 'static, + { + #[allow(non_snake_case)] + fn functor(&self, this: RawVc, arg: &dyn MagicAny) -> Result { + let task_fn = self.clone(); + let recv = VcOperation::::from(this); + + let ($($arg,)*) = get_args::<($($arg,)*)>(arg)?; + $( + let $arg = $arg.clone(); + )* + + Ok(Box::pin(async move { + Output::try_into_raw_vc((task_fn)(recv, $($arg,)*)) + })) + } + } + pub trait $async_fn_trait: Fn(A0, $($arg,)*) -> Self::OutputFuture { type OutputFuture: Future>::Output> + Send; type Output: TaskOutput; @@ -333,6 +362,28 @@ macro_rules! task_fn_impl { })) } } + + impl TaskFnInputFunctionWithThis for F + where + Recv: Sync + Send + 'static, + $($arg: TaskInput + 'static,)* + F: $async_fn_trait, $($arg,)*> + Clone + Send + Sync + 'static, + { + #[allow(non_snake_case)] + fn functor(&self, this: RawVc, arg: &dyn MagicAny) -> Result { + let task_fn = self.clone(); + let recv = VcOperation::::from(this); + + let ($($arg,)*) = get_args::<($($arg,)*)>(arg)?; + $( + let $arg = $arg.clone(); + )* + + Ok(Box::pin(async move { + , $($arg,)*>>::Output::try_into_raw_vc((task_fn)(recv, $($arg,)*).await) + })) + } + } }; } diff --git a/turbopack/crates/turbo-tasks/src/vc/mod.rs b/turbopack/crates/turbo-tasks/src/vc/mod.rs index 11fb6d74297e7..c5e9d021f1d19 100644 --- a/turbopack/crates/turbo-tasks/src/vc/mod.rs +++ b/turbopack/crates/turbo-tasks/src/vc/mod.rs @@ -1,6 +1,7 @@ pub(crate) mod cast; mod cell_mode; pub(crate) mod default; +pub(crate) mod operation; mod read; pub(crate) mod resolved; mod traits; @@ -13,13 +14,13 @@ use std::{ }; use anyhow::Result; -use auto_hash_map::AutoSet; use serde::{Deserialize, Serialize}; pub use self::{ cast::{VcCast, VcValueTraitCast, VcValueTypeCast}, cell_mode::{VcCellMode, VcCellNewMode, VcCellSharedMode}, default::ValueDefault, + operation::VcOperation, read::{ReadVcFuture, VcDefaultRead, VcRead, VcTransparentRead}, resolved::{ResolvedValue, ResolvedVc}, traits::{Dynamic, TypedForInput, Upcast, VcValueTrait, VcValueType}, @@ -29,7 +30,7 @@ use crate::{ manager::{create_local_cell, try_get_function_meta}, registry, trace::{TraceRawVcs, TraceRawVcsContext}, - CellId, CollectiblesSource, RawVc, ResolveTypeError, SharedReference, + CellId, RawVc, ResolveTypeError, SharedReference, }; /// A Value Cell (`Vc` for short) is a reference to a memoized computation @@ -320,11 +321,6 @@ impl Vc where T: ?Sized + Send, { - /// Connects the operation pointed to by this `Vc` to the current task. - pub fn connect(vc: Self) { - vc.node.connect() - } - /// Returns a debug identifier for this `Vc`. pub async fn debug_identifier(vc: Self) -> Result { let resolved = vc.resolve().await?; @@ -501,19 +497,6 @@ where } } -impl CollectiblesSource for Vc -where - T: ?Sized + Send, -{ - fn take_collectibles(self) -> AutoSet> { - self.node.take_collectibles() - } - - fn peek_collectibles(self) -> AutoSet> { - self.node.peek_collectibles() - } -} - impl From for Vc where T: ?Sized + Send, diff --git a/turbopack/crates/turbo-tasks/src/vc/operation.rs b/turbopack/crates/turbo-tasks/src/vc/operation.rs new file mode 100644 index 0000000000000..4c9d976ef5205 --- /dev/null +++ b/turbopack/crates/turbo-tasks/src/vc/operation.rs @@ -0,0 +1,151 @@ +use std::{fmt::Debug, hash::Hash}; + +use auto_hash_map::AutoSet; +use serde::{Deserialize, Serialize}; + +use crate::{trace::TraceRawVcs, CollectiblesSource, RawVc, TaskInput, Upcast, Vc, VcValueTrait}; + +#[must_use] +pub struct VcOperation +where + T: ?Sized + Send, +{ + pub(crate) node: Vc, +} + +impl VcOperation { + /// Creates a new `VcOperation` from a `Vc`. + /// + /// The caller must ensure that the `Vc` is not a local task and it points to a a single + /// operation. + pub fn new(node: Vc) -> Self { + // TODO to avoid this runtime check, we should mark functions with `(operation)` and return + // a VcOperation directly + assert!( + !node.is_local(), + "VcOperation::new can't be used on local tasks" + ); + Self { node } + } + + pub fn connect(self) -> Vc { + self.node.node.connect(); + self.node + } + + /// Returns the `RawVc` corresponding to this `Vc`. + pub fn into_raw(vc: Self) -> RawVc { + vc.node.node + } + + /// Upcasts the given `VcOperation` to a `VcOperation>`. + /// + /// This is also available as an `Into`/`From` conversion. + #[inline(always)] + pub fn upcast(vc: Self) -> VcOperation + where + T: Upcast, + K: VcValueTrait + ?Sized + Send, + { + VcOperation { + node: Vc::upcast(vc.node), + } + } +} + +impl Copy for VcOperation where T: ?Sized + Send {} + +impl Clone for VcOperation +where + T: ?Sized + Send, +{ + fn clone(&self) -> Self { + *self + } +} + +impl Hash for VcOperation +where + T: ?Sized + Send, +{ + fn hash(&self, state: &mut H) { + self.node.hash(state); + } +} + +impl PartialEq> for VcOperation +where + T: ?Sized + Send, +{ + fn eq(&self, other: &Self) -> bool { + self.node == other.node + } +} + +impl Eq for VcOperation where T: ?Sized + Send {} + +impl Debug for VcOperation +where + T: ?Sized + Send, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("VcOperation") + .field("node", &self.node.node) + .finish() + } +} + +impl TaskInput for VcOperation where T: ?Sized + Send {} + +impl From for VcOperation +where + T: ?Sized + Send, +{ + fn from(raw: RawVc) -> Self { + Self { + node: Vc::from(raw), + } + } +} + +impl Serialize for VcOperation +where + T: ?Sized + Send, +{ + fn serialize(&self, serializer: S) -> Result { + self.node.serialize(serializer) + } +} + +impl<'de, T> Deserialize<'de> for VcOperation +where + T: ?Sized + Send, +{ + fn deserialize>(deserializer: D) -> Result { + Ok(VcOperation { + node: Vc::deserialize(deserializer)?, + }) + } +} + +impl TraceRawVcs for VcOperation +where + T: ?Sized + Send, +{ + fn trace_raw_vcs(&self, trace_context: &mut crate::trace::TraceRawVcsContext) { + self.node.trace_raw_vcs(trace_context); + } +} + +impl CollectiblesSource for VcOperation +where + T: ?Sized + Send, +{ + fn take_collectibles(self) -> AutoSet> { + self.node.node.take_collectibles() + } + + fn peek_collectibles(self) -> AutoSet> { + self.node.node.peek_collectibles() + } +} diff --git a/turbopack/crates/turbopack-cli/src/build/mod.rs b/turbopack/crates/turbopack-cli/src/build/mod.rs index 66e558ce1866f..1639a883b10ff 100644 --- a/turbopack/crates/turbopack-cli/src/build/mod.rs +++ b/turbopack/crates/turbopack-cli/src/build/mod.rs @@ -7,7 +7,7 @@ use std::{ use anyhow::{bail, Context, Result}; use turbo_tasks::{ - RcStr, ReadConsistency, TransientInstance, TryJoinIterExt, TurboTasks, Value, Vc, + RcStr, ReadConsistency, TransientInstance, TryJoinIterExt, TurboTasks, Value, Vc, VcOperation, }; use turbo_tasks_fs::FileSystem; use turbo_tasks_memory::MemoryBackend; @@ -139,7 +139,7 @@ impl TurbopackBuildBuilder { }))); handle_issues( - build_result, + VcOperation::new(build_result), issue_reporter, IssueSeverity::Error.into(), None, diff --git a/turbopack/crates/turbopack-core/src/issue/mod.rs b/turbopack/crates/turbopack-core/src/issue/mod.rs index 90f97aae7454d..5a20ed5405489 100644 --- a/turbopack/crates/turbopack-core/src/issue/mod.rs +++ b/turbopack/crates/turbopack-core/src/issue/mod.rs @@ -13,7 +13,7 @@ use auto_hash_map::AutoSet; use serde::Serialize; use turbo_tasks::{ emit, CollectiblesSource, RawVc, RcStr, ReadRef, TransientInstance, TransientValue, - TryJoinIterExt, Upcast, ValueToString, Vc, + TryJoinIterExt, Upcast, ValueToString, Vc, VcOperation, }; use turbo_tasks_fs::{FileContent, FileLine, FileLinesContent, FileSystemPath}; use turbo_tasks_hash::{DeterministicHash, Xxh3Hash64Hasher}; @@ -983,18 +983,18 @@ where } pub async fn handle_issues( - source: Vc, + source: VcOperation, issue_reporter: Vc>, min_failing_severity: Vc, path: Option<&str>, operation: Option<&str>, ) -> Result<()> { - let _ = source.resolve_strongly_consistent().await?; + let _ = source.connect().resolve_strongly_consistent().await?; let issues = source.peek_issues_with_path().await?; let has_fatal = issue_reporter.report_issues( TransientInstance::new(issues), - TransientValue::new(Vc::into_raw(source)), + TransientValue::new(VcOperation::into_raw(source)), min_failing_severity, ); diff --git a/turbopack/crates/turbopack-core/src/reference/mod.rs b/turbopack/crates/turbopack-core/src/reference/mod.rs index f167ecd603b76..e5039687ccfbe 100644 --- a/turbopack/crates/turbopack-core/src/reference/mod.rs +++ b/turbopack/crates/turbopack-core/src/reference/mod.rs @@ -4,7 +4,7 @@ use anyhow::Result; use indexmap::IndexSet; use turbo_tasks::{ graph::{AdjacencyMap, GraphTraversal}, - RcStr, TryJoinIterExt, ValueToString, Vc, + RcStr, TryJoinIterExt, ValueToString, Vc, VcOperation, }; use crate::{ @@ -205,16 +205,22 @@ pub async fn primary_referenced_modules(module: Vc>) -> Result>) -> Result> { // TODO need to track import path here let mut queue = VecDeque::with_capacity(32); - queue.push_back((asset, referenced_modules_and_affecting_sources(asset))); + queue.push_back(( + asset, + VcOperation::new(referenced_modules_and_affecting_sources(asset)), + )); let mut assets = HashSet::new(); assets.insert(asset); while let Some((parent, references)) = queue.pop_front() { let references = references .issue_file_path(parent.ident().path(), "expanding references of asset") .await?; - for asset in references.await?.iter() { + for asset in references.connect().await?.iter() { if assets.insert(*asset) { - queue.push_back((*asset, referenced_modules_and_affecting_sources(*asset))); + queue.push_back(( + *asset, + VcOperation::new(referenced_modules_and_affecting_sources(*asset)), + )); } } } diff --git a/turbopack/crates/turbopack-dev-server/src/http.rs b/turbopack/crates/turbopack-dev-server/src/http.rs index ad8d50e0b0472..6c2a00f0f446b 100644 --- a/turbopack/crates/turbopack-dev-server/src/http.rs +++ b/turbopack/crates/turbopack-dev-server/src/http.rs @@ -10,7 +10,9 @@ use hyper::{ }; use mime::Mime; use tokio_util::io::{ReaderStream, StreamReader}; -use turbo_tasks::{util::SharedError, CollectiblesSource, ReadRef, TransientInstance, Vc}; +use turbo_tasks::{ + util::SharedError, CollectiblesSource, ReadRef, TransientInstance, Vc, VcOperation, +}; use turbo_tasks_bytes::Bytes; use turbo_tasks_fs::FileContent; use turbopack_core::{ @@ -59,7 +61,7 @@ async fn get_from_source( } } ResolveSourceRequestResult::HttpProxy(proxy) => { - GetFromSourceResult::HttpProxy(proxy.await?) + GetFromSourceResult::HttpProxy(proxy.connect().await?) } ResolveSourceRequestResult::NotFound => GetFromSourceResult::NotFound, } @@ -78,8 +80,8 @@ pub async fn process_request_with_content_source( )> { let original_path = request.uri().path().to_string(); let request = http_request_to_source_request(request).await?; - let result = get_from_source(source, TransientInstance::new(request)); - let resolved_result = result.resolve_strongly_consistent().await?; + let result = VcOperation::new(get_from_source(source, TransientInstance::new(request))); + let resolved_result = result.connect().resolve_strongly_consistent().await?; let side_effects: AutoSet>> = result.peek_collectibles(); handle_issues( result, diff --git a/turbopack/crates/turbopack-dev-server/src/lib.rs b/turbopack/crates/turbopack-dev-server/src/lib.rs index 41ccae1a02fb9..28a72cb7de8e5 100644 --- a/turbopack/crates/turbopack-dev-server/src/lib.rs +++ b/turbopack/crates/turbopack-dev-server/src/lib.rs @@ -32,7 +32,7 @@ use socket2::{Domain, Protocol, Socket, Type}; use tokio::task::JoinHandle; use tracing::{event, info_span, Instrument, Level, Span}; use turbo_tasks::{ - run_once_with_reason, trace::TraceRawVcs, util::FormatDuration, TurboTasksApi, Vc, + run_once_with_reason, trace::TraceRawVcs, util::FormatDuration, TurboTasksApi, Vc, VcOperation, }; use turbopack_core::{ error::PrettyPrintError, @@ -208,8 +208,10 @@ impl DevServerBuilder { let uri = request.uri(); let path = uri.path().to_string(); - let source = source_provider.get_source(); - let resolved_source = source.resolve_strongly_consistent().await?; + // TODO source_provider should be included in the operation too + let source = VcOperation::new(source_provider.get_source()); + let resolved_source = + source.connect().resolve_strongly_consistent().await?; handle_issues( source, issue_reporter, diff --git a/turbopack/crates/turbopack-dev-server/src/source/issue_context.rs b/turbopack/crates/turbopack-dev-server/src/source/issue_context.rs index e127955c19c73..5496099a1f149 100644 --- a/turbopack/crates/turbopack-dev-server/src/source/issue_context.rs +++ b/turbopack/crates/turbopack-dev-server/src/source/issue_context.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use turbo_tasks::{RcStr, Value, Vc}; +use turbo_tasks::{RcStr, Value, Vc, VcOperation}; use turbo_tasks_fs::FileSystemPath; use turbopack_core::{ introspect::{Introspectable, IntrospectableChildren}, @@ -51,11 +51,11 @@ impl ContentSource for IssueFilePathContentSource { #[turbo_tasks::function] async fn get_routes(self: Vc) -> Result> { let this = self.await?; - let routes = this - .source - .get_routes() + // TODO this.source should be included in the operation too + let routes = VcOperation::new(this.source.get_routes()) .issue_file_path(this.file_path, &*this.description) - .await?; + .await? + .connect(); Ok(routes.map_routes(Vc::upcast( IssueContextContentSourceMapper { source: self }.cell(), ))) @@ -91,6 +91,7 @@ impl MapGetContentSourceContent for IssueContextContentSourceMapper { #[turbo_tasks::value] struct IssueContextGetContentSourceContent { + // TODO this should be a VcOperation get_content: Vc>, source: Vc, } @@ -100,11 +101,11 @@ impl GetContentSourceContent for IssueContextGetContentSourceContent { #[turbo_tasks::function] async fn vary(&self) -> Result> { let source = self.source.await?; - let result = self - .get_content - .vary() + // TODO the operation should cover the get_content operation too + let result = VcOperation::new(self.get_content.vary()) .issue_file_path(source.file_path, &*source.description) - .await?; + .await? + .connect(); Ok(result) } @@ -115,11 +116,11 @@ impl GetContentSourceContent for IssueContextGetContentSourceContent { data: Value, ) -> Result> { let source = self.source.await?; - let result = self - .get_content - .get(path, data) + // TODO the operation should cover the get_content operation too + let result = VcOperation::new(self.get_content.get(path, data)) .issue_file_path(source.file_path, &*source.description) - .await?; + .await? + .connect(); Ok(result) } } diff --git a/turbopack/crates/turbopack-dev-server/src/source/mod.rs b/turbopack/crates/turbopack-dev-server/src/source/mod.rs index 0518db659f071..ee4357ce661e5 100644 --- a/turbopack/crates/turbopack-dev-server/src/source/mod.rs +++ b/turbopack/crates/turbopack-dev-server/src/source/mod.rs @@ -19,6 +19,7 @@ use futures::{stream::Stream as StreamTrait, TryStreamExt}; use serde::{Deserialize, Serialize}; use turbo_tasks::{ trace::TraceRawVcs, util::SharedError, Completion, RcStr, Upcast, Value, ValueDefault, Vc, + VcOperation, }; use turbo_tasks_bytes::{Bytes, Stream, StreamRead}; use turbo_tasks_fs::FileSystemPath; @@ -89,7 +90,7 @@ pub struct StaticContent { pub enum ContentSourceContent { NotFound, Static(Vc), - HttpProxy(Vc), + HttpProxy(VcOperation), Rewrite(Vc), /// Continue with the next route Next, diff --git a/turbopack/crates/turbopack-dev-server/src/source/resolve.rs b/turbopack/crates/turbopack-dev-server/src/source/resolve.rs index 8b56aa7c6c4e0..efbe177dcee97 100644 --- a/turbopack/crates/turbopack-dev-server/src/source/resolve.rs +++ b/turbopack/crates/turbopack-dev-server/src/source/resolve.rs @@ -8,7 +8,7 @@ use hyper::{ header::{HeaderName as HyperHeaderName, HeaderValue as HyperHeaderValue}, Uri, }; -use turbo_tasks::{RcStr, TransientInstance, Value, Vc}; +use turbo_tasks::{RcStr, TransientInstance, Value, Vc, VcOperation}; use super::{ headers::{HeaderValue, Headers}, @@ -25,7 +25,7 @@ use super::{ pub enum ResolveSourceRequestResult { NotFound, Static(Vc, Vc), - HttpProxy(Vc), + HttpProxy(VcOperation), } /// Resolves a [SourceRequest] within a [super::ContentSource], returning the diff --git a/turbopack/crates/turbopack-dev-server/src/update/stream.rs b/turbopack/crates/turbopack-dev-server/src/update/stream.rs index e62d2c88e5c0b..0f5a0678141bf 100644 --- a/turbopack/crates/turbopack-dev-server/src/update/stream.rs +++ b/turbopack/crates/turbopack-dev-server/src/update/stream.rs @@ -5,7 +5,7 @@ use futures::prelude::*; use tokio::sync::mpsc::Sender; use tokio_stream::wrappers::ReceiverStream; use tracing::Instrument; -use turbo_tasks::{IntoTraitRef, RcStr, ReadRef, TransientInstance, Vc}; +use turbo_tasks::{IntoTraitRef, RcStr, ReadRef, TransientInstance, Vc, VcOperation}; use turbo_tasks_fs::{FileSystem, FileSystemPath}; use turbopack_core::{ error::PrettyPrintError, @@ -24,7 +24,7 @@ use crate::source::{resolve::ResolveSourceRequestResult, ProxyResult}; type GetContentFn = Box Vc + Send + Sync>; -async fn peek_issues(source: Vc) -> Result>> { +async fn peek_issues(source: VcOperation) -> Result>> { let captured = source.peek_issues_with_path().await?; captured.get_plain_issues().await @@ -48,7 +48,7 @@ async fn get_update_stream_item( ) -> Result> { let content = get_content(); let _ = content.resolve_strongly_consistent().await?; - let mut plain_issues = peek_issues(content).await?; + let mut plain_issues = peek_issues(VcOperation::new(content)).await?; let content_value = match content.await { Ok(content) => content, @@ -91,7 +91,14 @@ async fn get_update_stream_item( let from = from.get(); let update = resolved_content.update(from); - extend_issues(&mut plain_issues, peek_issues(update).await?); + extend_issues( + &mut plain_issues, + peek_issues( + // TODO resolved_content operation should be part of the operation too + VcOperation::new(update), + ) + .await?, + ); let update = update.await?; @@ -102,7 +109,8 @@ async fn get_update_stream_item( .cell()) } ResolveSourceRequestResult::HttpProxy(proxy_result) => { - let proxy_result_value = proxy_result.await?; + let proxy_result_vc = proxy_result.connect(); + let proxy_result_value = proxy_result_vc.await?; if proxy_result_value.status == 404 { return Ok(UpdateStreamItem::NotFound.cell()); @@ -123,7 +131,7 @@ async fn get_update_stream_item( Ok(UpdateStreamItem::Found { update: Update::Total(TotalUpdate { - to: Vc::upcast::>(proxy_result) + to: Vc::upcast::>(proxy_result_vc) .into_trait_ref() .await?, }) @@ -194,7 +202,9 @@ impl UpdateStream { ResolveSourceRequestResult::Static(static_content, _) => { static_content.await?.content.version() } - ResolveSourceRequestResult::HttpProxy(proxy_result) => Vc::upcast(proxy_result), + ResolveSourceRequestResult::HttpProxy(proxy_result) => { + Vc::upcast(proxy_result.connect()) + } _ => Vc::upcast(NotFoundVersion::new()), }; let version_state = VersionState::new(version.into_trait_ref().await?).await?; diff --git a/turbopack/crates/turbopack-mdx/src/lib.rs b/turbopack/crates/turbopack-mdx/src/lib.rs index d7cf78fff4aee..32c9422a16cb7 100644 --- a/turbopack/crates/turbopack-mdx/src/lib.rs +++ b/turbopack/crates/turbopack-mdx/src/lib.rs @@ -3,7 +3,7 @@ use anyhow::Result; use mdxjs::{compile, MdxParseOptions, Options}; -use turbo_tasks::{RcStr, ValueDefault, Vc}; +use turbo_tasks::{RcStr, ValueDefault, Vc, VcOperation}; use turbo_tasks_fs::{rope::Rope, File, FileContent, FileSystemPath}; use turbopack_core::{ asset::{Asset, AssetContent}, @@ -122,10 +122,10 @@ impl Asset for MdxTransformedAsset { #[turbo_tasks::function] async fn content(self: Vc) -> Result> { let this = self.await?; - Ok(self - .process() + Ok(VcOperation::new(self.process()) .issue_file_path(this.source.ident().path(), "MDX processing") .await? + .connect() .await? .content) } diff --git a/turbopack/crates/turbopack-node/src/render/node_api_source.rs b/turbopack/crates/turbopack-node/src/render/node_api_source.rs index d81e0819c6a9f..075bbfa472e32 100644 --- a/turbopack/crates/turbopack-node/src/render/node_api_source.rs +++ b/turbopack/crates/turbopack-node/src/render/node_api_source.rs @@ -1,7 +1,7 @@ use anyhow::{anyhow, Result}; use indexmap::IndexSet; use serde_json::Value as JsonValue; -use turbo_tasks::{RcStr, Value, Vc}; +use turbo_tasks::{RcStr, Value, Vc, VcOperation}; use turbo_tasks_env::ProcessEnv; use turbo_tasks_fs::FileSystemPath; use turbopack_core::introspect::{ @@ -128,7 +128,7 @@ impl GetContentSourceContent for NodeApiContentSource { return Err(anyhow!("Missing request data")); }; let entry = self.entry.entry(data.clone()).await?; - Ok(ContentSourceContent::HttpProxy(render_proxy( + let render_proxy = render_proxy( self.cwd, self.env, self.server_root.join(path.clone()), @@ -151,8 +151,9 @@ impl GetContentSourceContent for NodeApiContentSource { .cell(), *body, self.debug, - )) - .cell()) + ); + // TODO entry should be included in the operation too + Ok(ContentSourceContent::HttpProxy(VcOperation::new(render_proxy)).cell()) } } diff --git a/turbopack/crates/turbopack-node/src/render/rendered_source.rs b/turbopack/crates/turbopack-node/src/render/rendered_source.rs index 7dc57cfa900a0..8b4e5c2d577cb 100644 --- a/turbopack/crates/turbopack-node/src/render/rendered_source.rs +++ b/turbopack/crates/turbopack-node/src/render/rendered_source.rs @@ -1,7 +1,7 @@ use anyhow::{anyhow, Result}; use indexmap::IndexSet; use serde_json::Value as JsonValue; -use turbo_tasks::{RcStr, Value, Vc}; +use turbo_tasks::{RcStr, Value, Vc, VcOperation}; use turbo_tasks_env::ProcessEnv; use turbo_tasks_fs::FileSystemPath; use turbopack_core::{ @@ -190,7 +190,8 @@ impl GetContentSourceContent for NodeRenderContentSource { return Err(anyhow!("Missing request data")); }; let entry = self.entry.entry(data.clone()).await?; - let result = render_static( + // TODO entry should be included in the operation too + let render_static = VcOperation::new(render_static( self.cwd, self.env, self.server_root.join(path.clone()), @@ -213,13 +214,14 @@ impl GetContentSourceContent for NodeRenderContentSource { } .cell(), self.debug, - ) - .issue_file_path( - entry.module.ident().path(), - format!("server-side rendering {}", pathname), - ) - .await?; - Ok(match *result.await? { + )); + let result = render_static + .issue_file_path( + entry.module.ident().path(), + format!("server-side rendering {}", pathname), + ) + .await?; + Ok(match *result.connect().await? { StaticResult::Content { content, status_code, @@ -231,20 +233,33 @@ impl GetContentSourceContent for NodeRenderContentSource { status, headers, ref body, - } => ContentSourceContent::HttpProxy( - ProxyResult { + } => { + let proxy_result = ProxyResult { status, headers: headers.await?.clone_value(), body: body.clone(), } - .cell(), - ) - .cell(), + .cell(); + ContentSourceContent::HttpProxy(VcOperation::new(proxy_result_operation( + render_static, + proxy_result, + ))) + .cell() + } StaticResult::Rewrite(rewrite) => ContentSourceContent::Rewrite(rewrite).cell(), }) } } +#[turbo_tasks::function] +fn proxy_result_operation( + render_static: VcOperation, + proxy_result: Vc, +) -> Vc { + let _ = render_static.connect(); + proxy_result +} + #[turbo_tasks::function] fn introspectable_type() -> Vc { Vc::cell("node render content source".into()) diff --git a/turbopack/crates/turbopack-node/src/transforms/postcss.rs b/turbopack/crates/turbopack-node/src/transforms/postcss.rs index d0a8004c9759f..f30cc2e6a9526 100644 --- a/turbopack/crates/turbopack-node/src/transforms/postcss.rs +++ b/turbopack/crates/turbopack-node/src/transforms/postcss.rs @@ -4,6 +4,7 @@ use indoc::formatdoc; use serde::{Deserialize, Serialize}; use turbo_tasks::{ trace::TraceRawVcs, Completion, Completions, RcStr, TaskInput, TryFlatJoinIterExt, Value, Vc, + VcOperation, }; use turbo_tasks_bytes::stream::SingleValue; use turbo_tasks_fs::{ @@ -152,10 +153,10 @@ impl Asset for PostCssTransformedAsset { #[turbo_tasks::function] async fn content(self: Vc) -> Result> { let this = self.await?; - Ok(self - .process() + Ok(VcOperation::new(self.process()) .issue_file_path(this.source.ident().path(), "PostCSS processing") .await? + .connect() .await? .content) } diff --git a/turbopack/crates/turbopack-tests/tests/execution.rs b/turbopack/crates/turbopack-tests/tests/execution.rs index ead0cf681d503..dcebb30cdfbe7 100644 --- a/turbopack/crates/turbopack-tests/tests/execution.rs +++ b/turbopack/crates/turbopack-tests/tests/execution.rs @@ -11,7 +11,7 @@ use indexmap::indexmap; use serde::{Deserialize, Serialize}; use turbo_tasks::{ debug::ValueDebugFormat, trace::TraceRawVcs, Completion, RcStr, TryJoinIterExt, TurboTasks, - Value, Vc, + Value, Vc, VcOperation, }; use turbo_tasks_bytes::stream::SingleValue; use turbo_tasks_env::CommandLineProcessEnv; @@ -170,7 +170,7 @@ async fn run(resource: PathBuf, snapshot_mode: IssueSnapshotMode) -> Result) -> Result> #[turbo_tasks::function] async fn snapshot_issues( prepared_test: Vc, - run_result: Vc, + run_operation: VcOperation, ) -> Result> { let PreparedTest { path, .. } = *prepared_test.await?; + let run_result = run_operation.connect(); let _ = run_result.resolve_strongly_consistent().await; - let captured_issues = run_result.peek_issues_with_path().await?; + let captured_issues = run_operation.peek_issues_with_path().await?; let plain_issues = captured_issues .iter_with_shortest_path() diff --git a/turbopack/crates/turbopack-tests/tests/snapshot.rs b/turbopack/crates/turbopack-tests/tests/snapshot.rs index fdab8e73f04bf..225dcc057578c 100644 --- a/turbopack/crates/turbopack-tests/tests/snapshot.rs +++ b/turbopack/crates/turbopack-tests/tests/snapshot.rs @@ -14,6 +14,7 @@ use serde::Deserialize; use serde_json::json; use turbo_tasks::{ RcStr, ReadConsistency, ReadRef, TryJoinIterExt, TurboTasks, Value, ValueToString, Vc, + VcOperation, }; use turbo_tasks_env::DotenvProcessEnv; use turbo_tasks_fs::{ @@ -158,8 +159,9 @@ async fn run(resource: PathBuf) -> Result<()> { let tt = TurboTasks::new(MemoryBackend::default()); let task = tt.spawn_once_task(async move { let out = run_test(resource.to_str().unwrap().into()); + let out_op = VcOperation::new(out); let _ = out.resolve_strongly_consistent().await?; - let captured_issues = out.peek_issues_with_path().await?; + let captured_issues = out_op.peek_issues_with_path().await?; let plain_issues = captured_issues .iter_with_shortest_path()