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 module_fragments::part::module::EcmascriptModulePartAsset,
36 references::esm::base::ReferencedAsset,
37 runtime_functions::{TURBOPACK_DYNAMIC, TURBOPACK_ESM},
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(async |esm_ref| {
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)) = eval_context
710 .imports
711 .exports_ids
712 .get(exported)
713 .map(|(id, _)| id)
714 {
715 Some((local.clone(), *ctxt))
716 } else {
717 bail!(
718 "Expected export to be in eval context {:?} {:?}",
719 exported,
720 eval_context.imports,
721 )
722 };
723 let (local, ctxt) = binding.unwrap_or_else(|| {
724 (
726 if name == "default" {
727 MAGIC_IDENTIFIER_DEFAULT_EXPORT_ATOM.clone()
728 } else {
729 name.as_str().into()
730 },
731 SyntaxContext::empty(),
732 )
733 });
734
735 let local = Ident::new(local, DUMMY_SP, ctxt);
736 match (liveness, export_usage_info.is_circuit_breaker) {
737 (Liveness::Constant, false) => ExportBinding::Value(Expr::Ident(local)),
738 (Liveness::Live, _) | (Liveness::Constant, true) => {
741 ExportBinding::Getter(quote!("() => $local" as Expr, local = local))
742 }
743 (Liveness::Mutable, _) => ExportBinding::GetterSetter(
744 quote!("() => $local" as Expr, local = local.clone()),
745 quote!(
746 "($new) => $local = $new" as Expr,
747 local: AssignTarget = AssignTarget::Simple(local.into()),
748 new = Ident::new(format!("new_{name}").into(), DUMMY_SP, ctxt),
749 ),
750 ),
751 }
752 }
753 EsmExport::ImportedBinding(esm_ref, name, mutable) => {
754 let referenced_asset =
755 ReferencedAsset::from_resolve_result(esm_ref.resolve_reference()).await?;
756 referenced_asset
757 .get_ident(chunking_context, Some(name.clone()), scope_hoisting_context)
758 .await?
759 .map(|ident| {
760 let expr = ident.as_expr_individual(DUMMY_SP);
761 let read_expr = expr.map_either(Expr::from, Expr::from).into_inner();
762 use crate::references::esm::base::ReferencedAssetIdent;
763 match &ident {
764 ReferencedAssetIdent::LocalBinding {ctxt, liveness,.. } => {
765 debug_assert!(*mutable == (*liveness == Liveness::Mutable), "If the re-export is mutable, the merged local must be too");
766 match (liveness, export_usage_info.is_circuit_breaker) {
768 (Liveness::Constant, false) => {
769 ExportBinding::Value(read_expr)
770 }
771 (Liveness::Live, _) | (Liveness::Constant, true) => {
774 ExportBinding::Getter(quote!("() => $local" as Expr, local: Expr = read_expr))
777 }
778 (Liveness::Mutable, _) => {
779 let assign_target = AssignTarget::Simple(
780 ident.as_expr_individual(DUMMY_SP).map_either(|i| SimpleAssignTarget::Ident(i.into()), SimpleAssignTarget::Member).into_inner());
781 ExportBinding::GetterSetter(
782 quote!("() => $local" as Expr, local: Expr= read_expr.clone()),
783 quote!(
784 "($new) => $lhs = $new" as Expr,
785 lhs: AssignTarget = assign_target,
786 new = Ident::new(format!("new_{name}").into(), DUMMY_SP, *ctxt),
787 )
788 )
789 }
790 }
791 },
792 ReferencedAssetIdent::Module { .. } => {
793 let getter = quote!("() => $expr" as Expr, expr: Expr = read_expr);
797 let assign_target = AssignTarget::Simple(
798 ident.as_expr_individual(DUMMY_SP).map_either(|i| SimpleAssignTarget::Ident(i.into()), SimpleAssignTarget::Member).into_inner());
799 if *mutable {
800 ExportBinding::GetterSetter(
801 getter,
802 quote!(
803 "($new) => $lhs = $new" as Expr,
804 lhs: AssignTarget = assign_target,
805 new = Ident::new(
806 format!("new_{name}").into(),
807 DUMMY_SP,
808 Default::default()
809 ),
810 ))
811 } else {
812 ExportBinding::Getter(getter)
813 }
814 }
815 }
816 }).unwrap_or(ExportBinding::None)
817 }
818 EsmExport::ImportedNamespace(esm_ref) => {
819 let referenced_asset =
820 ReferencedAsset::from_resolve_result(esm_ref.resolve_reference()).await?;
821 referenced_asset
822 .get_ident(chunking_context, None, scope_hoisting_context)
823 .await?
824 .map(|ident| {
825 let imported = ident.as_expr(DUMMY_SP, false);
826 if export_usage_info.is_circuit_breaker {
827 ExportBinding::Getter(quote!(
828 "(() => $imported)" as Expr,
829 imported: Expr = imported
830 ))
831 } else {
832 ExportBinding::Value(imported)
833 }
834 })
835 .unwrap_or(ExportBinding::None)
836 }
837 };
838 if exprs != ExportBinding::None {
839 getters.push(Some(
840 Expr::Lit(Lit::Str(Str {
841 span: DUMMY_SP,
842 value: exported.as_str().into(),
843 raw: None,
844 }))
845 .into(),
846 ));
847 match exprs {
848 ExportBinding::Getter(getter) => {
849 getters.push(Some(getter.into()));
850 }
851 ExportBinding::GetterSetter(getter, setter) => {
852 getters.push(Some(getter.into()));
853 getters.push(Some(setter.into()));
854 }
855 ExportBinding::Value(value) => {
856 getters.push(Some(Expr::Lit(Lit::Num(Number::from(0))).into()));
859 getters.push(Some(value.into()));
860 }
861 ExportBinding::None => {}
862 };
863 }
864 }
865 let getters = Expr::Array(ArrayLit {
866 span: DUMMY_SP,
867 elems: getters,
868 });
869 let dynamic_stmt = if !dynamic_exports.is_empty() {
870 vec![CodeGenerationHoistedStmt::new(
871 rcstr!("__turbopack_dynamic__"),
872 Stmt::Expr(ExprStmt {
873 span: DUMMY_SP,
874 expr: Expr::from_exprs(dynamic_exports),
875 }),
876 )]
877 } else {
878 vec![]
879 };
880
881 let has_dynamic_exports = !expanded.dynamic_exports.is_empty();
886 let esm_exports = vec![CodeGenerationHoistedStmt::new(
887 rcstr!("__turbopack_esm__"),
888 if let Some(module) = scope_hoisting_context.module() {
889 let id = module.chunk_item_id(chunking_context).await?;
890 if has_dynamic_exports {
891 quote!("$turbopack_esm($getters, $id, true);" as Stmt,
892 turbopack_esm: Expr = TURBOPACK_ESM.into(),
893 getters: Expr = getters,
894 id: Expr = module_id_to_lit(&id)
895 )
896 } else {
897 quote!("$turbopack_esm($getters, $id);" as Stmt,
898 turbopack_esm: Expr = TURBOPACK_ESM.into(),
899 getters: Expr = getters,
900 id: Expr = module_id_to_lit(&id)
901 )
902 }
903 } else if has_dynamic_exports {
904 quote!("$turbopack_esm($getters, undefined, true);" as Stmt,
905 turbopack_esm: Expr = TURBOPACK_ESM.into(),
906 getters: Expr = getters
907 )
908 } else {
909 quote!("$turbopack_esm($getters);" as Stmt,
910 turbopack_esm: Expr = TURBOPACK_ESM.into(),
911 getters: Expr = getters
912 )
913 },
914 )];
915 Ok(if export_usage_info.is_circuit_breaker {
918 CodeGeneration::new(vec![], dynamic_stmt, esm_exports, vec![], vec![])
919 } else {
920 CodeGeneration::new(vec![], vec![], vec![], dynamic_stmt, esm_exports)
921 })
922 }
923}