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