Skip to main content

turbopack_ecmascript/tree_shake/
side_effect_module.rs

1use anyhow::Result;
2use turbo_rcstr::{RcStr, rcstr};
3use turbo_tasks::{ResolvedVc, TryJoinIterExt, Vc};
4use turbopack_core::{
5    chunk::{ChunkableModule, ChunkingContext, EvaluatableAsset},
6    ident::AssetIdent,
7    module::{Module, ModuleSideEffects},
8    module_graph::ModuleGraph,
9    reference::{ModuleReferences, SingleChunkableModuleReference},
10    resolve::{ExportUsage, ModulePart},
11};
12
13use crate::{
14    EcmascriptModuleAsset,
15    chunk::{EcmascriptChunkPlaceable, EcmascriptExports},
16    tree_shake::chunk_item::SideEffectsModuleChunkItem,
17};
18
19#[turbo_tasks::value]
20pub(super) struct SideEffectsModule {
21    /// Original module
22    pub module: ResolvedVc<EcmascriptModuleAsset>,
23    /// The part of the original module that is the binding
24    pub part: ModulePart,
25    /// The module that is the binding
26    pub resolved_as: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
27    /// Side effects from the original module to the binding.
28    pub side_effects: Vec<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>>,
29}
30
31#[turbo_tasks::value_impl]
32impl SideEffectsModule {
33    #[turbo_tasks::function]
34    pub fn new(
35        module: ResolvedVc<EcmascriptModuleAsset>,
36        part: ModulePart,
37        resolved_as: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
38        side_effects: Vec<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>>,
39    ) -> Vc<Self> {
40        SideEffectsModule {
41            module,
42            part,
43            resolved_as,
44            side_effects,
45        }
46        .cell()
47    }
48}
49
50#[turbo_tasks::value_impl]
51impl Module for SideEffectsModule {
52    #[turbo_tasks::function]
53    async fn ident(&self) -> Result<Vc<AssetIdent>> {
54        let mut ident = self.module.ident().owned().await?;
55        ident.parts.push(self.part.clone());
56
57        ident.add_asset(
58            rcstr!("resolved"),
59            self.resolved_as.ident().to_resolved().await?,
60        );
61
62        ident.add_modifier(rcstr!("side effects"));
63
64        for (i, side_effect) in self.side_effects.iter().enumerate() {
65            ident.add_asset(
66                RcStr::from(format!("side effect {i}")),
67                side_effect.ident().to_resolved().await?,
68            );
69        }
70
71        Ok(AssetIdent::new(ident))
72    }
73
74    #[turbo_tasks::function]
75    fn source(&self) -> Vc<turbopack_core::source::OptionSource> {
76        Vc::cell(None)
77    }
78
79    #[turbo_tasks::function]
80    async fn references(&self) -> Result<Vc<ModuleReferences>> {
81        let mut references = vec![];
82
83        references.extend(
84            self.side_effects
85                .iter()
86                .map(|side_effect| async move {
87                    Ok(ResolvedVc::upcast(
88                        SingleChunkableModuleReference::new(
89                            *ResolvedVc::upcast(*side_effect),
90                            rcstr!("side effect"),
91                            ExportUsage::evaluation(),
92                        )
93                        .to_resolved()
94                        .await?,
95                    ))
96                })
97                .try_join()
98                .await?,
99        );
100
101        references.push(ResolvedVc::upcast(
102            SingleChunkableModuleReference::new(
103                *ResolvedVc::upcast(self.resolved_as),
104                rcstr!("resolved as"),
105                ExportUsage::all(),
106            )
107            .to_resolved()
108            .await?,
109        ));
110
111        Ok(Vc::cell(references))
112    }
113
114    #[turbo_tasks::function]
115    fn side_effects(self: Vc<Self>) -> Vc<ModuleSideEffects> {
116        // This module exists to collect side effects from references.  So it isn't side effectful
117        // but it may depend on side effectful modules.  use this mode to allow inner graph tree
118        // shaking to still potentially trim this module and its dependencies.
119        ModuleSideEffects::ModuleEvaluationIsSideEffectFree.cell()
120    }
121}
122
123#[turbo_tasks::value_impl]
124impl EcmascriptChunkPlaceable for SideEffectsModule {
125    #[turbo_tasks::function]
126    fn get_exports(&self) -> Vc<EcmascriptExports> {
127        self.resolved_as.get_exports()
128    }
129}
130
131#[turbo_tasks::value_impl]
132impl ChunkableModule for SideEffectsModule {
133    #[turbo_tasks::function]
134    async fn as_chunk_item(
135        self: Vc<Self>,
136        module_graph: ResolvedVc<ModuleGraph>,
137        chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
138    ) -> Result<Vc<Box<dyn turbopack_core::chunk::ChunkItem>>> {
139        Ok(Vc::upcast(
140            SideEffectsModuleChunkItem {
141                module: self.to_resolved().await?,
142                module_graph,
143                chunking_context,
144            }
145            .cell(),
146        ))
147    }
148}
149
150#[turbo_tasks::value_impl]
151impl EvaluatableAsset for SideEffectsModule {}