turbopack_ecmascript/tree_shake/
side_effect_module.rs

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