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