turbopack_core/module_graph/
async_module_info.rs

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