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