turbopack_core/module_graph/
export_usage.rs1use anyhow::Result;
4use auto_hash_map::AutoSet;
5use rustc_hash::{FxHashMap, FxHashSet};
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 let graph = graph.read_graphs().await?;
17 graph.traverse_all_edges_unordered(|(_, ref_data), target| {
18 let e = used_exports.entry(target.module).or_default();
19
20 e.add(&ref_data.export);
21
22 Ok(())
23 })?;
24
25 let mut circuit_breakers = FxHashSet::default();
29 graph.traverse_cycles(
30 |e| e.chunking_type.is_parallel(),
31 |cycle| {
32 circuit_breakers.extend(cycle.iter().map(|n| n.module));
44 },
45 )?;
46
47 Ok(ExportUsageInfo {
48 used_exports,
49 circuit_breakers,
50 }
51 .cell())
52}
53
54#[turbo_tasks::value(transparent)]
55pub struct OptionExportUsageInfo(Option<ResolvedVc<ExportUsageInfo>>);
56
57#[turbo_tasks::value]
58pub struct ExportUsageInfo {
59 used_exports: FxHashMap<ResolvedVc<Box<dyn Module>>, ModuleExportUsageInfo>,
60 circuit_breakers: FxHashSet<ResolvedVc<Box<dyn Module>>>,
61}
62
63#[turbo_tasks::value(shared)]
64pub struct ModuleExportUsage {
65 pub export_usage: ResolvedVc<ModuleExportUsageInfo>,
66 pub is_circuit_breaker: bool,
68}
69
70#[turbo_tasks::value_impl]
71impl ModuleExportUsage {
72 #[turbo_tasks::function]
73 pub async fn all() -> Result<Vc<Self>> {
74 Ok(Self {
75 export_usage: ModuleExportUsageInfo::all().to_resolved().await?,
76 is_circuit_breaker: true,
77 }
78 .cell())
79 }
80}
81
82impl ExportUsageInfo {
83 pub async fn used_exports(
84 &self,
85 module: ResolvedVc<Box<dyn Module>>,
86 ) -> Result<Vc<ModuleExportUsage>> {
87 let is_circuit_breaker = self.circuit_breakers.contains(&module);
88 let export_usage = if let Some(exports) = self.used_exports.get(&module) {
89 exports.clone().resolved_cell()
90 } else {
91 ModuleExportUsageInfo::all().to_resolved().await?
94 };
95 Ok(ModuleExportUsage {
96 export_usage,
97 is_circuit_breaker,
98 }
99 .cell())
100 }
101}
102
103#[turbo_tasks::value]
104#[derive(Default, Clone)]
105pub enum ModuleExportUsageInfo {
106 #[default]
108 Evaluation,
109 Exports(AutoSet<RcStr>),
110 All,
111}
112
113#[turbo_tasks::value_impl]
114impl ModuleExportUsageInfo {
115 #[turbo_tasks::function]
116 pub fn all() -> Vc<Self> {
117 ModuleExportUsageInfo::All.cell()
118 }
119}
120
121impl ModuleExportUsageInfo {
122 fn add(&mut self, usage: &ExportUsage) {
123 match (&mut *self, usage) {
124 (Self::All, _) => {}
125 (_, ExportUsage::All) => {
126 *self = Self::All;
127 }
128 (Self::Evaluation, ExportUsage::Named(name)) => {
129 *self = Self::Exports(AutoSet::from_iter([name.clone()]));
131 }
132
133 (Self::Exports(l), ExportUsage::Named(r)) => {
134 l.insert(r.clone());
136 }
137
138 (_, ExportUsage::Evaluation) => {
139 }
141 }
142 }
143
144 pub fn is_export_used(&self, export: &RcStr) -> bool {
145 match self {
146 Self::All => true,
147 Self::Evaluation => false,
148 Self::Exports(exports) => exports.contains(export),
149 }
150 }
151}