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