turbopack_ecmascript/side_effect_optimization/facade/
module.rs1use anyhow::{Result, bail};
2use turbo_frozenmap::FrozenMap;
3use turbo_tasks::{ResolvedVc, Vc};
4use turbopack_core::{
5 chunk::{
6 AsyncModuleInfo, ChunkableModule, ChunkingContext, EvaluatableAsset, MergeableModule,
7 MergeableModules, MergeableModulesExposed,
8 },
9 ident::AssetIdent,
10 module::{Module, ModuleSideEffects},
11 module_graph::ModuleGraph,
12 reference::ModuleReferences,
13 resolve::{ExportUsage, ModulePart},
14};
15
16use crate::{
17 AnalyzeEcmascriptModuleResult, EcmascriptAnalyzable, EcmascriptAnalyzableExt,
18 EcmascriptModuleContent, EcmascriptModuleContentOptions, EcmascriptOptions,
19 MergedEcmascriptModule, SpecifiedModuleType,
20 chunk::{
21 EcmascriptChunkItemContent, EcmascriptChunkPlaceable, EcmascriptExports,
22 ecmascript_chunk_item,
23 },
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]
39pub struct EcmascriptModuleFacadeModule {
40 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
41}
42
43#[turbo_tasks::value_impl]
44impl EcmascriptModuleFacadeModule {
45 #[turbo_tasks::function]
46 pub fn new(module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>) -> Vc<Self> {
47 EcmascriptModuleFacadeModule { module }.cell()
48 }
49
50 #[turbo_tasks::function]
51 pub async fn async_module(&self) -> Result<Vc<AsyncModule>> {
52 let (import_externals, has_top_level_await) =
53 if let Some(async_module) = *self.module.get_async_module().await? {
54 (
55 async_module.await?.import_externals,
56 async_module.await?.has_top_level_await,
57 )
58 } else {
59 (false, false)
60 };
61 Ok(AsyncModule {
62 has_top_level_await,
63 import_externals,
64 }
65 .cell())
66 }
67}
68
69impl EcmascriptModuleFacadeModule {
70 pub async fn specific_references(
71 &self,
72 ) -> Result<(
73 Vec<ResolvedVc<EcmascriptModulePartReference>>,
74 ResolvedVc<EsmAssetReferences>,
75 )> {
76 let Some(module) = ResolvedVc::try_sidecast::<Box<dyn EcmascriptAnalyzable>>(self.module)
77 else {
78 bail!(
79 "Expected EcmascriptModuleAsset for a EcmascriptModuleFacadeModule with \
80 ModulePart::Facade"
81 );
82 };
83 let result = module.analyze().await?;
84 Ok((
85 vec![
86 EcmascriptModulePartReference::new_part(
88 *self.module,
89 ModulePart::locals(),
90 ExportUsage::all(),
91 )
92 .to_resolved()
93 .await?,
94 ],
95 result.esm_reexport_references,
96 ))
97 }
98}
99
100#[turbo_tasks::value_impl]
101impl Module for EcmascriptModuleFacadeModule {
102 #[turbo_tasks::function]
103 async fn ident(&self) -> Result<Vc<AssetIdent>> {
104 Ok(self
105 .module
106 .ident()
107 .owned()
108 .await?
109 .with_part(ModulePart::Facade)
110 .into_vc())
111 }
112
113 #[turbo_tasks::function]
114 fn source(&self) -> Vc<turbopack_core::source::OptionSource> {
115 Vc::cell(None)
116 }
117
118 #[turbo_tasks::function]
119 async fn references(&self) -> Result<Vc<ModuleReferences>> {
120 let (part_references, esm_references) = self.specific_references().await?;
121 let references = part_references
122 .iter()
123 .map(|r| ResolvedVc::upcast(*r))
124 .chain(esm_references.await?.iter().map(|r| ResolvedVc::upcast(*r)))
125 .collect();
126 Ok(Vc::cell(references))
127 }
128
129 #[turbo_tasks::function]
130 async fn is_self_async(self: Vc<Self>) -> Result<Vc<bool>> {
131 let async_module = self.async_module();
132 let references = self.references();
133 let is_self_async = async_module
134 .to_resolved()
135 .await?
136 .is_self_async(*references.to_resolved().await?)
137 .to_resolved()
138 .await?;
139 Ok(*is_self_async)
140 }
141
142 #[turbo_tasks::function]
143 fn side_effects(&self) -> Vc<ModuleSideEffects> {
144 ModuleSideEffects::ModuleEvaluationIsSideEffectFree.cell()
145 }
146}
147
148#[turbo_tasks::value_impl]
149impl EcmascriptAnalyzable for EcmascriptModuleFacadeModule {
150 #[turbo_tasks::function]
151 fn analyze(&self) -> Result<Vc<AnalyzeEcmascriptModuleResult>> {
152 bail!("EcmascriptModuleFacadeModule::analyze shouldn't be called");
153 }
154
155 #[turbo_tasks::function]
156 fn module_content_without_analysis(
157 &self,
158 _generate_source_map: bool,
159 ) -> Result<Vc<EcmascriptModuleContent>> {
160 bail!("EcmascriptModuleFacadeModule::module_content_without_analysis shouldn't be called");
161 }
162
163 #[turbo_tasks::function]
164 async fn module_content_options(
165 self: ResolvedVc<Self>,
166 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
167 async_module_info: Option<ResolvedVc<AsyncModuleInfo>>,
168 ) -> Result<Vc<EcmascriptModuleContentOptions>> {
169 let (part_references, esm_references) = self.await?.specific_references().await?;
170
171 Ok(EcmascriptModuleContentOptions {
172 parsed: None,
173 module: ResolvedVc::upcast(self),
174 specified_module_type: SpecifiedModuleType::EcmaScript,
175 chunking_context,
176 references: self.references().to_resolved().await?,
177 part_references,
178 esm_references,
179 code_generation: CodeGens::empty().to_resolved().await?,
180 async_module: ResolvedVc::cell(Some(self.async_module().to_resolved().await?)),
181 generate_source_map: false,
185 original_source_map: None,
186 exports: self.get_exports().to_resolved().await?,
187 async_module_info,
188 }
189 .cell())
190 }
191}
192
193#[turbo_tasks::value_impl]
194impl EcmascriptChunkPlaceable for EcmascriptModuleFacadeModule {
195 #[turbo_tasks::function]
196 async fn get_exports(&self) -> Result<Vc<EcmascriptExports>> {
197 let EcmascriptExports::EsmExports(esm_exports) = &*self.module.get_exports().await? else {
198 bail!("EcmascriptModuleFacadeModule must only be used on modules with EsmExports");
199 };
200 let esm_exports = esm_exports.await?;
201 let mut exports = Vec::with_capacity(esm_exports.exports.len());
202 for (name, export) in &esm_exports.exports {
203 let name = name.clone();
204 match export {
205 EsmExport::LocalBinding(_, liveness) => {
206 exports.push((
207 name.clone(),
208 EsmExport::ImportedBinding(
209 ResolvedVc::upcast(
210 EcmascriptModulePartReference::new_part(
211 *self.module,
212 ModulePart::locals(),
213 ExportUsage::named(name.clone()),
214 )
215 .to_resolved()
216 .await?,
217 ),
218 name,
219 *liveness == Liveness::Mutable,
220 ),
221 ));
222 }
223 EsmExport::ImportedNamespace(reference) => {
224 exports.push((name, EsmExport::ImportedNamespace(*reference)));
225 }
226 EsmExport::ImportedBinding(reference, imported_name, mutable) => {
227 exports.push((
228 name,
229 EsmExport::ImportedBinding(*reference, imported_name.clone(), *mutable),
230 ));
231 }
232 EsmExport::Error => {
233 exports.push((name, EsmExport::Error));
234 }
235 }
236 }
237
238 let exports = EsmExports {
239 exports: FrozenMap::from_unique_sorted_box(exports.into_boxed_slice()),
240 star_exports: esm_exports.star_exports.clone(),
241 }
242 .resolved_cell();
243 Ok(EcmascriptExports::EsmExports(exports).cell())
244 }
245
246 #[turbo_tasks::function]
247 async fn get_async_module(self: Vc<Self>) -> Result<Vc<OptionAsyncModule>> {
248 Ok(Vc::cell(Some(self.async_module().to_resolved().await?)))
249 }
250
251 #[turbo_tasks::function]
252 async fn chunk_item_content(
253 self: Vc<Self>,
254 chunking_context: Vc<Box<dyn ChunkingContext>>,
255 _module_graph: Vc<ModuleGraph>,
256 async_module_info: Option<Vc<AsyncModuleInfo>>,
257 _estimated: bool,
258 ) -> Result<Vc<EcmascriptChunkItemContent>> {
259 let content = self.module_content(chunking_context, async_module_info);
260
261 let async_module_options = self.get_async_module().module_options(async_module_info);
262
263 Ok(EcmascriptChunkItemContent::new(
264 content,
265 chunking_context,
266 async_module_options,
267 ))
268 }
269}
270
271#[turbo_tasks::value_impl]
272impl ChunkableModule for EcmascriptModuleFacadeModule {
273 #[turbo_tasks::function]
274 fn as_chunk_item(
275 self: ResolvedVc<Self>,
276 module_graph: ResolvedVc<ModuleGraph>,
277 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
278 ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
279 ecmascript_chunk_item(ResolvedVc::upcast(self), module_graph, chunking_context)
280 }
281}
282
283#[turbo_tasks::value_impl]
284impl EvaluatableAsset for EcmascriptModuleFacadeModule {}
285
286#[turbo_tasks::value_impl]
287impl MergeableModule for EcmascriptModuleFacadeModule {
288 #[turbo_tasks::function]
289 async fn merge(
290 self: Vc<Self>,
291 modules: Vc<MergeableModulesExposed>,
292 entry_points: Vc<MergeableModules>,
293 ) -> Result<Vc<Box<dyn ChunkableModule>>> {
294 Ok(Vc::upcast(
295 *MergedEcmascriptModule::new(
296 modules,
297 entry_points,
298 EcmascriptOptions::default().resolved_cell(),
299 )
300 .await?,
301 ))
302 }
303}