turbopack_ecmascript/
module_canonicalization.rs1use anyhow::{Result, bail};
2use bincode::{Decode, Encode};
3use turbo_tasks::{ResolvedVc, Vc};
4use turbopack_core::resolve::ModulePart;
5
6use crate::{
7 EcmascriptModuleAsset,
8 chunk::EcmascriptChunkPlaceable,
9 module_fragments::part::module::EcmascriptModulePartAsset,
10 references::{FollowExportsResult, follow_reexports},
11 rename::module::EcmascriptModuleRenameModule,
12 side_effect_optimization::{
13 facade::module::EcmascriptModuleFacadeModule, locals::module::EcmascriptModuleLocalsModule,
14 },
15};
16
17#[turbo_tasks::task_input]
18#[derive(Clone, Debug, Hash, PartialEq, Eq, Encode, Decode)]
19pub enum EcmascriptModuleCanonicalization {
20 None,
21 ModuleFragments(ModulePart),
22 FollowReexports(Option<ModulePart>),
23}
24
25#[turbo_tasks::function]
26pub async fn canonicalize_ecmascript_module(
27 module: ResolvedVc<EcmascriptModuleAsset>,
28 canonicalization: EcmascriptModuleCanonicalization,
29) -> Result<Vc<Box<dyn EcmascriptChunkPlaceable>>> {
30 Ok(match canonicalization {
31 EcmascriptModuleCanonicalization::None => Vc::upcast(*module),
32 EcmascriptModuleCanonicalization::ModuleFragments(part) => {
33 EcmascriptModulePartAsset::select_part(*module, part)
34 }
35 EcmascriptModuleCanonicalization::FollowReexports(part) => {
36 if *module.get_exports().split_locals_and_reexports().await? {
37 if let Some(part) = part {
38 match part {
39 ModulePart::Evaluation => {
40 Vc::upcast(EcmascriptModuleLocalsModule::new(*module))
41 }
42 ModulePart::Export(_) | ModulePart::PartialExport { .. } => {
43 apply_reexport_tree_shaking(
44 Vc::upcast(
45 *EcmascriptModuleFacadeModule::new(Vc::upcast(*module))
46 .to_resolved()
47 .await?,
48 ),
49 part,
50 )
51 .await?
52 }
53 _ => bail!(
54 "Invalid module part \"{}\" for reexports only tree shaking mode",
55 part
56 ),
57 }
58 } else {
59 Vc::upcast(EcmascriptModuleFacadeModule::new(Vc::upcast(*module)))
60 }
61 } else {
62 Vc::upcast(*module)
63 }
64 }
65 })
66}
67
68async fn apply_reexport_tree_shaking(
69 module: Vc<Box<dyn EcmascriptChunkPlaceable>>,
70 part: ModulePart,
71) -> Result<Vc<Box<dyn EcmascriptChunkPlaceable>>> {
72 let (export, namespace_member) = match &part {
74 ModulePart::Export(export) => (export, None),
75 ModulePart::PartialExport { export, member } => (export, Some(member)),
76 _ => return Ok(module),
77 };
78
79 let FollowExportsResult {
80 module: final_module,
81 export_name: new_export,
82 ..
83 } = &*follow_reexports(module, export.clone(), true).await?;
84 Ok(if let Some(new_export) = new_export {
85 if *new_export == *export {
86 **final_module
87 } else {
88 Vc::upcast(EcmascriptModuleRenameModule::new(
89 **final_module,
90 ModulePart::renamed_export(new_export.clone(), export.clone()),
91 ))
92 }
93 } else {
94 Vc::upcast(EcmascriptModuleRenameModule::new(
95 **final_module,
96 match namespace_member {
97 Some(member) => {
98 ModulePart::renamed_partial_namespace(export.clone(), member.clone())
99 }
100 None => ModulePart::renamed_namespace(export.clone()),
101 },
102 ))
103 })
104}