turbopack_core/module_graph/
async_module_info.rs

1use anyhow::{Context, Result};
2use rustc_hash::FxHashSet;
3use turbo_tasks::{OperationVc, ResolvedVc, TryFlatJoinIterExt, Vc};
4
5use crate::{
6    module::Module,
7    module_graph::{GraphTraversalAction, ModuleGraph, ModuleGraphLayer},
8};
9
10#[turbo_tasks::value(transparent)]
11pub struct ModulesSet(FxHashSet<ResolvedVc<Box<dyn Module>>>);
12
13/// This lists all the modules that are async (self or transitively because they reference another
14/// module in this list).
15#[turbo_tasks::value(transparent, cell = "keyed")]
16pub struct AsyncModulesInfo(FxHashSet<ResolvedVc<Box<dyn Module>>>);
17
18impl AsyncModulesInfo {
19    pub async fn is_async(self: Vc<Self>, module: ResolvedVc<Box<dyn Module>>) -> Result<bool> {
20        self.contains_key(&module).await
21    }
22}
23
24#[turbo_tasks::function(operation)]
25pub async fn compute_async_module_info(
26    graphs: ResolvedVc<ModuleGraph>,
27) -> Result<Vc<AsyncModulesInfo>> {
28    // Layout segment optimization, we can individually compute the async modules for each graph.
29    let mut result = None;
30    for graph in graphs.iter_graphs().await? {
31        result = Some(compute_async_module_info_single(*graph, result));
32    }
33    Ok(result
34        .context("There must be at least one single graph in the module graph")?
35        .connect())
36}
37
38#[turbo_tasks::function(operation)]
39async fn compute_async_module_info_single(
40    graph: OperationVc<ModuleGraphLayer>,
41    parent_async_modules: Option<OperationVc<AsyncModulesInfo>>,
42) -> Result<Vc<AsyncModulesInfo>> {
43    let parent_async_modules = if let Some(parent_async_modules) = parent_async_modules {
44        Some(parent_async_modules.read_strongly_consistent().await?)
45    } else {
46        None
47    };
48    let graph = graph.read_strongly_consistent().await?;
49    let self_async_modules = graph
50        .enumerate_nodes()
51        .map(async |(_, node)| {
52            Ok(match node {
53                super::SingleModuleGraphNode::Module(node) => {
54                    node.is_self_async().await?.then_some(*node)
55                }
56                super::SingleModuleGraphNode::VisitedModule { idx: _, module } => {
57                    // If a module is async in the parent then we need to mark reverse dependencies
58                    // async in this graph as well.
59                    parent_async_modules
60                        .as_ref()
61                        .is_some_and(|set| set.contains(module))
62                        .then_some(*module)
63                }
64            })
65        })
66        .try_flat_join()
67        .await?;
68
69    // To determine which modules are async, we need to propagate the self-async flag to all
70    // importers, which is done using a reverse traversal over the graph
71    // Because we walk edges in the reverse direction we can trivially handle things like cycles
72    // without actually computing them.
73    let mut async_modules = FxHashSet::default();
74    async_modules.extend(self_async_modules.iter());
75
76    graph.traverse_edges_reverse_dfs(
77        self_async_modules,
78        &mut (),
79        // child is the previously visited module which must be async
80        // parent is a new module that depends on it
81        |child, parent, _state| {
82            Ok(if let Some((_, edge)) = child {
83                if edge.chunking_type.is_inherit_async() {
84                    async_modules.insert(parent);
85                    GraphTraversalAction::Continue
86                } else {
87                    // Wrong edge type to follow
88                    GraphTraversalAction::Exclude
89                }
90            } else {
91                // These are our entry points, just continue
92                GraphTraversalAction::Continue
93            })
94        },
95        |_, _, _| Ok(()),
96    )?;
97
98    // Accumulate the parent modules at the end. Not all parent async modules were in this graph
99    async_modules.extend(parent_async_modules.into_iter().flatten());
100
101    Ok(Vc::cell(async_modules))
102}