turbopack_ecmascript/module_fragments/part/
module.rs1use anyhow::Result;
2use turbo_rcstr::{RcStr, rcstr};
3use turbo_tasks::{ResolvedVc, Vc};
4use turbopack_core::{
5 chunk::{AsyncModuleInfo, ChunkableModule, ChunkingContext, EvaluatableAsset},
6 ident::AssetIdent,
7 module::{Module, ModuleSideEffects},
8 module_graph::ModuleGraph,
9 reference::{ModuleReference, ModuleReferences, SingleChunkableModuleReference},
10 resolve::{ExportUsage, ModulePart},
11};
12
13use crate::{
14 AnalyzeEcmascriptModuleResult, EcmascriptAnalyzable, EcmascriptAnalyzableExt,
15 EcmascriptModuleAsset, EcmascriptModuleContent, EcmascriptModuleContentOptions,
16 EcmascriptParsable, EnvVarInfo,
17 chunk::{
18 EcmascriptChunkItemContent, EcmascriptChunkPlaceable, EcmascriptExports,
19 ecmascript_chunk_item,
20 },
21 module_fragments::{
22 Key, SplitResult, get_part_id, part_of_module, side_effects::module::SideEffectsModule,
23 split_module,
24 },
25 parse::ParseResult,
26 references::{
27 FollowExportsResult, analyze_ecmascript_module, esm::FoundExportType,
28 exports::compute_ecmascript_module_exports, follow_reexports,
29 },
30 rename::module::EcmascriptModuleRenameModule,
31};
32
33#[turbo_tasks::value]
37pub struct EcmascriptModulePartAsset {
38 pub full_module: ResolvedVc<EcmascriptModuleAsset>,
39 pub part: ModulePart,
40}
41
42#[turbo_tasks::value_impl]
43impl EcmascriptParsable for EcmascriptModulePartAsset {
44 #[turbo_tasks::function]
45 fn failsafe_parse(&self) -> Result<Vc<ParseResult>> {
46 let split_data = split_module(*self.full_module);
47 assert_ne!(self.part, ModulePart::Facade);
48 Ok(part_of_module(split_data, self.part.clone()))
49 }
50}
51
52#[turbo_tasks::value_impl]
53impl EcmascriptAnalyzable for EcmascriptModulePartAsset {
54 #[turbo_tasks::function]
55 fn analyze(&self) -> Vc<AnalyzeEcmascriptModuleResult> {
56 analyze_ecmascript_module(*self.full_module, Some(self.part.clone()))
57 }
58
59 #[turbo_tasks::function]
60 async fn env_var_info(self: Vc<Self>) -> Result<Vc<EnvVarInfo>> {
61 Ok(*self.analyze().await?.env_var_info)
62 }
63
64 #[turbo_tasks::function]
65 fn module_content_without_analysis(
66 &self,
67 generate_source_map: bool,
68 ) -> Vc<EcmascriptModuleContent> {
69 self.full_module
70 .module_content_without_analysis(generate_source_map)
71 }
72
73 #[turbo_tasks::function]
74 async fn module_content_options(
75 self: ResolvedVc<Self>,
76 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
77 async_module_info: Option<ResolvedVc<AsyncModuleInfo>>,
78 ) -> Result<Vc<EcmascriptModuleContentOptions>> {
79 let module = self.await?;
80
81 let split_data = split_module(*module.full_module);
82 let parsed = part_of_module(split_data, module.part.clone())
83 .to_resolved()
84 .await?;
85
86 let analyze = self.analyze();
87 let analyze_ref = analyze.await?;
88
89 let module_type_result = module.full_module.determine_module_type().await?;
90 let generate_source_map = *chunking_context
91 .reference_module_source_maps(Vc::upcast(*self))
92 .await?;
93 Ok(EcmascriptModuleContentOptions {
94 parsed: Some(parsed),
95 module: ResolvedVc::upcast(self),
96 specified_module_type: module_type_result.module_type,
97 chunking_context,
98 references: analyze.references().to_resolved().await?,
99 esm_references: analyze_ref.esm_references,
100 part_references: vec![],
101 code_generation: analyze_ref.code_generation,
102 async_module: analyze_ref.async_module,
103 generate_source_map,
104 original_source_map: analyze_ref.source_map,
105 exports: self.get_exports().to_resolved().await?,
106 export_registration_mode: None,
107 async_module_info,
108 }
109 .cell())
110 }
111}
112
113#[turbo_tasks::value_impl]
114impl EcmascriptModulePartAsset {
115 #[turbo_tasks::function]
119 fn new_raw(module: ResolvedVc<EcmascriptModuleAsset>, part: ModulePart) -> Vc<Self> {
120 Self {
121 full_module: module,
122 part,
123 }
124 .cell()
125 }
126
127 #[turbo_tasks::function]
128 pub async fn new_with_resolved_part(
129 module: ResolvedVc<EcmascriptModuleAsset>,
130 part: ModulePart,
131 ) -> Result<Vc<Self>> {
132 if matches!(
133 part,
134 ModulePart::Internal(..) | ModulePart::Facade | ModulePart::Exports
135 ) {
136 return Ok(Self::new_raw(*module, part));
137 }
138
139 let split_result = split_module(*module).await?;
141 let part_id = get_part_id(&split_result, &part).await?;
142
143 Ok(Self::new_raw(*module, ModulePart::internal(part_id)))
144 }
145
146 #[turbo_tasks::function]
147 pub async fn select_part(
148 module: Vc<EcmascriptModuleAsset>,
149 part: ModulePart,
150 ) -> Result<Vc<Box<dyn EcmascriptChunkPlaceable>>> {
151 let SplitResult::Ok { entrypoints, .. } = &*split_module(module).await? else {
152 return Ok(Vc::upcast(module));
153 };
154
155 match part {
156 ModulePart::Evaluation => {
157 let idx = *entrypoints.get(&Key::ModuleEvaluation).unwrap();
159 return Ok(Vc::upcast(
160 EcmascriptModulePartAsset::new_with_resolved_part(
161 module,
162 ModulePart::internal(idx),
163 ),
164 ));
165 }
166
167 part @ (ModulePart::Export(_) | ModulePart::PartialExport { .. }) => {
168 let (export, namespace_member) = match part {
169 ModulePart::Export(export) => (export, None),
170 ModulePart::PartialExport { export, member } => (export, Some(member)),
171 _ => unreachable!(),
172 };
173 if entrypoints.contains_key(&Key::Export(export.clone())) {
174 return Ok(Vc::upcast(
175 EcmascriptModulePartAsset::new_with_resolved_part(
176 module,
177 ModulePart::Export(export),
178 ),
179 ));
180 }
181 let source_module = Vc::upcast(module);
182 let FollowExportsWithSideEffectsResult {
183 side_effects,
184 result,
185 } = &*follow_reexports_with_side_effects(source_module, export.clone()).await?;
186 let FollowExportsResult {
187 module: final_module,
188 export_name: new_export,
189 ..
190 } = &*result.await?;
191 let final_module = if let Some(new_export) = new_export {
192 if *new_export == export {
193 *final_module
194 } else {
195 ResolvedVc::upcast(
196 EcmascriptModuleRenameModule::new(
197 **final_module,
198 ModulePart::renamed_export(new_export.clone(), export.clone()),
199 )
200 .to_resolved()
201 .await?,
202 )
203 }
204 } else {
205 ResolvedVc::upcast(
206 EcmascriptModuleRenameModule::new(
207 **final_module,
208 match namespace_member {
209 Some(member) => {
210 ModulePart::renamed_partial_namespace(export.clone(), member)
211 }
212 None => ModulePart::renamed_namespace(export.clone()),
213 },
214 )
215 .to_resolved()
216 .await?,
217 )
218 };
219 if side_effects.is_empty() {
220 return Ok(*final_module);
221 }
222 let side_effects_module = SideEffectsModule::new(
223 module,
224 ModulePart::Export(export),
225 *final_module,
226 side_effects.iter().map(|v| **v).collect(),
227 );
228 return Ok(Vc::upcast(side_effects_module));
229 }
230 _ => (),
231 }
232
233 Ok(Vc::upcast(
234 EcmascriptModulePartAsset::new_with_resolved_part(module, part.clone()),
235 ))
236 }
237
238 #[turbo_tasks::function]
239 pub async fn is_async_module(self: Vc<Self>) -> Result<Vc<bool>> {
240 let this = self.await?;
241 let result = analyze_ecmascript_module(*this.full_module, Some(this.part.clone()));
242
243 if let Some(async_module) = *result.await?.async_module.await? {
244 Ok(async_module.is_self_async(self.references()))
245 } else {
246 Ok(Vc::cell(false))
247 }
248 }
249}
250
251#[turbo_tasks::value]
252struct FollowExportsWithSideEffectsResult {
253 side_effects: Vec<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>>,
254 result: ResolvedVc<FollowExportsResult>,
255}
256
257#[turbo_tasks::function]
258async fn follow_reexports_with_side_effects(
259 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
260 export_name: RcStr,
261) -> Result<Vc<FollowExportsWithSideEffectsResult>> {
262 let mut side_effects = vec![];
263
264 let mut current_module = module;
265 let mut current_export_name = export_name;
266 let result = loop {
267 if *current_module.side_effects().await? != ModuleSideEffects::SideEffectFree {
268 side_effects.push(only_effects(*current_module).to_resolved().await?);
269 }
270
271 let result = follow_reexports(*current_module, current_export_name.clone(), true)
273 .to_resolved()
274 .await?;
275
276 let FollowExportsResult {
277 module,
278 export_name,
279 ty,
280 } = &*result.await?;
281
282 match ty {
283 FoundExportType::SideEffects => {
284 current_module = *module;
285 current_export_name = export_name.clone().unwrap_or(current_export_name);
286 }
287 _ => break result,
288 }
289 };
290
291 Ok(FollowExportsWithSideEffectsResult {
292 side_effects,
293 result,
294 }
295 .cell())
296}
297
298#[turbo_tasks::value_impl]
299impl Module for EcmascriptModulePartAsset {
300 #[turbo_tasks::function]
301 async fn ident(&self) -> Result<Vc<AssetIdent>> {
302 Ok(self
303 .full_module
304 .ident()
305 .owned()
306 .await?
307 .with_part(self.part.clone())
308 .into_vc())
309 }
310
311 #[turbo_tasks::function]
312 fn source(&self) -> Vc<turbopack_core::source::OptionSource> {
313 Vc::cell(None)
314 }
315
316 #[turbo_tasks::function]
317 fn is_self_async(self: Vc<Self>) -> Vc<bool> {
318 self.is_async_module()
319 }
320
321 #[turbo_tasks::function]
322 async fn references(&self) -> Result<Vc<ModuleReferences>> {
323 let part_dep = |part: ModulePart| -> Vc<Box<dyn ModuleReference>> {
324 let export = match &part {
325 ModulePart::Export(export) | ModulePart::PartialExport { export, .. } => {
326 ExportUsage::named(export.clone())
327 }
328 ModulePart::Evaluation => ExportUsage::evaluation(),
329 _ => ExportUsage::all(),
330 };
331
332 Vc::upcast(SingleChunkableModuleReference::new(
333 Vc::upcast(EcmascriptModulePartAsset::new_with_resolved_part(
334 *self.full_module,
335 part,
336 )),
337 rcstr!("part reference"),
338 export,
339 ))
340 };
341
342 if let ModulePart::Facade = self.part {
343 let mut references = vec![];
345 references.push(part_dep(ModulePart::evaluation()).to_resolved().await?);
346 references.push(part_dep(ModulePart::exports()).to_resolved().await?);
347 return Ok(Vc::cell(references));
348 }
349
350 let analyze = analyze_ecmascript_module(*self.full_module, Some(self.part.clone()));
351
352 Ok(analyze.references())
353 }
354
355 #[turbo_tasks::function]
356 async fn side_effects(&self) -> Vc<ModuleSideEffects> {
357 match self.part {
358 ModulePart::Exports | ModulePart::Export(_) | ModulePart::PartialExport { .. } => {
359 ModuleSideEffects::SideEffectFree.cell()
360 }
361 _ => self.full_module.side_effects(),
362 }
363 }
364}
365
366#[turbo_tasks::value_impl]
367impl EcmascriptChunkPlaceable for EcmascriptModulePartAsset {
368 #[turbo_tasks::function]
369 async fn get_exports(&self) -> Result<Vc<EcmascriptExports>> {
370 Ok(
371 *compute_ecmascript_module_exports(*self.full_module, Some(self.part.clone()))
372 .await?
373 .exports,
374 )
375 }
376
377 #[turbo_tasks::function]
378 async fn chunk_item_content(
379 self: Vc<Self>,
380 chunking_context: Vc<Box<dyn ChunkingContext>>,
381 _module_graph: Vc<ModuleGraph>,
382 async_module_info: Option<Vc<AsyncModuleInfo>>,
383 _estimated: bool,
384 ) -> Result<Vc<EcmascriptChunkItemContent>> {
385 let analyze = self.analyze().await?;
386 let async_module_options = analyze.async_module.module_options(async_module_info);
387
388 let content = self.module_content(chunking_context, async_module_info);
389
390 Ok(EcmascriptChunkItemContent::new(
391 content,
392 chunking_context,
393 async_module_options,
394 ))
395 }
396}
397
398#[turbo_tasks::value_impl]
399impl ChunkableModule for EcmascriptModulePartAsset {
400 #[turbo_tasks::function]
401 fn as_chunk_item(
402 self: ResolvedVc<Self>,
403 module_graph: ResolvedVc<ModuleGraph>,
404 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
405 ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
406 ecmascript_chunk_item(ResolvedVc::upcast(self), module_graph, chunking_context)
407 }
408}
409
410#[turbo_tasks::value_impl]
411impl EcmascriptModulePartAsset {
412 #[turbo_tasks::function]
413 pub(super) fn analyze(&self) -> Vc<AnalyzeEcmascriptModuleResult> {
414 analyze_ecmascript_module(*self.full_module, Some(self.part.clone()))
415 }
416}
417
418#[turbo_tasks::value_impl]
419impl EvaluatableAsset for EcmascriptModulePartAsset {}
420
421#[turbo_tasks::function]
422async fn only_effects(
423 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
424) -> Result<Vc<Box<dyn EcmascriptChunkPlaceable>>> {
425 if let Some(module) = ResolvedVc::try_downcast_type::<EcmascriptModuleAsset>(module) {
426 let module =
427 EcmascriptModulePartAsset::new_with_resolved_part(*module, ModulePart::evaluation());
428 return Ok(Vc::upcast(module));
429 }
430
431 Ok(*module)
432}