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