use std::{iter::once, sync::Arc};
use anyhow::Result;
use next_api::{
analyze::{AnalyzeDataOutputAsset, ModulesDataOutputAsset, combine_output_assets},
project::ProjectContainer,
route::EndpointGroupKey,
};
use turbo_tasks::{Effects, ReadRef, ResolvedVc, TryJoinIterExt, Vc};
use turbopack_core::{
diagnostics::PlainDiagnostic,
issue::PlainIssue,
output::{OutputAsset, OutputAssets},
};
use crate::next_api::utils::strongly_consistent_catch_collectables;
#[turbo_tasks::value(serialization = "skip")]
pub struct WriteAnalyzeResult {
pub issues: Arc<Vec<ReadRef<PlainIssue>>>,
pub diagnostics: Arc<Vec<ReadRef<PlainDiagnostic>>>,
pub effects: Arc<Effects>,
}
#[turbo_tasks::function(operation)]
pub async fn write_analyze_data_with_issues_operation(
project: ResolvedVc<ProjectContainer>,
app_dir_only: bool,
) -> Result<Vc<WriteAnalyzeResult>> {
let analyze_data_op = write_analyze_data_with_issues_operation_inner(project, app_dir_only);
let filter = project.project().issue_filter();
let (_analyze_data, issues, diagnostics, effects) =
strongly_consistent_catch_collectables(analyze_data_op, filter).await?;
Ok(WriteAnalyzeResult {
issues,
diagnostics,
effects,
}
.cell())
}
#[turbo_tasks::function(operation)]
async fn write_analyze_data_with_issues_operation_inner(
project: ResolvedVc<ProjectContainer>,
app_dir_only: bool,
) -> Result<()> {
let analyze_data_op = get_analyze_data_operation(project, app_dir_only);
project
.project()
.emit_all_output_assets(analyze_data_op)
.as_side_effect()
.await?;
Ok(())
}
#[turbo_tasks::function(operation)]
async fn get_analyze_data_operation(
container: ResolvedVc<ProjectContainer>,
app_dir_only: bool,
) -> Result<Vc<OutputAssets>> {
let project = container.project();
let project = project.with_next_config(project.next_config().with_analyze_config());
let analyze_output_root = project
.node_root()
.owned()
.await?
.join("diagnostics/analyze/data")?;
let whole_app_module_graphs = project.whole_app_module_graphs();
let analyze_output_root = &analyze_output_root;
let endpoint_groups = project.get_all_endpoint_groups(app_dir_only).await?;
// Collect output assets from _app and _document to merge into each route's
// analyze.data so their modules are visible in every route's treemap.
let mut combined_output_assets: Vec<ResolvedVc<Box<dyn OutputAsset>>> = vec![];
for (key, endpoint_group) in endpoint_groups.iter() {
if matches!(
key,
EndpointGroupKey::PagesApp | EndpointGroupKey::PagesDocument
) {
combined_output_assets.extend(endpoint_group.output_assets().await?.iter().copied());
}
}
let has_combined = !combined_output_assets.is_empty();
let combined_vc = Vc::cell(combined_output_assets);
let analyze_data = endpoint_groups
.iter()
.map(|(key, endpoint_group)| async move {
let output_assets = if has_combined
&& !matches!(
key,
EndpointGroupKey::PagesApp | EndpointGroupKey::PagesDocument
) {
// Combine route output assets with _app and _document output assets so
// the generated analyze.data already includes their modules.
combine_output_assets(endpoint_group.output_assets(), combined_vc)
} else {
endpoint_group.output_assets()
};
let analyze_data = AnalyzeDataOutputAsset::new(
analyze_output_root
.join(&key.to_string())?
.join("analyze.data")?,
output_assets,
)
.to_resolved()
.await?;
Ok(ResolvedVc::upcast(analyze_data))
})
.try_join()
.await?;
whole_app_module_graphs.as_side_effect().await?;
let modules_data = ResolvedVc::upcast(
ModulesDataOutputAsset::new(
analyze_output_root.join("modules.data")?,
Vc::cell(vec![whole_app_module_graphs.await?.full]),
)
.to_resolved()
.await?,
);
Ok(Vc::cell(
analyze_data
.iter()
.cloned()
.chain(once(modules_data))
.collect(),
))
}