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