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 (ResolvedVc::try_downcast_type::<EcmascriptModuleAsset>(
305 module_part.to_resolved().await?,
306 ))
307 .is_none()
308 {
309 return Ok(find_export_from_reexports(module_part, export_name));
310 }
311 }
312
313 let all_export_names = get_all_export_names(*module).await?;
314 let esm_export = all_export_names.esm_exports.get(&export_name).copied();
315 Ok(FindExportFromReexportsResult {
316 esm_export,
317 dynamic_exporting_modules: all_export_names.dynamic_exporting_modules.clone(),
318 }
319 .cell())
320}
321
322#[turbo_tasks::value]
323struct AllExportNamesResult {
324 #[bincode(with = "turbo_bincode::indexmap")]
325 esm_exports: FxIndexMap<RcStr, ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>>,
326 dynamic_exporting_modules: Vec<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>>,
327}
328
329#[turbo_tasks::function]
330async fn get_all_export_names(
331 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
332) -> Result<Vc<AllExportNamesResult>> {
333 let exports = module.get_exports().await?;
334 let EcmascriptExports::EsmExports(exports) = &*exports else {
335 return Ok(AllExportNamesResult {
336 esm_exports: FxIndexMap::default(),
337 dynamic_exporting_modules: vec![module],
338 }
339 .cell());
340 };
341
342 let exports = exports.await?;
343 let mut esm_exports = FxIndexMap::default();
344 let mut dynamic_exporting_modules = Vec::new();
345 esm_exports.extend(exports.exports.keys().cloned().map(|n| (n, module)));
346 let star_export_names = exports
347 .star_exports
348 .iter()
349 .map(|esm_ref| async {
350 Ok(
351 if let ReferencedAsset::Some(m) =
352 *ReferencedAsset::from_resolve_result(esm_ref.resolve_reference()).await?
353 {
354 Some(expand_star_exports(*m))
355 } else {
356 None
357 },
358 )
359 })
360 .try_flat_join()
361 .await?;
362 for star_export_names in star_export_names {
363 let star_export_names = star_export_names.await?;
364 esm_exports.extend(
365 star_export_names
366 .esm_exports
367 .iter()
368 .map(|(k, &v)| (k.clone(), v)),
369 );
370 dynamic_exporting_modules
371 .extend(star_export_names.dynamic_exporting_modules.iter().copied());
372 }
373
374 Ok(AllExportNamesResult {
375 esm_exports,
376 dynamic_exporting_modules,
377 }
378 .cell())
379}
380
381#[turbo_tasks::value]
382pub struct ExpandStarResult {
383 #[bincode(with = "turbo_bincode::indexmap")]
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(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>,
495 pub star_exports: Vec<ResolvedVc<Box<dyn ModuleReference>>>,
497}
498
499#[turbo_tasks::value(shared)]
504#[derive(Hash, Debug)]
505pub struct ExpandedExports {
506 pub exports: BTreeMap<RcStr, EsmExport>,
507 pub dynamic_exports: Vec<ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>>,
509}
510
511#[turbo_tasks::value_impl]
512impl EsmExports {
513 #[turbo_tasks::function]
520 pub async fn reexport_including_default(
521 module_reference: Vc<Box<dyn ModuleReference>>,
522 ) -> Result<Vc<EcmascriptExports>> {
523 let module_reference = module_reference.to_resolved().await?;
524 let mut exports = BTreeMap::new();
525 let default = rcstr!("default");
526 exports.insert(
527 default.clone(),
528 EsmExport::ImportedBinding(module_reference, default, false),
529 );
530
531 Ok(EcmascriptExports::EsmExports(
532 EsmExports {
533 exports,
534 star_exports: vec![module_reference],
535 }
536 .resolved_cell(),
537 )
538 .cell())
539 }
540
541 #[turbo_tasks::function]
542 pub async fn expand_exports(
543 &self,
544 export_usage_info: Vc<ModuleExportUsageInfo>,
545 ) -> Result<Vc<ExpandedExports>> {
546 let mut exports: BTreeMap<RcStr, EsmExport> = self.exports.clone();
547 let mut dynamic_exports = vec![];
548 let export_usage_info = export_usage_info.await?;
549
550 if !matches!(*export_usage_info, ModuleExportUsageInfo::All) {
551 exports.retain(|export, _| export_usage_info.is_export_used(export));
552 }
553
554 for &esm_ref in self.star_exports.iter() {
555 let ReferencedAsset::Some(asset) =
558 &*ReferencedAsset::from_resolve_result(esm_ref.resolve_reference()).await?
559 else {
560 continue;
561 };
562
563 let export_info = expand_star_exports(**asset).await?;
564
565 for export in export_info.esm_exports.keys() {
566 if export == "default" {
567 continue;
568 }
569 if !export_usage_info.is_export_used(export) {
570 continue;
571 }
572
573 if !exports.contains_key(export) {
574 exports.insert(
575 export.clone(),
576 EsmExport::ImportedBinding(esm_ref, export.clone(), false),
577 );
578 }
579 }
580
581 if !export_info.dynamic_exporting_modules.is_empty() {
582 dynamic_exports.push(*asset);
583 }
584 }
585
586 Ok(ExpandedExports {
587 exports,
588 dynamic_exports,
589 }
590 .cell())
591 }
592}
593
594impl EsmExports {
595 pub async fn code_generation(
596 self: Vc<Self>,
597 chunking_context: Vc<Box<dyn ChunkingContext>>,
598 scope_hoisting_context: ScopeHoistingContext<'_>,
599 eval_context: &EvalContext,
600 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
601 ) -> Result<CodeGeneration> {
602 let export_usage_info = chunking_context
603 .module_export_usage(*ResolvedVc::upcast(module))
604 .await?;
605 let expanded = self.expand_exports(*export_usage_info.export_usage).await?;
606
607 if scope_hoisting_context.skip_module_exports() && expanded.dynamic_exports.is_empty() {
608 return Ok(CodeGeneration::empty());
614 }
615
616 let mut dynamic_exports = Vec::<Box<Expr>>::new();
617 {
618 let id = if let Some(module) = scope_hoisting_context.module()
619 && !expanded.dynamic_exports.is_empty()
620 {
621 Some(module.chunk_item_id(chunking_context).await?)
622 } else {
623 None
624 };
625
626 for dynamic_export_asset in &expanded.dynamic_exports {
627 let ident = ReferencedAsset::get_ident_from_placeable(
628 dynamic_export_asset,
629 chunking_context,
630 )
631 .await?;
632
633 if let Some(id) = &id {
634 dynamic_exports.push(quote_expr!(
635 "$turbopack_dynamic($arg, $id)",
636 turbopack_dynamic: Expr = TURBOPACK_DYNAMIC.into(),
637 arg: Expr = Ident::new(ident.into(), DUMMY_SP, Default::default()).into(),
638 id: Expr = module_id_to_lit(id)
639 ));
640 } else {
641 dynamic_exports.push(quote_expr!(
642 "$turbopack_dynamic($arg)",
643 turbopack_dynamic: Expr = TURBOPACK_DYNAMIC.into(),
644 arg: Expr = Ident::new(ident.into(), DUMMY_SP, Default::default()).into()
645 ));
646 }
647 }
648 }
649
650 #[derive(Eq, PartialEq)]
651 enum ExportBinding {
652 Getter(Expr),
653 GetterSetter(Expr, Expr),
654 Value(Expr),
655 None,
656 }
657
658 let mut getters = Vec::new();
659 for (exported, local) in &expanded.exports {
660 let exprs: ExportBinding = match local {
661 EsmExport::Error => ExportBinding::Getter(quote!(
662 "(() => { throw new Error(\"Failed binding. See build errors!\"); })" as Expr,
663 )),
664 EsmExport::LocalBinding(name, liveness) => {
665 let binding =
669 if let Some((local, ctxt)) = eval_context.imports.exports.get(exported) {
670 Some((Cow::Borrowed(local.as_str()), *ctxt))
671 } else {
672 bail!(
673 "Expected export to be in eval context {:?} {:?}",
674 exported,
675 eval_context.imports,
676 )
677 };
678 let (local, ctxt) = binding.unwrap_or_else(|| {
679 (
681 if name == "default" {
682 Cow::Owned(magic_identifier::mangle("default export"))
683 } else {
684 Cow::Borrowed(name.as_str())
685 },
686 SyntaxContext::empty(),
687 )
688 });
689
690 let local = Ident::new(local.into(), DUMMY_SP, ctxt);
691 match (liveness, export_usage_info.is_circuit_breaker) {
692 (Liveness::Constant, false) => ExportBinding::Value(Expr::Ident(local)),
693 (Liveness::Live, _) | (Liveness::Constant, true) => {
696 ExportBinding::Getter(quote!("() => $local" as Expr, local = local))
697 }
698 (Liveness::Mutable, _) => ExportBinding::GetterSetter(
699 quote!("() => $local" as Expr, local = local.clone()),
700 quote!(
701 "($new) => $local = $new" as Expr,
702 local: AssignTarget = AssignTarget::Simple(local.into()),
703 new = Ident::new(format!("new_{name}").into(), DUMMY_SP, ctxt),
704 ),
705 ),
706 }
707 }
708 EsmExport::ImportedBinding(esm_ref, name, mutable) => {
709 let referenced_asset =
710 ReferencedAsset::from_resolve_result(esm_ref.resolve_reference()).await?;
711 referenced_asset
712 .get_ident(chunking_context, Some(name.clone()), scope_hoisting_context)
713 .await?
714 .map(|ident| {
715 let expr = ident.as_expr_individual(DUMMY_SP);
716 let read_expr = expr.map_either(Expr::from, Expr::from).into_inner();
717 use crate::references::esm::base::ReferencedAssetIdent;
718 match &ident {
719 ReferencedAssetIdent::LocalBinding {ctxt, liveness,.. } => {
720 debug_assert!(*mutable == (*liveness == Liveness::Mutable), "If the re-export is mutable, the merged local must be too");
721 match (liveness, export_usage_info.is_circuit_breaker) {
723 (Liveness::Constant, false) => {
724 ExportBinding::Value(read_expr)
725 }
726 (Liveness::Live, _) | (Liveness::Constant, true) => {
729 ExportBinding::Getter(quote!("() => $local" as Expr, local: Expr = read_expr))
732 }
733 (Liveness::Mutable, _) => {
734 let assign_target = AssignTarget::Simple(
735 ident.as_expr_individual(DUMMY_SP).map_either(|i| SimpleAssignTarget::Ident(i.into()), SimpleAssignTarget::Member).into_inner());
736 ExportBinding::GetterSetter(
737 quote!("() => $local" as Expr, local: Expr= read_expr.clone()),
738 quote!(
739 "($new) => $lhs = $new" as Expr,
740 lhs: AssignTarget = assign_target,
741 new = Ident::new(format!("new_{name}").into(), DUMMY_SP, *ctxt),
742 )
743 )
744 }
745 }
746 },
747 ReferencedAssetIdent::Module { namespace_ident:_, ctxt:_, export:_ } => {
748 let getter = quote!("() => $expr" as Expr, expr: Expr = read_expr);
752 let assign_target = AssignTarget::Simple(
753 ident.as_expr_individual(DUMMY_SP).map_either(|i| SimpleAssignTarget::Ident(i.into()), SimpleAssignTarget::Member).into_inner());
754 if *mutable {
755 ExportBinding::GetterSetter(
756 getter,
757 quote!(
758 "($new) => $lhs = $new" as Expr,
759 lhs: AssignTarget = assign_target,
760 new = Ident::new(
761 format!("new_{name}").into(),
762 DUMMY_SP,
763 Default::default()
764 ),
765 ))
766 } else {
767 ExportBinding::Getter(getter)
768 }
769 }
770 }
771 }).unwrap_or(ExportBinding::None)
772 }
773 EsmExport::ImportedNamespace(esm_ref) => {
774 let referenced_asset =
775 ReferencedAsset::from_resolve_result(esm_ref.resolve_reference()).await?;
776 referenced_asset
777 .get_ident(chunking_context, None, scope_hoisting_context)
778 .await?
779 .map(|ident| {
780 let imported = ident.as_expr(DUMMY_SP, false);
781 if export_usage_info.is_circuit_breaker {
782 ExportBinding::Getter(quote!(
783 "(() => $imported)" as Expr,
784 imported: Expr = imported
785 ))
786 } else {
787 ExportBinding::Value(imported)
788 }
789 })
790 .unwrap_or(ExportBinding::None)
791 }
792 };
793 if exprs != ExportBinding::None {
794 getters.push(Some(
795 Expr::Lit(Lit::Str(Str {
796 span: DUMMY_SP,
797 value: exported.as_str().into(),
798 raw: None,
799 }))
800 .into(),
801 ));
802 match exprs {
803 ExportBinding::Getter(getter) => {
804 getters.push(Some(getter.into()));
805 }
806 ExportBinding::GetterSetter(getter, setter) => {
807 getters.push(Some(getter.into()));
808 getters.push(Some(setter.into()));
809 }
810 ExportBinding::Value(value) => {
811 getters.push(Some(Expr::Lit(Lit::Num(Number::from(0))).into()));
814 getters.push(Some(value.into()));
815 }
816 ExportBinding::None => {}
817 };
818 }
819 }
820 let getters = Expr::Array(ArrayLit {
821 span: DUMMY_SP,
822 elems: getters,
823 });
824 let dynamic_stmt = if !dynamic_exports.is_empty() {
825 vec![CodeGenerationHoistedStmt::new(
826 rcstr!("__turbopack_dynamic__"),
827 Stmt::Expr(ExprStmt {
828 span: DUMMY_SP,
829 expr: Expr::from_exprs(dynamic_exports),
830 }),
831 )]
832 } else {
833 vec![]
834 };
835
836 let esm_exports = vec![CodeGenerationHoistedStmt::new(
837 rcstr!("__turbopack_esm__"),
838 if let Some(module) = scope_hoisting_context.module() {
839 let id = module.chunk_item_id(chunking_context).await?;
840 quote!("$turbopack_esm($getters, $id);" as Stmt,
841 turbopack_esm: Expr = TURBOPACK_ESM.into(),
842 getters: Expr = getters,
843 id: Expr = module_id_to_lit(&id)
844 )
845 } else {
846 quote!("$turbopack_esm($getters);" as Stmt,
847 turbopack_esm: Expr = TURBOPACK_ESM.into(),
848 getters: Expr = getters
849 )
850 },
851 )];
852 Ok(if export_usage_info.is_circuit_breaker {
855 CodeGeneration::new(vec![], dynamic_stmt, esm_exports, vec![], vec![])
856 } else {
857 CodeGeneration::new(vec![], vec![], vec![], dynamic_stmt, esm_exports)
858 })
859 }
860}