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::{ChunkingContext, ChunkingType, ChunkingTypeOption, 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::throw_module_not_found_expr,
50 },
51 runtime_functions::{TURBOPACK_EXTERNAL_IMPORT, TURBOPACK_EXTERNAL_REQUIRE, TURBOPACK_IMPORT},
52 tree_shake::{TURBOPACK_PART_IMPORT_SOURCE, asset::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} with {annotations}")]
326pub struct EsmAssetReference {
327 pub module: ResolvedVc<EcmascriptModuleAsset>,
328 pub origin: ResolvedVc<Box<dyn ResolveOrigin>>,
329 pub request: RcStr,
331 pub annotations: 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.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: 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: 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}
401
402#[turbo_tasks::value_impl]
403impl EsmAssetReference {
404 #[turbo_tasks::function]
405 pub(crate) fn get_referenced_asset(self: Vc<Self>) -> Vc<ReferencedAsset> {
406 ReferencedAsset::from_resolve_result(self.resolve_reference())
407 }
408}
409
410#[turbo_tasks::value_impl]
411impl ModuleReference for EsmAssetReference {
412 #[turbo_tasks::function]
413 async fn resolve_reference(&self) -> Result<Vc<ModuleResolveResult>> {
414 let ty = if let Some(loader) = self.annotations.turbopack_loader() {
415 let origin = self.get_origin();
417 let origin_path = origin.origin_path().await?;
418 let loader_request = Request::parse(loader.loader.clone().into());
419 let resolved = resolve(
420 origin_path.parent(),
421 ReferenceType::Loader,
422 loader_request,
423 origin.resolve_options(),
424 );
425 let loader_fs_path = if let Some(source) = *resolved.first_source().await? {
426 (*source.ident().path().await?).clone()
427 } else {
428 bail!("Unable to resolve turbopackLoader '{}'", loader.loader);
429 };
430
431 EcmaScriptModulesReferenceSubType::ImportWithTurbopackUse {
432 loader: ResolvedWebpackLoaderItem {
433 loader: loader_fs_path,
434 options: loader.options.clone(),
435 },
436 rename_as: self.annotations.turbopack_rename_as().cloned(),
437 module_type: self.annotations.turbopack_module_type().cloned(),
438 }
439 } else if let Some(module_type) = self.annotations.module_type() {
440 EcmaScriptModulesReferenceSubType::ImportWithType(RcStr::from(
441 &*module_type.to_string_lossy(),
442 ))
443 } else if let Some(part) = &self.export_name {
444 EcmaScriptModulesReferenceSubType::ImportPart(part.clone())
445 } else {
446 EcmaScriptModulesReferenceSubType::Import
447 };
448
449 let request = Request::parse(self.request.clone().into());
450
451 if let Some(TreeShakingMode::ModuleFragments) = self.tree_shaking_mode {
452 if let Some(ModulePart::Evaluation) = &self.export_name
453 && *self.module.side_effects().await? == ModuleSideEffects::SideEffectFree
454 {
455 return Ok(ModuleResolveResult {
456 primary: Box::new([(RequestKey::default(), ModuleResolveResultItem::Ignore)]),
457 affecting_sources: Default::default(),
458 }
459 .cell());
460 }
461
462 if let Request::Module { module, .. } = &*request.await?
463 && module.is_match(TURBOPACK_PART_IMPORT_SOURCE)
464 {
465 if let Some(part) = &self.export_name {
466 return Ok(*ModuleResolveResult::module(ResolvedVc::upcast(
467 EcmascriptModulePartAsset::select_part(*self.module, part.clone())
468 .to_resolved()
469 .await?,
470 )));
471 }
472 bail!("export_name is required for part import")
473 }
474 }
475
476 let result = esm_resolve(
477 self.get_origin(),
478 request,
479 ty,
480 ResolveErrorMode::Error,
481 Some(self.issue_source),
482 )
483 .await?;
484
485 if let Some(ModulePart::Export(export_name)) = &self.export_name {
486 for &module in result.primary_modules().await? {
487 if let Some(module) = ResolvedVc::try_downcast(module)
488 && *is_export_missing(*module, export_name.clone()).await?
489 {
490 InvalidExport {
491 export: export_name.clone(),
492 module,
493 source: self.issue_source,
494 }
495 .resolved_cell()
496 .emit();
497 }
498 }
499 }
500
501 Ok(result)
502 }
503
504 #[turbo_tasks::function]
505 fn chunking_type(&self) -> Result<Vc<ChunkingTypeOption>> {
506 Ok(Vc::cell(
507 if let Some(chunking_type) = self.annotations.chunking_type() {
508 if chunking_type == "parallel" {
509 Some(ChunkingType::Parallel {
510 inherit_async: true,
511 hoisted: true,
512 })
513 } else if chunking_type == "none" {
514 None
515 } else {
516 return Err(anyhow!(
517 "unknown chunking_type: {}",
518 chunking_type.to_string_lossy()
519 ));
520 }
521 } else {
522 Some(ChunkingType::Parallel {
523 inherit_async: true,
524 hoisted: true,
525 })
526 },
527 ))
528 }
529
530 #[turbo_tasks::function]
531 fn binding_usage(&self) -> Vc<BindingUsage> {
532 BindingUsage {
533 import: self.import_usage.clone(),
534 export: match &self.export_name {
535 Some(ModulePart::Export(export_name)) => ExportUsage::Named(export_name.clone()),
536 Some(ModulePart::Evaluation) => ExportUsage::Evaluation,
537 _ => ExportUsage::All,
538 },
539 }
540 .cell()
541 }
542}
543
544impl EsmAssetReference {
545 pub async fn code_generation(
546 self: ResolvedVc<Self>,
547 chunking_context: Vc<Box<dyn ChunkingContext>>,
548 scope_hoisting_context: ScopeHoistingContext<'_>,
549 ) -> Result<CodeGeneration> {
550 let this = &*self.await?;
551
552 if chunking_context
553 .unused_references()
554 .contains_key(&ResolvedVc::upcast(self))
555 .await?
556 {
557 return Ok(CodeGeneration::empty());
558 }
559
560 if this.annotations.chunking_type().is_none_or(|v| v != "none") {
562 let import_externals = this.import_externals;
563 let referenced_asset = self.get_referenced_asset().await?;
564
565 match &*referenced_asset {
566 ReferencedAsset::Unresolvable => {
567 let request = &this.request;
570 let stmt = Stmt::Expr(ExprStmt {
571 expr: Box::new(throw_module_not_found_expr(request)),
572 span: DUMMY_SP,
573 });
574 return Ok(CodeGeneration::hoisted_stmt(
575 format!("throw {request}").into(),
576 stmt,
577 ));
578 }
579 ReferencedAsset::None => {}
580 _ => {
581 let mut result = vec![];
582
583 let merged_index = if let ReferencedAsset::Some(asset) = &*referenced_asset {
584 scope_hoisting_context.get_module_index(*asset)
585 } else {
586 None
587 };
588
589 if let Some(merged_index) = merged_index {
590 result.push(CodeGenerationHoistedStmt::new(
593 format!("hoisted {merged_index}").into(),
594 quote!(
595 "__turbopack_merged_esm__($id);" as Stmt,
596 id: Expr = Lit::Num(merged_index.into()).into(),
597 ),
598 ));
599 }
600
601 if merged_index.is_some()
602 && matches!(this.export_name, Some(ModulePart::Evaluation))
603 {
604 } else {
608 let ident = referenced_asset
609 .get_ident(
610 chunking_context,
611 this.export_name.as_ref().and_then(|e| match e {
612 ModulePart::Export(export_name) => Some(export_name.clone()),
613 _ => None,
614 }),
615 scope_hoisting_context,
616 )
617 .await?;
618 match ident {
619 Some(ReferencedAssetIdent::LocalBinding { .. }) => {
620 }
622 Some(ident @ ReferencedAssetIdent::Module { .. }) => {
623 let span = this
624 .issue_source
625 .to_swc_offsets()
626 .await?
627 .map_or(DUMMY_SP, |(start, end)| {
628 Span::new(BytePos(start), BytePos(end))
629 });
630 match &*referenced_asset {
631 ReferencedAsset::Unresolvable => {
632 unreachable!();
633 }
634 ReferencedAsset::Some(asset) => {
635 let id = asset.chunk_item_id(chunking_context).await?;
636 let (sym, ctxt) =
637 ident.into_module_namespace_ident().unwrap();
638 let name = Ident::new(
639 sym.into(),
640 DUMMY_SP,
641 ctxt.unwrap_or_default(),
642 );
643 let mut call_expr = quote!(
644 "$turbopack_import($id)" as Expr,
645 turbopack_import: Expr = TURBOPACK_IMPORT.into(),
646 id: Expr = module_id_to_lit(&id),
647 );
648 if this.is_pure_import {
649 call_expr.set_span(PURE_SP);
650 }
651 result.push(CodeGenerationHoistedStmt::new(
652 id.to_string().into(),
653 var_decl_with_span(
654 quote!(
655 "var $name = $call;" as Stmt,
656 name = name,
657 call: Expr = call_expr
658 ),
659 span,
660 ),
661 ));
662 }
663 ReferencedAsset::External(
664 request,
665 ExternalType::EcmaScriptModule,
666 ) => {
667 if !*chunking_context
668 .environment()
669 .supports_esm_externals()
670 .await?
671 {
672 bail!(
673 "the chunking context ({}) does not support \
674 external modules (esm request: {})",
675 chunking_context.name().await?,
676 request
677 );
678 }
679 let (sym, ctxt) =
680 ident.into_module_namespace_ident().unwrap();
681 let name = Ident::new(
682 sym.into(),
683 DUMMY_SP,
684 ctxt.unwrap_or_default(),
685 );
686 let mut call_expr = if import_externals {
687 quote!(
688 "$turbopack_external_import($id)" as Expr,
689 turbopack_external_import: Expr = TURBOPACK_EXTERNAL_IMPORT.into(),
690 id: Expr = Expr::Lit(request.clone().to_string().into())
691 )
692 } else {
693 quote!(
694 "$turbopack_external_require($id, () => require($id), true)" as Expr,
695 turbopack_external_require: Expr = TURBOPACK_EXTERNAL_REQUIRE.into(),
696 id: Expr = Expr::Lit(request.clone().to_string().into())
697 )
698 };
699 if this.is_pure_import {
700 call_expr.set_span(PURE_SP);
701 }
702 result.push(CodeGenerationHoistedStmt::new(
703 name.sym.as_str().into(),
704 var_decl_with_span(
705 quote!(
706 "var $name = $call;" as Stmt,
707 name = name,
708 call: Expr = call_expr,
709 ),
710 span,
711 ),
712 ));
713 }
714 ReferencedAsset::External(
715 request,
716 ExternalType::CommonJs | ExternalType::Url,
717 ) => {
718 if !*chunking_context
719 .environment()
720 .supports_commonjs_externals()
721 .await?
722 {
723 bail!(
724 "the chunking context ({}) does not support \
725 external modules (request: {})",
726 chunking_context.name().await?,
727 request
728 );
729 }
730 let (sym, ctxt) =
731 ident.into_module_namespace_ident().unwrap();
732 let name = Ident::new(
733 sym.into(),
734 DUMMY_SP,
735 ctxt.unwrap_or_default(),
736 );
737 let mut call_expr = quote!(
738 "$turbopack_external_require($id, () => require($id), true)" as Expr,
739 turbopack_external_require: Expr = TURBOPACK_EXTERNAL_REQUIRE.into(),
740 id: Expr = Expr::Lit(request.clone().to_string().into())
741 );
742 if this.is_pure_import {
743 call_expr.set_span(PURE_SP);
744 }
745 result.push(CodeGenerationHoistedStmt::new(
746 name.sym.as_str().into(),
747 var_decl_with_span(
748 quote!(
749 "var $name = $call;" as Stmt,
750 name = name,
751 call: Expr = call_expr,
752 ),
753 span,
754 ),
755 ));
756 }
757 #[allow(unreachable_patterns)]
759 ReferencedAsset::External(request, ty) => {
760 bail!(
761 "Unsupported external type {:?} for ESM reference \
762 with request: {:?}",
763 ty,
764 request
765 )
766 }
767 ReferencedAsset::None => {}
768 };
769 }
770 None => {
771 }
773 }
774 }
775 return Ok(CodeGeneration::hoisted_stmts(result));
776 }
777 }
778 };
779
780 Ok(CodeGeneration::empty())
781 }
782}
783
784fn var_decl_with_span(mut decl: Stmt, span: Span) -> Stmt {
785 match &mut decl {
786 Stmt::Decl(Decl::Var(decl)) => decl.span = span,
787 _ => panic!("Expected Stmt::Decl::Var"),
788 };
789 decl
790}
791
792#[turbo_tasks::value(shared)]
793pub struct InvalidExport {
794 export: RcStr,
795 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
796 source: IssueSource,
797}
798
799#[turbo_tasks::value_impl]
800impl Issue for InvalidExport {
801 fn severity(&self) -> IssueSeverity {
802 IssueSeverity::Error
803 }
804
805 #[turbo_tasks::function]
806 fn title(&self) -> Result<Vc<StyledString>> {
807 Ok(StyledString::Line(vec![
808 StyledString::Text(rcstr!("Export ")),
809 StyledString::Code(self.export.clone()),
810 StyledString::Text(rcstr!(" doesn't exist in target module")),
811 ])
812 .cell())
813 }
814
815 #[turbo_tasks::function]
816 fn stage(&self) -> Vc<IssueStage> {
817 IssueStage::Bindings.cell()
818 }
819
820 #[turbo_tasks::function]
821 fn file_path(&self) -> Vc<FileSystemPath> {
822 self.source.file_path()
823 }
824
825 #[turbo_tasks::function]
826 async fn description(&self) -> Result<Vc<OptionStyledString>> {
827 let export_names = all_known_export_names(*self.module).await?;
828 let did_you_mean = export_names
829 .iter()
830 .map(|s| (s, jaro(self.export.as_str(), s.as_str())))
831 .max_by(|a, b| a.1.partial_cmp(&b.1).unwrap())
832 .map(|(s, _)| s);
833 Ok(Vc::cell(Some(
834 StyledString::Stack(vec![
835 StyledString::Line(vec![
836 StyledString::Text(rcstr!("The export ")),
837 StyledString::Code(self.export.clone()),
838 StyledString::Text(rcstr!(" was not found in module ")),
839 StyledString::Strong(self.module.ident().to_string().owned().await?),
840 StyledString::Text(rcstr!(".")),
841 ]),
842 if let Some(did_you_mean) = did_you_mean {
843 StyledString::Line(vec![
844 StyledString::Text(rcstr!("Did you mean to import ")),
845 StyledString::Code(did_you_mean.clone()),
846 StyledString::Text(rcstr!("?")),
847 ])
848 } else {
849 StyledString::Strong(rcstr!("The module has no exports at all."))
850 },
851 StyledString::Text(
852 "All exports of the module are statically known (It doesn't have dynamic \
853 exports). So it's known statically that the requested export doesn't exist."
854 .into(),
855 ),
856 ])
857 .resolved_cell(),
858 )))
859 }
860
861 #[turbo_tasks::function]
862 async fn detail(&self) -> Result<Vc<OptionStyledString>> {
863 let export_names = all_known_export_names(*self.module).await?;
864 Ok(Vc::cell(Some(
865 StyledString::Line(vec![
866 StyledString::Text(rcstr!("These are the exports of the module:\n")),
867 StyledString::Code(
868 export_names
869 .iter()
870 .map(|s| s.as_str())
871 .intersperse(", ")
872 .collect::<String>()
873 .into(),
874 ),
875 ])
876 .resolved_cell(),
877 )))
878 }
879
880 #[turbo_tasks::function]
881 fn source(&self) -> Vc<OptionIssueSource> {
882 Vc::cell(Some(self.source))
883 }
884}
885
886#[turbo_tasks::value(shared)]
887pub struct CircularReExport {
888 export: RcStr,
889 import: Option<RcStr>,
890 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
891 module_cycle: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
892}
893
894#[turbo_tasks::value_impl]
895impl Issue for CircularReExport {
896 fn severity(&self) -> IssueSeverity {
897 IssueSeverity::Error
898 }
899
900 #[turbo_tasks::function]
901 async fn title(&self) -> Result<Vc<StyledString>> {
902 Ok(StyledString::Line(vec![
903 StyledString::Text(rcstr!("Export ")),
904 StyledString::Code(self.export.clone()),
905 StyledString::Text(rcstr!(" is a circular re-export")),
906 ])
907 .cell())
908 }
909
910 #[turbo_tasks::function]
911 fn stage(&self) -> Vc<IssueStage> {
912 IssueStage::Bindings.cell()
913 }
914
915 #[turbo_tasks::function]
916 fn file_path(&self) -> Vc<FileSystemPath> {
917 self.module.ident().path()
918 }
919
920 #[turbo_tasks::function]
921 async fn description(&self) -> Result<Vc<OptionStyledString>> {
922 Ok(Vc::cell(Some(
923 StyledString::Stack(vec![
924 StyledString::Line(vec![StyledString::Text(rcstr!("The export"))]),
925 StyledString::Line(vec![
926 StyledString::Code(self.export.clone()),
927 StyledString::Text(rcstr!(" of module ")),
928 StyledString::Strong(self.module.ident().to_string().owned().await?),
929 ]),
930 StyledString::Line(vec![StyledString::Text(rcstr!(
931 "is a re-export of the export"
932 ))]),
933 StyledString::Line(vec![
934 StyledString::Code(self.import.clone().unwrap_or_else(|| rcstr!("*"))),
935 StyledString::Text(rcstr!(" of module ")),
936 StyledString::Strong(self.module_cycle.ident().to_string().owned().await?),
937 StyledString::Text(rcstr!(".")),
938 ]),
939 ])
940 .resolved_cell(),
941 )))
942 }
943
944 #[turbo_tasks::function]
945 fn source(&self) -> Vc<OptionIssueSource> {
946 Vc::cell(None)
949 }
950}