turbopack_ecmascript/side_effect_optimization/facade/
module.rs1use std::collections::BTreeMap;
2
3use anyhow::{Result, bail};
4use turbo_rcstr::rcstr;
5use turbo_tasks::{ResolvedVc, Vc};
6use turbo_tasks_fs::{File, FileContent, glob::Glob};
7use turbopack_core::{
8 asset::{Asset, AssetContent},
9 chunk::{
10 AsyncModuleInfo, ChunkableModule, ChunkingContext, EvaluatableAsset, MergeableModule,
11 MergeableModules, MergeableModulesExposed,
12 },
13 ident::AssetIdent,
14 module::Module,
15 module_graph::ModuleGraph,
16 reference::ModuleReferences,
17 resolve::ModulePart,
18};
19
20use super::chunk_item::EcmascriptModuleFacadeChunkItem;
21use crate::{
22 AnalyzeEcmascriptModuleResult, EcmascriptAnalyzable, EcmascriptModuleContent,
23 EcmascriptModuleContentOptions, EcmascriptOptions, MergedEcmascriptModule, SpecifiedModuleType,
24 chunk::{EcmascriptChunkPlaceable, EcmascriptExports},
25 code_gen::CodeGens,
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,
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 EcmascriptModuleFacadeModule { module, part }.cell()
51 }
52
53 #[turbo_tasks::function]
54 pub async fn async_module(&self) -> Result<Vc<AsyncModule>> {
55 let (import_externals, has_top_level_await) =
56 if let Some(async_module) = *self.module.get_async_module().await? {
57 (
58 async_module.await?.import_externals,
59 async_module.await?.has_top_level_await,
60 )
61 } else {
62 (false, false)
63 };
64 Ok(AsyncModule {
65 has_top_level_await,
66 import_externals,
67 }
68 .cell())
69 }
70}
71
72impl EcmascriptModuleFacadeModule {
73 pub async fn specific_references(
74 &self,
75 ) -> Result<(
76 ResolvedVc<EsmAssetReferences>,
77 Vec<ResolvedVc<EcmascriptModulePartReference>>,
78 )> {
79 Ok(match &self.part {
80 ModulePart::Evaluation => {
81 let Some(module) =
82 ResolvedVc::try_sidecast::<Box<dyn EcmascriptAnalyzable>>(self.module)
83 else {
84 bail!(
85 "Expected EcmascriptModuleAsset for a EcmascriptModuleFacadeModule with \
86 ModulePart::Evaluation"
87 );
88 };
89 let result = module.analyze().await?;
90 (
91 result.esm_evaluation_references,
92 vec![
93 EcmascriptModulePartReference::new_part(*self.module, ModulePart::locals())
94 .to_resolved()
95 .await?,
96 ],
97 )
98 }
99 ModulePart::Exports => {
100 let Some(module) =
101 ResolvedVc::try_sidecast::<Box<dyn EcmascriptAnalyzable>>(self.module)
102 else {
103 bail!(
104 "Expected EcmascriptModuleAsset for a EcmascriptModuleFacadeModule with \
105 ModulePart::Exports"
106 );
107 };
108 let result = module.analyze().await?;
109 (
110 result.esm_reexport_references,
111 vec![
112 EcmascriptModulePartReference::new_part(*self.module, ModulePart::locals())
113 .to_resolved()
114 .await?,
115 ],
116 )
117 }
118 ModulePart::Facade => (
119 EsmAssetReferences::empty().to_resolved().await?,
120 vec![
121 EcmascriptModulePartReference::new_part(*self.module, ModulePart::evaluation())
122 .to_resolved()
123 .await?,
124 EcmascriptModulePartReference::new_part(*self.module, ModulePart::exports())
125 .to_resolved()
126 .await?,
127 ],
128 ),
129 ModulePart::RenamedNamespace { .. } => (
130 EsmAssetReferences::empty().to_resolved().await?,
131 vec![
132 EcmascriptModulePartReference::new_normal(*self.module, self.part.clone())
133 .to_resolved()
134 .await?,
135 ],
136 ),
137 ModulePart::RenamedExport { .. } => (
138 EsmAssetReferences::empty().to_resolved().await?,
139 vec![
140 EcmascriptModulePartReference::new_normal(*self.module, self.part.clone())
141 .to_resolved()
142 .await?,
143 ],
144 ),
145 _ => {
146 bail!("Unexpected ModulePart for EcmascriptModuleFacadeModule");
147 }
148 })
149 }
150}
151
152#[turbo_tasks::value_impl]
153impl Module for EcmascriptModuleFacadeModule {
154 #[turbo_tasks::function]
155 fn ident(&self) -> Vc<AssetIdent> {
156 self.module.ident().with_part(self.part.clone())
157 }
158
159 #[turbo_tasks::function]
160 async fn references(self: Vc<Self>) -> Result<Vc<ModuleReferences>> {
161 let (esm_references, part_references) = self.await?.specific_references().await?;
162 let references = esm_references
163 .await?
164 .iter()
165 .map(|r| ResolvedVc::upcast(*r))
166 .chain(part_references.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
185#[turbo_tasks::value_impl]
186impl Asset for EcmascriptModuleFacadeModule {
187 #[turbo_tasks::function]
188 fn content(&self) -> Vc<AssetContent> {
189 let f = File::from("");
190
191 AssetContent::file(FileContent::Content(f).cell())
192 }
193}
194
195#[turbo_tasks::value_impl]
196impl EcmascriptAnalyzable for EcmascriptModuleFacadeModule {
197 #[turbo_tasks::function]
198 fn analyze(&self) -> Result<Vc<AnalyzeEcmascriptModuleResult>> {
199 bail!("EcmascriptModuleFacadeModule::analyze shouldn't be called");
200 }
201
202 #[turbo_tasks::function]
203 fn module_content_without_analysis(
204 &self,
205 _generate_source_map: bool,
206 ) -> Result<Vc<EcmascriptModuleContent>> {
207 bail!("EcmascriptModuleFacadeModule::module_content_without_analysis shouldn't be called");
208 }
209
210 #[turbo_tasks::function]
211 async fn module_content_options(
212 self: ResolvedVc<Self>,
213 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
214 async_module_info: Option<ResolvedVc<AsyncModuleInfo>>,
215 ) -> Result<Vc<EcmascriptModuleContentOptions>> {
216 let (esm_references, part_references) = self.await?.specific_references().await?;
217
218 Ok(EcmascriptModuleContentOptions {
219 parsed: ParseResult::empty().to_resolved().await?,
220 module: ResolvedVc::upcast(self),
221 specified_module_type: SpecifiedModuleType::EcmaScript,
222 chunking_context,
223 references: self.references().to_resolved().await?,
224 esm_references,
225 part_references,
226 code_generation: CodeGens::empty().to_resolved().await?,
227 async_module: ResolvedVc::cell(Some(self.async_module().to_resolved().await?)),
228 generate_source_map: false,
232 original_source_map: None,
233 exports: self.get_exports().to_resolved().await?,
234 async_module_info,
235 }
236 .cell())
237 }
238}
239
240#[turbo_tasks::value_impl]
241impl EcmascriptChunkPlaceable for EcmascriptModuleFacadeModule {
242 #[turbo_tasks::function]
243 async fn get_exports(&self) -> Result<Vc<EcmascriptExports>> {
244 let mut exports = BTreeMap::new();
245 let mut star_exports = Vec::new();
246
247 match &self.part {
248 ModulePart::Exports => {
249 let EcmascriptExports::EsmExports(esm_exports) = *self.module.get_exports().await?
250 else {
251 bail!(
252 "EcmascriptModuleFacadeModule must only be used on modules with EsmExports"
253 );
254 };
255 let esm_exports = esm_exports.await?;
256 for (name, export) in &esm_exports.exports {
257 let name = name.clone();
258 match export {
259 EsmExport::LocalBinding(_, mutable) => {
260 exports.insert(
261 name.clone(),
262 EsmExport::ImportedBinding(
263 ResolvedVc::upcast(
264 EcmascriptModulePartReference::new_part(
265 *self.module,
266 ModulePart::locals(),
267 )
268 .to_resolved()
269 .await?,
270 ),
271 name,
272 *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::Facade => {
297 let EcmascriptExports::EsmExports(esm_exports) = *self.module.get_exports().await?
300 else {
301 bail!(
302 "EcmascriptModuleFacadeModule must only be used on modules with EsmExports"
303 );
304 };
305 let esm_exports = esm_exports.await?;
306 if esm_exports.exports.keys().any(|name| name == "default") {
307 exports.insert(
308 rcstr!("default"),
309 EsmExport::ImportedBinding(
310 ResolvedVc::upcast(
311 EcmascriptModulePartReference::new_part(
312 *self.module,
313 ModulePart::exports(),
314 )
315 .to_resolved()
316 .await?,
317 ),
318 rcstr!("default"),
319 false,
320 ),
321 );
322 }
323 star_exports.push(ResolvedVc::upcast(
324 EcmascriptModulePartReference::new_part(*self.module, ModulePart::exports())
325 .to_resolved()
326 .await?,
327 ));
328 }
329 ModulePart::RenamedExport {
330 original_export,
331 export,
332 } => {
333 exports.insert(
334 export.clone(),
335 EsmExport::ImportedBinding(
336 ResolvedVc::upcast(
337 EcmascriptModulePartReference::new_normal(
338 *self.module,
339 self.part.clone(),
340 )
341 .to_resolved()
342 .await?,
343 ),
344 original_export.clone(),
345 false,
346 ),
347 );
348 }
349 ModulePart::RenamedNamespace { export } => {
350 exports.insert(
351 export.clone(),
352 EsmExport::ImportedNamespace(ResolvedVc::upcast(
353 EcmascriptModulePartReference::new_normal(*self.module, self.part.clone())
354 .to_resolved()
355 .await?,
356 )),
357 );
358 }
359 ModulePart::Evaluation => {
360 }
362 _ => bail!("Unexpected ModulePart for EcmascriptModuleFacadeModule"),
363 }
364
365 let exports = EsmExports {
366 exports,
367 star_exports,
368 }
369 .resolved_cell();
370 Ok(EcmascriptExports::EsmExports(exports).cell())
371 }
372
373 #[turbo_tasks::function]
374 fn is_marked_as_side_effect_free(
375 &self,
376 side_effect_free_packages: Vc<Glob>,
377 ) -> Result<Vc<bool>> {
378 Ok(match self.part {
379 ModulePart::Evaluation | ModulePart::Facade => self
380 .module
381 .is_marked_as_side_effect_free(side_effect_free_packages),
382 ModulePart::Exports
383 | ModulePart::RenamedExport { .. }
384 | ModulePart::RenamedNamespace { .. } => Vc::cell(true),
385 _ => bail!("Unexpected ModulePart for EcmascriptModuleFacadeModule"),
386 })
387 }
388
389 #[turbo_tasks::function]
390 async fn get_async_module(self: Vc<Self>) -> Result<Vc<OptionAsyncModule>> {
391 Ok(Vc::cell(Some(self.async_module().to_resolved().await?)))
392 }
393}
394
395#[turbo_tasks::value_impl]
396impl ChunkableModule for EcmascriptModuleFacadeModule {
397 #[turbo_tasks::function]
398 fn as_chunk_item(
399 self: ResolvedVc<Self>,
400 _module_graph: ResolvedVc<ModuleGraph>,
401 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
402 ) -> Result<Vc<Box<dyn turbopack_core::chunk::ChunkItem>>> {
403 Ok(Vc::upcast(
404 EcmascriptModuleFacadeChunkItem {
405 module: self,
406 chunking_context,
407 }
408 .cell(),
409 ))
410 }
411}
412
413#[turbo_tasks::value_impl]
414impl EvaluatableAsset for EcmascriptModuleFacadeModule {}
415
416#[turbo_tasks::value_impl]
417impl MergeableModule for EcmascriptModuleFacadeModule {
418 #[turbo_tasks::function]
419 async fn merge(
420 self: Vc<Self>,
421 modules: Vc<MergeableModulesExposed>,
422 entry_points: Vc<MergeableModules>,
423 ) -> Result<Vc<Box<dyn ChunkableModule>>> {
424 Ok(Vc::upcast(
425 *MergedEcmascriptModule::new(
426 modules,
427 entry_points,
428 EcmascriptOptions::default().resolved_cell(),
429 )
430 .await?,
431 ))
432 }
433}