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 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 env_var_info(self: Vc<Self>) -> Vc<EnvVarInfo> {
157 EnvVarInfo::empty()
158 }
159
160 #[turbo_tasks::function]
161 fn module_content_without_analysis(
162 &self,
163 _generate_source_map: bool,
164 ) -> Result<Vc<EcmascriptModuleContent>> {
165 bail!("EcmascriptModuleFacadeModule::module_content_without_analysis shouldn't be called");
166 }
167
168 #[turbo_tasks::function]
169 async fn module_content_options(
170 self: ResolvedVc<Self>,
171 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
172 async_module_info: Option<ResolvedVc<AsyncModuleInfo>>,
173 ) -> Result<Vc<EcmascriptModuleContentOptions>> {
174 let (part_references, esm_references) = self.await?.specific_references().await?;
175
176 Ok(EcmascriptModuleContentOptions {
177 parsed: None,
178 module: ResolvedVc::upcast(self),
179 specified_module_type: SpecifiedModuleType::EcmaScript,
180 chunking_context,
181 references: self.references().to_resolved().await?,
182 part_references,
183 esm_references,
184 code_generation: CodeGens::empty().to_resolved().await?,
185 async_module: ResolvedVc::cell(Some(self.async_module().to_resolved().await?)),
186 generate_source_map: false,
190 original_source_map: None,
191 exports: self.get_exports().to_resolved().await?,
192 async_module_info,
193 }
194 .cell())
195 }
196}
197
198#[turbo_tasks::value_impl]
199impl EcmascriptChunkPlaceable for EcmascriptModuleFacadeModule {
200 #[turbo_tasks::function]
201 async fn get_exports(&self) -> Result<Vc<EcmascriptExports>> {
202 let EcmascriptExports::EsmExports(esm_exports) = &*self.module.get_exports().await? else {
203 bail!("EcmascriptModuleFacadeModule must only be used on modules with EsmExports");
204 };
205 let esm_exports = esm_exports.await?;
206 let mut exports = Vec::with_capacity(esm_exports.exports.len());
207 for (name, export) in &esm_exports.exports {
208 let name = name.clone();
209 match export {
210 EsmExport::LocalBinding(_, liveness) => {
211 exports.push((
212 name.clone(),
213 EsmExport::ImportedBinding(
214 ResolvedVc::upcast(
215 EcmascriptModulePartReference::new_part(
216 *self.module,
217 ModulePart::locals(),
218 ExportUsage::named(name.clone()),
219 )
220 .to_resolved()
221 .await?,
222 ),
223 name,
224 *liveness == Liveness::Mutable,
225 ),
226 ));
227 }
228 EsmExport::ImportedNamespace(reference) => {
229 exports.push((name, EsmExport::ImportedNamespace(*reference)));
230 }
231 EsmExport::ImportedBinding(reference, imported_name, mutable) => {
232 exports.push((
233 name,
234 EsmExport::ImportedBinding(*reference, imported_name.clone(), *mutable),
235 ));
236 }
237 EsmExport::Error => {
238 exports.push((name, EsmExport::Error));
239 }
240 }
241 }
242
243 let exports = EsmExports {
244 exports: FrozenMap::from_unique_sorted_box(exports.into_boxed_slice()),
245 star_exports: esm_exports.star_exports.clone(),
246 mangle_export_names: esm_exports.mangle_export_names,
247 }
248 .resolved_cell();
249 Ok(EcmascriptExports::EsmExports(exports).cell())
250 }
251
252 #[turbo_tasks::function]
253 async fn get_async_module(self: Vc<Self>) -> Result<Vc<OptionAsyncModule>> {
254 Ok(Vc::cell(Some(self.async_module().to_resolved().await?)))
255 }
256
257 #[turbo_tasks::function]
258 async fn chunk_item_content(
259 self: Vc<Self>,
260 chunking_context: Vc<Box<dyn ChunkingContext>>,
261 _module_graph: Vc<ModuleGraph>,
262 async_module_info: Option<Vc<AsyncModuleInfo>>,
263 _estimated: bool,
264 ) -> Result<Vc<EcmascriptChunkItemContent>> {
265 let content = self.module_content(chunking_context, async_module_info);
266
267 let async_module_options = self.get_async_module().module_options(async_module_info);
268
269 Ok(EcmascriptChunkItemContent::new(
270 content,
271 chunking_context,
272 async_module_options,
273 ))
274 }
275}
276
277#[turbo_tasks::value_impl]
278impl ChunkableModule for EcmascriptModuleFacadeModule {
279 #[turbo_tasks::function]
280 fn as_chunk_item(
281 self: ResolvedVc<Self>,
282 module_graph: ResolvedVc<ModuleGraph>,
283 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
284 ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
285 ecmascript_chunk_item(ResolvedVc::upcast(self), module_graph, chunking_context)
286 }
287}
288
289#[turbo_tasks::value_impl]
290impl EvaluatableAsset for EcmascriptModuleFacadeModule {}
291
292#[turbo_tasks::value_impl]
293impl MergeableModule for EcmascriptModuleFacadeModule {
294 #[turbo_tasks::function]
295 async fn merge(
296 self: Vc<Self>,
297 modules: Vc<MergeableModulesExposed>,
298 entry_points: Vc<MergeableModules>,
299 ) -> Result<Vc<Box<dyn ChunkableModule>>> {
300 Ok(Vc::upcast(
301 *MergedEcmascriptModule::new(
302 modules,
303 entry_points,
304 EcmascriptOptions::default().resolved_cell(),
305 )
306 .await?,
307 ))
308 }
309}