Skip to main content

next_napi_bindings/next_api/
analyze.rs

1use std::{iter::once, sync::Arc};
2
3use anyhow::Result;
4use next_api::{
5    analyze::{
6        AnalyzeDataOutputAsset, ModulesDataOutputAsset, combine_output_assets, combine_traced_files,
7    },
8    project::ProjectContainer,
9    route::EndpointGroupKey,
10};
11use turbo_tasks::{Effects, ReadRef, ResolvedVc, TryJoinIterExt, Vc};
12use turbo_tasks_fs::FileSystemPath;
13use turbopack_core::{
14    issue::PlainIssue,
15    output::{OutputAsset, OutputAssets},
16};
17
18use crate::next_api::utils::strongly_consistent_catch_collectables;
19
20#[turbo_tasks::value(serialization = "skip")]
21pub struct WriteAnalyzeResult {
22    pub issues: Arc<Vec<ReadRef<PlainIssue>>>,
23    pub effects: Arc<Effects>,
24}
25
26#[turbo_tasks::function(operation, root)]
27pub async fn write_analyze_data_with_issues_operation(
28    project: ResolvedVc<ProjectContainer>,
29    app_dir_only: bool,
30) -> Result<Vc<WriteAnalyzeResult>> {
31    let analyze_data_op = write_analyze_data_with_issues_operation_inner(project, app_dir_only);
32    let filter = project.project().issue_filter().await?;
33
34    let (_analyze_data, issues, effects) =
35        strongly_consistent_catch_collectables(analyze_data_op, &filter).await?;
36
37    Ok(WriteAnalyzeResult { issues, effects }.cell())
38}
39
40#[turbo_tasks::function(operation, root)]
41async fn write_analyze_data_with_issues_operation_inner(
42    project: ResolvedVc<ProjectContainer>,
43    app_dir_only: bool,
44) -> Result<()> {
45    let analyze_data_op = get_analyze_data_operation(project, app_dir_only);
46
47    project
48        .project()
49        .emit_all_output_assets(analyze_data_op)
50        .as_side_effect()
51        .await?;
52
53    Ok(())
54}
55
56#[turbo_tasks::function(operation)]
57async fn get_analyze_data_operation(
58    container: ResolvedVc<ProjectContainer>,
59    app_dir_only: bool,
60) -> Result<Vc<OutputAssets>> {
61    let project = container.project();
62    let project = project.with_next_config(project.next_config().with_analyze_config());
63
64    let analyze_output_root = project
65        .node_root()
66        .owned()
67        .await?
68        .join("diagnostics/analyze/data")?;
69    let whole_app_module_graphs = project.whole_app_module_graphs();
70    let analyze_output_root = &analyze_output_root;
71    let endpoint_groups = project.get_all_endpoint_groups(app_dir_only).await?;
72
73    // Collect output assets from _app and _document to merge into each route's
74    // analyze.data so their modules are visible in every route's treemap.
75    let mut combined_output_assets: Vec<ResolvedVc<Box<dyn OutputAsset>>> = vec![];
76    let mut combined_traced_files: Vec<FileSystemPath> = vec![];
77    for (key, endpoint_group) in endpoint_groups.iter() {
78        if matches!(
79            key,
80            EndpointGroupKey::PagesApp | EndpointGroupKey::PagesDocument
81        ) {
82            combined_output_assets.extend(endpoint_group.output_assets().await?.iter().copied());
83            combined_traced_files.extend(endpoint_group.traced_files().await?.iter().cloned());
84        }
85    }
86
87    let has_combined = !combined_output_assets.is_empty();
88    let combined_assets_vc = Vc::cell(combined_output_assets);
89    let combined_traced_vc = Vc::cell(combined_traced_files);
90
91    let analyze_data = endpoint_groups
92        .iter()
93        .map(async |(key, endpoint_group)| {
94            let output_assets = if has_combined
95                && !matches!(
96                    key,
97                    EndpointGroupKey::PagesApp | EndpointGroupKey::PagesDocument
98                ) {
99                // Combine route output assets with _app and _document output assets so
100                // the generated analyze.data already includes their modules.
101                combine_output_assets(endpoint_group.output_assets(), combined_assets_vc)
102            } else {
103                endpoint_group.output_assets()
104            };
105            let traced_files = if has_combined
106                && !matches!(
107                    key,
108                    EndpointGroupKey::PagesApp | EndpointGroupKey::PagesDocument
109                ) {
110                // Combine route traced files with _app and _document traced modules so
111                // the generated analyze.data already includes their modules.
112                combine_traced_files(endpoint_group.traced_files(), combined_traced_vc)
113            } else {
114                endpoint_group.traced_files()
115            };
116            let analyze_data = AnalyzeDataOutputAsset::new(
117                analyze_output_root
118                    .join(&key.to_string())?
119                    .join("analyze.data")?,
120                output_assets,
121                traced_files,
122            )
123            .to_resolved()
124            .await?;
125
126            Ok(ResolvedVc::upcast(analyze_data))
127        })
128        .try_join()
129        .await?;
130
131    let modules_data = ResolvedVc::upcast(
132        ModulesDataOutputAsset::new(
133            analyze_output_root.join("modules.data")?,
134            *whole_app_module_graphs.await?.full,
135        )
136        .to_resolved()
137        .await?,
138    );
139
140    Ok(Vc::cell(
141        analyze_data
142            .iter()
143            .cloned()
144            .chain(once(modules_data))
145            .collect(),
146    ))
147}