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 super::chunk_item::EcmascriptModuleFacadeChunkItem;
18use crate::{
19 AnalyzeEcmascriptModuleResult, EcmascriptAnalyzable, EcmascriptModuleContent,
20 EcmascriptModuleContentOptions, EcmascriptOptions, MergedEcmascriptModule, SpecifiedModuleType,
21 chunk::{EcmascriptChunkPlaceable, EcmascriptExports},
22 code_gen::CodeGens,
23 export::Liveness,
24 references::{
25 async_module::{AsyncModule, OptionAsyncModule},
26 esm::{EsmExport, EsmExports, base::EsmAssetReferences},
27 },
28 side_effect_optimization::reference::EcmascriptModulePartReference,
29};
30
31#[turbo_tasks::value]
35pub struct EcmascriptModuleFacadeModule {
36 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
37 part: ModulePart,
41}
42
43#[turbo_tasks::value_impl]
44impl EcmascriptModuleFacadeModule {
45 #[turbo_tasks::function]
46 pub fn new(
47 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
48 part: ModulePart,
49 ) -> Vc<Self> {
50 debug_assert!(
51 matches!(
52 part,
53 ModulePart::Facade
54 | ModulePart::RenamedExport { .. }
55 | ModulePart::RenamedNamespace { .. }
56 ),
57 "{part:?} is unexpected for EcmascriptModuleFacadeModule"
58 );
59 EcmascriptModuleFacadeModule { module, part }.cell()
60 }
61
62 #[turbo_tasks::function]
63 pub async fn async_module(&self) -> Result<Vc<AsyncModule>> {
64 let (import_externals, has_top_level_await) =
65 if let Some(async_module) = *self.module.get_async_module().await? {
66 (
67 async_module.await?.import_externals,
68 async_module.await?.has_top_level_await,
69 )
70 } else {
71 (false, false)
72 };
73 Ok(AsyncModule {
74 has_top_level_await,
75 import_externals,
76 }
77 .cell())
78 }
79}
80
81impl EcmascriptModuleFacadeModule {
82 pub async fn specific_references(
83 &self,
84 ) -> Result<(
85 Vec<ResolvedVc<EcmascriptModulePartReference>>,
86 ResolvedVc<EsmAssetReferences>,
87 )> {
88 Ok(match &self.part {
89 ModulePart::Facade => {
90 let Some(module) =
91 ResolvedVc::try_sidecast::<Box<dyn EcmascriptAnalyzable>>(self.module)
92 else {
93 bail!(
94 "Expected EcmascriptModuleAsset for a EcmascriptModuleFacadeModule with \
95 ModulePart::Facade"
96 );
97 };
98 let result = module.analyze().await?;
99 (
100 vec![
101 EcmascriptModulePartReference::new_part(
103 *self.module,
104 ModulePart::locals(),
105 ExportUsage::all(),
106 )
107 .to_resolved()
108 .await?,
109 ],
110 result.esm_reexport_references,
111 )
112 }
113 ModulePart::RenamedNamespace { .. } => (
114 vec![
115 EcmascriptModulePartReference::new_normal(
116 *self.module,
117 self.part.clone(),
118 ExportUsage::all(),
119 )
120 .to_resolved()
121 .await?,
122 ],
123 EsmAssetReferences::empty().to_resolved().await?,
124 ),
125 ModulePart::RenamedExport {
126 original_export, ..
127 } => (
128 vec![
129 EcmascriptModulePartReference::new_normal(
130 *self.module,
131 self.part.clone(),
132 ExportUsage::named(original_export.clone()),
133 )
134 .to_resolved()
135 .await?,
136 ],
137 EsmAssetReferences::empty().to_resolved().await?,
138 ),
139 _ => {
140 bail!("Unexpected ModulePart for EcmascriptModuleFacadeModule");
141 }
142 })
143 }
144}
145
146#[turbo_tasks::value_impl]
147impl Module for EcmascriptModuleFacadeModule {
148 #[turbo_tasks::function]
149 fn ident(&self) -> Vc<AssetIdent> {
150 self.module.ident().with_part(self.part.clone())
151 }
152
153 #[turbo_tasks::function]
154 fn source(&self) -> Vc<turbopack_core::source::OptionSource> {
155 Vc::cell(None)
156 }
157
158 #[turbo_tasks::function]
159 async fn references(&self) -> Result<Vc<ModuleReferences>> {
160 let (part_references, esm_references) = self.specific_references().await?;
161 let references = part_references
162 .iter()
163 .map(|r| ResolvedVc::upcast(*r))
164 .chain(esm_references.await?.iter().map(|r| ResolvedVc::upcast(*r)))
165 .collect();
166 Ok(Vc::cell(references))
167 }
168
169 #[turbo_tasks::function]
170 async fn is_self_async(self: Vc<Self>) -> Result<Vc<bool>> {
171 let async_module = self.async_module();
172 let references = self.references();
173 let is_self_async = async_module
174 .resolve()
175 .await?
176 .is_self_async(references.resolve().await?)
177 .resolve()
178 .await?;
179 Ok(is_self_async)
180 }
181
182 #[turbo_tasks::function]
183 fn side_effects(&self) -> Result<Vc<ModuleSideEffects>> {
184 Ok(match self.part {
185 ModulePart::Facade => self.module.side_effects(),
186 ModulePart::RenamedExport { .. } | ModulePart::RenamedNamespace { .. } => {
187 ModuleSideEffects::SideEffectFree.cell()
188 }
189 _ => bail!("Unexpected ModulePart for EcmascriptModuleFacadeModule"),
190 })
191 }
192}
193
194#[turbo_tasks::value_impl]
195impl EcmascriptAnalyzable for EcmascriptModuleFacadeModule {
196 #[turbo_tasks::function]
197 fn analyze(&self) -> Result<Vc<AnalyzeEcmascriptModuleResult>> {
198 bail!("EcmascriptModuleFacadeModule::analyze shouldn't be called");
199 }
200
201 #[turbo_tasks::function]
202 fn module_content_without_analysis(
203 &self,
204 _generate_source_map: bool,
205 ) -> Result<Vc<EcmascriptModuleContent>> {
206 bail!("EcmascriptModuleFacadeModule::module_content_without_analysis shouldn't be called");
207 }
208
209 #[turbo_tasks::function]
210 async fn module_content_options(
211 self: ResolvedVc<Self>,
212 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
213 async_module_info: Option<ResolvedVc<AsyncModuleInfo>>,
214 ) -> Result<Vc<EcmascriptModuleContentOptions>> {
215 let (part_references, esm_references) = self.await?.specific_references().await?;
216
217 Ok(EcmascriptModuleContentOptions {
218 parsed: None,
219 module: ResolvedVc::upcast(self),
220 specified_module_type: SpecifiedModuleType::EcmaScript,
221 chunking_context,
222 references: self.references().to_resolved().await?,
223 part_references,
224 esm_references,
225 code_generation: CodeGens::empty().to_resolved().await?,
226 async_module: ResolvedVc::cell(Some(self.async_module().to_resolved().await?)),
227 generate_source_map: false,
231 original_source_map: None,
232 exports: self.get_exports().to_resolved().await?,
233 async_module_info,
234 }
235 .cell())
236 }
237}
238
239#[turbo_tasks::value_impl]
240impl EcmascriptChunkPlaceable for EcmascriptModuleFacadeModule {
241 #[turbo_tasks::function]
242 async fn get_exports(&self) -> Result<Vc<EcmascriptExports>> {
243 let mut exports = BTreeMap::new();
244 let mut star_exports = Vec::new();
245
246 match &self.part {
247 ModulePart::Facade => {
248 let EcmascriptExports::EsmExports(esm_exports) = *self.module.get_exports().await?
249 else {
250 bail!(
251 "EcmascriptModuleFacadeModule must only be used on modules with EsmExports"
252 );
253 };
254 let esm_exports = esm_exports.await?;
255 for (name, export) in &esm_exports.exports {
256 let name = name.clone();
257 match export {
258 EsmExport::LocalBinding(_, liveness) => {
259 exports.insert(
260 name.clone(),
261 EsmExport::ImportedBinding(
262 ResolvedVc::upcast(
263 EcmascriptModulePartReference::new_part(
264 *self.module,
265 ModulePart::locals(),
266 ExportUsage::named(name.clone()),
267 )
268 .to_resolved()
269 .await?,
270 ),
271 name,
272 *liveness == Liveness::Mutable,
273 ),
274 );
275 }
276 EsmExport::ImportedNamespace(reference) => {
277 exports.insert(name, EsmExport::ImportedNamespace(*reference));
278 }
279 EsmExport::ImportedBinding(reference, imported_name, mutable) => {
280 exports.insert(
281 name,
282 EsmExport::ImportedBinding(
283 *reference,
284 imported_name.clone(),
285 *mutable,
286 ),
287 );
288 }
289 EsmExport::Error => {
290 exports.insert(name, EsmExport::Error);
291 }
292 }
293 }
294 star_exports.extend(esm_exports.star_exports.iter().copied());
295 }
296 ModulePart::RenamedExport {
297 original_export,
298 export,
299 } => {
300 exports.insert(
301 export.clone(),
302 EsmExport::ImportedBinding(
303 ResolvedVc::upcast(
304 EcmascriptModulePartReference::new_normal(
305 *self.module,
306 self.part.clone(),
307 ExportUsage::named(original_export.clone()),
308 )
309 .to_resolved()
310 .await?,
311 ),
312 original_export.clone(),
313 false,
314 ),
315 );
316 }
317 ModulePart::RenamedNamespace { export } => {
318 exports.insert(
319 export.clone(),
320 EsmExport::ImportedNamespace(ResolvedVc::upcast(
321 EcmascriptModulePartReference::new_normal(
322 *self.module,
323 self.part.clone(),
324 ExportUsage::all(),
325 )
326 .to_resolved()
327 .await?,
328 )),
329 );
330 }
331 _ => bail!("Unexpected ModulePart for EcmascriptModuleFacadeModule"),
332 }
333
334 let exports = EsmExports {
335 exports,
336 star_exports,
337 }
338 .resolved_cell();
339 Ok(EcmascriptExports::EsmExports(exports).cell())
340 }
341
342 #[turbo_tasks::function]
343 async fn get_async_module(self: Vc<Self>) -> Result<Vc<OptionAsyncModule>> {
344 Ok(Vc::cell(Some(self.async_module().to_resolved().await?)))
345 }
346}
347
348#[turbo_tasks::value_impl]
349impl ChunkableModule for EcmascriptModuleFacadeModule {
350 #[turbo_tasks::function]
351 fn as_chunk_item(
352 self: ResolvedVc<Self>,
353 _module_graph: ResolvedVc<ModuleGraph>,
354 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
355 ) -> Result<Vc<Box<dyn turbopack_core::chunk::ChunkItem>>> {
356 Ok(Vc::upcast(
357 EcmascriptModuleFacadeChunkItem {
358 module: self,
359 chunking_context,
360 }
361 .cell(),
362 ))
363 }
364}
365
366#[turbo_tasks::value_impl]
367impl EvaluatableAsset for EcmascriptModuleFacadeModule {}
368
369#[turbo_tasks::value_impl]
370impl MergeableModule for EcmascriptModuleFacadeModule {
371 #[turbo_tasks::function]
372 async fn merge(
373 self: Vc<Self>,
374 modules: Vc<MergeableModulesExposed>,
375 entry_points: Vc<MergeableModules>,
376 ) -> Result<Vc<Box<dyn ChunkableModule>>> {
377 Ok(Vc::upcast(
378 *MergedEcmascriptModule::new(
379 modules,
380 entry_points,
381 EcmascriptOptions::default().resolved_cell(),
382 )
383 .await?,
384 ))
385 }
386}