Skip to main content

turbopack_ecmascript/
module_canonicalization.rs

1use 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    // The namespace member is only applied if this export resolves to a namespace object.
73    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}