Skip to main content

turbopack_ecmascript/module_fragments/side_effects/
module.rs

1use anyhow::Result;
2use turbo_rcstr::{RcStr, rcstr};
3use turbo_tasks::{ResolvedVc, TryJoinIterExt, Vc};
4use turbo_tasks_fs::rope::RopeBuilder;
5use turbopack_core::{
6    chunk::{
7        AsyncModuleInfo, ChunkableModule, ChunkingContext, EvaluatableAsset, ModuleChunkItemIdExt,
8    },
9    ident::AssetIdent,
10    module::{Module, ModuleSideEffects},
11    module_graph::ModuleGraph,
12    reference::{ModuleReferences, SingleChunkableModuleReference},
13    resolve::{ExportUsage, ModulePart},
14};
15
16use crate::{
17    EcmascriptModuleAsset,
18    chunk::{
19        EcmascriptChunkItemContent, EcmascriptChunkItemOptions, EcmascriptChunkPlaceable,
20        EcmascriptExports, ecmascript_chunk_item, item::RewriteSourcePath,
21    },
22    references::async_module::AsyncModuleOptions,
23    runtime_functions::{TURBOPACK_EXPORT_NAMESPACE, TURBOPACK_IMPORT},
24    utils::StringifyModuleId,
25};
26
27#[turbo_tasks::value]
28pub struct SideEffectsModule {
29    /// Original module
30    pub module: ResolvedVc<EcmascriptModuleAsset>,
31    /// The part of the original module that is the binding
32    pub part: ModulePart,
33    /// The module that is the binding
34    pub resolved_as: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
35    /// Side effects from the original module to the binding.
36    pub side_effects: Vec<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>>,
37}
38
39#[turbo_tasks::value_impl]
40impl SideEffectsModule {
41    #[turbo_tasks::function]
42    pub fn new(
43        module: ResolvedVc<EcmascriptModuleAsset>,
44        part: ModulePart,
45        resolved_as: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
46        side_effects: Vec<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>>,
47    ) -> Vc<Self> {
48        SideEffectsModule {
49            module,
50            part,
51            resolved_as,
52            side_effects,
53        }
54        .cell()
55    }
56}
57
58fn side_effect_reference(
59    side_effect: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
60) -> Vc<SingleChunkableModuleReference> {
61    SingleChunkableModuleReference::new(
62        *ResolvedVc::upcast(side_effect),
63        rcstr!("side effect"),
64        ExportUsage::evaluation(),
65    )
66}
67
68#[turbo_tasks::value_impl]
69impl Module for SideEffectsModule {
70    #[turbo_tasks::function]
71    async fn ident(&self) -> Result<Vc<AssetIdent>> {
72        let mut ident = self
73            .module
74            .ident()
75            .owned()
76            .await?
77            .with_part(self.part.clone())
78            .with_asset(
79                rcstr!("resolved"),
80                self.resolved_as.ident().to_resolved().await?,
81            )
82            .with_modifier(rcstr!("side effects"));
83
84        for (i, side_effect) in self.side_effects.iter().enumerate() {
85            ident = ident.with_asset(
86                RcStr::from(format!("side effect {i}")),
87                side_effect.ident().to_resolved().await?,
88            );
89        }
90
91        Ok(ident.into_vc())
92    }
93
94    #[turbo_tasks::function]
95    fn source(&self) -> Vc<turbopack_core::source::OptionSource> {
96        Vc::cell(None)
97    }
98
99    #[turbo_tasks::function]
100    async fn references(&self) -> Result<Vc<ModuleReferences>> {
101        let mut references = vec![];
102
103        references.extend(
104            self.side_effects
105                .iter()
106                .map(async |side_effect| {
107                    Ok(ResolvedVc::upcast(
108                        side_effect_reference(*side_effect).to_resolved().await?,
109                    ))
110                })
111                .try_join()
112                .await?,
113        );
114
115        references.push(ResolvedVc::upcast(
116            SingleChunkableModuleReference::new(
117                *ResolvedVc::upcast(self.resolved_as),
118                rcstr!("resolved as"),
119                ExportUsage::all(),
120            )
121            .to_resolved()
122            .await?,
123        ));
124
125        Ok(Vc::cell(references))
126    }
127
128    #[turbo_tasks::function]
129    fn side_effects(self: Vc<Self>) -> Vc<ModuleSideEffects> {
130        // This module exists to collect side effects from references.  So it isn't side effectful
131        // but it may depend on side effectful modules.  use this mode to allow inner graph tree
132        // shaking to still potentially trim this module and its dependencies.
133        ModuleSideEffects::ModuleEvaluationIsSideEffectFree.cell()
134    }
135}
136
137#[turbo_tasks::value_impl]
138impl EcmascriptChunkPlaceable for SideEffectsModule {
139    #[turbo_tasks::function]
140    fn get_exports(&self) -> Vc<EcmascriptExports> {
141        // Borrowed from another module identity, so it must not carry a mangling decision.
142        self.resolved_as.get_exports().borrowed()
143    }
144
145    #[turbo_tasks::function]
146    async fn chunk_item_content(
147        self: Vc<Self>,
148        chunking_context: Vc<Box<dyn ChunkingContext>>,
149        _module_graph: Vc<ModuleGraph>,
150        _async_module_info: Option<Vc<AsyncModuleInfo>>,
151        _estimated: bool,
152    ) -> Result<Vc<EcmascriptChunkItemContent>> {
153        let module = self.await?;
154        let mut code = RopeBuilder::default();
155        let mut has_top_level_await = false;
156
157        let unused_references = chunking_context.unused_references();
158
159        for &side_effect in module.side_effects.iter() {
160            let reference = side_effect_reference(side_effect).to_resolved().await?;
161            if unused_references
162                .contains_key(&ResolvedVc::upcast(reference))
163                .await?
164            {
165                continue;
166            }
167
168            let need_await = 'need_await: {
169                let async_module = *side_effect.get_async_module().await?;
170                if let Some(async_module) = async_module
171                    && async_module.await?.has_top_level_await
172                {
173                    break 'need_await true;
174                }
175                false
176            };
177
178            if !has_top_level_await && need_await {
179                has_top_level_await = true;
180            }
181
182            code.push_bytes(
183                format!(
184                    "{}{TURBOPACK_IMPORT}({});\n",
185                    if need_await { "await " } else { "" },
186                    StringifyModuleId(&side_effect.chunk_item_id(chunking_context).await?)
187                )
188                .as_bytes(),
189            );
190        }
191
192        code.push_bytes(
193            format!(
194                "{TURBOPACK_EXPORT_NAMESPACE}({TURBOPACK_IMPORT}({}));\n",
195                StringifyModuleId(&module.resolved_as.chunk_item_id(chunking_context).await?)
196            )
197            .as_bytes(),
198        );
199
200        let code = code.build();
201
202        Ok(EcmascriptChunkItemContent {
203            inner_code: code,
204            source_map: None,
205            rewrite_source_path: RewriteSourcePath::None,
206            options: EcmascriptChunkItemOptions {
207                strict: true,
208                async_module: if has_top_level_await {
209                    Some(AsyncModuleOptions {
210                        has_top_level_await: true,
211                    })
212                } else {
213                    None
214                },
215                supports_arrow_functions: *chunking_context
216                    .environment()
217                    .runtime_versions()
218                    .supports_arrow_functions()
219                    .await?,
220                ..Default::default()
221            },
222            additional_ids: Default::default(),
223            placeholder_for_future_extensions: (),
224        }
225        .cell())
226    }
227}
228
229#[turbo_tasks::value_impl]
230impl ChunkableModule for SideEffectsModule {
231    #[turbo_tasks::function]
232    fn as_chunk_item(
233        self: ResolvedVc<Self>,
234        module_graph: ResolvedVc<ModuleGraph>,
235        chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
236    ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
237        ecmascript_chunk_item(ResolvedVc::upcast(self), module_graph, chunking_context)
238    }
239}
240
241#[turbo_tasks::value_impl]
242impl EvaluatableAsset for SideEffectsModule {}