1use anyhow::{Result, bail};
2use async_trait::async_trait;
3use bincode::{Decode, Encode};
4use either::Either;
5use strsim::jaro;
6use swc_core::{
7 common::{BytePos, DUMMY_SP, Span, SyntaxContext, source_map::PURE_SP},
8 ecma::ast::{
9 ComputedPropName, Decl, Expr, ExprStmt, Ident, Lit, MemberExpr, MemberProp, Number,
10 SeqExpr, Stmt, Str,
11 },
12 quote,
13};
14use turbo_rcstr::{RcStr, rcstr};
15use turbo_tasks::{
16 NonLocalValue, ResolvedVc, ValueToString, Vc, debug::ValueDebugFormat, trace::TraceRawVcs,
17 turbobail,
18};
19use turbo_tasks_fs::FileSystemPath;
20use turbopack_core::{
21 chunk::{ChunkingContext, ChunkingType, ModuleChunkItemIdExt},
22 issue::{
23 Issue, IssueExt, IssueSeverity, IssueSource, IssueStage, StyledString,
24 code_gen::CodeGenerationIssue,
25 },
26 loader::{ResolvedWebpackLoaderItem, WebpackLoaderItem},
27 module::{Module, ModuleSideEffects},
28 module_graph::binding_usage_info::ModuleExportUsageInfo,
29 reference::ModuleReference,
30 reference_type::{EcmaScriptModulesReferenceSubType, ReferenceType},
31 resolve::{
32 BindingUsage, ExportUsage, ExternalType, ImportUsage, ModulePart, ModuleResolveResult,
33 ModuleResolveResultItem, RequestKey, ResolveErrorMode,
34 origin::{ResolveOrigin, ResolveOriginExt},
35 parse::Request,
36 resolve,
37 },
38 source::Source,
39};
40use turbopack_resolve::ecmascript::esm_resolve;
41
42use crate::{
43 EcmascriptModuleAsset, ScopeHoistingContext,
44 analyzer::imports::ImportAnnotations,
45 chunk::{EcmascriptChunkPlaceable, EcmascriptExports},
46 code_gen::{CodeGeneration, CodeGenerationHoistedStmt},
47 export::Liveness,
48 magic_identifier,
49 module_fragments::{TURBOPACK_PART_IMPORT_SOURCE, part::module::EcmascriptModulePartAsset},
50 references::{
51 esm::{
52 EsmExport,
53 export::{all_known_export_names, is_export_missing},
54 },
55 util::{SpecifiedChunkingType, throw_module_not_found_expr},
56 },
57 runtime_functions::{TURBOPACK_EXTERNAL_IMPORT, TURBOPACK_EXTERNAL_REQUIRE, TURBOPACK_IMPORT},
58 utils::module_id_to_lit,
59};
60
61#[derive(PartialEq, Eq)]
62pub enum ReferencedAsset {
63 Some(ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>),
64 External(RcStr, ExternalType),
65 NonPlaceable(ResolvedVc<Box<dyn Module>>),
70 None,
71 Unresolvable,
72}
73
74#[derive(Debug)]
75pub enum ReferencedAssetIdent {
76 LocalBinding {
78 ident: RcStr,
79 ctxt: SyntaxContext,
80 liveness: Liveness,
81 },
82 Module {
84 namespace_ident: String,
88 ctxt: Option<SyntaxContext>,
89 export: Option<RcStr>,
90 import_source: ImportSource,
97 },
98}
99
100#[derive(Debug)]
102pub enum ImportSource {
103 Module {
105 asset: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
106 },
107 External { request: RcStr, ty: ExternalType },
111}
112
113impl ImportSource {
114 pub async fn get_namespace_ident(
116 &self,
117 chunking_context: Vc<Box<dyn ChunkingContext>>,
118 ) -> Result<String> {
119 Ok(match self {
120 ImportSource::Module { asset } => {
121 ReferencedAsset::get_ident_from_placeable(asset, chunking_context).await?
122 }
123 ImportSource::External { request, ty } => {
124 magic_identifier::mangle(&format!("{ty} external {request}"))
125 }
126 })
127 }
128}
129
130impl ReferencedAssetIdent {
131 pub fn into_module_namespace_ident(self) -> Option<(String, Option<SyntaxContext>)> {
132 match self {
133 ReferencedAssetIdent::Module {
134 namespace_ident,
135 ctxt,
136 ..
137 } => Some((namespace_ident, ctxt)),
138 ReferencedAssetIdent::LocalBinding { .. } => None,
139 }
140 }
141
142 pub fn as_expr_individual(&self, span: Span) -> Either<Ident, MemberExpr> {
143 match self {
144 ReferencedAssetIdent::LocalBinding {
145 ident,
146 ctxt,
147 liveness: _,
148 } => Either::Left(Ident::new(ident.as_str().into(), span, *ctxt)),
149 ReferencedAssetIdent::Module {
150 namespace_ident,
151 ctxt,
152 export,
153 import_source: _,
154 } => {
155 if let Some(export) = export {
156 Either::Right(MemberExpr {
157 span,
158 obj: Box::new(Expr::Ident(Ident::new(
159 namespace_ident.as_str().into(),
160 DUMMY_SP,
161 ctxt.unwrap_or_default(),
162 ))),
163 prop: MemberProp::Computed(ComputedPropName {
164 span: DUMMY_SP,
165 expr: Box::new(Expr::Lit(Lit::Str(Str {
166 span: DUMMY_SP,
167 value: export.as_str().into(),
168 raw: None,
169 }))),
170 }),
171 })
172 } else {
173 Either::Left(Ident::new(
174 namespace_ident.as_str().into(),
175 span,
176 ctxt.unwrap_or_default(),
177 ))
178 }
179 }
180 }
181 }
182 pub fn as_expr(&self, span: Span, is_callee: bool) -> Expr {
183 match self.as_expr_individual(span) {
184 Either::Left(ident) => ident.into(),
185 Either::Right(member) => {
186 if is_callee {
187 Expr::Seq(SeqExpr {
188 exprs: vec![
189 Box::new(Expr::Lit(Lit::Num(Number {
190 span: DUMMY_SP,
191 value: 0.0,
192 raw: None,
193 }))),
194 Box::new(member.into()),
195 ],
196 span: DUMMY_SP,
197 })
198 } else {
199 member.into()
200 }
201 }
202 }
203 }
204}
205
206impl ReferencedAsset {
207 pub async fn get_ident(
208 &self,
209 chunking_context: Vc<Box<dyn ChunkingContext>>,
210 export: Option<RcStr>,
211 scope_hoisting_context: ScopeHoistingContext<'_>,
212 ) -> Result<Option<ReferencedAssetIdent>> {
213 self.get_ident_inner(chunking_context, export, scope_hoisting_context, None)
214 .await
215 }
216
217 async fn get_ident_inner(
218 &self,
219 chunking_context: Vc<Box<dyn ChunkingContext>>,
220 export: Option<RcStr>,
221 scope_hoisting_context: ScopeHoistingContext<'_>,
222 initial: Option<&ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>>,
223 ) -> Result<Option<ReferencedAssetIdent>> {
224 Ok(match self {
225 ReferencedAsset::Some(asset) => {
226 if let Some(ctxt) = scope_hoisting_context.get_module_syntax_context(*asset)
227 && let Some(export) = &export
228 && let EcmascriptExports::EsmExports(exports) = *asset.get_exports().await?
229 {
230 let exports = exports.expand_exports(ModuleExportUsageInfo::all()).await?;
231 let esm_export = exports.exports.get(export);
232 match esm_export {
233 Some(EsmExport::LocalBinding(_name, liveness)) => {
234 return Ok(Some(ReferencedAssetIdent::LocalBinding {
238 ident: export.clone(),
239 ctxt,
240 liveness: *liveness,
241 }));
242 }
243 Some(b @ EsmExport::ImportedBinding(esm_ref, _, _))
244 | Some(b @ EsmExport::ImportedNamespace(esm_ref)) => {
245 let imported = if let EsmExport::ImportedBinding(_, export, _) = b {
246 Some(export.clone())
247 } else {
248 None
249 };
250
251 let referenced_asset =
252 ReferencedAsset::from_resolve_result(esm_ref.resolve_reference())
253 .await?;
254
255 if let Some(&initial) = initial
256 && referenced_asset == ReferencedAsset::Some(initial)
257 {
258 CircularReExport {
261 export: export.clone(),
262 import: imported.clone(),
263 module: *asset,
264 module_cycle: initial,
265 }
266 .resolved_cell()
267 .emit();
268 return Ok(None);
269 }
270
271 return Ok(
274 match Box::pin(referenced_asset.get_ident_inner(
275 chunking_context,
276 imported,
277 scope_hoisting_context,
278 Some(asset),
279 ))
280 .await?
281 {
282 Some(ReferencedAssetIdent::Module {
283 namespace_ident,
284 ctxt: None,
288 export,
289 import_source,
290 }) => Some(ReferencedAssetIdent::Module {
291 namespace_ident,
292 ctxt: Some(ctxt),
293 export,
294 import_source,
295 }),
296 ident => ident,
297 },
298 );
299 }
300 Some(EsmExport::Error) | None => {
301 }
304 }
305 }
306
307 let import_source = ImportSource::Module { asset: *asset };
308 Some(ReferencedAssetIdent::Module {
309 namespace_ident: import_source.get_namespace_ident(chunking_context).await?,
310 ctxt: None,
311 export,
312 import_source,
313 })
314 }
315 ReferencedAsset::External(request, ty) => {
316 let import_source = ImportSource::External {
317 request: request.clone(),
318 ty: *ty,
319 };
320 Some(ReferencedAssetIdent::Module {
321 namespace_ident: import_source.get_namespace_ident(chunking_context).await?,
322 ctxt: None,
323 export,
324 import_source,
325 })
326 }
327 ReferencedAsset::NonPlaceable(module) => {
328 CodeGenerationIssue {
332 severity: IssueSeverity::Error,
333 title: StyledString::Text(rcstr!("non-ecmascript placeable asset"))
334 .resolved_cell(),
335 message: StyledString::Text(
336 format!(
337 "{} has no ECMAScript exports, so {} can't be read from it. It can \
338 only be imported for its side effects.",
339 module.ident().to_string().await?,
340 match &export {
341 Some(export) => format!("the export {export:?}"),
342 None => "a namespace".to_string(),
343 }
344 )
345 .into(),
346 )
347 .resolved_cell(),
348 path: module.ident().await?.path.clone(),
349 source: None,
350 }
351 .resolved_cell()
352 .emit();
353 None
354 }
355 ReferencedAsset::None | ReferencedAsset::Unresolvable => None,
356 })
357 }
358
359 pub(crate) async fn get_ident_from_placeable(
360 asset: &Vc<Box<dyn EcmascriptChunkPlaceable>>,
361 chunking_context: Vc<Box<dyn ChunkingContext>>,
362 ) -> Result<String> {
363 let id = asset.chunk_item_id(chunking_context).await?;
364 Ok(magic_identifier::mangle(&format!("imported module {id}")))
367 }
368}
369
370impl ReferencedAsset {
371 pub async fn from_resolve_result(resolve_result: Vc<ModuleResolveResult>) -> Result<Self> {
372 let result = resolve_result.await?;
374 if result.is_unresolvable() {
375 return Ok(ReferencedAsset::Unresolvable);
376 }
377 let mut non_placeable = None;
378 for (_, result) in result.primary.iter() {
379 match result {
380 ModuleResolveResultItem::External {
381 name: request, ty, ..
382 } => {
383 return Ok(ReferencedAsset::External(request.clone(), *ty));
384 }
385 ModuleResolveResultItem::Module(module) => {
386 if let Some(placeable) =
387 ResolvedVc::try_downcast::<Box<dyn EcmascriptChunkPlaceable>>(*module)
388 {
389 return Ok(ReferencedAsset::Some(placeable));
390 }
391 non_placeable = non_placeable.or(Some(*module));
392 }
393 _ => {}
395 }
396 }
397 Ok(match non_placeable {
398 Some(module) => ReferencedAsset::NonPlaceable(module),
399 None => ReferencedAsset::None,
400 })
401 }
402}
403
404#[turbo_tasks::value(transparent)]
405pub struct EsmAssetReferences(Vec<ResolvedVc<EsmAssetReference>>);
406
407#[turbo_tasks::value_impl]
408impl EsmAssetReferences {
409 #[turbo_tasks::function]
410 pub fn empty() -> Vc<Self> {
411 Vc::cell(Vec::new())
412 }
413}
414
415#[turbo_tasks::value(shared)]
416#[derive(Hash, Debug, ValueToString)]
417#[value_to_string("import {request}")]
418pub struct EsmAssetReference {
419 pub module: ResolvedVc<EcmascriptModuleAsset>,
420 pub origin: ResolvedVc<Box<dyn ResolveOrigin>>,
422 pub request: RcStr,
424 pub issue_source: IssueSource,
425 pub export_name: Option<ModulePart>,
426 pub import_usage: ImportUsage,
427 pub import_externals: bool,
428 pub module_fragments_enabled: bool,
429 pub is_pure_import: bool,
430 extras: Option<Box<EsmReferenceExtras>>,
434}
435
436#[derive(
440 Clone,
441 Default,
442 PartialEq,
443 Eq,
444 Hash,
445 Debug,
446 TraceRawVcs,
447 ValueDebugFormat,
448 NonLocalValue,
449 Encode,
450 Decode,
451)]
452struct EsmReferenceExtras {
453 turbopack_loader: Option<WebpackLoaderItem>,
455 turbopack_rename_as: Option<RcStr>,
457 turbopack_module_type: Option<RcStr>,
459 module_type: Option<RcStr>,
461 chunking_type: Option<SpecifiedChunkingType>,
463 resolve_override: Option<ResolvedVc<Box<dyn Module>>>,
465}
466
467impl EsmReferenceExtras {
468 fn new(
471 annotations: Option<&ImportAnnotations>,
472 resolve_override: Option<ResolvedVc<Box<dyn Module>>>,
473 ) -> Option<Box<Self>> {
474 let extras = EsmReferenceExtras {
475 turbopack_loader: annotations.and_then(|a| a.turbopack_loader().cloned()),
476 turbopack_rename_as: annotations.and_then(|a| a.turbopack_rename_as().cloned()),
477 turbopack_module_type: annotations.and_then(|a| a.turbopack_module_type().cloned()),
478 module_type: annotations
479 .and_then(|a| a.module_type())
480 .map(|m| RcStr::from(&*m.to_string_lossy())),
481 chunking_type: annotations.and_then(|a| a.chunking_type()),
482 resolve_override,
483 };
484 (extras != EsmReferenceExtras::default()).then(|| Box::new(extras))
485 }
486}
487
488impl EsmAssetReference {
489 #[allow(clippy::too_many_arguments)]
490 async fn new_inner(
491 module: ResolvedVc<EcmascriptModuleAsset>,
492 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
493 request: RcStr,
494 issue_source: IssueSource,
495 annotations: Option<ImportAnnotations>,
496 export_name: Option<ModulePart>,
497 import_usage: ImportUsage,
498 import_externals: bool,
499 module_fragments_enabled: bool,
500 resolve_override: Option<ResolvedVc<Box<dyn Module>>>,
501 is_pure_import: bool,
502 ) -> Result<Self> {
503 let origin = if let Some(transition) = annotations.as_ref().and_then(|a| a.transition()) {
506 origin
507 .with_transition(transition.into())
508 .await?
509 .to_resolved()
510 .await?
511 } else {
512 origin
513 };
514 Ok(EsmAssetReference {
515 module,
516 origin,
517 request,
518 issue_source,
519 export_name,
520 import_usage,
521 import_externals,
522 module_fragments_enabled,
523 is_pure_import,
524 extras: EsmReferenceExtras::new(annotations.as_ref(), resolve_override),
525 })
526 }
527
528 #[allow(clippy::too_many_arguments)]
529 pub async fn new(
530 module: ResolvedVc<EcmascriptModuleAsset>,
531 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
532 request: RcStr,
533 issue_source: IssueSource,
534 annotations: Option<ImportAnnotations>,
535 export_name: Option<ModulePart>,
536 import_usage: ImportUsage,
537 import_externals: bool,
538 module_fragments_enabled: bool,
539 resolve_override: Option<ResolvedVc<Box<dyn Module>>>,
540 ) -> Result<Self> {
541 Self::new_inner(
542 module,
543 origin,
544 request,
545 issue_source,
546 annotations,
547 export_name,
548 import_usage,
549 import_externals,
550 module_fragments_enabled,
551 resolve_override,
552 false,
553 )
554 .await
555 }
556
557 #[allow(clippy::too_many_arguments)]
558 pub async fn new_pure(
559 module: ResolvedVc<EcmascriptModuleAsset>,
560 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
561 request: RcStr,
562 issue_source: IssueSource,
563 annotations: Option<ImportAnnotations>,
564 export_name: Option<ModulePart>,
565 import_usage: ImportUsage,
566 import_externals: bool,
567 module_fragments_enabled: bool,
568 resolve_override: Option<ResolvedVc<Box<dyn Module>>>,
569 ) -> Result<Self> {
570 Self::new_inner(
571 module,
572 origin,
573 request,
574 issue_source,
575 annotations,
576 export_name,
577 import_usage,
578 import_externals,
579 module_fragments_enabled,
580 resolve_override,
581 true,
582 )
583 .await
584 }
585
586 pub fn rewrite_for_export(&self, export_name: ModulePart) -> Self {
593 EsmAssetReference {
594 module: self.module,
595 origin: self.origin,
596 request: self.request.clone(),
597 issue_source: self.issue_source,
598 export_name: Some(export_name),
599 import_usage: self.import_usage.clone(),
603 import_externals: self.import_externals,
604 module_fragments_enabled: self.module_fragments_enabled,
605 is_pure_import: self.is_pure_import,
606 extras: self.extras.clone(),
607 }
608 }
609
610 pub(crate) fn get_referenced_asset(
611 self: Vc<Self>,
612 ) -> impl Future<Output = Result<ReferencedAsset>> {
613 ReferencedAsset::from_resolve_result(self.resolve_reference())
614 }
615}
616
617#[turbo_tasks::value_impl]
618impl ModuleReference for EsmAssetReference {
619 #[turbo_tasks::function]
620 async fn resolve_reference(&self) -> Result<Vc<ModuleResolveResult>> {
621 let extras = self.extras.as_deref();
622 if let Some(resolved) = extras.and_then(|e| e.resolve_override) {
623 return Ok(*ModuleResolveResult::module(resolved));
624 }
625 let ty = if let Some(loader) = extras.and_then(|e| e.turbopack_loader.as_ref()) {
626 let origin_ref = self.origin.into_trait_ref().await?;
628 let origin_path = origin_ref.origin_path();
629 let loader_request = Request::parse(loader.loader.clone().into());
630 let resolved = resolve(
631 origin_path.parent(),
632 ReferenceType::Loader,
633 loader_request,
634 origin_ref.resolve_options(),
635 );
636 let loader_fs_path = if let Some(source) = resolved.await?.first_source() {
637 source.ident().await?.path.clone()
638 } else {
639 bail!("Unable to resolve turbopackLoader '{}'", loader.loader);
640 };
641
642 EcmaScriptModulesReferenceSubType::ImportWithTurbopackUse {
643 loader: ResolvedWebpackLoaderItem {
644 loader: loader_fs_path,
645 options: loader.options.clone(),
646 },
647 rename_as: extras.and_then(|e| e.turbopack_rename_as.clone()),
648 module_type: extras.and_then(|e| e.turbopack_module_type.clone()),
649 }
650 } else if let Some(module_type) = extras.and_then(|e| e.module_type.as_ref()) {
651 EcmaScriptModulesReferenceSubType::ImportWithType(module_type.clone())
652 } else if let Some(part) = &self.export_name {
653 EcmaScriptModulesReferenceSubType::ImportPart(part.clone())
654 } else {
655 EcmaScriptModulesReferenceSubType::Import
656 };
657
658 let request = Request::parse(self.request.clone().into());
659
660 if self.module_fragments_enabled {
661 if let Some(ModulePart::Evaluation) = &self.export_name
662 && *self.module.side_effects().await? == ModuleSideEffects::SideEffectFree
663 {
664 return Ok(ModuleResolveResult {
665 primary: Box::new([(RequestKey::default(), ModuleResolveResultItem::Ignore)]),
666 affecting_sources: Default::default(),
667 }
668 .cell());
669 }
670
671 if let Request::Module { module, .. } = &*request.await?
672 && module.is_match(TURBOPACK_PART_IMPORT_SOURCE)
673 {
674 if let Some(part) = &self.export_name {
675 return Ok(*ModuleResolveResult::module(ResolvedVc::upcast(
676 EcmascriptModulePartAsset::select_part(*self.module, part.clone())
677 .to_resolved()
678 .await?,
679 )));
680 }
681 bail!("export_name is required for part import")
682 }
683 }
684
685 let result = esm_resolve(
686 *self.origin,
687 request,
688 ty,
689 ResolveErrorMode::Error,
690 Some(self.issue_source),
691 )
692 .await?;
693
694 if let Some(ModulePart::Export(export_name)) = &self.export_name {
695 for &module in result.await?.primary_modules().await?.iter() {
696 if let Some(module) = ResolvedVc::try_downcast(module)
697 && *is_export_missing(*module, export_name.clone()).await?
698 {
699 InvalidExport {
700 export: export_name.clone(),
701 module,
702 source: self.issue_source,
703 }
704 .resolved_cell()
705 .emit();
706 }
707 }
708 }
709
710 Ok(result)
711 }
712
713 fn chunking_type(&self) -> Option<ChunkingType> {
714 self.extras
715 .as_deref()
716 .and_then(|e| e.chunking_type)
717 .map_or_else(
718 || {
719 Some(ChunkingType::Parallel {
720 inherit_async: true,
721 hoisted: true,
722 })
723 },
724 |c| c.as_chunking_type(true, true),
725 )
726 }
727
728 fn binding_usage(&self) -> BindingUsage {
729 BindingUsage {
730 import: self.import_usage.clone(),
731 export: match &self.export_name {
732 Some(ModulePart::Export(export_name)) => ExportUsage::Named(export_name.clone()),
733 Some(ModulePart::Evaluation) => ExportUsage::Evaluation,
734 _ => ExportUsage::All,
735 },
736 }
737 }
738
739 fn source(&self) -> Option<IssueSource> {
740 Some(self.issue_source)
741 }
742}
743
744impl EsmAssetReference {
745 pub async fn code_generation(
746 self: ResolvedVc<Self>,
747 chunking_context: Vc<Box<dyn ChunkingContext>>,
748 scope_hoisting_context: ScopeHoistingContext<'_>,
749 ) -> Result<CodeGeneration> {
750 let this = &*self.await?;
751
752 if chunking_context
753 .unused_references()
754 .contains_key(&ResolvedVc::upcast(self))
755 .await?
756 {
757 return Ok(CodeGeneration::empty());
758 }
759
760 if this
762 .extras
763 .as_deref()
764 .and_then(|e| e.chunking_type)
765 .is_none_or(|v| v != SpecifiedChunkingType::None)
766 {
767 let import_externals = this.import_externals;
768 let referenced_asset = self.get_referenced_asset().await?;
769
770 match &referenced_asset {
771 ReferencedAsset::Unresolvable => {
772 let request = &this.request;
775 let stmt = Stmt::Expr(ExprStmt {
776 expr: Box::new(throw_module_not_found_expr(request)),
777 span: DUMMY_SP,
778 });
779 return Ok(CodeGeneration::hoisted_stmt(
780 format!("throw {request}").into(),
781 stmt,
782 ));
783 }
784 ReferencedAsset::None | ReferencedAsset::NonPlaceable(_) => {}
787 _ => {
788 let mut result = vec![];
789
790 let merged_index = if let ReferencedAsset::Some(asset) = &referenced_asset {
791 scope_hoisting_context.get_module_index(*asset)
792 } else {
793 None
794 };
795
796 if let Some(merged_index) = merged_index {
797 result.push(CodeGenerationHoistedStmt::new(
800 format!("hoisted {merged_index}").into(),
801 quote!(
802 "__turbopack_merged_esm__($id);" as Stmt,
803 id: Expr = Lit::Num(merged_index.into()).into(),
804 ),
805 ));
806 }
807
808 if merged_index.is_some()
809 && matches!(this.export_name, Some(ModulePart::Evaluation))
810 {
811 } else {
815 let ident = referenced_asset
816 .get_ident(
817 chunking_context,
818 this.export_name.as_ref().and_then(|e| match e {
819 ModulePart::Export(export_name) => Some(export_name.clone()),
820 _ => None,
821 }),
822 scope_hoisting_context,
823 )
824 .await?;
825 drop(referenced_asset);
831 match ident {
832 Some(ReferencedAssetIdent::LocalBinding { .. }) => {
833 }
835 Some(ReferencedAssetIdent::Module {
836 namespace_ident,
837 ctxt,
838 export: _,
839 import_source,
840 }) => {
841 let span = this
842 .issue_source
843 .to_swc_offsets()
844 .await?
845 .map_or(DUMMY_SP, |(start, end)| {
846 Span::new(BytePos(start), BytePos(end))
847 });
848 let name = Ident::new(
849 namespace_ident.into(),
850 DUMMY_SP,
851 ctxt.unwrap_or_default(),
852 );
853 let (key, mut call_expr) = match import_source {
854 ImportSource::Module { asset } => {
855 let id = asset.chunk_item_id(chunking_context).await?;
856 (
861 format!("{} {:?}", id, ctxt).into(),
862 quote!(
863 "$turbopack_import($id)" as Expr,
864 turbopack_import: Expr = TURBOPACK_IMPORT.into(),
865 id: Expr = module_id_to_lit(&id),
866 ),
867 )
868 }
869 ImportSource::External {
870 request,
871 ty: ExternalType::EcmaScriptModule,
872 } => {
873 if !*chunking_context
874 .environment()
875 .supports_esm_externals()
876 .await?
877 {
878 turbobail!(
879 "the chunking context ({}) does not support \
880 external modules (esm request: {request})",
881 chunking_context.name()
882 );
883 }
884 let call = if import_externals {
885 quote!(
886 "$turbopack_external_import($id)" as Expr,
887 turbopack_external_import: Expr = TURBOPACK_EXTERNAL_IMPORT.into(),
888 id: Expr = Expr::Lit(request.to_string().into())
889 )
890 } else {
891 quote!(
892 "$turbopack_external_require($id, () => require($id), true)" as Expr,
893 turbopack_external_require: Expr = TURBOPACK_EXTERNAL_REQUIRE.into(),
894 id: Expr = Expr::Lit(request.to_string().into())
895 )
896 };
897 (name.sym.as_str().into(), call)
898 }
899 ImportSource::External {
900 request,
901 ty: ExternalType::CommonJs | ExternalType::Url,
902 } => {
903 if !*chunking_context
904 .environment()
905 .supports_commonjs_externals()
906 .await?
907 {
908 turbobail!(
909 "the chunking context ({}) does not support \
910 external modules (request: {request})",
911 chunking_context.name()
912 );
913 }
914 let call = quote!(
915 "$turbopack_external_require($id, () => require($id), true)" as Expr,
916 turbopack_external_require: Expr = TURBOPACK_EXTERNAL_REQUIRE.into(),
917 id: Expr = Expr::Lit(request.to_string().into())
918 );
919 (name.sym.as_str().into(), call)
920 }
921 #[allow(unreachable_patterns)]
923 ImportSource::External { request, ty, .. } => {
924 bail!(
925 "Unsupported external type {:?} for ESM reference \
926 with request: {:?}",
927 ty,
928 request
929 )
930 }
931 };
932 if this.is_pure_import {
933 call_expr.set_span(PURE_SP);
934 }
935 result.push(CodeGenerationHoistedStmt::new(
936 key,
937 var_decl_with_span(
938 quote!(
939 "var $name = $call;" as Stmt,
940 name = name,
941 call: Expr = call_expr
942 ),
943 span,
944 ),
945 ));
946 }
947 None => {
948 }
950 }
951 }
952 return Ok(CodeGeneration::hoisted_stmts(result));
953 }
954 }
955 };
956
957 Ok(CodeGeneration::empty())
958 }
959}
960
961fn var_decl_with_span(mut decl: Stmt, span: Span) -> Stmt {
962 match &mut decl {
963 Stmt::Decl(Decl::Var(decl)) => decl.span = span,
964 _ => panic!("Expected Stmt::Decl::Var"),
965 };
966 decl
967}
968
969#[turbo_tasks::value(shared)]
970pub struct InvalidExport {
971 export: RcStr,
972 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
973 source: IssueSource,
974}
975
976#[async_trait]
977#[turbo_tasks::value_impl]
978impl Issue for InvalidExport {
979 fn severity(&self) -> IssueSeverity {
980 IssueSeverity::Error
981 }
982
983 async fn title(&self) -> Result<StyledString> {
984 Ok(StyledString::Line(vec![
985 StyledString::Text(rcstr!("Export ")),
986 StyledString::Code(self.export.clone()),
987 StyledString::Text(rcstr!(" doesn't exist in target module")),
988 ]))
989 }
990
991 fn stage(&self) -> IssueStage {
992 IssueStage::Bindings
993 }
994
995 async fn file_path(&self) -> Result<FileSystemPath> {
996 self.source.file_path().await
997 }
998
999 async fn description(&self) -> Result<Option<StyledString>> {
1000 let export_names = all_known_export_names(*self.module).await?;
1001 let did_you_mean = export_names
1002 .iter()
1003 .map(|s| (s, jaro(self.export.as_str(), s.as_str())))
1004 .max_by(|a, b| a.1.partial_cmp(&b.1).unwrap())
1005 .map(|(s, _)| s);
1006 Ok(Some(StyledString::Stack(vec![
1007 StyledString::Line(vec![
1008 StyledString::Text(rcstr!("The export ")),
1009 StyledString::Code(self.export.clone()),
1010 StyledString::Text(rcstr!(" was not found in module ")),
1011 StyledString::Strong(self.module.ident().to_string().owned().await?),
1012 StyledString::Text(rcstr!(".")),
1013 ]),
1014 if let Some(did_you_mean) = did_you_mean {
1015 StyledString::Line(vec![
1016 StyledString::Text(rcstr!("Did you mean to import ")),
1017 StyledString::Code(did_you_mean.clone()),
1018 StyledString::Text(rcstr!("?")),
1019 ])
1020 } else {
1021 StyledString::Strong(rcstr!("The module has no exports at all."))
1022 },
1023 StyledString::Text(
1024 "All exports of the module are statically known (It doesn't have dynamic \
1025 exports). So it's known statically that the requested export doesn't exist."
1026 .into(),
1027 ),
1028 ])))
1029 }
1030
1031 async fn detail(&self) -> Result<Option<StyledString>> {
1032 let export_names = all_known_export_names(*self.module).await?;
1033 Ok(Some(StyledString::Line(vec![
1034 StyledString::Text(rcstr!("These are the exports of the module:\n")),
1035 StyledString::Code(
1036 export_names
1037 .iter()
1038 .map(|s| s.as_str())
1039 .intersperse(", ")
1040 .collect::<String>()
1041 .into(),
1042 ),
1043 ])))
1044 }
1045
1046 fn source(&self) -> Option<IssueSource> {
1047 Some(self.source)
1048 }
1049}
1050
1051#[turbo_tasks::value(shared)]
1052pub struct CircularReExport {
1053 export: RcStr,
1054 import: Option<RcStr>,
1055 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
1056 module_cycle: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
1057}
1058
1059#[async_trait]
1060#[turbo_tasks::value_impl]
1061impl Issue for CircularReExport {
1062 fn severity(&self) -> IssueSeverity {
1063 IssueSeverity::Error
1064 }
1065
1066 async fn title(&self) -> Result<StyledString> {
1067 Ok(StyledString::Line(vec![
1068 StyledString::Text(rcstr!("Export ")),
1069 StyledString::Code(self.export.clone()),
1070 StyledString::Text(rcstr!(" is a circular re-export")),
1071 ]))
1072 }
1073
1074 fn stage(&self) -> IssueStage {
1075 IssueStage::Bindings
1076 }
1077
1078 async fn file_path(&self) -> Result<FileSystemPath> {
1079 Ok(self.module.ident().await?.path.clone())
1080 }
1081
1082 async fn description(&self) -> Result<Option<StyledString>> {
1083 Ok(Some(StyledString::Stack(vec![
1084 StyledString::Line(vec![StyledString::Text(rcstr!("The export"))]),
1085 StyledString::Line(vec![
1086 StyledString::Code(self.export.clone()),
1087 StyledString::Text(rcstr!(" of module ")),
1088 StyledString::Strong(self.module.ident().to_string().owned().await?),
1089 ]),
1090 StyledString::Line(vec![StyledString::Text(rcstr!(
1091 "is a re-export of the export"
1092 ))]),
1093 StyledString::Line(vec![
1094 StyledString::Code(self.import.clone().unwrap_or_else(|| rcstr!("*"))),
1095 StyledString::Text(rcstr!(" of module ")),
1096 StyledString::Strong(self.module_cycle.ident().to_string().owned().await?),
1097 StyledString::Text(rcstr!(".")),
1098 ]),
1099 ])))
1100 }
1101
1102 fn source(&self) -> Option<IssueSource> {
1103 None
1106 }
1107}