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