turbopack_ecmascript/side_effect_optimization/facade/
module.rs1use std::collections::BTreeMap;
2
3use anyhow::{Result, bail};
4use turbo_tasks::{ResolvedVc, Vc};
5use turbopack_core::{
6 chunk::{
7 AsyncModuleInfo, ChunkableModule, ChunkingContext, EvaluatableAsset, MergeableModule,
8 MergeableModules, MergeableModulesExposed,
9 },
10 ident::AssetIdent,
11 module::{Module, ModuleSideEffects},
12 module_graph::ModuleGraph,
13 reference::ModuleReferences,
14 resolve::{ExportUsage, ModulePart},
15};
16
17use crate::{
18 AnalyzeEcmascriptModuleResult, EcmascriptAnalyzable, EcmascriptAnalyzableExt,
19 EcmascriptModuleContent, EcmascriptModuleContentOptions, EcmascriptOptions,
20 MergedEcmascriptModule, SpecifiedModuleType,
21 chunk::{
22 EcmascriptChunkItemContent, EcmascriptChunkPlaceable, EcmascriptExports,
23 ecmascript_chunk_item,
24 },
25 code_gen::CodeGens,
26 export::Liveness,
27 references::{
28 async_module::{AsyncModule, OptionAsyncModule},
29 esm::{EsmExport, EsmExports, base::EsmAssetReferences},
30 },
31 side_effect_optimization::reference::EcmascriptModulePartReference,
32};
33
34#[turbo_tasks::value]
40pub struct EcmascriptModuleFacadeModule {
41 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
42}
43
44#[turbo_tasks::value_impl]
45impl EcmascriptModuleFacadeModule {
46 #[turbo_tasks::function]
47 pub fn new(module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>) -> Vc<Self> {
48 EcmascriptModuleFacadeModule { module }.cell()
49 }
50
51 #[turbo_tasks::function]
52 pub async fn async_module(&self) -> Result<Vc<AsyncModule>> {
53 let (import_externals, has_top_level_await) =
54 if let Some(async_module) = *self.module.get_async_module().await? {
55 (
56 async_module.await?.import_externals,
57 async_module.await?.has_top_level_await,
58 )
59 } else {
60 (false, false)
61 };
62 Ok(AsyncModule {
63 has_top_level_await,
64 import_externals,
65 }
66 .cell())
67 }
68}
69
70impl EcmascriptModuleFacadeModule {
71 pub async fn specific_references(
72 &self,
73 ) -> Result<(
74 Vec<ResolvedVc<EcmascriptModulePartReference>>,
75 ResolvedVc<EsmAssetReferences>,
76 )> {
77 let Some(module) = ResolvedVc::try_sidecast::<Box<dyn EcmascriptAnalyzable>>(self.module)
78 else {
79 bail!(
80 "Expected EcmascriptModuleAsset for a EcmascriptModuleFacadeModule with \
81 ModulePart::Facade"
82 );
83 };
84 let result = module.analyze().await?;
85 Ok((
86 vec![
87 EcmascriptModulePartReference::new_part(
89 *self.module,
90 ModulePart::locals(),
91 ExportUsage::all(),
92 )
93 .to_resolved()
94 .await?,
95 ],
96 result.esm_reexport_references,
97 ))
98 }
99}
100
101#[turbo_tasks::value_impl]
102impl Module for EcmascriptModuleFacadeModule {
103 #[turbo_tasks::function]
104 fn ident(&self) -> Vc<AssetIdent> {
105 self.module.ident().with_part(ModulePart::Facade)
106 }
107
108 #[turbo_tasks::function]
109 fn source(&self) -> Vc<turbopack_core::source::OptionSource> {
110 Vc::cell(None)
111 }
112
113 #[turbo_tasks::function]
114 async fn references(&self) -> Result<Vc<ModuleReferences>> {
115 let (part_references, esm_references) = self.specific_references().await?;
116 let references = part_references
117 .iter()
118 .map(|r| ResolvedVc::upcast(*r))
119 .chain(esm_references.await?.iter().map(|r| ResolvedVc::upcast(*r)))
120 .collect();
121 Ok(Vc::cell(references))
122 }
123
124 #[turbo_tasks::function]
125 async fn is_self_async(self: Vc<Self>) -> Result<Vc<bool>> {
126 let async_module = self.async_module();
127 let references = self.references();
128 let is_self_async = async_module
129 .resolve()
130 .await?
131 .is_self_async(references.resolve().await?)
132 .resolve()
133 .await?;
134 Ok(is_self_async)
135 }
136
137 #[turbo_tasks::function]
138 fn side_effects(&self) -> Vc<ModuleSideEffects> {
139 ModuleSideEffects::ModuleEvaluationIsSideEffectFree.cell()
140 }
141}
142
143#[turbo_tasks::value_impl]
144impl EcmascriptAnalyzable for EcmascriptModuleFacadeModule {
145 #[turbo_tasks::function]
146 fn analyze(&self) -> Result<Vc<AnalyzeEcmascriptModuleResult>> {
147 bail!("EcmascriptModuleFacadeModule::analyze shouldn't be called");
148 }
149
150 #[turbo_tasks::function]
151 fn module_content_without_analysis(
152 &self,
153 _generate_source_map: bool,
154 ) -> Result<Vc<EcmascriptModuleContent>> {
155 bail!("EcmascriptModuleFacadeModule::module_content_without_analysis shouldn't be called");
156 }
157
158 #[turbo_tasks::function]
159 async fn module_content_options(
160 self: ResolvedVc<Self>,
161 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
162 async_module_info: Option<ResolvedVc<AsyncModuleInfo>>,
163 ) -> Result<Vc<EcmascriptModuleContentOptions>> {
164 let (part_references, esm_references) = self.await?.specific_references().await?;
165
166 Ok(EcmascriptModuleContentOptions {
167 parsed: None,
168 module: ResolvedVc::upcast(self),
169 specified_module_type: SpecifiedModuleType::EcmaScript,
170 chunking_context,
171 references: self.references().to_resolved().await?,
172 part_references,
173 esm_references,
174 code_generation: CodeGens::empty().to_resolved().await?,
175 async_module: ResolvedVc::cell(Some(self.async_module().to_resolved().await?)),
176 generate_source_map: false,
180 original_source_map: None,
181 exports: self.get_exports().to_resolved().await?,
182 async_module_info,
183 }
184 .cell())
185 }
186}
187
188#[turbo_tasks::value_impl]
189impl EcmascriptChunkPlaceable for EcmascriptModuleFacadeModule {
190 #[turbo_tasks::function]
191 async fn get_exports(&self) -> Result<Vc<EcmascriptExports>> {
192 let mut exports = BTreeMap::new();
193 let mut star_exports = Vec::new();
194
195 let EcmascriptExports::EsmExports(esm_exports) = &*self.module.get_exports().await? else {
196 bail!("EcmascriptModuleFacadeModule must only be used on modules with EsmExports");
197 };
198 let esm_exports = esm_exports.await?;
199 for (name, export) in &esm_exports.exports {
200 let name = name.clone();
201 match export {
202 EsmExport::LocalBinding(_, liveness) => {
203 exports.insert(
204 name.clone(),
205 EsmExport::ImportedBinding(
206 ResolvedVc::upcast(
207 EcmascriptModulePartReference::new_part(
208 *self.module,
209 ModulePart::locals(),
210 ExportUsage::named(name.clone()),
211 )
212 .to_resolved()
213 .await?,
214 ),
215 name,
216 *liveness == Liveness::Mutable,
217 ),
218 );
219 }
220 EsmExport::ImportedNamespace(reference) => {
221 exports.insert(name, EsmExport::ImportedNamespace(*reference));
222 }
223 EsmExport::ImportedBinding(reference, imported_name, mutable) => {
224 exports.insert(
225 name,
226 EsmExport::ImportedBinding(*reference, imported_name.clone(), *mutable),
227 );
228 }
229 EsmExport::Error => {
230 exports.insert(name, EsmExport::Error);
231 }
232 }
233 }
234 star_exports.extend(esm_exports.star_exports.iter().copied());
235
236 let exports = EsmExports {
237 exports,
238 star_exports,
239 }
240 .resolved_cell();
241 Ok(EcmascriptExports::EsmExports(exports).cell())
242 }
243
244 #[turbo_tasks::function]
245 async fn get_async_module(self: Vc<Self>) -> Result<Vc<OptionAsyncModule>> {
246 Ok(Vc::cell(Some(self.async_module().to_resolved().await?)))
247 }
248
249 #[turbo_tasks::function]
250 async fn chunk_item_content(
251 self: Vc<Self>,
252 chunking_context: Vc<Box<dyn ChunkingContext>>,
253 _module_graph: Vc<ModuleGraph>,
254 async_module_info: Option<Vc<AsyncModuleInfo>>,
255 _estimated: bool,
256 ) -> Result<Vc<EcmascriptChunkItemContent>> {
257 let content = self.module_content(chunking_context, async_module_info);
258
259 let async_module_options = self.get_async_module().module_options(async_module_info);
260
261 Ok(EcmascriptChunkItemContent::new(
262 content,
263 chunking_context,
264 async_module_options,
265 ))
266 }
267}
268
269#[turbo_tasks::value_impl]
270impl ChunkableModule for EcmascriptModuleFacadeModule {
271 #[turbo_tasks::function]
272 fn as_chunk_item(
273 self: ResolvedVc<Self>,
274 module_graph: ResolvedVc<ModuleGraph>,
275 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
276 ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
277 ecmascript_chunk_item(ResolvedVc::upcast(self), module_graph, chunking_context)
278 }
279}
280
281#[turbo_tasks::value_impl]
282impl EvaluatableAsset for EcmascriptModuleFacadeModule {}
283
284#[turbo_tasks::value_impl]
285impl MergeableModule for EcmascriptModuleFacadeModule {
286 #[turbo_tasks::function]
287 async fn merge(
288 self: Vc<Self>,
289 modules: Vc<MergeableModulesExposed>,
290 entry_points: Vc<MergeableModules>,
291 ) -> Result<Vc<Box<dyn ChunkableModule>>> {
292 Ok(Vc::upcast(
293 *MergedEcmascriptModule::new(
294 modules,
295 entry_points,
296 EcmascriptOptions::default().resolved_cell(),
297 )
298 .await?,
299 ))
300 }
301}