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            let mangle_via_materialized_namespace_object = module
37                .options()
38                .await?
39                .mangle_via_materialized_namespace_object;
40            if *module
41                .get_exports()
42                .split_locals_and_reexports(mangle_via_materialized_namespace_object)
43                .await?
44            {
45                if let Some(part) = part {
46                    match part {
47                        ModulePart::Evaluation => {
48                            Vc::upcast(EcmascriptModuleLocalsModule::new(*module))
49                        }
50                        ModulePart::Export(_) | ModulePart::PartialExport { .. } => {
51                            apply_reexport_tree_shaking(
52                                Vc::upcast(
53                                    *EcmascriptModuleFacadeModule::new(Vc::upcast(*module))
54                                        .to_resolved()
55                                        .await?,
56                                ),
57                                part,
58                            )
59                            .await?
60                        }
61                        _ => bail!(
62                            "Invalid module part \"{}\" for reexports only tree shaking mode",
63                            part
64                        ),
65                    }
66                } else {
67                    Vc::upcast(EcmascriptModuleFacadeModule::new(Vc::upcast(*module)))
68                }
69            } else {
70                Vc::upcast(*module)
71            }
72        }
73    })
74}
75
76async fn apply_reexport_tree_shaking(
77    module: Vc<Box<dyn EcmascriptChunkPlaceable>>,
78    part: ModulePart,
79) -> Result<Vc<Box<dyn EcmascriptChunkPlaceable>>> {
80    // The namespace member is only applied if this export resolves to a namespace object.
81    let (export, namespace_member) = match &part {
82        ModulePart::Export(export) => (export, None),
83        ModulePart::PartialExport { export, member } => (export, Some(member)),
84        _ => return Ok(module),
85    };
86
87    let FollowExportsResult {
88        module: final_module,
89        export_name: new_export,
90        ..
91    } = &*follow_reexports(module, export.clone(), true).await?;
92    Ok(if let Some(new_export) = new_export {
93        if *new_export == *export {
94            **final_module
95        } else {
96            Vc::upcast(EcmascriptModuleRenameModule::new(
97                **final_module,
98                ModulePart::renamed_export(new_export.clone(), export.clone()),
99            ))
100        }
101    } else {
102        Vc::upcast(EcmascriptModuleRenameModule::new(
103            **final_module,
104            match namespace_member {
105                Some(member) => {
106                    ModulePart::renamed_partial_namespace(export.clone(), member.clone())
107                }
108                None => ModulePart::renamed_namespace(export.clone()),
109            },
110        ))
111    })
112}