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