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
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(|side_effect| async move {
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 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 self.resolved_as.get_exports()
138 }
139
140 #[turbo_tasks::function]
141 async fn chunk_item_content(
142 self: Vc<Self>,
143 chunking_context: Vc<Box<dyn ChunkingContext>>,
144 _module_graph: Vc<ModuleGraph>,
145 _async_module_info: Option<Vc<AsyncModuleInfo>>,
146 _estimated: bool,
147 ) -> Result<Vc<EcmascriptChunkItemContent>> {
148 let module = self.await?;
149 let mut code = RopeBuilder::default();
150 let mut has_top_level_await = false;
151
152 for &side_effect in module.side_effects.iter() {
153 let need_await = 'need_await: {
154 let async_module = *side_effect.get_async_module().await?;
155 if let Some(async_module) = async_module
156 && async_module.await?.has_top_level_await
157 {
158 break 'need_await true;
159 }
160 false
161 };
162
163 if !has_top_level_await && need_await {
164 has_top_level_await = true;
165 }
166
167 code.push_bytes(
168 format!(
169 "{}{TURBOPACK_IMPORT}({});\n",
170 if need_await { "await " } else { "" },
171 StringifyModuleId(&side_effect.chunk_item_id(chunking_context).await?)
172 )
173 .as_bytes(),
174 );
175 }
176
177 code.push_bytes(
178 format!(
179 "{TURBOPACK_EXPORT_NAMESPACE}({TURBOPACK_IMPORT}({}));\n",
180 StringifyModuleId(&module.resolved_as.chunk_item_id(chunking_context).await?)
181 )
182 .as_bytes(),
183 );
184
185 let code = code.build();
186
187 Ok(EcmascriptChunkItemContent {
188 inner_code: code,
189 source_map: None,
190 rewrite_source_path: RewriteSourcePath::None,
191 options: EcmascriptChunkItemOptions {
192 strict: true,
193 async_module: if has_top_level_await {
194 Some(AsyncModuleOptions {
195 has_top_level_await: true,
196 })
197 } else {
198 None
199 },
200 supports_arrow_functions: *chunking_context
201 .environment()
202 .runtime_versions()
203 .supports_arrow_functions()
204 .await?,
205 ..Default::default()
206 },
207 additional_ids: Default::default(),
208 placeholder_for_future_extensions: (),
209 }
210 .cell())
211 }
212}
213
214#[turbo_tasks::value_impl]
215impl ChunkableModule for SideEffectsModule {
216 #[turbo_tasks::function]
217 fn as_chunk_item(
218 self: ResolvedVc<Self>,
219 module_graph: ResolvedVc<ModuleGraph>,
220 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
221 ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
222 ecmascript_chunk_item(ResolvedVc::upcast(self), module_graph, chunking_context)
223 }
224}
225
226#[turbo_tasks::value_impl]
227impl EvaluatableAsset for SideEffectsModule {}