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 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 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}