turbopack_ecmascript/tree_shake/part/
module.rs1use anyhow::Result;
2use turbo_rcstr::{RcStr, rcstr};
3use turbo_tasks::{ResolvedVc, Vc};
4use turbopack_core::{
5 chunk::{AsyncModuleInfo, ChunkableModule, ChunkingContext, EvaluatableAsset},
6 ident::AssetIdent,
7 module::{Module, ModuleSideEffects},
8 module_graph::ModuleGraph,
9 reference::{ModuleReference, ModuleReferences, SingleChunkableModuleReference},
10 resolve::{ExportUsage, ModulePart},
11};
12
13use crate::{
14 AnalyzeEcmascriptModuleResult, EcmascriptAnalyzable, EcmascriptAnalyzableExt,
15 EcmascriptModuleAsset, EcmascriptModuleContent, EcmascriptModuleContentOptions,
16 EcmascriptParsable,
17 chunk::{
18 EcmascriptChunkItemContent, EcmascriptChunkPlaceable, EcmascriptExports,
19 ecmascript_chunk_item,
20 },
21 parse::ParseResult,
22 references::{
23 FollowExportsResult, analyze_ecmascript_module, esm::FoundExportType,
24 exports::compute_ecmascript_module_exports, follow_reexports,
25 },
26 rename::module::EcmascriptModuleRenameModule,
27 tree_shake::{
28 Key, SplitResult, get_part_id, part_of_module, side_effects::module::SideEffectsModule,
29 split_module,
30 },
31};
32
33#[turbo_tasks::value]
37pub struct EcmascriptModulePartAsset {
38 pub full_module: ResolvedVc<EcmascriptModuleAsset>,
39 pub part: ModulePart,
40}
41
42#[turbo_tasks::value_impl]
43impl EcmascriptParsable for EcmascriptModulePartAsset {
44 #[turbo_tasks::function]
45 fn failsafe_parse(&self) -> Result<Vc<ParseResult>> {
46 let split_data = split_module(*self.full_module);
47 assert_ne!(self.part, ModulePart::Facade);
48 Ok(part_of_module(split_data, self.part.clone()))
49 }
50}
51
52#[turbo_tasks::value_impl]
53impl EcmascriptAnalyzable for EcmascriptModulePartAsset {
54 #[turbo_tasks::function]
55 fn analyze(&self) -> Vc<AnalyzeEcmascriptModuleResult> {
56 analyze_ecmascript_module(*self.full_module, Some(self.part.clone()))
57 }
58
59 #[turbo_tasks::function]
60 fn module_content_without_analysis(
61 &self,
62 generate_source_map: bool,
63 ) -> Vc<EcmascriptModuleContent> {
64 self.full_module
65 .module_content_without_analysis(generate_source_map)
66 }
67
68 #[turbo_tasks::function]
69 async fn module_content_options(
70 self: ResolvedVc<Self>,
71 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
72 async_module_info: Option<ResolvedVc<AsyncModuleInfo>>,
73 ) -> Result<Vc<EcmascriptModuleContentOptions>> {
74 let module = self.await?;
75
76 let split_data = split_module(*module.full_module);
77 let parsed = part_of_module(split_data, module.part.clone())
78 .to_resolved()
79 .await?;
80
81 let analyze = self.analyze();
82 let analyze_ref = analyze.await?;
83
84 let module_type_result = module.full_module.determine_module_type().await?;
85 let generate_source_map = *chunking_context
86 .reference_module_source_maps(Vc::upcast(*self))
87 .await?;
88 Ok(EcmascriptModuleContentOptions {
89 parsed: Some(parsed),
90 module: ResolvedVc::upcast(self),
91 specified_module_type: module_type_result.module_type,
92 chunking_context,
93 references: analyze.references().to_resolved().await?,
94 esm_references: analyze_ref.esm_references,
95 part_references: vec![],
96 code_generation: analyze_ref.code_generation,
97 async_module: analyze_ref.async_module,
98 generate_source_map,
99 original_source_map: analyze_ref.source_map,
100 exports: self.get_exports().to_resolved().await?,
101 async_module_info,
102 }
103 .cell())
104 }
105}
106
107#[turbo_tasks::value_impl]
108impl EcmascriptModulePartAsset {
109 #[turbo_tasks::function]
113 fn new_raw(module: ResolvedVc<EcmascriptModuleAsset>, part: ModulePart) -> Vc<Self> {
114 Self {
115 full_module: module,
116 part,
117 }
118 .cell()
119 }
120
121 #[turbo_tasks::function]
122 pub async fn new_with_resolved_part(
123 module: ResolvedVc<EcmascriptModuleAsset>,
124 part: ModulePart,
125 ) -> Result<Vc<Self>> {
126 if matches!(
127 part,
128 ModulePart::Internal(..) | ModulePart::Facade | ModulePart::Exports
129 ) {
130 return Ok(Self::new_raw(*module, part));
131 }
132
133 let split_result = split_module(*module).await?;
135 let part_id = get_part_id(&split_result, &part).await?;
136
137 Ok(Self::new_raw(*module, ModulePart::internal(part_id)))
138 }
139
140 #[turbo_tasks::function]
141 pub async fn select_part(
142 module: Vc<EcmascriptModuleAsset>,
143 part: ModulePart,
144 ) -> Result<Vc<Box<dyn EcmascriptChunkPlaceable>>> {
145 let SplitResult::Ok { entrypoints, .. } = &*split_module(module).await? else {
146 return Ok(Vc::upcast(module));
147 };
148
149 match part {
150 ModulePart::Evaluation => {
151 let idx = *entrypoints.get(&Key::ModuleEvaluation).unwrap();
153 return Ok(Vc::upcast(
154 EcmascriptModulePartAsset::new_with_resolved_part(
155 module,
156 ModulePart::internal(idx),
157 ),
158 ));
159 }
160
161 ModulePart::Export(export) => {
162 if entrypoints.contains_key(&Key::Export(export.clone())) {
163 return Ok(Vc::upcast(
164 EcmascriptModulePartAsset::new_with_resolved_part(
165 module,
166 ModulePart::Export(export),
167 ),
168 ));
169 }
170 let source_module = Vc::upcast(module);
171 let FollowExportsWithSideEffectsResult {
172 side_effects,
173 result,
174 } = &*follow_reexports_with_side_effects(source_module, export.clone()).await?;
175 let FollowExportsResult {
176 module: final_module,
177 export_name: new_export,
178 ..
179 } = &*result.await?;
180 let final_module = if let Some(new_export) = new_export {
181 if *new_export == export {
182 *final_module
183 } else {
184 ResolvedVc::upcast(
185 EcmascriptModuleRenameModule::new(
186 **final_module,
187 ModulePart::renamed_export(new_export.clone(), export.clone()),
188 )
189 .to_resolved()
190 .await?,
191 )
192 }
193 } else {
194 ResolvedVc::upcast(
195 EcmascriptModuleRenameModule::new(
196 **final_module,
197 ModulePart::renamed_namespace(export.clone()),
198 )
199 .to_resolved()
200 .await?,
201 )
202 };
203 if side_effects.is_empty() {
204 return Ok(*final_module);
205 }
206 let side_effects_module = SideEffectsModule::new(
207 module,
208 ModulePart::Export(export),
209 *final_module,
210 side_effects.iter().map(|v| **v).collect(),
211 );
212 return Ok(Vc::upcast(side_effects_module));
213 }
214 _ => (),
215 }
216
217 Ok(Vc::upcast(
218 EcmascriptModulePartAsset::new_with_resolved_part(module, part.clone()),
219 ))
220 }
221
222 #[turbo_tasks::function]
223 pub async fn is_async_module(self: Vc<Self>) -> Result<Vc<bool>> {
224 let this = self.await?;
225 let result = analyze_ecmascript_module(*this.full_module, Some(this.part.clone()));
226
227 if let Some(async_module) = *result.await?.async_module.await? {
228 Ok(async_module.is_self_async(self.references()))
229 } else {
230 Ok(Vc::cell(false))
231 }
232 }
233}
234
235#[turbo_tasks::value]
236struct FollowExportsWithSideEffectsResult {
237 side_effects: Vec<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>>,
238 result: ResolvedVc<FollowExportsResult>,
239}
240
241#[turbo_tasks::function]
242async fn follow_reexports_with_side_effects(
243 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
244 export_name: RcStr,
245) -> Result<Vc<FollowExportsWithSideEffectsResult>> {
246 let mut side_effects = vec![];
247
248 let mut current_module = module;
249 let mut current_export_name = export_name;
250 let result = loop {
251 if *current_module.side_effects().await? != ModuleSideEffects::SideEffectFree {
252 side_effects.push(only_effects(*current_module).to_resolved().await?);
253 }
254
255 let result = follow_reexports(*current_module, current_export_name.clone(), true)
257 .to_resolved()
258 .await?;
259
260 let FollowExportsResult {
261 module,
262 export_name,
263 ty,
264 } = &*result.await?;
265
266 match ty {
267 FoundExportType::SideEffects => {
268 current_module = *module;
269 current_export_name = export_name.clone().unwrap_or(current_export_name);
270 }
271 _ => break result,
272 }
273 };
274
275 Ok(FollowExportsWithSideEffectsResult {
276 side_effects,
277 result,
278 }
279 .cell())
280}
281
282#[turbo_tasks::value_impl]
283impl Module for EcmascriptModulePartAsset {
284 #[turbo_tasks::function]
285 async fn ident(&self) -> Result<Vc<AssetIdent>> {
286 Ok(self
287 .full_module
288 .ident()
289 .owned()
290 .await?
291 .with_part(self.part.clone())
292 .into_vc())
293 }
294
295 #[turbo_tasks::function]
296 fn source(&self) -> Vc<turbopack_core::source::OptionSource> {
297 Vc::cell(None)
298 }
299
300 #[turbo_tasks::function]
301 fn is_self_async(self: Vc<Self>) -> Vc<bool> {
302 self.is_async_module()
303 }
304
305 #[turbo_tasks::function]
306 async fn references(&self) -> Result<Vc<ModuleReferences>> {
307 let part_dep = |part: ModulePart| -> Vc<Box<dyn ModuleReference>> {
308 let export = match &part {
309 ModulePart::Export(export) => ExportUsage::named(export.clone()),
310 ModulePart::Evaluation => ExportUsage::evaluation(),
311 _ => ExportUsage::all(),
312 };
313
314 Vc::upcast(SingleChunkableModuleReference::new(
315 Vc::upcast(EcmascriptModulePartAsset::new_with_resolved_part(
316 *self.full_module,
317 part,
318 )),
319 rcstr!("part reference"),
320 export,
321 ))
322 };
323
324 if let ModulePart::Facade = self.part {
325 let mut references = vec![];
327 references.push(part_dep(ModulePart::evaluation()).to_resolved().await?);
328 references.push(part_dep(ModulePart::exports()).to_resolved().await?);
329 return Ok(Vc::cell(references));
330 }
331
332 let analyze = analyze_ecmascript_module(*self.full_module, Some(self.part.clone()));
333
334 Ok(analyze.references())
335 }
336
337 #[turbo_tasks::function]
338 async fn side_effects(&self) -> Vc<ModuleSideEffects> {
339 match self.part {
340 ModulePart::Exports | ModulePart::Export(..) => {
341 ModuleSideEffects::SideEffectFree.cell()
342 }
343 _ => self.full_module.side_effects(),
344 }
345 }
346}
347
348#[turbo_tasks::value_impl]
349impl EcmascriptChunkPlaceable for EcmascriptModulePartAsset {
350 #[turbo_tasks::function]
351 async fn get_exports(&self) -> Result<Vc<EcmascriptExports>> {
352 Ok(
353 *compute_ecmascript_module_exports(*self.full_module, Some(self.part.clone()))
354 .await?
355 .exports,
356 )
357 }
358
359 #[turbo_tasks::function]
360 async fn chunk_item_content(
361 self: Vc<Self>,
362 chunking_context: Vc<Box<dyn ChunkingContext>>,
363 _module_graph: Vc<ModuleGraph>,
364 async_module_info: Option<Vc<AsyncModuleInfo>>,
365 _estimated: bool,
366 ) -> Result<Vc<EcmascriptChunkItemContent>> {
367 let analyze = self.analyze().await?;
368 let async_module_options = analyze.async_module.module_options(async_module_info);
369
370 let content = self.module_content(chunking_context, async_module_info);
371
372 Ok(EcmascriptChunkItemContent::new(
373 content,
374 chunking_context,
375 async_module_options,
376 ))
377 }
378}
379
380#[turbo_tasks::value_impl]
381impl ChunkableModule for EcmascriptModulePartAsset {
382 #[turbo_tasks::function]
383 fn as_chunk_item(
384 self: ResolvedVc<Self>,
385 module_graph: ResolvedVc<ModuleGraph>,
386 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
387 ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
388 ecmascript_chunk_item(ResolvedVc::upcast(self), module_graph, chunking_context)
389 }
390}
391
392#[turbo_tasks::value_impl]
393impl EcmascriptModulePartAsset {
394 #[turbo_tasks::function]
395 pub(super) fn analyze(&self) -> Vc<AnalyzeEcmascriptModuleResult> {
396 analyze_ecmascript_module(*self.full_module, Some(self.part.clone()))
397 }
398}
399
400#[turbo_tasks::value_impl]
401impl EvaluatableAsset for EcmascriptModulePartAsset {}
402
403#[turbo_tasks::function]
404async fn only_effects(
405 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
406) -> Result<Vc<Box<dyn EcmascriptChunkPlaceable>>> {
407 if let Some(module) = ResolvedVc::try_downcast_type::<EcmascriptModuleAsset>(module) {
408 let module =
409 EcmascriptModulePartAsset::new_with_resolved_part(*module, ModulePart::evaluation());
410 return Ok(Vc::upcast(module));
411 }
412
413 Ok(*module)
414}