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