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