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, glob::Glob};
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,
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 async fn references(self: Vc<Self>) -> Result<Vc<ModuleReferences>> {
157 let (part_references, esm_references) = self.await?.specific_references().await?;
158 let references = part_references
159 .iter()
160 .map(|r| ResolvedVc::upcast(*r))
161 .chain(esm_references.await?.iter().map(|r| ResolvedVc::upcast(*r)))
162 .collect();
163 Ok(Vc::cell(references))
164 }
165
166 #[turbo_tasks::function]
167 async fn is_self_async(self: Vc<Self>) -> Result<Vc<bool>> {
168 let async_module = self.async_module();
169 let references = self.references();
170 let is_self_async = async_module
171 .resolve()
172 .await?
173 .is_self_async(references.resolve().await?)
174 .resolve()
175 .await?;
176 Ok(is_self_async)
177 }
178}
179
180#[turbo_tasks::value_impl]
181impl Asset for EcmascriptModuleFacadeModule {
182 #[turbo_tasks::function]
183 fn content(&self) -> Vc<AssetContent> {
184 let f = File::from("");
185
186 AssetContent::file(FileContent::Content(f).cell())
187 }
188}
189
190#[turbo_tasks::value_impl]
191impl EcmascriptAnalyzable for EcmascriptModuleFacadeModule {
192 #[turbo_tasks::function]
193 fn analyze(&self) -> Result<Vc<AnalyzeEcmascriptModuleResult>> {
194 bail!("EcmascriptModuleFacadeModule::analyze shouldn't be called");
195 }
196
197 #[turbo_tasks::function]
198 fn module_content_without_analysis(
199 &self,
200 _generate_source_map: bool,
201 ) -> Result<Vc<EcmascriptModuleContent>> {
202 bail!("EcmascriptModuleFacadeModule::module_content_without_analysis shouldn't be called");
203 }
204
205 #[turbo_tasks::function]
206 async fn module_content_options(
207 self: ResolvedVc<Self>,
208 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
209 async_module_info: Option<ResolvedVc<AsyncModuleInfo>>,
210 ) -> Result<Vc<EcmascriptModuleContentOptions>> {
211 let (part_references, esm_references) = self.await?.specific_references().await?;
212
213 Ok(EcmascriptModuleContentOptions {
214 parsed: None,
215 module: ResolvedVc::upcast(self),
216 specified_module_type: SpecifiedModuleType::EcmaScript,
217 chunking_context,
218 references: self.references().to_resolved().await?,
219 part_references,
220 esm_references,
221 code_generation: CodeGens::empty().to_resolved().await?,
222 async_module: ResolvedVc::cell(Some(self.async_module().to_resolved().await?)),
223 generate_source_map: false,
227 original_source_map: None,
228 exports: self.get_exports().to_resolved().await?,
229 async_module_info,
230 }
231 .cell())
232 }
233}
234
235#[turbo_tasks::value_impl]
236impl EcmascriptChunkPlaceable for EcmascriptModuleFacadeModule {
237 #[turbo_tasks::function]
238 async fn get_exports(&self) -> Result<Vc<EcmascriptExports>> {
239 let mut exports = BTreeMap::new();
240 let mut star_exports = Vec::new();
241
242 match &self.part {
243 ModulePart::Facade => {
244 let EcmascriptExports::EsmExports(esm_exports) = *self.module.get_exports().await?
245 else {
246 bail!(
247 "EcmascriptModuleFacadeModule must only be used on modules with EsmExports"
248 );
249 };
250 let esm_exports = esm_exports.await?;
251 for (name, export) in &esm_exports.exports {
252 let name = name.clone();
253 match export {
254 EsmExport::LocalBinding(_, liveness) => {
255 exports.insert(
256 name.clone(),
257 EsmExport::ImportedBinding(
258 ResolvedVc::upcast(
259 EcmascriptModulePartReference::new_part(
260 *self.module,
261 ModulePart::locals(),
262 ExportUsage::named(name.clone()),
263 )
264 .to_resolved()
265 .await?,
266 ),
267 name,
268 *liveness == Liveness::Mutable,
269 ),
270 );
271 }
272 EsmExport::ImportedNamespace(reference) => {
273 exports.insert(name, EsmExport::ImportedNamespace(*reference));
274 }
275 EsmExport::ImportedBinding(reference, imported_name, mutable) => {
276 exports.insert(
277 name,
278 EsmExport::ImportedBinding(
279 *reference,
280 imported_name.clone(),
281 *mutable,
282 ),
283 );
284 }
285 EsmExport::Error => {
286 exports.insert(name, EsmExport::Error);
287 }
288 }
289 }
290 star_exports.extend(esm_exports.star_exports.iter().copied());
291 }
292 ModulePart::RenamedExport {
293 original_export,
294 export,
295 } => {
296 exports.insert(
297 export.clone(),
298 EsmExport::ImportedBinding(
299 ResolvedVc::upcast(
300 EcmascriptModulePartReference::new_normal(
301 *self.module,
302 self.part.clone(),
303 ExportUsage::named(original_export.clone()),
304 )
305 .to_resolved()
306 .await?,
307 ),
308 original_export.clone(),
309 false,
310 ),
311 );
312 }
313 ModulePart::RenamedNamespace { export } => {
314 exports.insert(
315 export.clone(),
316 EsmExport::ImportedNamespace(ResolvedVc::upcast(
317 EcmascriptModulePartReference::new_normal(
318 *self.module,
319 self.part.clone(),
320 ExportUsage::all(),
321 )
322 .to_resolved()
323 .await?,
324 )),
325 );
326 }
327 _ => bail!("Unexpected ModulePart for EcmascriptModuleFacadeModule"),
328 }
329
330 let exports = EsmExports {
331 exports,
332 star_exports,
333 }
334 .resolved_cell();
335 Ok(EcmascriptExports::EsmExports(exports).cell())
336 }
337
338 #[turbo_tasks::function]
339 fn is_marked_as_side_effect_free(
340 &self,
341 side_effect_free_packages: Vc<Glob>,
342 ) -> Result<Vc<bool>> {
343 Ok(match self.part {
344 ModulePart::Facade => self
345 .module
346 .is_marked_as_side_effect_free(side_effect_free_packages),
347 ModulePart::RenamedExport { .. } | ModulePart::RenamedNamespace { .. } => {
348 Vc::cell(true)
349 }
350 _ => bail!("Unexpected ModulePart for EcmascriptModuleFacadeModule"),
351 })
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}