turbopack_ecmascript/tree_shake/side_effects/
module.rs1use 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 pub module: ResolvedVc<EcmascriptModuleAsset>,
31 pub part: ModulePart,
33 pub resolved_as: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
35 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.module.ident().owned().await?;
63 ident.parts.push(self.part.clone());
64
65 ident.add_asset(
66 rcstr!("resolved"),
67 self.resolved_as.ident().to_resolved().await?,
68 );
69
70 ident.add_modifier(rcstr!("side effects"));
71
72 for (i, side_effect) in self.side_effects.iter().enumerate() {
73 ident.add_asset(
74 RcStr::from(format!("side effect {i}")),
75 side_effect.ident().to_resolved().await?,
76 );
77 }
78
79 Ok(AssetIdent::new(ident))
80 }
81
82 #[turbo_tasks::function]
83 fn source(&self) -> Vc<turbopack_core::source::OptionSource> {
84 Vc::cell(None)
85 }
86
87 #[turbo_tasks::function]
88 async fn references(&self) -> Result<Vc<ModuleReferences>> {
89 let mut references = vec![];
90
91 references.extend(
92 self.side_effects
93 .iter()
94 .map(|side_effect| async move {
95 Ok(ResolvedVc::upcast(
96 SingleChunkableModuleReference::new(
97 *ResolvedVc::upcast(*side_effect),
98 rcstr!("side effect"),
99 ExportUsage::evaluation(),
100 )
101 .to_resolved()
102 .await?,
103 ))
104 })
105 .try_join()
106 .await?,
107 );
108
109 references.push(ResolvedVc::upcast(
110 SingleChunkableModuleReference::new(
111 *ResolvedVc::upcast(self.resolved_as),
112 rcstr!("resolved as"),
113 ExportUsage::all(),
114 )
115 .to_resolved()
116 .await?,
117 ));
118
119 Ok(Vc::cell(references))
120 }
121
122 #[turbo_tasks::function]
123 fn side_effects(self: Vc<Self>) -> Vc<ModuleSideEffects> {
124 ModuleSideEffects::ModuleEvaluationIsSideEffectFree.cell()
128 }
129}
130
131#[turbo_tasks::value_impl]
132impl EcmascriptChunkPlaceable for SideEffectsModule {
133 #[turbo_tasks::function]
134 fn get_exports(&self) -> Vc<EcmascriptExports> {
135 self.resolved_as.get_exports()
136 }
137
138 #[turbo_tasks::function]
139 async fn chunk_item_content(
140 self: Vc<Self>,
141 chunking_context: Vc<Box<dyn ChunkingContext>>,
142 _module_graph: Vc<ModuleGraph>,
143 _async_module_info: Option<Vc<AsyncModuleInfo>>,
144 _estimated: bool,
145 ) -> Result<Vc<EcmascriptChunkItemContent>> {
146 let module = self.await?;
147 let mut code = RopeBuilder::default();
148 let mut has_top_level_await = false;
149
150 for &side_effect in module.side_effects.iter() {
151 let need_await = 'need_await: {
152 let async_module = *side_effect.get_async_module().await?;
153 if let Some(async_module) = async_module
154 && async_module.await?.has_top_level_await
155 {
156 break 'need_await true;
157 }
158 false
159 };
160
161 if !has_top_level_await && need_await {
162 has_top_level_await = true;
163 }
164
165 code.push_bytes(
166 format!(
167 "{}{TURBOPACK_IMPORT}({});\n",
168 if need_await { "await " } else { "" },
169 StringifyModuleId(&side_effect.chunk_item_id(chunking_context).await?)
170 )
171 .as_bytes(),
172 );
173 }
174
175 code.push_bytes(
176 format!(
177 "{TURBOPACK_EXPORT_NAMESPACE}({TURBOPACK_IMPORT}({}));\n",
178 StringifyModuleId(&module.resolved_as.chunk_item_id(chunking_context).await?)
179 )
180 .as_bytes(),
181 );
182
183 let code = code.build();
184
185 Ok(EcmascriptChunkItemContent {
186 inner_code: code,
187 source_map: None,
188 rewrite_source_path: RewriteSourcePath::None,
189 options: EcmascriptChunkItemOptions {
190 strict: true,
191 async_module: if has_top_level_await {
192 Some(AsyncModuleOptions {
193 has_top_level_await: true,
194 })
195 } else {
196 None
197 },
198 ..Default::default()
199 },
200 additional_ids: Default::default(),
201 placeholder_for_future_extensions: (),
202 }
203 .cell())
204 }
205}
206
207#[turbo_tasks::value_impl]
208impl ChunkableModule for SideEffectsModule {
209 #[turbo_tasks::function]
210 fn as_chunk_item(
211 self: ResolvedVc<Self>,
212 module_graph: ResolvedVc<ModuleGraph>,
213 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
214 ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
215 ecmascript_chunk_item(ResolvedVc::upcast(self), module_graph, chunking_context)
216 }
217}
218
219#[turbo_tasks::value_impl]
220impl EvaluatableAsset for SideEffectsModule {}