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