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