1use anyhow::{Result, anyhow, bail};
2use either::Either;
3use strsim::jaro;
4use swc_core::{
5 common::{BytePos, DUMMY_SP, Span, SyntaxContext, source_map::PURE_SP},
6 ecma::ast::{
7 ComputedPropName, Decl, Expr, ExprStmt, Ident, Lit, MemberExpr, MemberProp, Number,
8 SeqExpr, Stmt, Str,
9 },
10 quote,
11};
12use turbo_rcstr::{RcStr, rcstr};
13use turbo_tasks::{ResolvedVc, ValueToString, Vc};
14use turbo_tasks_fs::FileSystemPath;
15use turbopack_core::{
16 chunk::{
17 ChunkableModuleReference, ChunkingContext, ChunkingType, ChunkingTypeOption,
18 ModuleChunkItemIdExt,
19 },
20 context::AssetContext,
21 issue::{
22 Issue, IssueExt, IssueSeverity, IssueSource, IssueStage, OptionIssueSource,
23 OptionStyledString, StyledString,
24 },
25 module::Module,
26 module_graph::export_usage::ModuleExportUsageInfo,
27 reference::ModuleReference,
28 reference_type::{EcmaScriptModulesReferenceSubType, ImportWithType},
29 resolve::{
30 ExportUsage, ExternalType, ModulePart, ModuleResolveResult, ModuleResolveResultItem,
31 RequestKey,
32 origin::{ResolveOrigin, ResolveOriginExt},
33 parse::Request,
34 },
35};
36use turbopack_resolve::ecmascript::esm_resolve;
37
38use super::export::{all_known_export_names, is_export_missing};
39use crate::{
40 ScopeHoistingContext, TreeShakingMode,
41 analyzer::imports::ImportAnnotations,
42 chunk::{EcmascriptChunkPlaceable, EcmascriptExports},
43 code_gen::{CodeGeneration, CodeGenerationHoistedStmt},
44 magic_identifier,
45 references::{
46 esm::EsmExport,
47 util::{request_to_string, throw_module_not_found_expr},
48 },
49 runtime_functions::{TURBOPACK_EXTERNAL_IMPORT, TURBOPACK_EXTERNAL_REQUIRE, TURBOPACK_IMPORT},
50 tree_shake::{TURBOPACK_PART_IMPORT_SOURCE, asset::EcmascriptModulePartAsset},
51 utils::module_id_to_lit,
52};
53
54#[turbo_tasks::value]
55pub enum ReferencedAsset {
56 Some(ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>),
57 External(RcStr, ExternalType),
58 None,
59 Unresolvable,
60}
61
62#[derive(Debug)]
63pub enum ReferencedAssetIdent {
64 LocalBinding { ident: RcStr, ctxt: SyntaxContext },
66 Module {
68 namespace_ident: String,
69 ctxt: Option<SyntaxContext>,
70 export: Option<RcStr>,
71 },
72}
73
74impl ReferencedAssetIdent {
75 pub fn into_module_namespace_ident(self) -> Option<(String, Option<SyntaxContext>)> {
76 match self {
77 ReferencedAssetIdent::Module {
78 namespace_ident,
79 ctxt,
80 ..
81 } => Some((namespace_ident, ctxt)),
82 ReferencedAssetIdent::LocalBinding { .. } => None,
83 }
84 }
85
86 pub fn as_expr_individual(&self, span: Span) -> Either<Ident, MemberExpr> {
87 match self {
88 ReferencedAssetIdent::LocalBinding { ident, ctxt } => {
89 Either::Left(Ident::new(ident.as_str().into(), span, *ctxt))
90 }
91 ReferencedAssetIdent::Module {
92 namespace_ident,
93 ctxt,
94 export,
95 } => {
96 let ns = Ident::new(
97 namespace_ident.as_str().into(),
98 span,
99 ctxt.unwrap_or_default(),
100 );
101 if let Some(export) = export {
102 Either::Right(MemberExpr {
103 span,
104 obj: Box::new(Expr::Ident(ns)),
105 prop: MemberProp::Computed(ComputedPropName {
106 span,
107 expr: Box::new(Expr::Lit(Lit::Str(Str {
108 span,
109 value: export.as_str().into(),
110 raw: None,
111 }))),
112 }),
113 })
114 } else {
115 Either::Left(ns)
116 }
117 }
118 }
119 }
120 pub fn as_expr(&self, span: Span, is_callee: bool) -> Expr {
121 match self.as_expr_individual(span) {
122 Either::Left(ident) => ident.into(),
123 Either::Right(member) => {
124 if is_callee {
125 Expr::Seq(SeqExpr {
126 exprs: vec![
127 Box::new(Expr::Lit(Lit::Num(Number {
128 span,
129 value: 0.0,
130 raw: None,
131 }))),
132 Box::new(member.into()),
133 ],
134 span,
135 })
136 } else {
137 member.into()
138 }
139 }
140 }
141 }
142}
143
144impl ReferencedAsset {
145 pub async fn get_ident(
146 &self,
147 chunking_context: Vc<Box<dyn ChunkingContext>>,
148 export: Option<RcStr>,
149 scope_hoisting_context: ScopeHoistingContext<'_>,
150 ) -> Result<Option<ReferencedAssetIdent>> {
151 self.get_ident_inner(chunking_context, export, scope_hoisting_context, None)
152 .await
153 }
154
155 async fn get_ident_inner(
156 &self,
157 chunking_context: Vc<Box<dyn ChunkingContext>>,
158 export: Option<RcStr>,
159 scope_hoisting_context: ScopeHoistingContext<'_>,
160 initial: Option<&ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>>,
161 ) -> Result<Option<ReferencedAssetIdent>> {
162 Ok(match self {
163 ReferencedAsset::Some(asset) => {
164 if let Some(ctxt) =
165 scope_hoisting_context.get_module_syntax_context(ResolvedVc::upcast(*asset))
166 && let Some(export) = &export
167 && let EcmascriptExports::EsmExports(exports) = *asset.get_exports().await?
168 {
169 let exports = exports.expand_exports(ModuleExportUsageInfo::all()).await?;
170 let esm_export = exports.exports.get(export);
171 match esm_export {
172 Some(EsmExport::LocalBinding(_, _)) => {
173 return Ok(Some(ReferencedAssetIdent::LocalBinding {
177 ident: export.clone(),
178 ctxt,
179 }));
180 }
181 Some(b @ EsmExport::ImportedBinding(esm_ref, _, _))
182 | Some(b @ EsmExport::ImportedNamespace(esm_ref)) => {
183 let imported = if let EsmExport::ImportedBinding(_, export, _) = b {
184 Some(export.clone())
185 } else {
186 None
187 };
188
189 let referenced_asset =
190 ReferencedAsset::from_resolve_result(esm_ref.resolve_reference())
191 .await?;
192
193 if let Some(&initial) = initial
194 && *referenced_asset == ReferencedAsset::Some(initial)
195 {
196 CircularReExport {
199 export: export.clone(),
200 import: imported.clone(),
201 module: *asset,
202 module_cycle: initial,
203 }
204 .resolved_cell()
205 .emit();
206 return Ok(None);
207 }
208
209 return Ok(
212 match Box::pin(referenced_asset.get_ident_inner(
213 chunking_context,
214 imported,
215 scope_hoisting_context,
216 Some(asset),
217 ))
218 .await?
219 {
220 Some(ReferencedAssetIdent::Module {
221 namespace_ident,
222 ctxt: None,
226 export,
227 }) => Some(ReferencedAssetIdent::Module {
228 namespace_ident,
229 ctxt: Some(ctxt),
230 export,
231 }),
232 ident => ident,
233 },
234 );
235 }
236 Some(EsmExport::Error) | None => {
237 }
240 }
241 }
242
243 Some(ReferencedAssetIdent::Module {
244 namespace_ident: Self::get_ident_from_placeable(asset, chunking_context)
245 .await?,
246 ctxt: None,
247 export,
248 })
249 }
250 ReferencedAsset::External(request, ty) => Some(ReferencedAssetIdent::Module {
251 namespace_ident: magic_identifier::mangle(&format!("{ty} external {request}")),
252 ctxt: None,
253 export,
254 }),
255 ReferencedAsset::None | ReferencedAsset::Unresolvable => None,
256 })
257 }
258
259 pub(crate) async fn get_ident_from_placeable(
260 asset: &Vc<Box<dyn EcmascriptChunkPlaceable>>,
261 chunking_context: Vc<Box<dyn ChunkingContext>>,
262 ) -> Result<String> {
263 let id = asset.chunk_item_id(Vc::upcast(chunking_context)).await?;
264 Ok(magic_identifier::mangle(&format!("imported module {id}")))
265 }
266}
267
268#[turbo_tasks::value_impl]
269impl ReferencedAsset {
270 #[turbo_tasks::function]
271 pub async fn from_resolve_result(resolve_result: Vc<ModuleResolveResult>) -> Result<Vc<Self>> {
272 let result = resolve_result.await?;
274 if result.is_unresolvable_ref() {
275 return Ok(ReferencedAsset::Unresolvable.cell());
276 }
277 for (_, result) in result.primary.iter() {
278 match result {
279 ModuleResolveResultItem::External {
280 name: request, ty, ..
281 } => {
282 return Ok(ReferencedAsset::External(request.clone(), *ty).cell());
283 }
284 &ModuleResolveResultItem::Module(module) => {
285 if let Some(placeable) =
286 ResolvedVc::try_downcast::<Box<dyn EcmascriptChunkPlaceable>>(module)
287 {
288 return Ok(ReferencedAsset::Some(placeable).cell());
289 }
290 }
291 _ => {}
293 }
294 }
295 Ok(ReferencedAsset::None.cell())
296 }
297}
298
299#[turbo_tasks::value(transparent)]
300pub struct EsmAssetReferences(Vec<ResolvedVc<EsmAssetReference>>);
301
302#[turbo_tasks::value_impl]
303impl EsmAssetReferences {
304 #[turbo_tasks::function]
305 pub fn empty() -> Vc<Self> {
306 Vc::cell(Vec::new())
307 }
308}
309
310#[turbo_tasks::value(shared)]
311#[derive(Hash, Debug)]
312pub struct EsmAssetReference {
313 pub origin: ResolvedVc<Box<dyn ResolveOrigin>>,
314 pub request: ResolvedVc<Request>,
315 pub annotations: ImportAnnotations,
316 pub issue_source: IssueSource,
317 pub export_name: Option<ModulePart>,
318 pub import_externals: bool,
319 pub is_pure_import: bool,
320}
321
322impl EsmAssetReference {
323 fn get_origin(&self) -> Vc<Box<dyn ResolveOrigin>> {
324 if let Some(transition) = self.annotations.transition() {
325 self.origin.with_transition(transition.into())
326 } else {
327 *self.origin
328 }
329 }
330}
331
332impl EsmAssetReference {
333 pub fn new(
334 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
335 request: ResolvedVc<Request>,
336 issue_source: IssueSource,
337 annotations: ImportAnnotations,
338 export_name: Option<ModulePart>,
339 import_externals: bool,
340 ) -> Self {
341 EsmAssetReference {
342 origin,
343 request,
344 issue_source,
345 annotations,
346 export_name,
347 import_externals,
348 is_pure_import: false,
349 }
350 }
351
352 pub fn new_pure(
353 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
354 request: ResolvedVc<Request>,
355 issue_source: IssueSource,
356 annotations: ImportAnnotations,
357 export_name: Option<ModulePart>,
358 import_externals: bool,
359 ) -> Self {
360 EsmAssetReference {
361 origin,
362 request,
363 issue_source,
364 annotations,
365 export_name,
366 import_externals,
367 is_pure_import: true,
368 }
369 }
370}
371
372#[turbo_tasks::value_impl]
373impl EsmAssetReference {
374 #[turbo_tasks::function]
375 pub(crate) fn get_referenced_asset(self: Vc<Self>) -> Vc<ReferencedAsset> {
376 ReferencedAsset::from_resolve_result(self.resolve_reference())
377 }
378}
379
380#[turbo_tasks::value_impl]
381impl ModuleReference for EsmAssetReference {
382 #[turbo_tasks::function]
383 async fn resolve_reference(&self) -> Result<Vc<ModuleResolveResult>> {
384 let ty = if matches!(self.annotations.module_type(), Some("json")) {
385 EcmaScriptModulesReferenceSubType::ImportWithType(ImportWithType::Json)
386 } else if let Some(part) = &self.export_name {
387 EcmaScriptModulesReferenceSubType::ImportPart(part.clone())
388 } else {
389 EcmaScriptModulesReferenceSubType::Import
390 };
391
392 if let Some(ModulePart::Evaluation) = &self.export_name {
393 let module: ResolvedVc<crate::EcmascriptModuleAsset> =
394 ResolvedVc::try_downcast_type(self.origin)
395 .expect("EsmAssetReference origin should be a EcmascriptModuleAsset");
396
397 let tree_shaking_mode = module.options().await?.tree_shaking_mode;
398
399 if let Some(TreeShakingMode::ModuleFragments) = tree_shaking_mode {
400 let side_effect_free_packages = module.asset_context().side_effect_free_packages();
401
402 if *module
403 .is_marked_as_side_effect_free(side_effect_free_packages)
404 .await?
405 {
406 return Ok(ModuleResolveResult {
407 primary: Box::new([(
408 RequestKey::default(),
409 ModuleResolveResultItem::Ignore,
410 )]),
411 affecting_sources: Default::default(),
412 }
413 .cell());
414 }
415 }
416 }
417
418 if let Request::Module { module, .. } = &*self.request.await?
419 && module == TURBOPACK_PART_IMPORT_SOURCE
420 {
421 if let Some(part) = &self.export_name {
422 let module: ResolvedVc<crate::EcmascriptModuleAsset> =
423 ResolvedVc::try_downcast_type(self.origin)
424 .expect("EsmAssetReference origin should be a EcmascriptModuleAsset");
425
426 return Ok(*ModuleResolveResult::module(ResolvedVc::upcast(
427 EcmascriptModulePartAsset::select_part(*module, part.clone())
428 .to_resolved()
429 .await?,
430 )));
431 }
432
433 bail!("export_name is required for part import")
434 }
435
436 let result = esm_resolve(
437 self.get_origin().resolve().await?,
438 *self.request,
439 ty,
440 false,
441 Some(self.issue_source),
442 )
443 .await?;
444
445 if let Some(ModulePart::Export(export_name)) = &self.export_name {
446 for &module in result.primary_modules().await? {
447 if let Some(module) = ResolvedVc::try_downcast(module)
448 && *is_export_missing(*module, export_name.clone()).await?
449 {
450 InvalidExport {
451 export: export_name.clone(),
452 module,
453 source: self.issue_source,
454 }
455 .resolved_cell()
456 .emit();
457 }
458 }
459 }
460
461 Ok(result)
462 }
463}
464
465#[turbo_tasks::value_impl]
466impl ValueToString for EsmAssetReference {
467 #[turbo_tasks::function]
468 async fn to_string(&self) -> Result<Vc<RcStr>> {
469 Ok(Vc::cell(
470 format!(
471 "import {} with {}",
472 self.request.to_string().await?,
473 self.annotations
474 )
475 .into(),
476 ))
477 }
478}
479
480#[turbo_tasks::value_impl]
481impl ChunkableModuleReference for EsmAssetReference {
482 #[turbo_tasks::function]
483 fn chunking_type(&self) -> Result<Vc<ChunkingTypeOption>> {
484 Ok(Vc::cell(
485 if let Some(chunking_type) = self.annotations.chunking_type() {
486 match chunking_type {
487 "parallel" => Some(ChunkingType::Parallel {
488 inherit_async: true,
489 hoisted: true,
490 }),
491 "none" => None,
492 _ => return Err(anyhow!("unknown chunking_type: {}", chunking_type)),
493 }
494 } else {
495 Some(ChunkingType::Parallel {
496 inherit_async: true,
497 hoisted: true,
498 })
499 },
500 ))
501 }
502
503 #[turbo_tasks::function]
504 fn export_usage(&self) -> Vc<ExportUsage> {
505 match &self.export_name {
506 Some(ModulePart::Export(export_name)) => ExportUsage::named(export_name.clone()),
507 Some(ModulePart::Evaluation) => ExportUsage::evaluation(),
508 _ => ExportUsage::all(),
509 }
510 }
511}
512
513impl EsmAssetReference {
514 pub async fn code_generation(
515 self: Vc<Self>,
516 chunking_context: Vc<Box<dyn ChunkingContext>>,
517 scope_hoisting_context: ScopeHoistingContext<'_>,
518 ) -> Result<CodeGeneration> {
519 let this = &*self.await?;
520
521 if this.annotations.chunking_type() != Some("none") {
523 let import_externals = this.import_externals;
524 let referenced_asset = self.get_referenced_asset().await?;
525
526 match &*referenced_asset {
527 ReferencedAsset::Unresolvable => {
528 let request = request_to_string(*this.request).await?.to_string();
531 let stmt = Stmt::Expr(ExprStmt {
532 expr: Box::new(throw_module_not_found_expr(&request)),
533 span: DUMMY_SP,
534 });
535 return Ok(CodeGeneration::hoisted_stmt(
536 format!("throw {request}").into(),
537 stmt,
538 ));
539 }
540 ReferencedAsset::None => {}
541 _ => {
542 let mut result = vec![];
543
544 let merged_index = if let ReferencedAsset::Some(asset) = &*referenced_asset {
545 scope_hoisting_context.get_module_index(*asset)
546 } else {
547 None
548 };
549
550 if let Some(merged_index) = merged_index {
551 result.push(CodeGenerationHoistedStmt::new(
554 format!("hoisted {merged_index}").into(),
555 quote!(
556 "__turbopack_merged_esm__($id);" as Stmt,
557 id: Expr = Lit::Num(merged_index.into()).into(),
558 ),
559 ));
560 }
561
562 if merged_index.is_some()
563 && matches!(*self.export_usage().await?, ExportUsage::Evaluation)
564 {
565 } else {
569 let ident = referenced_asset
570 .get_ident(
571 chunking_context,
572 this.export_name.as_ref().and_then(|e| match e {
573 ModulePart::Export(export_name) => Some(export_name.clone()),
574 _ => None,
575 }),
576 scope_hoisting_context,
577 )
578 .await?;
579 match ident {
580 Some(ReferencedAssetIdent::LocalBinding { .. }) => {
581 }
583 Some(ident @ ReferencedAssetIdent::Module { .. }) => {
584 let span = this
585 .issue_source
586 .to_swc_offsets()
587 .await?
588 .map_or(DUMMY_SP, |(start, end)| {
589 Span::new(BytePos(start), BytePos(end))
590 });
591 match &*referenced_asset {
592 ReferencedAsset::Unresolvable => {
593 unreachable!();
594 }
595 ReferencedAsset::Some(asset) => {
596 let id = asset
597 .chunk_item_id(Vc::upcast(chunking_context))
598 .await?;
599 let (sym, ctxt) =
600 ident.into_module_namespace_ident().unwrap();
601 let name = Ident::new(
602 sym.into(),
603 DUMMY_SP,
604 ctxt.unwrap_or_default(),
605 );
606 let mut call_expr = quote!(
607 "$turbopack_import($id)" as Expr,
608 turbopack_import: Expr = TURBOPACK_IMPORT.into(),
609 id: Expr = module_id_to_lit(&id),
610 );
611 if this.is_pure_import {
612 call_expr.set_span(PURE_SP);
613 }
614 result.push(CodeGenerationHoistedStmt::new(
615 id.to_string().into(),
616 var_decl_with_span(
617 quote!(
618 "var $name = $call;" as Stmt,
619 name = name,
620 call: Expr = call_expr
621 ),
622 span,
623 ),
624 ));
625 }
626 ReferencedAsset::External(
627 request,
628 ExternalType::EcmaScriptModule,
629 ) => {
630 if !*chunking_context
631 .environment()
632 .supports_esm_externals()
633 .await?
634 {
635 bail!(
636 "the chunking context ({}) does not support \
637 external modules (esm request: {})",
638 chunking_context.name().await?,
639 request
640 );
641 }
642 let (sym, ctxt) =
643 ident.into_module_namespace_ident().unwrap();
644 let name = Ident::new(
645 sym.into(),
646 DUMMY_SP,
647 ctxt.unwrap_or_default(),
648 );
649 let mut call_expr = if import_externals {
650 quote!(
651 "$turbopack_external_import($id)" as Expr,
652 turbopack_external_import: Expr = TURBOPACK_EXTERNAL_IMPORT.into(),
653 id: Expr = Expr::Lit(request.clone().to_string().into())
654 )
655 } else {
656 quote!(
657 "$turbopack_external_require($id, () => require($id), true)" as Expr,
658 turbopack_external_require: Expr = TURBOPACK_EXTERNAL_REQUIRE.into(),
659 id: Expr = Expr::Lit(request.clone().to_string().into())
660 )
661 };
662 if this.is_pure_import {
663 call_expr.set_span(PURE_SP);
664 }
665 result.push(CodeGenerationHoistedStmt::new(
666 name.sym.as_str().into(),
667 var_decl_with_span(
668 quote!(
669 "var $name = $call;" as Stmt,
670 name = name,
671 call: Expr = call_expr,
672 ),
673 span,
674 ),
675 ));
676 }
677 ReferencedAsset::External(
678 request,
679 ExternalType::CommonJs | ExternalType::Url,
680 ) => {
681 if !*chunking_context
682 .environment()
683 .supports_commonjs_externals()
684 .await?
685 {
686 bail!(
687 "the chunking context ({}) does not support \
688 external modules (request: {})",
689 chunking_context.name().await?,
690 request
691 );
692 }
693 let (sym, ctxt) =
694 ident.into_module_namespace_ident().unwrap();
695 let name = Ident::new(
696 sym.into(),
697 DUMMY_SP,
698 ctxt.unwrap_or_default(),
699 );
700 let mut call_expr = quote!(
701 "$turbopack_external_require($id, () => require($id), true)" as Expr,
702 turbopack_external_require: Expr = TURBOPACK_EXTERNAL_REQUIRE.into(),
703 id: Expr = Expr::Lit(request.clone().to_string().into())
704 );
705 if this.is_pure_import {
706 call_expr.set_span(PURE_SP);
707 }
708 result.push(CodeGenerationHoistedStmt::new(
709 name.sym.as_str().into(),
710 var_decl_with_span(
711 quote!(
712 "var $name = $call;" as Stmt,
713 name = name,
714 call: Expr = call_expr,
715 ),
716 span,
717 ),
718 ));
719 }
720 #[allow(unreachable_patterns)]
722 ReferencedAsset::External(request, ty) => {
723 bail!(
724 "Unsupported external type {:?} for ESM reference \
725 with request: {:?}",
726 ty,
727 request
728 )
729 }
730 ReferencedAsset::None => {}
731 };
732 }
733 None => {
734 }
736 }
737 }
738 return Ok(CodeGeneration::hoisted_stmts(result));
739 }
740 }
741 };
742
743 Ok(CodeGeneration::empty())
744 }
745}
746
747fn var_decl_with_span(mut decl: Stmt, span: Span) -> Stmt {
748 match &mut decl {
749 Stmt::Decl(Decl::Var(decl)) => decl.span = span,
750 _ => panic!("Expected Stmt::Decl::Var"),
751 };
752 decl
753}
754
755#[turbo_tasks::value(shared)]
756pub struct InvalidExport {
757 export: RcStr,
758 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
759 source: IssueSource,
760}
761
762#[turbo_tasks::value_impl]
763impl Issue for InvalidExport {
764 fn severity(&self) -> IssueSeverity {
765 IssueSeverity::Error
766 }
767
768 #[turbo_tasks::function]
769 fn title(&self) -> Result<Vc<StyledString>> {
770 Ok(StyledString::Line(vec![
771 StyledString::Text(rcstr!("Export ")),
772 StyledString::Code(self.export.clone()),
773 StyledString::Text(rcstr!(" doesn't exist in target module")),
774 ])
775 .cell())
776 }
777
778 #[turbo_tasks::function]
779 fn stage(&self) -> Vc<IssueStage> {
780 IssueStage::Bindings.into()
781 }
782
783 #[turbo_tasks::function]
784 fn file_path(&self) -> Vc<FileSystemPath> {
785 self.source.file_path()
786 }
787
788 #[turbo_tasks::function]
789 async fn description(&self) -> Result<Vc<OptionStyledString>> {
790 let export_names = all_known_export_names(*self.module).await?;
791 let did_you_mean = export_names
792 .iter()
793 .map(|s| (s, jaro(self.export.as_str(), s.as_str())))
794 .max_by(|a, b| a.1.partial_cmp(&b.1).unwrap())
795 .map(|(s, _)| s);
796 Ok(Vc::cell(Some(
797 StyledString::Stack(vec![
798 StyledString::Line(vec![
799 StyledString::Text(rcstr!("The export ")),
800 StyledString::Code(self.export.clone()),
801 StyledString::Text(rcstr!(" was not found in module ")),
802 StyledString::Strong(self.module.ident().to_string().owned().await?),
803 StyledString::Text(rcstr!(".")),
804 ]),
805 if let Some(did_you_mean) = did_you_mean {
806 StyledString::Line(vec![
807 StyledString::Text(rcstr!("Did you mean to import ")),
808 StyledString::Code(did_you_mean.clone()),
809 StyledString::Text(rcstr!("?")),
810 ])
811 } else {
812 StyledString::Strong(rcstr!("The module has no exports at all."))
813 },
814 StyledString::Text(
815 "All exports of the module are statically known (It doesn't have dynamic \
816 exports). So it's known statically that the requested export doesn't exist."
817 .into(),
818 ),
819 ])
820 .resolved_cell(),
821 )))
822 }
823
824 #[turbo_tasks::function]
825 async fn detail(&self) -> Result<Vc<OptionStyledString>> {
826 let export_names = all_known_export_names(*self.module).await?;
827 Ok(Vc::cell(Some(
828 StyledString::Line(vec![
829 StyledString::Text(rcstr!("These are the exports of the module:\n")),
830 StyledString::Code(
831 export_names
832 .iter()
833 .map(|s| s.as_str())
834 .intersperse(", ")
835 .collect::<String>()
836 .into(),
837 ),
838 ])
839 .resolved_cell(),
840 )))
841 }
842
843 #[turbo_tasks::function]
844 fn source(&self) -> Vc<OptionIssueSource> {
845 Vc::cell(Some(self.source))
846 }
847}
848
849#[turbo_tasks::value(shared)]
850pub struct CircularReExport {
851 export: RcStr,
852 import: Option<RcStr>,
853 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
854 module_cycle: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
855}
856
857#[turbo_tasks::value_impl]
858impl Issue for CircularReExport {
859 fn severity(&self) -> IssueSeverity {
860 IssueSeverity::Error
861 }
862
863 #[turbo_tasks::function]
864 async fn title(&self) -> Result<Vc<StyledString>> {
865 Ok(StyledString::Line(vec![
866 StyledString::Text("Export ".into()),
867 StyledString::Code(self.export.clone()),
868 StyledString::Text(" is a circular re-export".into()),
869 ])
870 .cell())
871 }
872
873 #[turbo_tasks::function]
874 fn stage(&self) -> Vc<IssueStage> {
875 IssueStage::Bindings.into()
876 }
877
878 #[turbo_tasks::function]
879 fn file_path(&self) -> Vc<FileSystemPath> {
880 self.module.ident().path()
881 }
882
883 #[turbo_tasks::function]
884 async fn description(&self) -> Result<Vc<OptionStyledString>> {
885 Ok(Vc::cell(Some(
886 StyledString::Stack(vec![
887 StyledString::Line(vec![StyledString::Text("The export".into())]),
888 StyledString::Line(vec![
889 StyledString::Code(self.export.clone()),
890 StyledString::Text(" of module ".into()),
891 StyledString::Strong(self.module.ident().to_string().owned().await?),
892 ]),
893 StyledString::Line(vec![StyledString::Text(
894 "is a re-export of the export".into(),
895 )]),
896 StyledString::Line(vec![
897 StyledString::Code(self.import.clone().unwrap_or_else(|| "*".into())),
898 StyledString::Text(" of module ".into()),
899 StyledString::Strong(self.module_cycle.ident().to_string().owned().await?),
900 StyledString::Text(".".into()),
901 ]),
902 ])
903 .resolved_cell(),
904 )))
905 }
906
907 #[turbo_tasks::function]
908 fn source(&self) -> Vc<OptionIssueSource> {
909 Vc::cell(None)
912 }
913}