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