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