turbopack_core/module_graph/
export_usage.rs

1//! Intermediate tree shaking that uses global information but not good as the full tree shaking.
2
3use anyhow::Result;
4use auto_hash_map::AutoSet;
5use rustc_hash::FxHashMap;
6use turbo_rcstr::RcStr;
7use turbo_tasks::{ResolvedVc, Vc};
8
9use crate::{module::Module, module_graph::ModuleGraph, resolve::ExportUsage};
10
11#[turbo_tasks::function(operation)]
12pub async fn compute_export_usage_info(
13    graph: ResolvedVc<ModuleGraph>,
14) -> Result<Vc<ExportUsageInfo>> {
15    let mut used_exports = FxHashMap::<_, ModuleExportUsageInfo>::default();
16
17    graph
18        .await?
19        .traverse_all_edges_unordered(|(_, ref_data), target| {
20            if let Some(target_module) = ResolvedVc::try_downcast::<Box<dyn Module>>(target.module)
21            {
22                let e = used_exports.entry(target_module).or_default();
23
24                e.add(&ref_data.export);
25            }
26
27            Ok(())
28        })
29        .await?;
30
31    Ok(ExportUsageInfo { used_exports }.cell())
32}
33
34#[turbo_tasks::value(transparent)]
35pub struct OptionExportUsageInfo(Option<ResolvedVc<ExportUsageInfo>>);
36
37#[turbo_tasks::value]
38pub struct ExportUsageInfo {
39    used_exports: FxHashMap<ResolvedVc<Box<dyn Module>>, ModuleExportUsageInfo>,
40}
41
42impl ExportUsageInfo {
43    pub fn used_exports(&self, module: ResolvedVc<Box<dyn Module>>) -> Vc<ModuleExportUsageInfo> {
44        if let Some(exports) = self.used_exports.get(&module) {
45            exports.clone().cell()
46        } else {
47            // We exclude template files from tree shaking because they are entrypoints to the
48            // module graph.
49            ModuleExportUsageInfo::all()
50        }
51    }
52}
53
54#[turbo_tasks::value]
55#[derive(Default, Clone)]
56pub enum ModuleExportUsageInfo {
57    /// Only the side effects are needed, no exports is used.
58    #[default]
59    Evaluation,
60    Exports(AutoSet<RcStr>),
61    All,
62}
63
64#[turbo_tasks::value_impl]
65impl ModuleExportUsageInfo {
66    #[turbo_tasks::function]
67    pub fn all() -> Vc<Self> {
68        ModuleExportUsageInfo::All.cell()
69    }
70}
71
72impl ModuleExportUsageInfo {
73    fn add(&mut self, usage: &ExportUsage) {
74        match (&mut *self, usage) {
75            (Self::All, _) => {}
76            (_, ExportUsage::All) => {
77                *self = Self::All;
78            }
79            (Self::Evaluation, ExportUsage::Named(name)) => {
80                // Promote evaluation to something more specific
81                *self = Self::Exports(AutoSet::from_iter([name.clone()]));
82            }
83
84            (Self::Exports(l), ExportUsage::Named(r)) => {
85                // Merge exports
86                l.insert(r.clone());
87            }
88
89            (_, ExportUsage::Evaluation) => {
90                // Ignore evaluation
91            }
92        }
93    }
94
95    pub fn is_export_used(&self, export: &RcStr) -> bool {
96        match self {
97            Self::All => true,
98            Self::Evaluation => false,
99            Self::Exports(exports) => exports.contains(export),
100        }
101    }
102}