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