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::{AsyncModuleInfo, ChunkableModule, ChunkingContext, EvaluatableAsset},
9 ident::AssetIdent,
10 module::Module,
11 module_graph::ModuleGraph,
12 reference::ModuleReferences,
13 resolve::ModulePart,
14};
15
16use super::chunk_item::EcmascriptModuleFacadeChunkItem;
17use crate::{
18 AnalyzeEcmascriptModuleResult, EcmascriptAnalyzable, EcmascriptModuleContent,
19 EcmascriptModuleContentOptions, SpecifiedModuleType,
20 chunk::{EcmascriptChunkPlaceable, EcmascriptExports},
21 code_gen::CodeGens,
22 parse::ParseResult,
23 references::{
24 async_module::{AsyncModule, OptionAsyncModule},
25 esm::{EsmExport, EsmExports, base::EsmAssetReferences},
26 },
27 side_effect_optimization::reference::EcmascriptModulePartReference,
28};
29
30#[turbo_tasks::value]
34pub struct EcmascriptModuleFacadeModule {
35 pub module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
36 pub ty: ModulePart,
37}
38
39#[turbo_tasks::value_impl]
40impl EcmascriptModuleFacadeModule {
41 #[turbo_tasks::function]
42 pub fn new(module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>, ty: ModulePart) -> Vc<Self> {
43 EcmascriptModuleFacadeModule { module, ty }.cell()
44 }
45
46 #[turbo_tasks::function]
47 pub async fn async_module(&self) -> Result<Vc<AsyncModule>> {
48 let (import_externals, has_top_level_await) =
49 if let Some(async_module) = *self.module.get_async_module().await? {
50 (
51 async_module.await?.import_externals,
52 async_module.await?.has_top_level_await,
53 )
54 } else {
55 (false, false)
56 };
57 Ok(AsyncModule {
58 has_top_level_await,
59 import_externals,
60 }
61 .cell())
62 }
63}
64
65impl EcmascriptModuleFacadeModule {
66 pub async fn specific_references(
67 &self,
68 ) -> Result<(
69 ResolvedVc<EsmAssetReferences>,
70 Vec<ResolvedVc<EcmascriptModulePartReference>>,
71 )> {
72 Ok(match &self.ty {
73 ModulePart::Evaluation => {
74 let Some(module) =
75 ResolvedVc::try_sidecast::<Box<dyn EcmascriptAnalyzable>>(self.module)
76 else {
77 bail!(
78 "Expected EcmascriptModuleAsset for a EcmascriptModuleFacadeModule with \
79 ModulePart::Evaluation"
80 );
81 };
82 let result = module.analyze().await?;
83 (
84 result.esm_evaluation_references,
85 vec![
86 EcmascriptModulePartReference::new_part(*self.module, ModulePart::locals())
87 .to_resolved()
88 .await?,
89 ],
90 )
91 }
92 ModulePart::Exports => {
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::Exports"
99 );
100 };
101 let result = module.analyze().await?;
102 (
103 result.esm_reexport_references,
104 vec![
105 EcmascriptModulePartReference::new_part(*self.module, ModulePart::locals())
106 .to_resolved()
107 .await?,
108 ],
109 )
110 }
111 ModulePart::Facade => (
112 EsmAssetReferences::empty().to_resolved().await?,
113 vec![
114 EcmascriptModulePartReference::new_part(*self.module, ModulePart::evaluation())
115 .to_resolved()
116 .await?,
117 EcmascriptModulePartReference::new_part(*self.module, ModulePart::exports())
118 .to_resolved()
119 .await?,
120 ],
121 ),
122 ModulePart::RenamedNamespace { .. } => (
123 EsmAssetReferences::empty().to_resolved().await?,
124 vec![
125 EcmascriptModulePartReference::new(*self.module)
126 .to_resolved()
127 .await?,
128 ],
129 ),
130 ModulePart::RenamedExport { .. } => (
131 EsmAssetReferences::empty().to_resolved().await?,
132 vec![
133 EcmascriptModulePartReference::new(*self.module)
134 .to_resolved()
135 .await?,
136 ],
137 ),
138 _ => {
139 bail!("Unexpected ModulePart for EcmascriptModuleFacadeModule");
140 }
141 })
142 }
143}
144
145#[turbo_tasks::value_impl]
146impl Module for EcmascriptModuleFacadeModule {
147 #[turbo_tasks::function]
148 async fn ident(&self) -> Result<Vc<AssetIdent>> {
149 let inner = self.module.ident();
150
151 Ok(inner.with_part(self.ty.clone()))
152 }
153
154 #[turbo_tasks::function]
155 async fn references(self: Vc<Self>) -> Result<Vc<ModuleReferences>> {
156 let (esm_references, part_references) = self.await?.specific_references().await?;
157 let references = esm_references
158 .await?
159 .iter()
160 .map(|r| ResolvedVc::upcast(*r))
161 .chain(part_references.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: Vc<Self>,
208 module_graph: ResolvedVc<ModuleGraph>,
209 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
210 async_module_info: Option<ResolvedVc<AsyncModuleInfo>>,
211 ) -> Result<Vc<EcmascriptModuleContentOptions>> {
212 let (esm_references, part_references) = self.await?.specific_references().await?;
213
214 Ok(EcmascriptModuleContentOptions {
215 parsed: ParseResult::empty().to_resolved().await?,
216 ident: self.ident().to_resolved().await?,
217 specified_module_type: SpecifiedModuleType::EcmaScript,
218 module_graph,
219 chunking_context,
220 references: self.references().to_resolved().await?,
221 esm_references,
222 part_references,
223 code_generation: CodeGens::empty().to_resolved().await?,
224 async_module: ResolvedVc::cell(Some(self.async_module().to_resolved().await?)),
225 generate_source_map: false,
226 original_source_map: None,
227 exports: self.get_exports().to_resolved().await?,
228 async_module_info,
229 }
230 .cell())
231 }
232}
233
234#[turbo_tasks::value_impl]
235impl EcmascriptChunkPlaceable for EcmascriptModuleFacadeModule {
236 #[turbo_tasks::function]
237 async fn get_exports(&self) -> Result<Vc<EcmascriptExports>> {
238 let mut exports = BTreeMap::new();
239 let mut star_exports = Vec::new();
240
241 match &self.ty {
242 ModulePart::Exports => {
243 let EcmascriptExports::EsmExports(esm_exports) = *self.module.get_exports().await?
244 else {
245 bail!(
246 "EcmascriptModuleFacadeModule must only be used on modules with EsmExports"
247 );
248 };
249 let esm_exports = esm_exports.await?;
250 for (name, export) in &esm_exports.exports {
251 let name = name.clone();
252 match export {
253 EsmExport::LocalBinding(_, mutable) => {
254 exports.insert(
255 name.clone(),
256 EsmExport::ImportedBinding(
257 ResolvedVc::upcast(
258 EcmascriptModulePartReference::new_part(
259 *self.module,
260 ModulePart::locals(),
261 )
262 .to_resolved()
263 .await?,
264 ),
265 name,
266 *mutable,
267 ),
268 );
269 }
270 EsmExport::ImportedNamespace(reference) => {
271 exports.insert(name, EsmExport::ImportedNamespace(*reference));
272 }
273 EsmExport::ImportedBinding(reference, imported_name, mutable) => {
274 exports.insert(
275 name,
276 EsmExport::ImportedBinding(
277 *reference,
278 imported_name.clone(),
279 *mutable,
280 ),
281 );
282 }
283 EsmExport::Error => {
284 exports.insert(name, EsmExport::Error);
285 }
286 }
287 }
288 star_exports.extend(esm_exports.star_exports.iter().copied());
289 }
290 ModulePart::Facade => {
291 let EcmascriptExports::EsmExports(esm_exports) = *self.module.get_exports().await?
294 else {
295 bail!(
296 "EcmascriptModuleFacadeModule must only be used on modules with EsmExports"
297 );
298 };
299 let esm_exports = esm_exports.await?;
300 if esm_exports.exports.keys().any(|name| name == "default") {
301 exports.insert(
302 "default".into(),
303 EsmExport::ImportedBinding(
304 ResolvedVc::upcast(
305 EcmascriptModulePartReference::new_part(
306 *self.module,
307 ModulePart::exports(),
308 )
309 .to_resolved()
310 .await?,
311 ),
312 "default".into(),
313 false,
314 ),
315 );
316 }
317 star_exports.push(ResolvedVc::upcast(
318 EcmascriptModulePartReference::new_part(*self.module, ModulePart::exports())
319 .to_resolved()
320 .await?,
321 ));
322 }
323 ModulePart::RenamedExport {
324 original_export,
325 export,
326 } => {
327 exports.insert(
328 export.clone(),
329 EsmExport::ImportedBinding(
330 ResolvedVc::upcast(
331 EcmascriptModulePartReference::new(*self.module)
332 .to_resolved()
333 .await?,
334 ),
335 original_export.clone(),
336 false,
337 ),
338 );
339 }
340 ModulePart::RenamedNamespace { export } => {
341 exports.insert(
342 export.clone(),
343 EsmExport::ImportedNamespace(ResolvedVc::upcast(
344 EcmascriptModulePartReference::new(*self.module)
345 .to_resolved()
346 .await?,
347 )),
348 );
349 }
350 ModulePart::Evaluation => {
351 }
353 _ => bail!("Unexpected ModulePart for EcmascriptModuleFacadeModule"),
354 }
355
356 let exports = EsmExports {
357 exports,
358 star_exports,
359 }
360 .resolved_cell();
361 Ok(EcmascriptExports::EsmExports(exports).cell())
362 }
363
364 #[turbo_tasks::function]
365 async fn is_marked_as_side_effect_free(
366 &self,
367 side_effect_free_packages: Vc<Glob>,
368 ) -> Result<Vc<bool>> {
369 Ok(match self.ty {
370 ModulePart::Evaluation | ModulePart::Facade => self
371 .module
372 .is_marked_as_side_effect_free(side_effect_free_packages),
373 ModulePart::Exports
374 | ModulePart::RenamedExport { .. }
375 | ModulePart::RenamedNamespace { .. } => Vc::cell(true),
376 _ => bail!("Unexpected ModulePart for EcmascriptModuleFacadeModule"),
377 })
378 }
379
380 #[turbo_tasks::function]
381 async fn get_async_module(self: Vc<Self>) -> Result<Vc<OptionAsyncModule>> {
382 Ok(Vc::cell(Some(self.async_module().to_resolved().await?)))
383 }
384}
385
386#[turbo_tasks::value_impl]
387impl ChunkableModule for EcmascriptModuleFacadeModule {
388 #[turbo_tasks::function]
389 async fn as_chunk_item(
390 self: ResolvedVc<Self>,
391 module_graph: ResolvedVc<ModuleGraph>,
392 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
393 ) -> Result<Vc<Box<dyn turbopack_core::chunk::ChunkItem>>> {
394 Ok(Vc::upcast(
395 EcmascriptModuleFacadeChunkItem {
396 module: self,
397 module_graph,
398 chunking_context,
399 }
400 .cell(),
401 ))
402 }
403}
404
405#[turbo_tasks::value_impl]
406impl EvaluatableAsset for EcmascriptModuleFacadeModule {}