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
58#[turbo_tasks::value_impl]
59impl Module for SideEffectsModule {
60    #[turbo_tasks::function]
61    async fn ident(&self) -> Result<Vc<AssetIdent>> {
62        let mut ident = self
63            .module
64            .ident()
65            .owned()
66            .await?
67            .with_part(self.part.clone())
68            .with_asset(
69                rcstr!("resolved"),
70                self.resolved_as.ident().to_resolved().await?,
71            )
72            .with_modifier(rcstr!("side effects"));
73
74        for (i, side_effect) in self.side_effects.iter().enumerate() {
75            ident = ident.with_asset(
76                RcStr::from(format!("side effect {i}")),
77                side_effect.ident().to_resolved().await?,
78            );
79        }
80
81        Ok(ident.into_vc())
82    }
83
84    #[turbo_tasks::function]
85    fn source(&self) -> Vc<turbopack_core::source::OptionSource> {
86        Vc::cell(None)
87    }
88
89    #[turbo_tasks::function]
90    async fn references(&self) -> Result<Vc<ModuleReferences>> {
91        let mut references = vec![];
92
93        references.extend(
94            self.side_effects
95                .iter()
96                .map(async |side_effect| {
97                    Ok(ResolvedVc::upcast(
98                        SingleChunkableModuleReference::new(
99                            *ResolvedVc::upcast(*side_effect),
100                            rcstr!("side effect"),
101                            ExportUsage::evaluation(),
102                        )
103                        .to_resolved()
104                        .await?,
105                    ))
106                })
107                .try_join()
108                .await?,
109        );
110
111        references.push(ResolvedVc::upcast(
112            SingleChunkableModuleReference::new(
113                *ResolvedVc::upcast(self.resolved_as),
114                rcstr!("resolved as"),
115                ExportUsage::all(),
116            )
117            .to_resolved()
118            .await?,
119        ));
120
121        Ok(Vc::cell(references))
122    }
123
124    #[turbo_tasks::function]
125    fn side_effects(self: Vc<Self>) -> Vc<ModuleSideEffects> {
126        // This module exists to collect side effects from references.  So it isn't side effectful
127        // but it may depend on side effectful modules.  use this mode to allow inner graph tree
128        // shaking to still potentially trim this module and its dependencies.
129        ModuleSideEffects::ModuleEvaluationIsSideEffectFree.cell()
130    }
131}
132
133#[turbo_tasks::value_impl]
134impl EcmascriptChunkPlaceable for SideEffectsModule {
135    #[turbo_tasks::function]
136    fn get_exports(&self) -> Vc<EcmascriptExports> {
137        // Borrowed from another module identity, so it must not carry a mangling decision.
138        self.resolved_as.get_exports().borrowed()
139    }
140
141    #[turbo_tasks::function]
142    async fn chunk_item_content(
143        self: Vc<Self>,
144        chunking_context: Vc<Box<dyn ChunkingContext>>,
145        _module_graph: Vc<ModuleGraph>,
146        _async_module_info: Option<Vc<AsyncModuleInfo>>,
147        _estimated: bool,
148    ) -> Result<Vc<EcmascriptChunkItemContent>> {
149        let module = self.await?;
150        let mut code = RopeBuilder::default();
151        let mut has_top_level_await = false;
152
153        for &side_effect in module.side_effects.iter() {
154            let need_await = 'need_await: {
155                let async_module = *side_effect.get_async_module().await?;
156                if let Some(async_module) = async_module
157                    && async_module.await?.has_top_level_await
158                {
159                    break 'need_await true;
160                }
161                false
162            };
163
164            if !has_top_level_await && need_await {
165                has_top_level_await = true;
166            }
167
168            code.push_bytes(
169                format!(
170                    "{}{TURBOPACK_IMPORT}({});\n",
171                    if need_await { "await " } else { "" },
172                    StringifyModuleId(&side_effect.chunk_item_id(chunking_context).await?)
173                )
174                .as_bytes(),
175            );
176        }
177
178        code.push_bytes(
179            format!(
180                "{TURBOPACK_EXPORT_NAMESPACE}({TURBOPACK_IMPORT}({}));\n",
181                StringifyModuleId(&module.resolved_as.chunk_item_id(chunking_context).await?)
182            )
183            .as_bytes(),
184        );
185
186        let code = code.build();
187
188        Ok(EcmascriptChunkItemContent {
189            inner_code: code,
190            source_map: None,
191            rewrite_source_path: RewriteSourcePath::None,
192            options: EcmascriptChunkItemOptions {
193                strict: true,
194                async_module: if has_top_level_await {
195                    Some(AsyncModuleOptions {
196                        has_top_level_await: true,
197                    })
198                } else {
199                    None
200                },
201                supports_arrow_functions: *chunking_context
202                    .environment()
203                    .runtime_versions()
204                    .supports_arrow_functions()
205                    .await?,
206                ..Default::default()
207            },
208            additional_ids: Default::default(),
209            placeholder_for_future_extensions: (),
210        }
211        .cell())
212    }
213}
214
215#[turbo_tasks::value_impl]
216impl ChunkableModule for SideEffectsModule {
217    #[turbo_tasks::function]
218    fn as_chunk_item(
219        self: ResolvedVc<Self>,
220        module_graph: ResolvedVc<ModuleGraph>,
221        chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
222    ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
223        ecmascript_chunk_item(ResolvedVc::upcast(self), module_graph, chunking_context)
224    }
225}
226
227#[turbo_tasks::value_impl]
228impl EvaluatableAsset for SideEffectsModule {}