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) =
301 Vc::try_resolve_downcast_type::<EcmascriptModulePartAsset>(*module).await?
302 && matches!(module.await?.part, ModulePart::Exports)
303 {
304 let module_part = EcmascriptModulePartAsset::select_part(
305 *module.await?.full_module,
306 ModulePart::export(export_name.clone()),
307 );
308
309 if (Vc::try_resolve_downcast_type::<EcmascriptModuleAsset>(module_part).await?).is_none() {
312 return Ok(find_export_from_reexports(
313 Vc::upcast(module_part),
314 export_name,
315 ));
316 }
317 }
318
319 let all_export_names = get_all_export_names(*module).await?;
320 let esm_export = all_export_names.esm_exports.get(&export_name).copied();
321 Ok(FindExportFromReexportsResult {
322 esm_export,
323 dynamic_exporting_modules: all_export_names.dynamic_exporting_modules.clone(),
324 }
325 .cell())
326}
327
328#[turbo_tasks::value]
329struct AllExportNamesResult {
330 esm_exports: FxIndexMap<RcStr, ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>>,
331 dynamic_exporting_modules: Vec<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>>,
332}
333
334#[turbo_tasks::function]
335async fn get_all_export_names(
336 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
337) -> Result<Vc<AllExportNamesResult>> {
338 let exports = module.get_exports().await?;
339 let EcmascriptExports::EsmExports(exports) = &*exports else {
340 return Ok(AllExportNamesResult {
341 esm_exports: FxIndexMap::default(),
342 dynamic_exporting_modules: vec![module],
343 }
344 .cell());
345 };
346
347 let exports = exports.await?;
348 let mut esm_exports = FxIndexMap::default();
349 let mut dynamic_exporting_modules = Vec::new();
350 esm_exports.extend(exports.exports.keys().cloned().map(|n| (n, module)));
351 let star_export_names = exports
352 .star_exports
353 .iter()
354 .map(|esm_ref| async {
355 Ok(
356 if let ReferencedAsset::Some(m) =
357 *ReferencedAsset::from_resolve_result(esm_ref.resolve_reference()).await?
358 {
359 Some(expand_star_exports(*m))
360 } else {
361 None
362 },
363 )
364 })
365 .try_flat_join()
366 .await?;
367 for star_export_names in star_export_names {
368 let star_export_names = star_export_names.await?;
369 esm_exports.extend(
370 star_export_names
371 .esm_exports
372 .iter()
373 .map(|(k, &v)| (k.clone(), v)),
374 );
375 dynamic_exporting_modules
376 .extend(star_export_names.dynamic_exporting_modules.iter().copied());
377 }
378
379 Ok(AllExportNamesResult {
380 esm_exports,
381 dynamic_exporting_modules,
382 }
383 .cell())
384}
385
386#[turbo_tasks::value]
387pub struct ExpandStarResult {
388 pub esm_exports: FxIndexMap<RcStr, ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>>,
389 pub dynamic_exporting_modules: Vec<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>>,
390}
391
392#[turbo_tasks::function]
393pub async fn expand_star_exports(
394 root_module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
395) -> Result<Vc<ExpandStarResult>> {
396 let mut esm_exports = FxIndexMap::default();
397 let mut dynamic_exporting_modules = Vec::new();
398 let mut checked_modules = FxHashSet::default();
399 checked_modules.insert(root_module);
400 let mut queue = vec![(root_module, root_module.get_exports())];
401 while let Some((asset, exports)) = queue.pop() {
402 match &*exports.await? {
403 EcmascriptExports::EsmExports(exports) => {
404 let exports = exports.await?;
405 for key in exports.exports.keys() {
406 if key == "default" {
407 continue;
408 }
409 esm_exports.entry(key.clone()).or_insert_with(|| asset);
410 }
411 for esm_ref in exports.star_exports.iter() {
412 if let ReferencedAsset::Some(asset) =
413 &*ReferencedAsset::from_resolve_result(esm_ref.resolve_reference()).await?
414 && checked_modules.insert(*asset)
415 {
416 queue.push((*asset, asset.get_exports()));
417 }
418 }
419 }
420 EcmascriptExports::None | EcmascriptExports::EmptyCommonJs => {
421 emit_star_exports_issue(
422 asset.ident(),
423 format!(
424 "export * used with module {} which has no exports\nTypescript only: Did \
425 you want to export only types with `export type * from \"...\"`?\nNote: \
426 Using `export type` is more efficient than `export *` as it won't emit \
427 any runtime code.",
428 asset.ident().to_string().await?
429 )
430 .into(),
431 )
432 .await?
433 }
434 EcmascriptExports::Value => {
435 emit_star_exports_issue(
436 asset.ident(),
437 format!(
438 "export * used with module {} which only has a default export (default \
439 export is not exported with export *)\nDid you want to use `export {{ \
440 default }} from \"...\";` instead?",
441 asset.ident().to_string().await?
442 )
443 .into(),
444 )
445 .await?
446 }
447 EcmascriptExports::CommonJs => {
448 dynamic_exporting_modules.push(asset);
449 emit_star_exports_issue(
450 asset.ident(),
451 format!(
452 "export * used with module {} which is a CommonJS module with exports \
453 only available at runtime\nList all export names manually (`export {{ a, \
454 b, c }} from \"...\") or rewrite the module to ESM, to avoid the \
455 additional runtime code.`",
456 asset.ident().to_string().await?
457 )
458 .into(),
459 )
460 .await?;
461 }
462 EcmascriptExports::DynamicNamespace => {
463 dynamic_exporting_modules.push(asset);
464 }
465 EcmascriptExports::Unknown => {
466 dynamic_exporting_modules.push(asset);
468 }
469 }
470 }
471
472 Ok(ExpandStarResult {
473 esm_exports,
474 dynamic_exporting_modules,
475 }
476 .cell())
477}
478
479async fn emit_star_exports_issue(source_ident: Vc<AssetIdent>, message: RcStr) -> Result<()> {
480 AnalyzeIssue::new(
481 IssueSeverity::Warning,
482 source_ident,
483 Vc::cell(rcstr!("unexpected export *")),
484 StyledString::Text(message).cell(),
485 None,
486 None,
487 )
488 .to_resolved()
489 .await?
490 .emit();
491 Ok(())
492}
493
494#[turbo_tasks::value(shared)]
495#[derive(Hash, Debug)]
496pub struct EsmExports {
497 pub exports: BTreeMap<RcStr, EsmExport>,
498 pub star_exports: Vec<ResolvedVc<Box<dyn ModuleReference>>>,
499}
500
501#[turbo_tasks::value(shared)]
506#[derive(Hash, Debug)]
507pub struct ExpandedExports {
508 pub exports: BTreeMap<RcStr, EsmExport>,
509 pub dynamic_exports: Vec<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>>,
511}
512
513#[turbo_tasks::value_impl]
514impl EsmExports {
515 #[turbo_tasks::function]
516 pub async fn expand_exports(
517 &self,
518 export_usage_info: Vc<ModuleExportUsageInfo>,
519 ) -> Result<Vc<ExpandedExports>> {
520 let mut exports: BTreeMap<RcStr, EsmExport> = self.exports.clone();
521 let mut dynamic_exports = vec![];
522 let export_usage_info = export_usage_info.await?;
523
524 if !matches!(*export_usage_info, ModuleExportUsageInfo::All) {
525 exports.retain(|export, _| export_usage_info.is_export_used(export));
526 }
527
528 for &esm_ref in self.star_exports.iter() {
529 let ReferencedAsset::Some(asset) =
532 &*ReferencedAsset::from_resolve_result(esm_ref.resolve_reference()).await?
533 else {
534 continue;
535 };
536
537 let export_info = expand_star_exports(**asset).await?;
538
539 for export in export_info.esm_exports.keys() {
540 if export == "default" {
541 continue;
542 }
543 if !export_usage_info.is_export_used(export) {
544 continue;
545 }
546
547 if !exports.contains_key(export) {
548 exports.insert(
549 export.clone(),
550 EsmExport::ImportedBinding(
551 ResolvedVc::upcast(esm_ref),
552 export.clone(),
553 false,
554 ),
555 );
556 }
557 }
558
559 if !export_info.dynamic_exporting_modules.is_empty() {
560 dynamic_exports.push(*asset);
561 }
562 }
563
564 Ok(ExpandedExports {
565 exports,
566 dynamic_exports,
567 }
568 .cell())
569 }
570}
571
572impl EsmExports {
573 pub async fn code_generation(
574 self: Vc<Self>,
575 chunking_context: Vc<Box<dyn ChunkingContext>>,
576 scope_hoisting_context: ScopeHoistingContext<'_>,
577 eval_context: &EvalContext,
578 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
579 ) -> Result<CodeGeneration> {
580 let export_usage_info = chunking_context
581 .module_export_usage(*ResolvedVc::upcast(module))
582 .await?;
583 let expanded = self.expand_exports(*export_usage_info.export_usage).await?;
584
585 if scope_hoisting_context.skip_module_exports() && expanded.dynamic_exports.is_empty() {
586 return Ok(CodeGeneration::empty());
592 }
593
594 let mut dynamic_exports = Vec::<Box<Expr>>::new();
595 {
596 let id = if let Some(module) = scope_hoisting_context.module()
597 && !expanded.dynamic_exports.is_empty()
598 {
599 Some(module.chunk_item_id(Vc::upcast(chunking_context)).await?)
600 } else {
601 None
602 };
603
604 for dynamic_export_asset in &expanded.dynamic_exports {
605 let ident = ReferencedAsset::get_ident_from_placeable(
606 dynamic_export_asset,
607 chunking_context,
608 )
609 .await?;
610
611 if let Some(id) = &id {
612 dynamic_exports.push(quote_expr!(
613 "$turbopack_dynamic($arg, $id)",
614 turbopack_dynamic: Expr = TURBOPACK_DYNAMIC.into(),
615 arg: Expr = Ident::new(ident.into(), DUMMY_SP, Default::default()).into(),
616 id: Expr = module_id_to_lit(id)
617 ));
618 } else {
619 dynamic_exports.push(quote_expr!(
620 "$turbopack_dynamic($arg)",
621 turbopack_dynamic: Expr = TURBOPACK_DYNAMIC.into(),
622 arg: Expr = Ident::new(ident.into(), DUMMY_SP, Default::default()).into()
623 ));
624 }
625 }
626 }
627
628 #[derive(Eq, PartialEq)]
629 enum ExportBinding {
630 Getter(Expr),
631 GetterSetter(Expr, Expr),
632 Value(Expr),
633 None,
634 }
635
636 let mut getters = Vec::new();
637 for (exported, local) in &expanded.exports {
638 let exprs: ExportBinding = match local {
639 EsmExport::Error => ExportBinding::Getter(quote!(
640 "(() => { throw new Error(\"Failed binding. See build errors!\"); })" as Expr,
641 )),
642 EsmExport::LocalBinding(name, liveness) => {
643 let binding =
647 if let Some((local, ctxt)) = eval_context.imports.exports.get(exported) {
648 Some((Cow::Borrowed(local.as_str()), *ctxt))
649 } else {
650 bail!(
651 "Expected export to be in eval context {:?} {:?}",
652 exported,
653 eval_context.imports,
654 )
655 };
656 let (local, ctxt) = binding.unwrap_or_else(|| {
657 (
659 if name == "default" {
660 Cow::Owned(magic_identifier::mangle("default export"))
661 } else {
662 Cow::Borrowed(name.as_str())
663 },
664 SyntaxContext::empty(),
665 )
666 });
667
668 let local = Ident::new(local.into(), DUMMY_SP, ctxt);
669 match (liveness, export_usage_info.is_circuit_breaker) {
670 (Liveness::Constant, false) => ExportBinding::Value(Expr::Ident(local)),
671 (Liveness::Live, _) | (Liveness::Constant, true) => {
674 ExportBinding::Getter(quote!("() => $local" as Expr, local = local))
675 }
676 (Liveness::Mutable, _) => ExportBinding::GetterSetter(
677 quote!("() => $local" as Expr, local = local.clone()),
678 quote!(
679 "($new) => $local = $new" as Expr,
680 local: AssignTarget = AssignTarget::Simple(local.into()),
681 new = Ident::new(format!("new_{name}").into(), DUMMY_SP, ctxt),
682 ),
683 ),
684 }
685 }
686 EsmExport::ImportedBinding(esm_ref, name, mutable) => {
687 let referenced_asset =
688 ReferencedAsset::from_resolve_result(esm_ref.resolve_reference()).await?;
689 referenced_asset
690 .get_ident(chunking_context, Some(name.clone()), scope_hoisting_context)
691 .await?
692 .map(|ident| {
693 let expr = ident.as_expr_individual(DUMMY_SP);
694 let read_expr = expr.map_either(Expr::from, Expr::from).into_inner();
695 use crate::references::esm::base::ReferencedAssetIdent;
696 match &ident {
697 ReferencedAssetIdent::LocalBinding {ctxt, liveness,.. } => {
698 debug_assert!(*mutable == (*liveness == Liveness::Mutable), "If the re-export is mutable, the merged local must be too");
699 match (liveness, export_usage_info.is_circuit_breaker) {
701 (Liveness::Constant, false) => {
702 ExportBinding::Value(read_expr)
703 }
704 (Liveness::Live, _) | (Liveness::Constant, true) => {
707 ExportBinding::Getter(quote!("() => $local" as Expr, local: Expr = read_expr))
710 }
711 (Liveness::Mutable, _) => {
712 let assign_target = AssignTarget::Simple(
713 ident.as_expr_individual(DUMMY_SP).map_either(|i| SimpleAssignTarget::Ident(i.into()), SimpleAssignTarget::Member).into_inner());
714 ExportBinding::GetterSetter(
715 quote!("() => $local" as Expr, local: Expr= read_expr.clone()),
716 quote!(
717 "($new) => $lhs = $new" as Expr,
718 lhs: AssignTarget = assign_target,
719 new = Ident::new(format!("new_{name}").into(), DUMMY_SP, *ctxt),
720 )
721 )
722 }
723 }
724 },
725 ReferencedAssetIdent::Module { namespace_ident:_, ctxt:_, export:_ } => {
726 let getter = quote!("() => $expr" as Expr, expr: Expr = read_expr);
730 let assign_target = AssignTarget::Simple(
731 ident.as_expr_individual(DUMMY_SP).map_either(|i| SimpleAssignTarget::Ident(i.into()), SimpleAssignTarget::Member).into_inner());
732 if *mutable {
733 ExportBinding::GetterSetter(
734 getter,
735 quote!(
736 "($new) => $lhs = $new" as Expr,
737 lhs: AssignTarget = assign_target,
738 new = Ident::new(
739 format!("new_{name}").into(),
740 DUMMY_SP,
741 Default::default()
742 ),
743 ))
744 } else {
745 ExportBinding::Getter(getter)
746 }
747 }
748 }
749 }).unwrap_or(ExportBinding::None)
750 }
751 EsmExport::ImportedNamespace(esm_ref) => {
752 let referenced_asset =
753 ReferencedAsset::from_resolve_result(esm_ref.resolve_reference()).await?;
754 referenced_asset
755 .get_ident(chunking_context, None, scope_hoisting_context)
756 .await?
757 .map(|ident| {
758 let imported = ident.as_expr(DUMMY_SP, false);
759 if export_usage_info.is_circuit_breaker {
760 ExportBinding::Getter(quote!(
761 "(() => $imported)" as Expr,
762 imported: Expr = imported
763 ))
764 } else {
765 ExportBinding::Value(imported)
766 }
767 })
768 .unwrap_or(ExportBinding::None)
769 }
770 };
771 if exprs != ExportBinding::None {
772 getters.push(Some(
773 Expr::Lit(Lit::Str(Str {
774 span: DUMMY_SP,
775 value: exported.as_str().into(),
776 raw: None,
777 }))
778 .into(),
779 ));
780 match exprs {
781 ExportBinding::Getter(getter) => {
782 getters.push(Some(getter.into()));
783 }
784 ExportBinding::GetterSetter(getter, setter) => {
785 getters.push(Some(getter.into()));
786 getters.push(Some(setter.into()));
787 }
788 ExportBinding::Value(value) => {
789 getters.push(Some(Expr::Lit(Lit::Num(Number::from(0))).into()));
792 getters.push(Some(value.into()));
793 }
794 ExportBinding::None => {}
795 };
796 }
797 }
798 let getters = Expr::Array(ArrayLit {
799 span: DUMMY_SP,
800 elems: getters,
801 });
802 let dynamic_stmt = if !dynamic_exports.is_empty() {
803 vec![CodeGenerationHoistedStmt::new(
804 rcstr!("__turbopack_dynamic__"),
805 Stmt::Expr(ExprStmt {
806 span: DUMMY_SP,
807 expr: Expr::from_exprs(dynamic_exports),
808 }),
809 )]
810 } else {
811 vec![]
812 };
813
814 let esm_exports = vec![CodeGenerationHoistedStmt::new(
815 rcstr!("__turbopack_esm__"),
816 if let Some(module) = scope_hoisting_context.module() {
817 let id = module.chunk_item_id(Vc::upcast(chunking_context)).await?;
818 quote!("$turbopack_esm($getters, $id);" as Stmt,
819 turbopack_esm: Expr = TURBOPACK_ESM.into(),
820 getters: Expr = getters,
821 id: Expr = module_id_to_lit(&id)
822 )
823 } else {
824 quote!("$turbopack_esm($getters);" as Stmt,
825 turbopack_esm: Expr = TURBOPACK_ESM.into(),
826 getters: Expr = getters
827 )
828 },
829 )];
830 Ok(if export_usage_info.is_circuit_breaker {
833 CodeGeneration::new(vec![], dynamic_stmt, esm_exports, vec![], vec![])
834 } else {
835 CodeGeneration::new(vec![], vec![], vec![], dynamic_stmt, esm_exports)
836 })
837 }
838}