1use std::{borrow::Cow, collections::BTreeMap, ops::ControlFlow};
2
3use anyhow::{Result, bail};
4use bincode::{Decode, Encode};
5use indexmap::map::Entry;
6use rustc_hash::FxHashSet;
7use swc_core::{
8 common::{DUMMY_SP, SyntaxContext},
9 ecma::ast::{
10 ArrayLit, AssignTarget, Expr, ExprStmt, Ident, Lit, Number, SimpleAssignTarget, Stmt, Str,
11 },
12 quote, quote_expr,
13};
14use turbo_rcstr::{RcStr, rcstr};
15use turbo_tasks::{
16 FxIndexMap, NonLocalValue, ResolvedVc, TryFlatJoinIterExt, Vc, trace::TraceRawVcs, turbofmt,
17};
18use turbopack_core::{
19 chunk::{ChunkingContext, ModuleChunkItemIdExt},
20 ident::AssetIdent,
21 issue::{IssueExt, IssueSeverity, StyledString, analyze::AnalyzeIssue},
22 module::{Module, ModuleSideEffects},
23 module_graph::binding_usage_info::ModuleExportUsageInfo,
24 reference::ModuleReference,
25 resolve::ModulePart,
26};
27
28use crate::{
29 EcmascriptModuleAsset, ScopeHoistingContext,
30 analyzer::graph::EvalContext,
31 chunk::{EcmascriptChunkPlaceable, EcmascriptExports},
32 code_gen::{CodeGeneration, CodeGenerationHoistedStmt},
33 magic_identifier,
34 references::esm::base::ReferencedAsset,
35 runtime_functions::{TURBOPACK_DYNAMIC, TURBOPACK_ESM},
36 tree_shake::part::module::EcmascriptModulePartAsset,
37 utils::module_id_to_lit,
38};
39
40#[derive(Copy, Clone, Hash, Debug, PartialEq, Eq, TraceRawVcs, NonLocalValue, Encode, Decode)]
44pub enum Liveness {
45 Constant,
47 Live,
49 Mutable,
53}
54
55#[derive(Clone, Hash, Debug, PartialEq, Eq, TraceRawVcs, NonLocalValue, Encode, Decode)]
56pub enum EsmExport {
57 LocalBinding(RcStr, Liveness),
61 ImportedBinding(ResolvedVc<Box<dyn ModuleReference>>, RcStr, bool),
65 ImportedNamespace(ResolvedVc<Box<dyn ModuleReference>>),
67 Error,
69}
70
71#[turbo_tasks::function]
72pub async fn is_export_missing(
73 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
74 export_name: RcStr,
75) -> Result<Vc<bool>> {
76 if export_name == "__turbopack_module_id__" {
77 return Ok(Vc::cell(false));
78 }
79
80 let exports = module.get_exports().await?;
81 let exports = match &*exports {
82 EcmascriptExports::None => return Ok(Vc::cell(true)),
83 EcmascriptExports::Unknown => return Ok(Vc::cell(false)),
84 EcmascriptExports::Value => return Ok(Vc::cell(false)),
85 EcmascriptExports::CommonJs => return Ok(Vc::cell(false)),
86 EcmascriptExports::EmptyCommonJs => return Ok(Vc::cell(export_name != "default")),
87 EcmascriptExports::DynamicNamespace => return Ok(Vc::cell(false)),
88 EcmascriptExports::EsmExports(exports) => *exports,
89 };
90
91 let exports = exports.await?;
92 if exports.exports.contains_key(&export_name) {
93 return Ok(Vc::cell(false));
94 }
95 if export_name == "default" {
96 return Ok(Vc::cell(true));
97 }
98
99 if exports.star_exports.is_empty() {
100 return Ok(Vc::cell(true));
101 }
102
103 let all_export_names = get_all_export_names(*module).await?;
104 if all_export_names.esm_exports.contains_key(&export_name) {
105 return Ok(Vc::cell(false));
106 }
107
108 for &dynamic_module in &all_export_names.dynamic_exporting_modules {
109 let exports = dynamic_module.get_exports().await?;
110 match &*exports {
111 EcmascriptExports::Value
112 | EcmascriptExports::CommonJs
113 | EcmascriptExports::DynamicNamespace
114 | EcmascriptExports::Unknown => {
115 return Ok(Vc::cell(false));
116 }
117 EcmascriptExports::None
118 | EcmascriptExports::EmptyCommonJs
119 | EcmascriptExports::EsmExports(_) => {}
120 }
121 }
122
123 Ok(Vc::cell(true))
124}
125
126#[turbo_tasks::function]
127pub async fn all_known_export_names(
128 module: Vc<Box<dyn EcmascriptChunkPlaceable>>,
129) -> Result<Vc<Vec<RcStr>>> {
130 let export_names = get_all_export_names(module).await?;
131 Ok(Vc::cell(export_names.esm_exports.keys().cloned().collect()))
132}
133
134#[derive(Copy, Clone, Debug, PartialEq, Eq, TraceRawVcs, NonLocalValue, Encode, Decode)]
135pub enum FoundExportType {
136 Found,
137 Dynamic,
138 NotFound,
139 SideEffects,
140 Unknown,
141}
142
143#[turbo_tasks::value]
144pub struct FollowExportsResult {
145 pub module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
146 pub export_name: Option<RcStr>,
147 pub ty: FoundExportType,
148}
149
150#[turbo_tasks::function]
151pub async fn follow_reexports(
152 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
153 export_name: RcStr,
154 ignore_side_effect_of_entry: bool,
155) -> Result<Vc<FollowExportsResult>> {
156 let mut ignore_side_effects = ignore_side_effect_of_entry;
157
158 let mut module = module;
159 let mut export_name = export_name;
160 loop {
161 if !ignore_side_effects
162 && *module.side_effects().await? != ModuleSideEffects::SideEffectFree
163 {
164 return Ok(FollowExportsResult::cell(FollowExportsResult {
168 module,
169 export_name: Some(export_name),
170 ty: FoundExportType::SideEffects,
171 }));
172 }
173 ignore_side_effects = false;
174
175 let exports = module.get_exports().await?;
176 let EcmascriptExports::EsmExports(exports) = &*exports else {
177 return Ok(FollowExportsResult::cell(FollowExportsResult {
178 module,
179 export_name: Some(export_name),
180 ty: FoundExportType::Dynamic,
181 }));
182 };
183
184 let exports_ref = exports.await?;
186 if let Some(export) = exports_ref.exports.get(&export_name) {
187 match handle_declared_export(module, export_name, export).await? {
188 ControlFlow::Continue((m, n)) => {
189 module = m.to_resolved().await?;
190 export_name = n;
191 continue;
192 }
193 ControlFlow::Break(result) => {
194 return Ok(result.cell());
195 }
196 }
197 }
198
199 if !exports_ref.star_exports.is_empty() && &*export_name != "default" {
201 let result = find_export_from_reexports(*module, export_name.clone()).await?;
202 match &*result {
203 FindExportFromReexportsResult::NotFound => {
204 return Ok(FollowExportsResult::cell(FollowExportsResult {
205 module,
206 export_name: Some(export_name),
207 ty: FoundExportType::NotFound,
208 }));
209 }
210 FindExportFromReexportsResult::EsmExport(esm_export) => {
211 match handle_declared_export(module, export_name, esm_export).await? {
212 ControlFlow::Continue((m, n)) => {
213 module = m.to_resolved().await?;
214 export_name = n;
215 continue;
216 }
217 ControlFlow::Break(result) => {
218 return Ok(result.cell());
219 }
220 }
221 }
222 FindExportFromReexportsResult::Dynamic(dynamic_exporting_modules) => {
223 return match &dynamic_exporting_modules[..] {
224 [] => unreachable!(),
225 [module] => Ok(FollowExportsResult {
226 module: *module,
227 export_name: Some(export_name),
228 ty: FoundExportType::Dynamic,
229 }
230 .cell()),
231 _ => Ok(FollowExportsResult {
232 module,
233 export_name: Some(export_name),
234 ty: FoundExportType::Dynamic,
235 }
236 .cell()),
237 };
238 }
239 }
240 }
241
242 return Ok(FollowExportsResult::cell(FollowExportsResult {
243 module,
244 export_name: Some(export_name),
245 ty: FoundExportType::NotFound,
246 }));
247 }
248}
249
250async fn handle_declared_export(
251 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
252 export_name: RcStr,
253 export: &EsmExport,
254) -> Result<ControlFlow<FollowExportsResult, (Vc<Box<dyn EcmascriptChunkPlaceable>>, RcStr)>> {
255 match export {
256 EsmExport::ImportedBinding(reference, name, _) => {
257 if let ReferencedAsset::Some(module) =
258 *ReferencedAsset::from_resolve_result(reference.resolve_reference()).await?
259 {
260 return Ok(ControlFlow::Continue((*module, name.clone())));
261 }
262 }
263 EsmExport::ImportedNamespace(reference) => {
264 if let ReferencedAsset::Some(module) =
265 *ReferencedAsset::from_resolve_result(reference.resolve_reference()).await?
266 {
267 return Ok(ControlFlow::Break(FollowExportsResult {
268 module,
269 export_name: None,
270 ty: FoundExportType::Found,
271 }));
272 }
273 }
274 EsmExport::LocalBinding(..) => {
275 return Ok(ControlFlow::Break(FollowExportsResult {
276 module,
277 export_name: Some(export_name),
278 ty: FoundExportType::Found,
279 }));
280 }
281 EsmExport::Error => {
282 return Ok(ControlFlow::Break(FollowExportsResult {
283 module,
284 export_name: Some(export_name),
285 ty: FoundExportType::Unknown,
286 }));
287 }
288 }
289 Ok(ControlFlow::Break(FollowExportsResult {
290 module,
291 export_name: Some(export_name),
292 ty: FoundExportType::Unknown,
293 }))
294}
295
296#[turbo_tasks::value]
297enum FindExportFromReexportsResult {
298 NotFound,
299 EsmExport(EsmExport),
300 Dynamic(Vec<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>>),
301}
302
303#[turbo_tasks::function]
304async fn find_export_from_reexports(
305 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
306 export_name: RcStr,
307) -> Result<Vc<FindExportFromReexportsResult>> {
308 if let Some(module) = ResolvedVc::try_downcast_type::<EcmascriptModulePartAsset>(module)
310 && matches!(module.await?.part, ModulePart::Exports)
311 {
312 let module_part = EcmascriptModulePartAsset::select_part(
313 *module.await?.full_module,
314 ModulePart::export(export_name.clone()),
315 );
316
317 if (ResolvedVc::try_downcast_type::<EcmascriptModuleAsset>(
320 module_part.to_resolved().await?,
321 ))
322 .is_none()
323 {
324 return Ok(find_export_from_reexports(module_part, export_name));
325 }
326 }
327
328 let all_export_names = get_all_export_names(*module).await?;
329 Ok(
330 if let Some(esm_export) = all_export_names.esm_exports.get(&export_name) {
331 FindExportFromReexportsResult::EsmExport(esm_export.clone())
332 } else if all_export_names.dynamic_exporting_modules.is_empty() {
333 FindExportFromReexportsResult::NotFound
334 } else {
335 FindExportFromReexportsResult::Dynamic(
336 all_export_names.dynamic_exporting_modules.clone(),
337 )
338 }
339 .cell(),
340 )
341}
342
343#[turbo_tasks::value]
344struct AllExportNamesResult {
345 #[bincode(with = "turbo_bincode::indexmap")]
347 esm_exports: FxIndexMap<RcStr, EsmExport>,
348 dynamic_exporting_modules: Vec<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>>,
350}
351
352#[turbo_tasks::function]
353async fn get_all_export_names(
354 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
355) -> Result<Vc<AllExportNamesResult>> {
356 let exports = module.get_exports().await?;
357 let EcmascriptExports::EsmExports(exports) = &*exports else {
358 return Ok(AllExportNamesResult {
359 esm_exports: FxIndexMap::default(),
360 dynamic_exporting_modules: vec![module],
361 }
362 .cell());
363 };
364
365 let exports = exports.await?;
366 let mut esm_exports = FxIndexMap::default();
367 let mut dynamic_exporting_modules = Vec::new();
368 esm_exports.extend(
369 exports
370 .exports
371 .iter()
372 .map(|(name, esm_export)| (name.clone(), esm_export.clone())),
373 );
374 let star_export_names = exports
375 .star_exports
376 .iter()
377 .map(|esm_ref| async {
378 Ok(
379 if let ReferencedAsset::Some(m) =
380 *ReferencedAsset::from_resolve_result(esm_ref.resolve_reference()).await?
381 {
382 Some(expand_star_exports(**esm_ref, *m))
383 } else {
384 None
385 },
386 )
387 })
388 .try_flat_join()
389 .await?;
390 for star_export_names in star_export_names {
391 let star_export_names = star_export_names.await?;
392 esm_exports.extend(
393 star_export_names
394 .esm_exports
395 .iter()
396 .map(|(k, v)| (k.clone(), v.clone())),
397 );
398 dynamic_exporting_modules
399 .extend(star_export_names.dynamic_exporting_modules.iter().copied());
400 }
401
402 Ok(AllExportNamesResult {
403 esm_exports,
404 dynamic_exporting_modules,
405 }
406 .cell())
407}
408
409#[turbo_tasks::value]
410pub struct ExpandStarResult {
411 #[bincode(with = "turbo_bincode::indexmap")]
412 pub esm_exports: FxIndexMap<RcStr, EsmExport>,
413 pub dynamic_exporting_modules: Vec<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>>,
414}
415
416#[turbo_tasks::function]
417pub async fn expand_star_exports(
418 root_reference: ResolvedVc<Box<dyn ModuleReference>>,
419 root_module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
420) -> Result<Vc<ExpandStarResult>> {
421 let mut esm_exports = FxIndexMap::default();
422 let mut dynamic_exporting_modules = Vec::new();
423 let mut checked_modules = FxHashSet::default();
424 checked_modules.insert(root_module);
425 let mut queue = vec![(root_reference, root_module, root_module.get_exports())];
426 while let Some((reference, asset, exports)) = queue.pop() {
427 match &*exports.await? {
428 EcmascriptExports::EsmExports(exports) => {
429 let exports = exports.await?;
430 for (key, esm_export) in exports.exports.iter() {
431 if key == "default" {
432 continue;
433 }
434 if let Entry::Vacant(entry) = esm_exports.entry(key.clone()) {
435 entry.insert(match esm_export {
436 EsmExport::LocalBinding(_, liveness) => EsmExport::ImportedBinding(
437 reference,
438 key.clone(),
439 *liveness == Liveness::Mutable,
440 ),
441 _ => esm_export.clone(),
442 });
443 }
444 }
445 for esm_ref in exports.star_exports.iter() {
446 if let ReferencedAsset::Some(asset) =
447 &*ReferencedAsset::from_resolve_result(esm_ref.resolve_reference()).await?
448 && checked_modules.insert(*asset)
449 {
450 queue.push((*esm_ref, *asset, asset.get_exports()));
451 }
452 }
453 }
454 EcmascriptExports::None | EcmascriptExports::EmptyCommonJs => {
455 emit_star_exports_issue(
456 asset.ident(),
457 turbofmt!(
458 "export * used with module {} which has no exports\nTypescript only: Did \
459 you want to export only types with `export type * from \"...\"`?\nNote: \
460 Using `export type` is more efficient than `export *` as it won't emit \
461 any runtime code.",
462 asset.ident()
463 )
464 .await?,
465 )
466 .await?
467 }
468 EcmascriptExports::Value => {
469 emit_star_exports_issue(
470 asset.ident(),
471 turbofmt!(
472 "export * used with module {} which only has a default export (default \
473 export is not exported with export *)\nDid you want to use `export {{ \
474 default }} from \"...\";` instead?",
475 asset.ident()
476 )
477 .await?,
478 )
479 .await?
480 }
481 EcmascriptExports::CommonJs => {
482 dynamic_exporting_modules.push(asset);
483 emit_star_exports_issue(
484 asset.ident(),
485 turbofmt!(
486 "export * used with module {} which is a CommonJS module with exports \
487 only available at runtime\nList all export names manually (`export {{ a, \
488 b, c }} from \"...\") or rewrite the module to ESM, to avoid the \
489 additional runtime code.`",
490 asset.ident()
491 )
492 .await?,
493 )
494 .await?;
495 }
496 EcmascriptExports::DynamicNamespace => {
497 dynamic_exporting_modules.push(asset);
498 }
499 EcmascriptExports::Unknown => {
500 dynamic_exporting_modules.push(asset);
502 }
503 }
504 }
505
506 Ok(ExpandStarResult {
507 esm_exports,
508 dynamic_exporting_modules,
509 }
510 .cell())
511}
512
513async fn emit_star_exports_issue(source_ident: Vc<AssetIdent>, message: RcStr) -> Result<()> {
514 AnalyzeIssue::new(
515 IssueSeverity::Warning,
516 source_ident,
517 Vc::cell(rcstr!("unexpected export *")),
518 StyledString::Text(message).cell(),
519 None,
520 None,
521 )
522 .to_resolved()
523 .await?
524 .emit();
525 Ok(())
526}
527
528#[turbo_tasks::value(shared)]
529#[derive(Hash, Debug)]
530pub struct EsmExports {
531 pub exports: BTreeMap<RcStr, EsmExport>,
533 pub star_exports: Vec<ResolvedVc<Box<dyn ModuleReference>>>,
535}
536
537#[turbo_tasks::value(shared)]
542#[derive(Hash, Debug)]
543pub struct ExpandedExports {
544 pub exports: BTreeMap<RcStr, EsmExport>,
545 pub dynamic_exports: Vec<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>>,
547}
548
549#[turbo_tasks::value_impl]
550impl EsmExports {
551 #[turbo_tasks::function]
558 pub async fn reexport_including_default(
559 module_reference: Vc<Box<dyn ModuleReference>>,
560 ) -> Result<Vc<EcmascriptExports>> {
561 let module_reference = module_reference.to_resolved().await?;
562 let mut exports = BTreeMap::new();
563 let default = rcstr!("default");
564 exports.insert(
565 default.clone(),
566 EsmExport::ImportedBinding(module_reference, default, false),
567 );
568
569 Ok(EcmascriptExports::EsmExports(
570 EsmExports {
571 exports,
572 star_exports: vec![module_reference],
573 }
574 .resolved_cell(),
575 )
576 .cell())
577 }
578
579 #[turbo_tasks::function]
580 pub async fn expand_exports(
581 &self,
582 export_usage_info: Vc<ModuleExportUsageInfo>,
583 ) -> Result<Vc<ExpandedExports>> {
584 let mut exports: BTreeMap<RcStr, EsmExport> = self.exports.clone();
585 let mut dynamic_exports = vec![];
586 let export_usage_info = export_usage_info.await?;
587
588 if !matches!(*export_usage_info, ModuleExportUsageInfo::All) {
589 exports.retain(|export, _| export_usage_info.is_export_used(export));
590 }
591
592 for &esm_ref in self.star_exports.iter() {
593 let ReferencedAsset::Some(asset) =
596 &*ReferencedAsset::from_resolve_result(esm_ref.resolve_reference()).await?
597 else {
598 continue;
599 };
600
601 let export_info = expand_star_exports(*esm_ref, **asset).await?;
602
603 for export in export_info.esm_exports.keys() {
604 if export == "default" {
605 continue;
606 }
607 if !export_usage_info.is_export_used(export) {
608 continue;
609 }
610
611 if !exports.contains_key(export) {
612 exports.insert(
613 export.clone(),
614 EsmExport::ImportedBinding(esm_ref, export.clone(), false),
615 );
616 }
617 }
618
619 if !export_info.dynamic_exporting_modules.is_empty() {
620 dynamic_exports.push(*asset);
621 }
622 }
623
624 Ok(ExpandedExports {
625 exports,
626 dynamic_exports,
627 }
628 .cell())
629 }
630}
631
632impl EsmExports {
633 pub async fn code_generation(
634 self: Vc<Self>,
635 chunking_context: Vc<Box<dyn ChunkingContext>>,
636 scope_hoisting_context: ScopeHoistingContext<'_>,
637 eval_context: &EvalContext,
638 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
639 ) -> Result<CodeGeneration> {
640 let export_usage_info = chunking_context
641 .module_export_usage(*ResolvedVc::upcast(module))
642 .await?;
643 let expanded = self.expand_exports(*export_usage_info.export_usage).await?;
644
645 if scope_hoisting_context.skip_module_exports() && expanded.dynamic_exports.is_empty() {
646 return Ok(CodeGeneration::empty());
652 }
653
654 let mut dynamic_exports = Vec::<Box<Expr>>::new();
655 {
656 let id = if let Some(module) = scope_hoisting_context.module()
657 && !expanded.dynamic_exports.is_empty()
658 {
659 Some(module.chunk_item_id(chunking_context).await?)
660 } else {
661 None
662 };
663
664 for dynamic_export_asset in &expanded.dynamic_exports {
665 let ident = ReferencedAsset::get_ident_from_placeable(
666 dynamic_export_asset,
667 chunking_context,
668 )
669 .await?;
670
671 if let Some(id) = &id {
672 dynamic_exports.push(quote_expr!(
673 "$turbopack_dynamic($arg, $id)",
674 turbopack_dynamic: Expr = TURBOPACK_DYNAMIC.into(),
675 arg: Expr = Ident::new(ident.into(), DUMMY_SP, Default::default()).into(),
676 id: Expr = module_id_to_lit(id)
677 ));
678 } else {
679 dynamic_exports.push(quote_expr!(
680 "$turbopack_dynamic($arg)",
681 turbopack_dynamic: Expr = TURBOPACK_DYNAMIC.into(),
682 arg: Expr = Ident::new(ident.into(), DUMMY_SP, Default::default()).into()
683 ));
684 }
685 }
686 }
687
688 #[derive(Eq, PartialEq)]
689 enum ExportBinding {
690 Getter(Expr),
691 GetterSetter(Expr, Expr),
692 Value(Expr),
693 None,
694 }
695
696 let mut getters = Vec::new();
697 for (exported, local) in &expanded.exports {
698 let exprs: ExportBinding = match local {
699 EsmExport::Error => ExportBinding::Getter(quote!(
700 "(() => { throw new Error(\"Failed binding. See build errors!\"); })" as Expr,
701 )),
702 EsmExport::LocalBinding(name, liveness) => {
703 let binding =
707 if let Some((local, ctxt)) = eval_context.imports.exports.get(exported) {
708 Some((Cow::Borrowed(local.as_str()), *ctxt))
709 } else {
710 bail!(
711 "Expected export to be in eval context {:?} {:?}",
712 exported,
713 eval_context.imports,
714 )
715 };
716 let (local, ctxt) = binding.unwrap_or_else(|| {
717 (
719 if name == "default" {
720 Cow::Owned(magic_identifier::mangle("default export"))
721 } else {
722 Cow::Borrowed(name.as_str())
723 },
724 SyntaxContext::empty(),
725 )
726 });
727
728 let local = Ident::new(local.into(), DUMMY_SP, ctxt);
729 match (liveness, export_usage_info.is_circuit_breaker) {
730 (Liveness::Constant, false) => ExportBinding::Value(Expr::Ident(local)),
731 (Liveness::Live, _) | (Liveness::Constant, true) => {
734 ExportBinding::Getter(quote!("() => $local" as Expr, local = local))
735 }
736 (Liveness::Mutable, _) => ExportBinding::GetterSetter(
737 quote!("() => $local" as Expr, local = local.clone()),
738 quote!(
739 "($new) => $local = $new" as Expr,
740 local: AssignTarget = AssignTarget::Simple(local.into()),
741 new = Ident::new(format!("new_{name}").into(), DUMMY_SP, ctxt),
742 ),
743 ),
744 }
745 }
746 EsmExport::ImportedBinding(esm_ref, name, mutable) => {
747 let referenced_asset =
748 ReferencedAsset::from_resolve_result(esm_ref.resolve_reference()).await?;
749 referenced_asset
750 .get_ident(chunking_context, Some(name.clone()), scope_hoisting_context)
751 .await?
752 .map(|ident| {
753 let expr = ident.as_expr_individual(DUMMY_SP);
754 let read_expr = expr.map_either(Expr::from, Expr::from).into_inner();
755 use crate::references::esm::base::ReferencedAssetIdent;
756 match &ident {
757 ReferencedAssetIdent::LocalBinding {ctxt, liveness,.. } => {
758 debug_assert!(*mutable == (*liveness == Liveness::Mutable), "If the re-export is mutable, the merged local must be too");
759 match (liveness, export_usage_info.is_circuit_breaker) {
761 (Liveness::Constant, false) => {
762 ExportBinding::Value(read_expr)
763 }
764 (Liveness::Live, _) | (Liveness::Constant, true) => {
767 ExportBinding::Getter(quote!("() => $local" as Expr, local: Expr = read_expr))
770 }
771 (Liveness::Mutable, _) => {
772 let assign_target = AssignTarget::Simple(
773 ident.as_expr_individual(DUMMY_SP).map_either(|i| SimpleAssignTarget::Ident(i.into()), SimpleAssignTarget::Member).into_inner());
774 ExportBinding::GetterSetter(
775 quote!("() => $local" as Expr, local: Expr= read_expr.clone()),
776 quote!(
777 "($new) => $lhs = $new" as Expr,
778 lhs: AssignTarget = assign_target,
779 new = Ident::new(format!("new_{name}").into(), DUMMY_SP, *ctxt),
780 )
781 )
782 }
783 }
784 },
785 ReferencedAssetIdent::Module { namespace_ident:_, ctxt:_, export:_ } => {
786 let getter = quote!("() => $expr" as Expr, expr: Expr = read_expr);
790 let assign_target = AssignTarget::Simple(
791 ident.as_expr_individual(DUMMY_SP).map_either(|i| SimpleAssignTarget::Ident(i.into()), SimpleAssignTarget::Member).into_inner());
792 if *mutable {
793 ExportBinding::GetterSetter(
794 getter,
795 quote!(
796 "($new) => $lhs = $new" as Expr,
797 lhs: AssignTarget = assign_target,
798 new = Ident::new(
799 format!("new_{name}").into(),
800 DUMMY_SP,
801 Default::default()
802 ),
803 ))
804 } else {
805 ExportBinding::Getter(getter)
806 }
807 }
808 }
809 }).unwrap_or(ExportBinding::None)
810 }
811 EsmExport::ImportedNamespace(esm_ref) => {
812 let referenced_asset =
813 ReferencedAsset::from_resolve_result(esm_ref.resolve_reference()).await?;
814 referenced_asset
815 .get_ident(chunking_context, None, scope_hoisting_context)
816 .await?
817 .map(|ident| {
818 let imported = ident.as_expr(DUMMY_SP, false);
819 if export_usage_info.is_circuit_breaker {
820 ExportBinding::Getter(quote!(
821 "(() => $imported)" as Expr,
822 imported: Expr = imported
823 ))
824 } else {
825 ExportBinding::Value(imported)
826 }
827 })
828 .unwrap_or(ExportBinding::None)
829 }
830 };
831 if exprs != ExportBinding::None {
832 getters.push(Some(
833 Expr::Lit(Lit::Str(Str {
834 span: DUMMY_SP,
835 value: exported.as_str().into(),
836 raw: None,
837 }))
838 .into(),
839 ));
840 match exprs {
841 ExportBinding::Getter(getter) => {
842 getters.push(Some(getter.into()));
843 }
844 ExportBinding::GetterSetter(getter, setter) => {
845 getters.push(Some(getter.into()));
846 getters.push(Some(setter.into()));
847 }
848 ExportBinding::Value(value) => {
849 getters.push(Some(Expr::Lit(Lit::Num(Number::from(0))).into()));
852 getters.push(Some(value.into()));
853 }
854 ExportBinding::None => {}
855 };
856 }
857 }
858 let getters = Expr::Array(ArrayLit {
859 span: DUMMY_SP,
860 elems: getters,
861 });
862 let dynamic_stmt = if !dynamic_exports.is_empty() {
863 vec![CodeGenerationHoistedStmt::new(
864 rcstr!("__turbopack_dynamic__"),
865 Stmt::Expr(ExprStmt {
866 span: DUMMY_SP,
867 expr: Expr::from_exprs(dynamic_exports),
868 }),
869 )]
870 } else {
871 vec![]
872 };
873
874 let esm_exports = vec![CodeGenerationHoistedStmt::new(
875 rcstr!("__turbopack_esm__"),
876 if let Some(module) = scope_hoisting_context.module() {
877 let id = module.chunk_item_id(chunking_context).await?;
878 quote!("$turbopack_esm($getters, $id);" as Stmt,
879 turbopack_esm: Expr = TURBOPACK_ESM.into(),
880 getters: Expr = getters,
881 id: Expr = module_id_to_lit(&id)
882 )
883 } else {
884 quote!("$turbopack_esm($getters);" as Stmt,
885 turbopack_esm: Expr = TURBOPACK_ESM.into(),
886 getters: Expr = getters
887 )
888 },
889 )];
890 Ok(if export_usage_info.is_circuit_breaker {
893 CodeGeneration::new(vec![], dynamic_stmt, esm_exports, vec![], vec![])
894 } else {
895 CodeGeneration::new(vec![], vec![], vec![], dynamic_stmt, esm_exports)
896 })
897 }
898}