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, mangle::mangled_export_names},
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 pub mangle_export_names: bool,
540}
541
542#[turbo_tasks::value(shared)]
547#[derive(Hash, Debug)]
548pub struct ExpandedExports {
549 pub exports: FrozenMap<RcStr, EsmExport>,
550 pub dynamic_exports: Vec<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>>,
552}
553
554#[turbo_tasks::value_impl]
555impl EsmExports {
556 #[turbo_tasks::function]
563 pub async fn reexport_including_default(
564 module_reference: Vc<Box<dyn ModuleReference>>,
565 ) -> Result<Vc<EcmascriptExports>> {
566 let module_reference = module_reference.to_resolved().await?;
567 let mut exports = Vec::new();
568 let default = rcstr!("default");
569 exports.push((
570 default.clone(),
571 EsmExport::ImportedBinding(module_reference, default, false),
572 ));
573
574 Ok(EcmascriptExports::EsmExports(
575 EsmExports {
576 exports: FrozenMap::from(exports),
577 star_exports: vec![module_reference],
578 mangle_export_names: false,
581 }
582 .resolved_cell(),
583 )
584 .cell())
585 }
586
587 #[turbo_tasks::function]
588 pub async fn expand_exports(
589 &self,
590 export_usage_info: Vc<ModuleExportUsageInfo>,
591 ) -> Result<Vc<ExpandedExports>> {
592 let mut exports: BTreeMap<_, _> = self
593 .exports
594 .iter()
595 .map(|(k, v)| (k.clone(), v.clone()))
596 .collect();
597 let mut dynamic_exports = vec![];
598 let export_usage_info = export_usage_info.await?;
599
600 if !matches!(*export_usage_info, ModuleExportUsageInfo::All) {
601 exports.retain(|export, _| export_usage_info.is_export_used(export));
602 }
603
604 for &esm_ref in self.star_exports.iter() {
605 let ReferencedAsset::Some(asset) =
608 &ReferencedAsset::from_resolve_result(esm_ref.resolve_reference()).await?
609 else {
610 continue;
611 };
612
613 let export_info = expand_star_exports(*esm_ref, **asset).await?;
614
615 for export in export_info.esm_exports.keys() {
616 if export == "default" {
617 continue;
618 }
619 if !export_usage_info.is_export_used(export) {
620 continue;
621 }
622
623 exports
625 .entry(export.clone())
626 .or_insert_with(|| EsmExport::ImportedBinding(esm_ref, export.clone(), false));
627 }
628
629 if !export_info.dynamic_exporting_modules.is_empty() {
630 dynamic_exports.push(*asset);
631 }
632 }
633
634 Ok(ExpandedExports {
635 exports: FrozenMap::from(exports),
636 dynamic_exports,
637 }
638 .cell())
639 }
640}
641
642impl EsmExports {
643 pub async fn code_generation(
644 self: Vc<Self>,
645 chunking_context: Vc<Box<dyn ChunkingContext>>,
646 scope_hoisting_context: ScopeHoistingContext<'_>,
647 eval_context: &EvalContext,
648 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
649 ) -> Result<CodeGeneration> {
650 let export_usage_info = chunking_context
651 .module_export_usage(*ResolvedVc::upcast(module))
652 .await?;
653 let expanded = self.expand_exports(*export_usage_info.export_usage).await?;
654
655 if scope_hoisting_context.skip_module_exports() && expanded.dynamic_exports.is_empty() {
656 return Ok(CodeGeneration::empty());
662 }
663
664 let mut dynamic_exports = Vec::<Box<Expr>>::new();
665 {
666 let id = if let Some(module) = scope_hoisting_context.module()
667 && !expanded.dynamic_exports.is_empty()
668 {
669 Some(module.chunk_item_id(chunking_context).await?)
670 } else {
671 None
672 };
673
674 for dynamic_export_asset in &expanded.dynamic_exports {
675 let ident = ReferencedAsset::get_ident_from_placeable(
676 dynamic_export_asset,
677 chunking_context,
678 )
679 .await?;
680
681 if let Some(id) = &id {
682 dynamic_exports.push(quote_expr!(
683 "$turbopack_dynamic($arg, $id)",
684 turbopack_dynamic: Expr = TURBOPACK_DYNAMIC.into(),
685 arg: Expr = Ident::new(ident.into(), DUMMY_SP, Default::default()).into(),
686 id: Expr = module_id_to_lit(id)
687 ));
688 } else {
689 dynamic_exports.push(quote_expr!(
690 "$turbopack_dynamic($arg)",
691 turbopack_dynamic: Expr = TURBOPACK_DYNAMIC.into(),
692 arg: Expr = Ident::new(ident.into(), DUMMY_SP, Default::default()).into()
693 ));
694 }
695 }
696 }
697
698 #[derive(Eq, PartialEq)]
699 enum ExportBinding {
700 Getter(Expr),
701 GetterSetter(Expr, Expr),
702 Value(Expr),
703 None,
704 }
705
706 let mut getters = Vec::new();
707 let mangled_names = mangled_export_names(*module, chunking_context).await?;
710 for (exported, local) in &expanded.exports {
711 let exprs: ExportBinding = match local {
712 EsmExport::Error => ExportBinding::Getter(quote!(
713 "(() => { throw new Error(\"Failed binding. See build errors!\"); })" as Expr,
714 )),
715 EsmExport::LocalBinding(name, liveness) => {
716 let binding = if let Some((local, ctxt)) = eval_context
720 .imports
721 .exports_ids
722 .get(exported)
723 .map(|(id, _)| id)
724 {
725 Some((local.clone(), *ctxt))
726 } else {
727 bail!(
728 "Expected export to be in eval context {:?} {:?}",
729 exported,
730 eval_context.imports,
731 )
732 };
733 let (local, ctxt) = binding.unwrap_or_else(|| {
734 (
736 if name == "default" {
737 MAGIC_IDENTIFIER_DEFAULT_EXPORT_ATOM.clone()
738 } else {
739 name.as_str().into()
740 },
741 SyntaxContext::empty(),
742 )
743 });
744
745 let local = Ident::new(local, DUMMY_SP, ctxt);
746 match (liveness, export_usage_info.is_circuit_breaker) {
747 (Liveness::Constant, false) => ExportBinding::Value(Expr::Ident(local)),
748 (Liveness::Live, _) | (Liveness::Constant, true) => {
751 ExportBinding::Getter(quote!("() => $local" as Expr, local = local))
752 }
753 (Liveness::Mutable, _) => ExportBinding::GetterSetter(
754 quote!("() => $local" as Expr, local = local.clone()),
755 quote!(
756 "($new) => $local = $new" as Expr,
757 local: AssignTarget = AssignTarget::Simple(local.into()),
758 new = Ident::new(format!("new_{name}").into(), DUMMY_SP, ctxt),
759 ),
760 ),
761 }
762 }
763 EsmExport::ImportedBinding(esm_ref, name, mutable) => {
764 let referenced_asset =
765 ReferencedAsset::from_resolve_result(esm_ref.resolve_reference()).await?;
766 referenced_asset
767 .get_ident(chunking_context, Some(name.clone()), scope_hoisting_context)
768 .await?
769 .map(|ident| {
770 let expr = ident.as_expr_individual(DUMMY_SP);
771 let read_expr = expr.map_either(Expr::from, Expr::from).into_inner();
772 use crate::references::esm::base::ReferencedAssetIdent;
773 match &ident {
774 ReferencedAssetIdent::LocalBinding {ctxt, liveness,.. } => {
775 debug_assert!(*mutable == (*liveness == Liveness::Mutable), "If the re-export is mutable, the merged local must be too");
776 match (liveness, export_usage_info.is_circuit_breaker) {
778 (Liveness::Constant, false) => {
779 ExportBinding::Value(read_expr)
780 }
781 (Liveness::Live, _) | (Liveness::Constant, true) => {
784 ExportBinding::Getter(quote!("() => $local" as Expr, local: Expr = read_expr))
787 }
788 (Liveness::Mutable, _) => {
789 let assign_target = AssignTarget::Simple(
790 ident.as_expr_individual(DUMMY_SP).map_either(|i| SimpleAssignTarget::Ident(i.into()), SimpleAssignTarget::Member).into_inner());
791 ExportBinding::GetterSetter(
792 quote!("() => $local" as Expr, local: Expr= read_expr.clone()),
793 quote!(
794 "($new) => $lhs = $new" as Expr,
795 lhs: AssignTarget = assign_target,
796 new = Ident::new(format!("new_{name}").into(), DUMMY_SP, *ctxt),
797 )
798 )
799 }
800 }
801 },
802 ReferencedAssetIdent::Module { .. } => {
803 let getter = quote!("() => $expr" as Expr, expr: Expr = read_expr);
807 let assign_target = AssignTarget::Simple(
808 ident.as_expr_individual(DUMMY_SP).map_either(|i| SimpleAssignTarget::Ident(i.into()), SimpleAssignTarget::Member).into_inner());
809 if *mutable {
810 ExportBinding::GetterSetter(
811 getter,
812 quote!(
813 "($new) => $lhs = $new" as Expr,
814 lhs: AssignTarget = assign_target,
815 new = Ident::new(
816 format!("new_{name}").into(),
817 DUMMY_SP,
818 Default::default()
819 ),
820 ))
821 } else {
822 ExportBinding::Getter(getter)
823 }
824 }
825 }
826 }).unwrap_or(ExportBinding::None)
827 }
828 EsmExport::ImportedNamespace(esm_ref) => {
829 let referenced_asset =
830 ReferencedAsset::from_resolve_result(esm_ref.resolve_reference()).await?;
831 referenced_asset
832 .get_ident(chunking_context, None, scope_hoisting_context)
833 .await?
834 .map(|ident| {
835 let imported = ident.as_expr(DUMMY_SP, false);
836 if export_usage_info.is_circuit_breaker {
837 ExportBinding::Getter(quote!(
838 "(() => $imported)" as Expr,
839 imported: Expr = imported
840 ))
841 } else {
842 ExportBinding::Value(imported)
843 }
844 })
845 .unwrap_or(ExportBinding::None)
846 }
847 };
848 if exprs != ExportBinding::None {
849 getters.push(Some(
850 Expr::Lit(Lit::Str(Str {
851 span: DUMMY_SP,
852 value: mangled_names
855 .as_ref()
856 .and_then(|names| names.get(exported))
857 .unwrap_or(exported)
858 .as_str()
859 .into(),
860 raw: None,
861 }))
862 .into(),
863 ));
864 match exprs {
865 ExportBinding::Getter(getter) => {
866 getters.push(Some(getter.into()));
867 }
868 ExportBinding::GetterSetter(getter, setter) => {
869 getters.push(Some(getter.into()));
870 getters.push(Some(setter.into()));
871 }
872 ExportBinding::Value(value) => {
873 getters.push(Some(Expr::Lit(Lit::Num(Number::from(0))).into()));
876 getters.push(Some(value.into()));
877 }
878 ExportBinding::None => {}
879 };
880 }
881 }
882 let getters = Expr::Array(ArrayLit {
883 span: DUMMY_SP,
884 elems: getters,
885 });
886 let dynamic_stmt = if !dynamic_exports.is_empty() {
887 vec![CodeGenerationHoistedStmt::new(
888 rcstr!("__turbopack_dynamic__"),
889 Stmt::Expr(ExprStmt {
890 span: DUMMY_SP,
891 expr: Expr::from_exprs(dynamic_exports),
892 }),
893 )]
894 } else {
895 vec![]
896 };
897
898 let has_dynamic_exports = !expanded.dynamic_exports.is_empty();
903 let esm_exports = vec![CodeGenerationHoistedStmt::new(
904 rcstr!("__turbopack_esm__"),
905 if let Some(module) = scope_hoisting_context.module() {
906 let id = module.chunk_item_id(chunking_context).await?;
907 if has_dynamic_exports {
908 quote!("$turbopack_esm($getters, $id, true);" as Stmt,
909 turbopack_esm: Expr = TURBOPACK_ESM.into(),
910 getters: Expr = getters,
911 id: Expr = module_id_to_lit(&id)
912 )
913 } else {
914 quote!("$turbopack_esm($getters, $id);" as Stmt,
915 turbopack_esm: Expr = TURBOPACK_ESM.into(),
916 getters: Expr = getters,
917 id: Expr = module_id_to_lit(&id)
918 )
919 }
920 } else if has_dynamic_exports {
921 quote!("$turbopack_esm($getters, undefined, true);" as Stmt,
922 turbopack_esm: Expr = TURBOPACK_ESM.into(),
923 getters: Expr = getters
924 )
925 } else {
926 quote!("$turbopack_esm($getters);" as Stmt,
927 turbopack_esm: Expr = TURBOPACK_ESM.into(),
928 getters: Expr = getters
929 )
930 },
931 )];
932 Ok(if export_usage_info.is_circuit_breaker {
935 CodeGeneration::new(vec![], dynamic_stmt, esm_exports, vec![], vec![])
936 } else {
937 CodeGeneration::new(vec![], vec![], vec![], dynamic_stmt, esm_exports)
938 })
939 }
940}