1use std::{
2 borrow::Cow,
3 collections::BTreeMap,
4 fmt::{Display, Formatter, Write},
5 future::Future,
6 iter::{empty, once},
7};
8
9use anyhow::{Result, bail};
10use bincode::{Decode, Encode};
11use either::Either;
12use once_cell::sync::Lazy;
13use rustc_hash::{FxHashMap, FxHashSet};
14use serde::{Deserialize, Serialize};
15use smallvec::SmallVec;
16use tracing::{Instrument, Level};
17use turbo_frozenmap::{FrozenMap, FrozenSet};
18use turbo_rcstr::{RcStr, rcstr};
19use turbo_tasks::{
20 FxIndexMap, FxIndexSet, NonLocalValue, ReadRef, ResolvedVc, TaskInput, TryFlatJoinIterExt,
21 TryJoinIterExt, ValueToString, ValueToStringRef, Vc, trace::TraceRawVcs,
22};
23use turbo_tasks_fs::{FileSystemEntryType, FileSystemPath};
24use turbo_unix_path::normalize_request;
25
26use crate::{
27 context::AssetContext,
28 data_uri_source::DataUriSource,
29 file_source::FileSource,
30 issue::{
31 Issue, IssueExt, IssueSource, module::emit_unknown_module_type_error,
32 resolve::ResolvingIssue,
33 },
34 module::{Module, Modules, OptionModule},
35 package_json::{PackageJsonIssue, read_package_json},
36 raw_module::RawModule,
37 reference_type::ReferenceType,
38 resolve::{
39 alias_map::AliasKey,
40 error::{handle_resolve_error, resolve_error_severity},
41 node::{node_cjs_resolve_options, node_esm_resolve_options},
42 options::{
43 ConditionValue, ImportMapResult, ResolveInPackage, ResolveIntoPackage, ResolveModules,
44 ResolveModulesOptions, ResolveOptions, resolve_modules_options,
45 },
46 origin::ResolveOrigin,
47 parse::{Request, stringify_data_uri},
48 pattern::{Pattern, PatternMatch, read_matches},
49 plugin::{AfterResolvePlugin, AfterResolvePluginCondition, BeforeResolvePlugin},
50 remap::{ExportsField, ImportsField, ReplacedSubpathValueResult},
51 },
52 source::{OptionSource, Source, Sources},
53};
54
55mod alias_map;
56pub mod error;
57pub mod node;
58pub mod options;
59pub mod origin;
60pub mod parse;
61pub mod pattern;
62pub mod plugin;
63pub(crate) mod remap;
64
65pub use alias_map::{
66 AliasMap, AliasMapIntoIter, AliasMapLookupIterator, AliasMatch, AliasPattern, AliasTemplate,
67};
68pub use remap::{ResolveAliasMap, SubpathValue};
69
70#[turbo_tasks::value(shared)]
72#[derive(Debug, Clone, Copy, Default, Hash, TaskInput)]
73pub enum ResolveErrorMode {
74 #[default]
76 Error,
77 Warn,
79 Ignore,
81}
82
83type AfterResolvePluginWithCondition = (
85 ResolvedVc<Box<dyn AfterResolvePlugin>>,
86 ResolvedVc<AfterResolvePluginCondition>,
87);
88
89#[turbo_tasks::value(shared)]
90#[derive(Clone, Debug)]
91pub enum ModuleResolveResultItem {
92 Module(ResolvedVc<Box<dyn Module>>),
93 External {
94 name: RcStr,
96 ty: ExternalType,
97 },
98 Unknown(ResolvedVc<Box<dyn Source>>),
100 Ignore,
102 Error(ResolvedVc<Box<dyn Issue>>),
104 Empty,
106 Custom(u8),
107}
108
109impl ModuleResolveResultItem {
110 async fn as_module(&self) -> Result<Option<ResolvedVc<Box<dyn Module>>>> {
111 Ok(match *self {
112 ModuleResolveResultItem::Module(module) => Some(module),
113 ModuleResolveResultItem::Unknown(source) => {
114 emit_unknown_module_type_error(*source).await?;
115 None
116 }
117 ModuleResolveResultItem::Error(_err) => {
118 None
120 }
121 _ => None,
122 })
123 }
124}
125
126#[turbo_tasks::value(shared)]
127#[derive(Clone, Debug, Hash, Default, Serialize, Deserialize)]
128pub struct BindingUsage {
129 pub import: ImportUsage,
130 pub export: ExportUsage,
131}
132
133#[turbo_tasks::value_impl]
134impl BindingUsage {
135 #[turbo_tasks::function]
136 pub fn all() -> Vc<Self> {
137 Self::default().cell()
138 }
139}
140
141#[turbo_tasks::value(shared)]
143#[derive(Debug, Clone, Default, Hash, Serialize, Deserialize)]
144pub enum ImportUsage {
145 #[default]
148 TopLevel,
149 Exports(FrozenSet<RcStr>),
155}
156
157#[turbo_tasks::value]
159#[derive(Debug, Clone, Default, Hash, Serialize, Deserialize)]
160pub enum ExportUsage {
161 Named(RcStr),
162 PartialNamespaceObject(SmallVec<[RcStr; 1]>),
164 #[default]
166 All,
167 Evaluation,
169}
170
171impl Display for ExportUsage {
172 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
173 match self {
174 ExportUsage::Named(name) => write!(f, "export {name}"),
175 ExportUsage::PartialNamespaceObject(names) => {
176 write!(f, "exports ")?;
177 for (i, name) in names.iter().enumerate() {
178 if i > 0 {
179 write!(f, ", ")?;
180 }
181 write!(f, "{name}")?;
182 }
183 Ok(())
184 }
185 ExportUsage::All => write!(f, "all"),
186 ExportUsage::Evaluation => write!(f, "evaluation"),
187 }
188 }
189}
190
191#[turbo_tasks::value_impl]
192impl ExportUsage {
193 #[turbo_tasks::function]
194 pub fn all() -> Vc<Self> {
195 Self::All.cell()
196 }
197
198 #[turbo_tasks::function]
199 pub fn evaluation() -> Vc<Self> {
200 Self::Evaluation.cell()
201 }
202
203 #[turbo_tasks::function]
204 pub fn named(name: RcStr) -> Vc<Self> {
205 Self::Named(name).cell()
206 }
207}
208
209#[turbo_tasks::value(shared)]
210#[derive(Clone, Debug)]
211pub struct ModuleResolveResult {
212 pub primary: Box<[(RequestKey, ModuleResolveResultItem)]>,
213 pub affecting_sources: Box<[ResolvedVc<Box<dyn Source>>]>,
216}
217
218impl ModuleResolveResult {
219 pub fn unresolvable() -> ResolvedVc<Self> {
220 ModuleResolveResult {
221 primary: Default::default(),
222 affecting_sources: Default::default(),
223 }
224 .resolved_cell()
225 }
226
227 pub fn module(module: ResolvedVc<Box<dyn Module>>) -> ResolvedVc<Self> {
228 Self::module_with_key(RequestKey::default(), module)
229 }
230
231 pub fn module_with_key(
232 request_key: RequestKey,
233 module: ResolvedVc<Box<dyn Module>>,
234 ) -> ResolvedVc<Self> {
235 ModuleResolveResult {
236 primary: vec![(request_key, ModuleResolveResultItem::Module(module))]
237 .into_boxed_slice(),
238 affecting_sources: Default::default(),
239 }
240 .resolved_cell()
241 }
242
243 pub fn modules(
244 modules: impl IntoIterator<Item = (RequestKey, ResolvedVc<Box<dyn Module>>)>,
245 ) -> ResolvedVc<Self> {
246 ModuleResolveResult {
247 primary: modules
248 .into_iter()
249 .map(|(k, v)| (k, ModuleResolveResultItem::Module(v)))
250 .collect(),
251 affecting_sources: Default::default(),
252 }
253 .resolved_cell()
254 }
255
256 pub fn modules_with_affecting_sources(
257 modules: impl IntoIterator<Item = (RequestKey, ResolvedVc<Box<dyn Module>>)>,
258 affecting_sources: Vec<ResolvedVc<Box<dyn Source>>>,
259 ) -> ResolvedVc<Self> {
260 ModuleResolveResult {
261 primary: modules
262 .into_iter()
263 .map(|(k, v)| (k, ModuleResolveResultItem::Module(v)))
264 .collect(),
265 affecting_sources: affecting_sources.into_boxed_slice(),
266 }
267 .resolved_cell()
268 }
269}
270
271impl ModuleResolveResult {
272 pub fn primary_modules_raw_iter(
274 &self,
275 ) -> impl Iterator<Item = ResolvedVc<Box<dyn Module>>> + '_ {
276 self.primary.iter().filter_map(|(_, item)| match *item {
277 ModuleResolveResultItem::Module(a) => Some(a),
278 _ => None,
279 })
280 }
281
282 pub async fn primary_modules_ref(&self) -> Result<Vec<ResolvedVc<Box<dyn Module>>>> {
284 let mut set = FxIndexSet::default();
285 for (_, item) in self.primary.iter() {
286 if let Some(module) = item.as_module().await? {
287 set.insert(module);
288 }
289 }
290 Ok(set.into_iter().collect())
291 }
292
293 pub fn affecting_sources_iter(&self) -> impl Iterator<Item = ResolvedVc<Box<dyn Source>>> + '_ {
294 self.affecting_sources.iter().copied()
295 }
296
297 pub fn is_unresolvable_ref(&self) -> bool {
298 self.primary.is_empty()
299 }
300
301 pub fn errors(&self) -> impl Iterator<Item = ResolvedVc<Box<dyn Issue>>> + '_ {
302 self.primary.iter().filter_map(|i| match &i.1 {
303 ModuleResolveResultItem::Error(e) => Some(*e),
304 _ => None,
305 })
306 }
307}
308
309pub struct ModuleResolveResultBuilder {
310 pub primary: FxIndexMap<RequestKey, ModuleResolveResultItem>,
311 pub affecting_sources: Vec<ResolvedVc<Box<dyn Source>>>,
312}
313
314impl From<ModuleResolveResultBuilder> for ModuleResolveResult {
315 fn from(v: ModuleResolveResultBuilder) -> Self {
316 ModuleResolveResult {
317 primary: v.primary.into_iter().collect(),
318 affecting_sources: v.affecting_sources.into_boxed_slice(),
319 }
320 }
321}
322impl From<ModuleResolveResult> for ModuleResolveResultBuilder {
323 fn from(v: ModuleResolveResult) -> Self {
324 ModuleResolveResultBuilder {
325 primary: IntoIterator::into_iter(v.primary).collect(),
326 affecting_sources: v.affecting_sources.into_vec(),
327 }
328 }
329}
330impl ModuleResolveResultBuilder {
331 pub fn merge_alternatives(&mut self, other: &ModuleResolveResult) {
332 for (k, v) in other.primary.iter() {
333 if !self.primary.contains_key(k) {
334 self.primary.insert(k.clone(), v.clone());
335 }
336 }
337 let set = self
338 .affecting_sources
339 .iter()
340 .copied()
341 .collect::<FxHashSet<_>>();
342 self.affecting_sources.extend(
343 other
344 .affecting_sources
345 .iter()
346 .filter(|source| !set.contains(source))
347 .copied(),
348 );
349 }
350}
351
352#[turbo_tasks::value_impl]
353impl ModuleResolveResult {
354 #[turbo_tasks::function]
355 pub async fn alternatives(results: Vec<Vc<ModuleResolveResult>>) -> Result<Vc<Self>> {
356 if results.len() == 1 {
357 return Ok(results.into_iter().next().unwrap());
358 }
359 let mut iter = results.into_iter().try_join().await?.into_iter();
360 if let Some(current) = iter.next() {
361 let mut current: ModuleResolveResultBuilder = ReadRef::into_owned(current).into();
362 for result in iter {
363 let other = &*result;
365 current.merge_alternatives(other);
366 }
367 Ok(Self::cell(current.into()))
368 } else {
369 Ok(*ModuleResolveResult::unresolvable())
370 }
371 }
372
373 #[turbo_tasks::function]
374 pub fn is_unresolvable(&self) -> Vc<bool> {
375 Vc::cell(self.is_unresolvable_ref())
376 }
377
378 #[turbo_tasks::function]
379 pub async fn first_module(&self) -> Result<Vc<OptionModule>> {
380 for (_, item) in self.primary.iter() {
381 if let Some(module) = item.as_module().await? {
382 return Ok(Vc::cell(Some(module)));
383 }
384 }
385 Ok(Vc::cell(None))
386 }
387
388 #[turbo_tasks::function]
391 pub async fn primary_modules(&self) -> Result<Vc<Modules>> {
392 let mut set = FxIndexSet::default();
393 for (_, item) in self.primary.iter() {
394 if let Some(module) = item.as_module().await? {
395 set.insert(module);
396 }
397 }
398 Ok(Vc::cell(set.into_iter().collect()))
399 }
400}
401
402#[derive(
403 Copy,
404 Clone,
405 Debug,
406 PartialEq,
407 Eq,
408 TaskInput,
409 Hash,
410 NonLocalValue,
411 TraceRawVcs,
412 Serialize,
413 Deserialize,
414 Encode,
415 Decode,
416)]
417pub enum ExternalTraced {
418 Untraced,
419 Traced,
420}
421
422impl Display for ExternalTraced {
423 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
424 match self {
425 ExternalTraced::Untraced => write!(f, "untraced"),
426 ExternalTraced::Traced => write!(f, "traced"),
427 }
428 }
429}
430
431#[derive(
432 Copy,
433 Clone,
434 Debug,
435 Eq,
436 PartialEq,
437 Hash,
438 Serialize,
439 Deserialize,
440 TraceRawVcs,
441 TaskInput,
442 NonLocalValue,
443 Encode,
444 Decode,
445)]
446pub enum ExternalType {
447 Url,
448 CommonJs,
449 EcmaScriptModule,
450 Global,
451 Script,
452}
453
454impl Display for ExternalType {
455 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
456 match self {
457 ExternalType::CommonJs => write!(f, "commonjs"),
458 ExternalType::EcmaScriptModule => write!(f, "esm"),
459 ExternalType::Url => write!(f, "url"),
460 ExternalType::Global => write!(f, "global"),
461 ExternalType::Script => write!(f, "script"),
462 }
463 }
464}
465
466#[turbo_tasks::value(shared)]
467#[derive(Debug, Clone)]
468pub enum ResolveResultItem {
469 Source(ResolvedVc<Box<dyn Source>>),
470 External {
471 name: RcStr,
473 ty: ExternalType,
474 traced: ExternalTraced,
475 target: Option<FileSystemPath>,
478 },
479 Ignore,
481 Error(ResolvedVc<Box<dyn Issue>>),
483 Empty,
485 Custom(u8),
486}
487
488#[derive(Clone, Debug, Default, Hash, TaskInput)]
495#[turbo_tasks::value]
496pub struct RequestKey {
497 pub request: Option<RcStr>,
498 pub conditions: FrozenMap<RcStr, bool>,
499}
500
501impl Display for RequestKey {
502 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
503 if let Some(request) = &self.request {
504 write!(f, "{request}")?;
505 } else {
506 write!(f, "<default>")?;
507 }
508 if !self.conditions.is_empty() {
509 write!(f, " (")?;
510 for (i, (k, v)) in self.conditions.iter().enumerate() {
511 if i > 0 {
512 write!(f, ", ")?;
513 }
514 write!(f, "{k}={v}")?;
515 }
516 write!(f, ")")?;
517 }
518 Ok(())
519 }
520}
521
522impl RequestKey {
523 pub fn new(request: RcStr) -> Self {
524 RequestKey {
525 request: Some(request),
526 ..Default::default()
527 }
528 }
529}
530
531#[turbo_tasks::value(shared)]
532#[derive(Clone)]
533pub struct ResolveResult {
534 pub primary: Box<[(RequestKey, ResolveResultItem)]>,
535 pub affecting_sources: Box<[ResolvedVc<Box<dyn Source>>]>,
538}
539
540#[turbo_tasks::value_impl]
541impl ValueToString for ResolveResult {
542 #[turbo_tasks::function]
543 async fn to_string(&self) -> Result<Vc<RcStr>> {
544 let mut result = String::new();
545 if self.is_unresolvable_ref() {
546 result.push_str("unresolvable");
547 }
548 for (i, (request, item)) in self.primary.iter().enumerate() {
549 if i > 0 {
550 result.push_str(", ");
551 }
552 write!(result, "{request} -> ").unwrap();
553 match item {
554 ResolveResultItem::Source(a) => {
555 result.push_str(&a.ident().to_string().await?);
556 }
557 ResolveResultItem::External {
558 name: s,
559 ty,
560 traced,
561 target,
562 } => {
563 result.push_str("external ");
564 result.push_str(s);
565 write!(
566 result,
567 " ({ty}, {traced}, {:?})",
568 if let Some(target) = target {
569 Some(target.to_string_ref().await?)
570 } else {
571 None
572 }
573 )?;
574 }
575 ResolveResultItem::Ignore => {
576 result.push_str("ignore");
577 }
578 ResolveResultItem::Empty => {
579 result.push_str("empty");
580 }
581 ResolveResultItem::Error(_) => {
582 result.push_str("error");
583 }
584 ResolveResultItem::Custom(_) => {
585 result.push_str("custom");
586 }
587 }
588 result.push('\n');
589 }
590 if !self.affecting_sources.is_empty() {
591 result.push_str(" (affecting sources: ");
592 for (i, source) in self.affecting_sources.iter().enumerate() {
593 if i > 0 {
594 result.push_str(", ");
595 }
596 result.push_str(&source.ident().to_string().await?);
597 }
598 result.push(')');
599 }
600 Ok(Vc::cell(result.into()))
601 }
602}
603
604impl ResolveResult {
605 pub fn unresolvable() -> Self {
606 ResolveResult {
607 primary: Default::default(),
608 affecting_sources: Default::default(),
609 }
610 }
611
612 pub fn unresolvable_with_affecting_sources(
613 affecting_sources: Vec<ResolvedVc<Box<dyn Source>>>,
614 ) -> Self {
615 ResolveResult {
616 primary: Default::default(),
617 affecting_sources: affecting_sources.into_boxed_slice(),
618 }
619 }
620
621 pub fn primary(result: ResolveResultItem) -> Self {
622 Self::primary_with_key(RequestKey::default(), result)
623 }
624
625 pub fn primary_with_key(request_key: RequestKey, result: ResolveResultItem) -> Self {
626 ResolveResult {
627 primary: vec![(request_key, result)].into_boxed_slice(),
628 affecting_sources: Default::default(),
629 }
630 }
631
632 pub fn primary_with_affecting_sources(
633 request_key: RequestKey,
634 result: ResolveResultItem,
635 affecting_sources: Vec<ResolvedVc<Box<dyn Source>>>,
636 ) -> Self {
637 ResolveResult {
638 primary: vec![(request_key, result)].into_boxed_slice(),
639 affecting_sources: affecting_sources.into_boxed_slice(),
640 }
641 }
642
643 pub fn source(source: ResolvedVc<Box<dyn Source>>) -> Self {
644 Self::source_with_key(RequestKey::default(), source)
645 }
646
647 fn source_with_key(request_key: RequestKey, source: ResolvedVc<Box<dyn Source>>) -> Self {
648 ResolveResult {
649 primary: vec![(request_key, ResolveResultItem::Source(source))].into_boxed_slice(),
650 affecting_sources: Default::default(),
651 }
652 }
653
654 fn source_with_affecting_sources(
655 request_key: RequestKey,
656 source: ResolvedVc<Box<dyn Source>>,
657 affecting_sources: Vec<ResolvedVc<Box<dyn Source>>>,
658 ) -> Self {
659 ResolveResult {
660 primary: vec![(request_key, ResolveResultItem::Source(source))].into_boxed_slice(),
661 affecting_sources: affecting_sources.into_boxed_slice(),
662 }
663 }
664
665 pub fn errors(&self) -> impl Iterator<Item = ResolvedVc<Box<dyn Issue>>> + '_ {
666 self.primary.iter().filter_map(|i| match &i.1 {
667 ResolveResultItem::Error(e) => Some(*e),
668 _ => None,
669 })
670 }
671}
672
673impl ResolveResult {
674 pub fn get_affecting_sources(&self) -> impl Iterator<Item = ResolvedVc<Box<dyn Source>>> + '_ {
677 self.affecting_sources.iter().copied()
678 }
679
680 pub fn is_unresolvable_ref(&self) -> bool {
681 self.primary.is_empty()
682 }
683
684 pub async fn map_module<A, AF>(&self, source_fn: A) -> Result<ModuleResolveResult>
685 where
686 A: Fn(ResolvedVc<Box<dyn Source>>) -> AF,
687 AF: Future<Output = Result<ModuleResolveResultItem>>,
688 {
689 Ok(ModuleResolveResult {
690 primary: self
691 .primary
692 .iter()
693 .map(|(request, item)| {
694 let asset_fn = &source_fn;
695 let request = request.clone();
696 let item = item.clone();
697 async move {
698 Ok((
699 request,
700 match item {
701 ResolveResultItem::Source(source) => asset_fn(source).await?,
702 ResolveResultItem::External {
703 name,
704 ty,
705 traced,
706 target,
707 } => {
708 if traced == ExternalTraced::Traced || target.is_some() {
709 bail!("map_module doesn't handle traced externals");
711 }
712 ModuleResolveResultItem::External { name, ty }
713 }
714 ResolveResultItem::Ignore => ModuleResolveResultItem::Ignore,
715 ResolveResultItem::Empty => ModuleResolveResultItem::Empty,
716 ResolveResultItem::Error(e) => ModuleResolveResultItem::Error(e),
717 ResolveResultItem::Custom(u8) => {
718 ModuleResolveResultItem::Custom(u8)
719 }
720 },
721 ))
722 }
723 })
724 .try_join()
725 .await?
726 .into_iter()
727 .collect(),
728 affecting_sources: self.affecting_sources.clone(),
729 })
730 }
731
732 pub async fn map_primary_items<A, AF>(&self, item_fn: A) -> Result<ModuleResolveResult>
733 where
734 A: Fn(ResolveResultItem) -> AF,
735 AF: Future<Output = Result<ModuleResolveResultItem>>,
736 {
737 Ok(ModuleResolveResult {
738 primary: self
739 .primary
740 .iter()
741 .map(|(request, item)| {
742 let asset_fn = &item_fn;
743 let request = request.clone();
744 let item = item.clone();
745 async move { Ok((request, asset_fn(item).await?)) }
746 })
747 .try_join()
748 .await?
749 .into_iter()
750 .collect(),
751 affecting_sources: self.affecting_sources.clone(),
752 })
753 }
754
755 fn with_request_ref(&self, request: RcStr) -> Self {
758 let new_primary = self
759 .primary
760 .iter()
761 .map(|(k, v)| {
762 (
763 RequestKey {
764 request: Some(request.clone()),
765 conditions: k.conditions.clone(),
766 },
767 v.clone(),
768 )
769 })
770 .collect();
771 ResolveResult {
772 primary: new_primary,
773 affecting_sources: self.affecting_sources.clone(),
774 }
775 }
776
777 pub fn with_conditions(&self, new_conditions: &[(RcStr, bool)]) -> Self {
778 let primary = self
779 .primary
780 .iter()
781 .map(|(k, v)| {
782 (
783 RequestKey {
784 request: k.request.clone(),
785 conditions: k.conditions.extend(new_conditions.iter().cloned()),
786 },
787 v.clone(),
788 )
789 })
790 .collect::<FxIndexMap<_, _>>() .into_iter()
792 .collect();
793 ResolveResult {
794 primary,
795 affecting_sources: self.affecting_sources.clone(),
796 }
797 }
798}
799
800struct ResolveResultBuilder {
801 primary: FxIndexMap<RequestKey, ResolveResultItem>,
802 affecting_sources: Vec<ResolvedVc<Box<dyn Source>>>,
803}
804
805impl From<ResolveResultBuilder> for ResolveResult {
806 fn from(v: ResolveResultBuilder) -> Self {
807 ResolveResult {
808 primary: v.primary.into_iter().collect(),
809 affecting_sources: v.affecting_sources.into_boxed_slice(),
810 }
811 }
812}
813impl From<ResolveResult> for ResolveResultBuilder {
814 fn from(v: ResolveResult) -> Self {
815 ResolveResultBuilder {
816 primary: IntoIterator::into_iter(v.primary).collect(),
817 affecting_sources: v.affecting_sources.into_vec(),
818 }
819 }
820}
821impl ResolveResultBuilder {
822 pub fn merge_alternatives(&mut self, other: &ResolveResult) {
823 for (k, v) in other.primary.iter() {
824 if !self.primary.contains_key(k) {
825 self.primary.insert(k.clone(), v.clone());
826 }
827 }
828 let set = self
829 .affecting_sources
830 .iter()
831 .copied()
832 .collect::<FxHashSet<_>>();
833 self.affecting_sources.extend(
834 other
835 .affecting_sources
836 .iter()
837 .filter(|source| !set.contains(source))
838 .copied(),
839 );
840 }
841}
842
843#[turbo_tasks::value_impl]
844impl ResolveResult {
845 #[turbo_tasks::function]
846 pub async fn as_raw_module_result(&self) -> Result<Vc<ModuleResolveResult>> {
847 Ok(self
848 .map_module(|asset| async move {
849 Ok(ModuleResolveResultItem::Module(ResolvedVc::upcast(
850 RawModule::new(*asset).to_resolved().await?,
851 )))
852 })
853 .await?
854 .cell())
855 }
856
857 #[turbo_tasks::function]
858 fn with_affecting_sources(
859 &self,
860 sources: Vec<ResolvedVc<Box<dyn Source>>>,
861 ) -> Result<Vc<Self>> {
862 Ok(Self {
863 primary: self.primary.clone(),
864 affecting_sources: self
865 .affecting_sources
866 .iter()
867 .copied()
868 .chain(sources)
869 .collect(),
870 }
871 .cell())
872 }
873
874 #[turbo_tasks::function]
875 async fn alternatives(results: Vec<Vc<ResolveResult>>) -> Result<Vc<Self>> {
876 if results.len() == 1 {
877 return Ok(results.into_iter().next().unwrap());
878 }
879 let mut iter = results.into_iter().try_join().await?.into_iter();
880 if let Some(current) = iter.next() {
881 let mut current: ResolveResultBuilder = ReadRef::into_owned(current).into();
882 for result in iter {
883 let other = &*result;
885 current.merge_alternatives(other);
886 }
887 Ok(Self::cell(current.into()))
888 } else {
889 Ok(ResolveResult::unresolvable().cell())
890 }
891 }
892
893 #[turbo_tasks::function]
894 async fn alternatives_with_affecting_sources(
895 results: Vec<Vc<ResolveResult>>,
896 affecting_sources: Vec<ResolvedVc<Box<dyn Source>>>,
897 ) -> Result<Vc<Self>> {
898 debug_assert!(
899 !affecting_sources.is_empty(),
900 "Caller should not call this function if there are no affecting sources"
901 );
902 if results.len() == 1 {
903 return Ok(results
904 .into_iter()
905 .next()
906 .unwrap()
907 .with_affecting_sources(affecting_sources.into_iter().map(|src| *src).collect()));
908 }
909 let mut iter = results.into_iter().try_join().await?.into_iter();
910 if let Some(current) = iter.next() {
911 let mut current: ResolveResultBuilder = ReadRef::into_owned(current).into();
912 for result in iter {
913 let other = &*result;
915 current.merge_alternatives(other);
916 }
917 current.affecting_sources.extend(affecting_sources);
918 Ok(Self::cell(current.into()))
919 } else {
920 Ok(ResolveResult::unresolvable_with_affecting_sources(affecting_sources).cell())
921 }
922 }
923
924 #[turbo_tasks::function]
925 pub fn is_unresolvable(&self) -> Vc<bool> {
926 Vc::cell(self.is_unresolvable_ref())
927 }
928
929 #[turbo_tasks::function]
930 pub fn first_source(&self) -> Vc<OptionSource> {
931 Vc::cell(self.primary.iter().find_map(|(_, item)| {
932 if let &ResolveResultItem::Source(a) = item {
933 Some(a)
934 } else {
935 None
936 }
937 }))
938 }
939
940 #[turbo_tasks::function]
941 pub fn primary_sources(&self) -> Vc<Sources> {
942 Vc::cell(
943 self.primary
944 .iter()
945 .filter_map(|(_, item)| {
946 if let &ResolveResultItem::Source(a) = item {
947 Some(a)
948 } else {
949 None
950 }
951 })
952 .collect(),
953 )
954 }
955
956 #[turbo_tasks::function]
961 fn with_replaced_request_key(
962 &self,
963 old_request_key: RcStr,
964 request_key: RequestKey,
965 ) -> Result<Vc<Self>> {
966 let new_primary = self
967 .primary
968 .iter()
969 .filter_map(|(k, v)| {
970 let remaining = k.request.as_ref()?.strip_prefix(&*old_request_key)?;
971 Some((
972 RequestKey {
973 request: request_key
974 .request
975 .as_ref()
976 .map(|r| format!("{r}{remaining}").into()),
977 conditions: request_key.conditions.clone(),
978 },
979 v.clone(),
980 ))
981 })
982 .collect();
983 Ok(ResolveResult {
984 primary: new_primary,
985 affecting_sources: self.affecting_sources.clone(),
986 }
987 .cell())
988 }
989
990 #[turbo_tasks::function]
994 fn with_stripped_request_key_prefix(&self, prefix: RcStr) -> Result<Vc<Self>> {
995 let new_primary = self
996 .primary
997 .iter()
998 .filter_map(|(k, v)| {
999 let remaining = k.request.as_ref()?.strip_prefix(&*prefix)?;
1000 Some((
1001 RequestKey {
1002 request: Some(remaining.into()),
1003 conditions: k.conditions.clone(),
1004 },
1005 v.clone(),
1006 ))
1007 })
1008 .collect();
1009 Ok(ResolveResult {
1010 primary: new_primary,
1011 affecting_sources: self.affecting_sources.clone(),
1012 }
1013 .cell())
1014 }
1015
1016 #[turbo_tasks::function]
1021 async fn with_replaced_request_key_pattern(
1022 &self,
1023 old_request_key: Vc<Pattern>,
1024 request_key: Vc<Pattern>,
1025 ) -> Result<Vc<Self>> {
1026 let old_request_key = &*old_request_key.await?;
1027 let request_key = &*request_key.await?;
1028
1029 let new_primary = self
1030 .primary
1031 .iter()
1032 .map(|(k, v)| {
1033 (
1034 RequestKey {
1035 request: k
1036 .request
1037 .as_ref()
1038 .and_then(|r| old_request_key.match_apply_template(r, request_key))
1039 .map(Into::into),
1040 conditions: k.conditions.clone(),
1041 },
1042 v.clone(),
1043 )
1044 })
1045 .collect();
1046 Ok(ResolveResult {
1047 primary: new_primary,
1048 affecting_sources: self.affecting_sources.clone(),
1049 }
1050 .cell())
1051 }
1052
1053 #[turbo_tasks::function]
1056 fn with_request(&self, request: RcStr) -> Vc<Self> {
1057 let new_primary = self
1058 .primary
1059 .iter()
1060 .map(|(k, v)| {
1061 (
1062 RequestKey {
1063 request: Some(request.clone()),
1064 conditions: k.conditions.clone(),
1065 },
1066 v.clone(),
1067 )
1068 })
1069 .collect();
1070 ResolveResult {
1071 primary: new_primary,
1072 affecting_sources: self.affecting_sources.clone(),
1073 }
1074 .cell()
1075 }
1076}
1077
1078#[turbo_tasks::value(transparent)]
1079pub struct ResolveResultOption(Option<ResolvedVc<ResolveResult>>);
1080
1081#[turbo_tasks::value_impl]
1082impl ResolveResultOption {
1083 #[turbo_tasks::function]
1084 pub fn some(result: ResolvedVc<ResolveResult>) -> Vc<Self> {
1085 ResolveResultOption(Some(result)).cell()
1086 }
1087
1088 #[turbo_tasks::function]
1089 pub fn none() -> Vc<Self> {
1090 ResolveResultOption(None).cell()
1091 }
1092}
1093
1094async fn exists(
1095 fs_path: &FileSystemPath,
1096 refs: Option<&mut Vec<ResolvedVc<Box<dyn Source>>>>,
1097) -> Result<Option<FileSystemPath>> {
1098 type_exists(fs_path, FileSystemEntryType::File, refs).await
1099}
1100
1101async fn dir_exists(
1102 fs_path: &FileSystemPath,
1103 refs: Option<&mut Vec<ResolvedVc<Box<dyn Source>>>>,
1104) -> Result<Option<FileSystemPath>> {
1105 type_exists(fs_path, FileSystemEntryType::Directory, refs).await
1106}
1107
1108async fn type_exists(
1109 fs_path: &FileSystemPath,
1110 ty: FileSystemEntryType,
1111 refs: Option<&mut Vec<ResolvedVc<Box<dyn Source>>>>,
1112) -> Result<Option<FileSystemPath>> {
1113 let path = realpath(fs_path, refs).await?;
1114 Ok(if *path.get_type().await? == ty {
1115 Some(path)
1116 } else {
1117 None
1118 })
1119}
1120
1121async fn realpath(
1122 fs_path: &FileSystemPath,
1123 refs: Option<&mut Vec<ResolvedVc<Box<dyn Source>>>>,
1124) -> Result<FileSystemPath> {
1125 let result = fs_path.realpath_with_links().await?;
1126 if let Some(refs) = refs {
1127 refs.extend(
1128 result
1129 .symlinks
1130 .iter()
1131 .map(|path| async move {
1132 Ok(ResolvedVc::upcast(
1133 FileSource::new(path.clone()).to_resolved().await?,
1134 ))
1135 })
1136 .try_join()
1137 .await?,
1138 );
1139 }
1140 match &result.path_result {
1141 Ok(path) => Ok(path.clone()),
1142 Err(e) => bail!(e.as_error_message(fs_path, &result).await?),
1143 }
1144}
1145
1146#[turbo_tasks::value(shared)]
1147enum ExportsFieldResult {
1148 Some(#[turbo_tasks(debug_ignore, trace_ignore)] ExportsField),
1149 None,
1150}
1151
1152#[turbo_tasks::function]
1155async fn exports_field(
1156 package_json_path: ResolvedVc<Box<dyn Source>>,
1157) -> Result<Vc<ExportsFieldResult>> {
1158 let read = read_package_json(*package_json_path).await?;
1159 let package_json = match &*read {
1160 Some(json) => json,
1161 None => return Ok(ExportsFieldResult::None.cell()),
1162 };
1163
1164 let Some(exports) = package_json.get("exports") else {
1165 return Ok(ExportsFieldResult::None.cell());
1166 };
1167 match exports.try_into() {
1168 Ok(exports) => Ok(ExportsFieldResult::Some(exports).cell()),
1169 Err(err) => {
1170 PackageJsonIssue {
1171 error_message: err.to_string().into(),
1172 source: IssueSource::from_source_only(package_json_path),
1174 }
1175 .resolved_cell()
1176 .emit();
1177 Ok(ExportsFieldResult::None.cell())
1178 }
1179 }
1180}
1181
1182#[turbo_tasks::value(shared)]
1183enum ImportsFieldResult {
1184 Some(
1185 #[turbo_tasks(debug_ignore, trace_ignore)] ImportsField,
1186 FileSystemPath,
1187 ),
1188 None,
1189}
1190
1191#[turbo_tasks::function]
1194async fn imports_field(lookup_path: FileSystemPath) -> Result<Vc<ImportsFieldResult>> {
1195 let package_json_context =
1197 find_context_file(lookup_path, *package_json().to_resolved().await?, false).await?;
1198 let FindContextFileResult::Found(package_json_path, _refs) = &*package_json_context else {
1199 return Ok(ImportsFieldResult::None.cell());
1200 };
1201 let source = Vc::upcast::<Box<dyn Source>>(FileSource::new(package_json_path.clone()))
1202 .to_resolved()
1203 .await?;
1204
1205 let read = read_package_json(*source).await?;
1206 let package_json = match &*read {
1207 Some(json) => json,
1208 None => return Ok(ImportsFieldResult::None.cell()),
1209 };
1210
1211 let Some(imports) = package_json.get("imports") else {
1212 return Ok(ImportsFieldResult::None.cell());
1213 };
1214 match imports.try_into() {
1215 Ok(imports) => Ok(ImportsFieldResult::Some(imports, package_json_path.clone()).cell()),
1216 Err(err) => {
1217 PackageJsonIssue {
1218 error_message: err.to_string().into(),
1219 source: IssueSource::from_source_only(source),
1221 }
1222 .resolved_cell()
1223 .emit();
1224 Ok(ImportsFieldResult::None.cell())
1225 }
1226 }
1227}
1228
1229#[turbo_tasks::function]
1230pub fn package_json() -> Vc<Vec<RcStr>> {
1231 Vc::cell(vec![rcstr!("package.json")])
1232}
1233
1234#[turbo_tasks::value(shared)]
1235pub enum FindContextFileResult {
1236 Found(FileSystemPath, Vec<ResolvedVc<Box<dyn Source>>>),
1237 NotFound(Vec<ResolvedVc<Box<dyn Source>>>),
1238}
1239
1240#[turbo_tasks::function]
1241pub async fn find_context_file(
1242 lookup_path: FileSystemPath,
1243 names: Vc<Vec<RcStr>>,
1244 collect_affecting_sources: bool,
1245) -> Result<Vc<FindContextFileResult>> {
1246 let mut refs = Vec::new();
1247 for name in &*names.await? {
1248 let fs_path = lookup_path.join(name)?;
1249 if let Some(fs_path) = exists(
1250 &fs_path,
1251 if collect_affecting_sources {
1252 Some(&mut refs)
1253 } else {
1254 None
1255 },
1256 )
1257 .await?
1258 {
1259 return Ok(FindContextFileResult::Found(fs_path, refs).cell());
1260 }
1261 }
1262 if lookup_path.is_root() {
1263 return Ok(FindContextFileResult::NotFound(refs).cell());
1264 }
1265 if refs.is_empty() {
1266 Ok(find_context_file(
1268 lookup_path.parent(),
1269 names,
1270 collect_affecting_sources,
1271 ))
1272 } else {
1273 let parent_result =
1274 find_context_file(lookup_path.parent(), names, collect_affecting_sources).await?;
1275 Ok(match &*parent_result {
1276 FindContextFileResult::Found(p, r) => {
1277 refs.extend(r.iter().copied());
1278 FindContextFileResult::Found(p.clone(), refs)
1279 }
1280 FindContextFileResult::NotFound(r) => {
1281 refs.extend(r.iter().copied());
1282 FindContextFileResult::NotFound(refs)
1283 }
1284 }
1285 .cell())
1286 }
1287}
1288
1289#[turbo_tasks::function]
1292pub async fn find_context_file_or_package_key(
1293 lookup_path: FileSystemPath,
1294 names: Vc<Vec<RcStr>>,
1295 package_key: RcStr,
1296) -> Result<Vc<FindContextFileResult>> {
1297 let package_json_path = lookup_path.join("package.json")?;
1298 if let Some(package_json_path) = exists(&package_json_path, None).await?
1299 && let Some(json) =
1300 &*read_package_json(Vc::upcast(FileSource::new(package_json_path.clone()))).await?
1301 && json.get(&*package_key).is_some()
1302 {
1303 return Ok(FindContextFileResult::Found(package_json_path, Vec::new()).cell());
1304 }
1305 for name in &*names.await? {
1306 let fs_path = lookup_path.join(name)?;
1307 if let Some(fs_path) = exists(&fs_path, None).await? {
1308 return Ok(FindContextFileResult::Found(fs_path, Vec::new()).cell());
1309 }
1310 }
1311 if lookup_path.is_root() {
1312 return Ok(FindContextFileResult::NotFound(Vec::new()).cell());
1313 }
1314
1315 Ok(find_context_file(lookup_path.parent(), names, false))
1316}
1317
1318#[derive(Clone, PartialEq, Eq, TraceRawVcs, Debug, NonLocalValue, Encode, Decode)]
1319enum FindPackageItem {
1320 PackageDirectory { name: RcStr, dir: FileSystemPath },
1321 PackageFile { name: RcStr, file: FileSystemPath },
1322}
1323
1324#[turbo_tasks::value]
1325#[derive(Debug)]
1326struct FindPackageResult {
1327 packages: Vec<FindPackageItem>,
1328 affecting_sources: Vec<ResolvedVc<Box<dyn Source>>>,
1330}
1331
1332#[turbo_tasks::function]
1333async fn find_package(
1334 lookup_path: FileSystemPath,
1335 package_name: Pattern,
1336 options: Vc<ResolveModulesOptions>,
1337 collect_affecting_sources: bool,
1338) -> Result<Vc<FindPackageResult>> {
1339 let mut packages = vec![];
1340 let mut affecting_sources = vec![];
1341 let options = options.await?;
1342 let package_name_cell = Pattern::new(package_name.clone());
1343
1344 fn get_package_name(basepath: &FileSystemPath, package_dir: &FileSystemPath) -> Result<RcStr> {
1345 if let Some(name) = basepath.get_path_to(package_dir) {
1346 Ok(name.into())
1347 } else {
1348 bail!("Package directory {package_dir} is not inside the lookup path {basepath}",);
1349 }
1350 }
1351
1352 for resolve_modules in &options.modules {
1353 match resolve_modules {
1354 ResolveModules::Nested(root, names) => {
1355 let mut lookup_path = lookup_path.clone();
1356 let mut lookup_path_value = lookup_path.clone();
1357 while lookup_path_value.is_inside_ref(root) {
1358 for name in names.iter() {
1359 let fs_path = lookup_path.join(name)?;
1360 if let Some(fs_path) = dir_exists(
1361 &fs_path,
1362 collect_affecting_sources.then_some(&mut affecting_sources),
1363 )
1364 .await?
1365 {
1366 let matches =
1367 read_matches(fs_path.clone(), rcstr!(""), true, package_name_cell)
1368 .await?;
1369 for m in &*matches {
1370 if let PatternMatch::Directory(_, package_dir) = m {
1371 packages.push(FindPackageItem::PackageDirectory {
1372 name: get_package_name(&fs_path, package_dir)?,
1373 dir: realpath(
1374 package_dir,
1375 collect_affecting_sources
1376 .then_some(&mut affecting_sources),
1377 )
1378 .await?,
1379 });
1380 }
1381 }
1382 }
1383 }
1384 lookup_path = lookup_path.parent();
1385 let new_context_value = lookup_path.clone();
1386 if new_context_value == lookup_path_value {
1387 break;
1388 }
1389 lookup_path_value = new_context_value;
1390 }
1391 }
1392 ResolveModules::Path {
1393 dir,
1394 excluded_extensions,
1395 } => {
1396 let matches =
1397 read_matches(dir.clone(), rcstr!(""), true, package_name_cell).await?;
1398 for m in &*matches {
1399 match m {
1400 PatternMatch::Directory(_, package_dir) => {
1401 packages.push(FindPackageItem::PackageDirectory {
1402 name: get_package_name(dir, package_dir)?,
1403 dir: realpath(
1404 package_dir,
1405 collect_affecting_sources.then_some(&mut affecting_sources),
1406 )
1407 .await?,
1408 });
1409 }
1410 PatternMatch::File(_, package_file) => {
1411 packages.push(FindPackageItem::PackageFile {
1412 name: get_package_name(dir, package_file)?,
1413 file: realpath(
1414 package_file,
1415 collect_affecting_sources.then_some(&mut affecting_sources),
1416 )
1417 .await?,
1418 });
1419 }
1420 }
1421 }
1422
1423 let excluded_extensions = excluded_extensions.await?;
1424 let mut package_name_with_extensions = package_name.clone();
1425 package_name_with_extensions.push(Pattern::alternatives(
1426 options
1427 .extensions
1428 .iter()
1429 .filter(|ext| !excluded_extensions.contains(*ext))
1430 .cloned()
1431 .map(Pattern::from),
1432 ));
1433 let package_name_with_extensions = Pattern::new(package_name_with_extensions);
1434
1435 let matches =
1436 read_matches(dir.clone(), rcstr!(""), true, package_name_with_extensions)
1437 .await?;
1438 for m in matches {
1439 if let PatternMatch::File(_, package_file) = m {
1440 packages.push(FindPackageItem::PackageFile {
1441 name: get_package_name(dir, package_file)?,
1442 file: realpath(
1443 package_file,
1444 collect_affecting_sources.then_some(&mut affecting_sources),
1445 )
1446 .await?,
1447 });
1448 }
1449 }
1450 }
1451 }
1452 }
1453 Ok(FindPackageResult::cell(FindPackageResult {
1454 packages,
1455 affecting_sources,
1456 }))
1457}
1458
1459fn merge_results(results: Vec<Vc<ResolveResult>>) -> Vc<ResolveResult> {
1460 match results.len() {
1461 0 => ResolveResult::unresolvable().cell(),
1462 1 => results.into_iter().next().unwrap(),
1463 _ => ResolveResult::alternatives(results),
1464 }
1465}
1466
1467fn merge_results_with_affecting_sources(
1468 results: Vec<Vc<ResolveResult>>,
1469 affecting_sources: Vec<ResolvedVc<Box<dyn Source>>>,
1470) -> Vc<ResolveResult> {
1471 if affecting_sources.is_empty() {
1472 return merge_results(results);
1473 }
1474 match results.len() {
1475 0 => ResolveResult::unresolvable_with_affecting_sources(affecting_sources).cell(),
1476 1 => results
1477 .into_iter()
1478 .next()
1479 .unwrap()
1480 .with_affecting_sources(affecting_sources.into_iter().map(|src| *src).collect()),
1481 _ => ResolveResult::alternatives_with_affecting_sources(
1482 results,
1483 affecting_sources.into_iter().map(|src| *src).collect(),
1484 ),
1485 }
1486}
1487
1488#[turbo_tasks::function]
1490pub async fn resolve_raw(
1491 lookup_dir: FileSystemPath,
1492 path: Vc<Pattern>,
1493 collect_affecting_sources: bool,
1494 force_in_lookup_dir: bool,
1495) -> Result<Vc<ResolveResult>> {
1496 async fn to_result(
1497 request: RcStr,
1498 path: &FileSystemPath,
1499 collect_affecting_sources: bool,
1500 ) -> Result<ResolveResult> {
1501 let result = &*path.realpath_with_links().await?;
1502 let path = match &result.path_result {
1503 Ok(path) => path,
1504 Err(e) => bail!(e.as_error_message(path, result).await?),
1505 };
1506 let request_key = RequestKey::new(request);
1507 let source = ResolvedVc::upcast(FileSource::new(path.clone()).to_resolved().await?);
1508 Ok(if collect_affecting_sources {
1509 ResolveResult::source_with_affecting_sources(
1510 request_key,
1511 source,
1512 result
1513 .symlinks
1514 .iter()
1515 .map(|symlink| {
1516 Vc::upcast::<Box<dyn Source>>(FileSource::new(symlink.clone()))
1517 .to_resolved()
1518 })
1519 .try_join()
1520 .await?,
1521 )
1522 } else {
1523 ResolveResult::source_with_key(request_key, source)
1524 })
1525 }
1526
1527 async fn collect_matches(
1528 matches: &[PatternMatch],
1529 collect_affecting_sources: bool,
1530 ) -> Result<Vec<Vc<ResolveResult>>> {
1531 Ok(matches
1532 .iter()
1533 .map(|m| async move {
1534 Ok(if let PatternMatch::File(request, path) = m {
1535 Some(to_result(request.clone(), path, collect_affecting_sources).await?)
1536 } else {
1537 None
1538 })
1539 })
1540 .try_flat_join()
1541 .await?
1542 .into_iter()
1545 .map(|res| res.cell())
1546 .collect())
1547 }
1548
1549 let mut results = Vec::new();
1550
1551 let pat = path.await?;
1552 if let Some(pat) = pat
1553 .filter_could_match("/ROOT/")
1554 .and_then(|pat| pat.filter_could_not_match("/ROOT/fsd8nz8og54z"))
1557 {
1558 let path = Pattern::new(pat);
1559 let matches = read_matches(
1560 lookup_dir.root().owned().await?,
1561 rcstr!("/ROOT/"),
1562 true,
1563 path,
1564 )
1565 .await?;
1566 results.extend(collect_matches(&matches, collect_affecting_sources).await?);
1567 }
1568
1569 {
1570 let matches =
1571 read_matches(lookup_dir.clone(), rcstr!(""), force_in_lookup_dir, path).await?;
1572
1573 results.extend(collect_matches(&matches, collect_affecting_sources).await?);
1574 }
1575
1576 Ok(merge_results(results))
1577}
1578
1579#[turbo_tasks::function]
1580pub async fn resolve(
1581 lookup_path: FileSystemPath,
1582 reference_type: ReferenceType,
1583 request: Vc<Request>,
1584 options: Vc<ResolveOptions>,
1585) -> Result<Vc<ResolveResult>> {
1586 resolve_inline(lookup_path, reference_type, request, options).await
1587}
1588
1589pub async fn resolve_inline(
1590 lookup_path: FileSystemPath,
1591 reference_type: ReferenceType,
1592 request: Vc<Request>,
1593 options: Vc<ResolveOptions>,
1594) -> Result<Vc<ResolveResult>> {
1595 let span = tracing::info_span!(
1596 "resolving",
1597 lookup_path = display(lookup_path.to_string_ref().await?),
1598 name = tracing::field::Empty,
1599 reference_type = display(&reference_type),
1600 );
1601 if !span.is_disabled() {
1602 span.record("name", request.to_string().await?.as_str());
1604 }
1605
1606 async {
1607 let options_value = options.await?;
1609
1610 let has_before_plugins = !options_value.before_resolve_plugins.is_empty();
1612 let has_after_plugins = !options_value.after_resolve_plugins.is_empty();
1613
1614 let before_plugins_result = if has_before_plugins {
1615 handle_before_resolve_plugins(
1616 lookup_path.clone(),
1617 reference_type.clone(),
1618 request,
1619 options,
1620 )
1621 .await?
1622 } else {
1623 None
1624 };
1625
1626 let raw_result = match before_plugins_result {
1627 Some(result) => result,
1628 None => {
1629 *resolve_internal(lookup_path.clone(), request, options)
1630 .to_resolved()
1631 .await?
1632 }
1633 };
1634
1635 let result = if has_after_plugins {
1636 handle_after_resolve_plugins(lookup_path, reference_type, request, options, raw_result)
1637 .await?
1638 } else {
1639 raw_result
1640 };
1641
1642 Ok(result)
1643 }
1644 .instrument(span)
1645 .await
1646}
1647
1648#[turbo_tasks::function]
1649pub async fn url_resolve(
1650 origin: Vc<Box<dyn ResolveOrigin>>,
1651 request: Vc<Request>,
1652 reference_type: ReferenceType,
1653 issue_source: Option<IssueSource>,
1654 error_mode: ResolveErrorMode,
1655) -> Result<Vc<ModuleResolveResult>> {
1656 let resolve_options = origin.resolve_options();
1657 let rel_request = request.as_relative();
1658 let origin_path_parent = origin.origin_path().await?.parent();
1659 let rel_result = resolve(
1660 origin_path_parent.clone(),
1661 reference_type.clone(),
1662 rel_request,
1663 resolve_options,
1664 );
1665 let result =
1666 if *rel_result.is_unresolvable().await? && *rel_request.to_resolved().await? != request {
1667 let result = resolve(
1668 origin_path_parent,
1669 reference_type.clone(),
1670 request,
1671 resolve_options,
1672 );
1673 if resolve_options.await?.collect_affecting_sources {
1674 result.with_affecting_sources(
1675 rel_result
1676 .await?
1677 .get_affecting_sources()
1678 .map(|src| *src)
1679 .collect(),
1680 )
1681 } else {
1682 result
1683 }
1684 } else {
1685 rel_result
1686 };
1687 let result = origin
1688 .asset_context()
1689 .process_resolve_result(result, reference_type.clone());
1690 handle_resolve_error(
1691 result,
1692 reference_type,
1693 origin,
1694 request,
1695 resolve_options,
1696 error_mode,
1697 issue_source,
1698 )
1699 .await
1700}
1701
1702#[turbo_tasks::value(transparent)]
1703struct MatchingBeforeResolvePlugins(Vec<ResolvedVc<Box<dyn BeforeResolvePlugin>>>);
1704
1705#[turbo_tasks::function]
1706async fn get_matching_before_resolve_plugins(
1707 options: Vc<ResolveOptions>,
1708 request: Vc<Request>,
1709) -> Result<Vc<MatchingBeforeResolvePlugins>> {
1710 let mut matching_plugins = Vec::new();
1711 for &plugin in &options.await?.before_resolve_plugins {
1712 let condition = plugin.before_resolve_condition().to_resolved().await?;
1713 if *condition.matches(request).await? {
1714 matching_plugins.push(plugin);
1715 }
1716 }
1717 Ok(Vc::cell(matching_plugins))
1718}
1719
1720#[tracing::instrument(level = "trace", skip_all)]
1721async fn handle_before_resolve_plugins(
1722 lookup_path: FileSystemPath,
1723 reference_type: ReferenceType,
1724 request: Vc<Request>,
1725 options: Vc<ResolveOptions>,
1726) -> Result<Option<Vc<ResolveResult>>> {
1727 for plugin in get_matching_before_resolve_plugins(options, request).await? {
1728 if let Some(result) = *plugin
1729 .before_resolve(lookup_path.clone(), reference_type.clone(), request)
1730 .await?
1731 {
1732 return Ok(Some(*result));
1733 }
1734 }
1735 Ok(None)
1736}
1737
1738#[tracing::instrument(level = "trace", skip_all)]
1739async fn handle_after_resolve_plugins(
1740 lookup_path: FileSystemPath,
1741 reference_type: ReferenceType,
1742 request: Vc<Request>,
1743 options: Vc<ResolveOptions>,
1744 result: Vc<ResolveResult>,
1745) -> Result<Vc<ResolveResult>> {
1746 let options_value = options.await?;
1748
1749 let resolved_conditions = options_value
1751 .after_resolve_plugins
1752 .iter()
1753 .map(async |p| Ok((*p, p.after_resolve_condition().to_resolved().await?)))
1754 .try_join()
1755 .await?;
1756
1757 async fn apply_plugins_to_path(
1758 path: FileSystemPath,
1759 lookup_path: FileSystemPath,
1760 reference_type: ReferenceType,
1761 request: Vc<Request>,
1762 plugins_with_conditions: &[AfterResolvePluginWithCondition],
1763 ) -> Result<Option<Vc<ResolveResult>>> {
1764 for (plugin, after_resolve_condition) in plugins_with_conditions {
1765 if *after_resolve_condition.matches(path.clone()).await?
1766 && let Some(result) = *plugin
1767 .after_resolve(
1768 path.clone(),
1769 lookup_path.clone(),
1770 reference_type.clone(),
1771 request,
1772 )
1773 .await?
1774 {
1775 return Ok(Some(*result));
1776 }
1777 }
1778 Ok(None)
1779 }
1780
1781 let mut changed = false;
1782 let result_value = result.await?;
1783
1784 let mut new_primary = FxIndexMap::default();
1785 let mut new_affecting_sources = Vec::new();
1786
1787 for (key, primary) in result_value.primary.iter() {
1788 if let &ResolveResultItem::Source(source) = primary {
1789 let path = source.ident().path().owned().await?;
1790 if let Some(new_result) = apply_plugins_to_path(
1791 path.clone(),
1792 lookup_path.clone(),
1793 reference_type.clone(),
1794 request,
1795 &resolved_conditions,
1796 )
1797 .await?
1798 {
1799 let new_result = new_result.await?;
1800 changed = true;
1801 new_primary.extend(
1802 new_result
1803 .primary
1804 .iter()
1805 .map(|(_, item)| (key.clone(), item.clone())),
1806 );
1807 new_affecting_sources.extend(new_result.affecting_sources.iter().copied());
1808 } else {
1809 new_primary.insert(key.clone(), primary.clone());
1810 }
1811 } else {
1812 new_primary.insert(key.clone(), primary.clone());
1813 }
1814 }
1815
1816 if !changed {
1817 return Ok(result);
1818 }
1819
1820 let mut affecting_sources = result_value.affecting_sources.to_vec();
1821 affecting_sources.append(&mut new_affecting_sources);
1822
1823 Ok(ResolveResult {
1824 primary: new_primary.into_iter().collect(),
1825 affecting_sources: affecting_sources.into_boxed_slice(),
1826 }
1827 .cell())
1828}
1829
1830#[turbo_tasks::function]
1831async fn resolve_internal(
1832 lookup_path: FileSystemPath,
1833 request: ResolvedVc<Request>,
1834 options: ResolvedVc<ResolveOptions>,
1835) -> Result<Vc<ResolveResult>> {
1836 resolve_internal_inline(lookup_path.clone(), *request, *options).await
1837}
1838
1839async fn resolve_internal_inline(
1840 lookup_path: FileSystemPath,
1841 request: Vc<Request>,
1842 options: Vc<ResolveOptions>,
1843) -> Result<Vc<ResolveResult>> {
1844 let span = tracing::info_span!(
1845 "internal resolving",
1846 lookup_path = display(lookup_path.to_string_ref().await?),
1847 name = tracing::field::Empty
1848 );
1849 if !span.is_disabled() {
1850 span.record("name", request.to_string().await?.as_str());
1852 }
1853
1854 async move {
1855 let options_value: &ResolveOptions = &*options.await?;
1856
1857 let request_value = request.await?;
1858
1859 let mut has_alias = false;
1861 if let Some(import_map) = &options_value.import_map {
1862 let request_parts = match &*request_value {
1863 Request::Alternatives { requests } => requests.as_slice(),
1864 _ => &[request.to_resolved().await?],
1865 };
1866 for &request in request_parts {
1867 let result = import_map
1868 .await?
1869 .lookup(lookup_path.clone(), *request)
1870 .await?;
1871 if !matches!(result, ImportMapResult::NoEntry) {
1872 has_alias = true;
1873 let resolved_result = resolve_import_map_result(
1874 &result,
1875 lookup_path.clone(),
1876 lookup_path.clone(),
1877 *request,
1878 options,
1879 request.query().owned().await?,
1880 )
1881 .await?;
1882 if let Some(resolved_result) = resolved_result {
1889 let resolved_result = resolved_result.into_cell_if_resolvable().await?;
1890 if let Some(result) = resolved_result {
1891 return Ok(result);
1892 }
1893 }
1894 }
1895 }
1896 }
1897
1898 let result = match &*request_value {
1899 Request::Dynamic => ResolveResult::unresolvable().cell(),
1900 Request::Alternatives { requests } => {
1901 let results = requests
1902 .iter()
1903 .map(|req| async {
1904 resolve_internal_inline(lookup_path.clone(), **req, options).await
1905 })
1906 .try_join()
1907 .await?;
1908
1909 merge_results(results)
1910 }
1911 Request::Raw {
1912 path,
1913 query,
1914 force_in_lookup_dir,
1915 fragment,
1916 } => {
1917 let mut results = Vec::new();
1918 let matches = read_matches(
1919 lookup_path.clone(),
1920 rcstr!(""),
1921 *force_in_lookup_dir,
1922 *Pattern::new(path.clone()).to_resolved().await?,
1923 )
1924 .await?;
1925
1926 for m in matches.iter() {
1927 match m {
1928 PatternMatch::File(matched_pattern, path) => {
1929 results.push(
1930 resolved(
1931 RequestKey::new(matched_pattern.clone()),
1932 path.clone(),
1933 lookup_path.clone(),
1934 request,
1935 options_value,
1936 options,
1937 query.clone(),
1938 fragment.clone(),
1939 )
1940 .await?
1941 .into_cell(),
1942 );
1943 }
1944 PatternMatch::Directory(matched_pattern, path) => {
1945 results.push(
1946 resolve_into_folder(path.clone(), options)
1947 .with_request(matched_pattern.clone()),
1948 );
1949 }
1950 }
1951 }
1952
1953 merge_results(results)
1954 }
1955 Request::Relative {
1956 path,
1957 query,
1958 force_in_lookup_dir,
1959 fragment,
1960 } => {
1961 resolve_relative_request(
1962 lookup_path.clone(),
1963 request,
1964 options,
1965 options_value,
1966 path,
1967 query.clone(),
1968 *force_in_lookup_dir,
1969 fragment.clone(),
1970 )
1971 .await?
1972 }
1973 Request::Module {
1974 module,
1975 path,
1976 query,
1977 fragment,
1978 } => {
1979 resolve_module_request(
1980 lookup_path.clone(),
1981 request,
1982 options,
1983 options_value,
1984 module,
1985 path,
1986 query.clone(),
1987 fragment.clone(),
1988 )
1989 .await?
1990 }
1991 Request::ServerRelative {
1992 path,
1993 query,
1994 fragment,
1995 } => {
1996 let mut new_pat = path.clone();
1997 new_pat.push_front(rcstr!(".").into());
1998 let relative = Request::relative(new_pat, query.clone(), fragment.clone(), true);
1999
2000 if !has_alias {
2001 ResolvingIssue {
2002 severity: resolve_error_severity(options).await?,
2003 request_type: "server relative import: not implemented yet".to_string(),
2004 request: relative.to_resolved().await?,
2005 file_path: lookup_path.clone(),
2006 resolve_options: options.to_resolved().await?,
2007 error_message: Some(
2008 "server relative imports are not implemented yet. Please try an \
2009 import relative to the file you are importing from."
2010 .to_string(),
2011 ),
2012 source: None,
2013 }
2014 .resolved_cell()
2015 .emit();
2016 }
2017
2018 Box::pin(resolve_internal_inline(
2019 lookup_path.root().owned().await?,
2020 relative,
2021 options,
2022 ))
2023 .await?
2024 }
2025 Request::Windows {
2026 path: _,
2027 query: _,
2028 fragment: _,
2029 } => {
2030 if !has_alias {
2031 ResolvingIssue {
2032 severity: resolve_error_severity(options).await?,
2033 request_type: "windows import: not implemented yet".to_string(),
2034 request: request.to_resolved().await?,
2035 file_path: lookup_path.clone(),
2036 resolve_options: options.to_resolved().await?,
2037 error_message: Some("windows imports are not implemented yet".to_string()),
2038 source: None,
2039 }
2040 .resolved_cell()
2041 .emit();
2042 }
2043
2044 ResolveResult::unresolvable().cell()
2045 }
2046 Request::Empty => ResolveResult::unresolvable().cell(),
2047 Request::PackageInternal { path } => {
2048 let (conditions, unspecified_conditions) = options_value
2049 .in_package
2050 .iter()
2051 .find_map(|item| match item {
2052 ResolveInPackage::ImportsField {
2053 conditions,
2054 unspecified_conditions,
2055 } => Some((Cow::Borrowed(conditions), *unspecified_conditions)),
2056 _ => None,
2057 })
2058 .unwrap_or_else(|| (Default::default(), ConditionValue::Unset));
2059 resolve_package_internal_with_imports_field(
2060 lookup_path.clone(),
2061 request,
2062 options,
2063 path,
2064 &conditions,
2065 &unspecified_conditions,
2066 )
2067 .await?
2068 }
2069 Request::DataUri {
2070 media_type,
2071 encoding,
2072 data,
2073 } => {
2074 let uri: RcStr = stringify_data_uri(media_type, encoding, *data)
2076 .await?
2077 .into();
2078 if options_value.parse_data_uris {
2079 ResolveResult::primary_with_key(
2080 RequestKey::new(uri.clone()),
2081 ResolveResultItem::Source(ResolvedVc::upcast(
2082 DataUriSource::new(
2083 media_type.clone(),
2084 encoding.clone(),
2085 **data,
2086 lookup_path.clone(),
2087 )
2088 .to_resolved()
2089 .await?,
2090 )),
2091 )
2092 .cell()
2093 } else {
2094 ResolveResult::primary_with_key(
2095 RequestKey::new(uri.clone()),
2096 ResolveResultItem::External {
2097 name: uri,
2098 ty: ExternalType::Url,
2099 traced: ExternalTraced::Untraced,
2100 target: None,
2101 },
2102 )
2103 .cell()
2104 }
2105 }
2106 Request::Uri {
2107 protocol,
2108 remainder,
2109 query: _,
2110 fragment: _,
2111 } => {
2112 let uri: RcStr = format!("{protocol}{remainder}").into();
2113 ResolveResult::primary_with_key(
2114 RequestKey::new(uri.clone()),
2115 ResolveResultItem::External {
2116 name: uri,
2117 ty: ExternalType::Url,
2118 traced: ExternalTraced::Untraced,
2119 target: None,
2120 },
2121 )
2122 .cell()
2123 }
2124 Request::Unknown { path } => {
2125 if !has_alias {
2126 ResolvingIssue {
2127 severity: resolve_error_severity(options).await?,
2128 request_type: format!("unknown import: `{}`", path.describe_as_string()),
2129 request: request.to_resolved().await?,
2130 file_path: lookup_path.clone(),
2131 resolve_options: options.to_resolved().await?,
2132 error_message: None,
2133 source: None,
2134 }
2135 .resolved_cell()
2136 .emit();
2137 }
2138 ResolveResult::unresolvable().cell()
2139 }
2140 };
2141
2142 if !matches!(*request_value, Request::Alternatives { .. }) {
2145 if let Some(import_map) = &options_value.fallback_import_map
2147 && *result.is_unresolvable().await?
2148 {
2149 let result = import_map
2150 .await?
2151 .lookup(lookup_path.clone(), request)
2152 .await?;
2153 let resolved_result = resolve_import_map_result(
2154 &result,
2155 lookup_path.clone(),
2156 lookup_path.clone(),
2157 request,
2158 options,
2159 request.query().owned().await?,
2160 )
2161 .await?;
2162 if let Some(resolved_result) = resolved_result {
2163 let resolved_result = resolved_result.into_cell_if_resolvable().await?;
2164 if let Some(result) = resolved_result {
2165 return Ok(result);
2166 }
2167 }
2168 }
2169 }
2170
2171 Ok(result)
2172 }
2173 .instrument(span)
2174 .await
2175}
2176
2177#[turbo_tasks::function]
2178async fn resolve_into_folder(
2179 package_path: FileSystemPath,
2180 options: Vc<ResolveOptions>,
2181) -> Result<Vc<ResolveResult>> {
2182 let options_value = options.await?;
2183
2184 let mut affecting_sources = vec![];
2185 if let Some(package_json_path) = exists(
2186 &package_path.join("package.json")?,
2187 if options_value.collect_affecting_sources {
2188 Some(&mut affecting_sources)
2189 } else {
2190 None
2191 },
2192 )
2193 .await?
2194 {
2195 for resolve_into_package in options_value.into_package.iter() {
2196 match resolve_into_package {
2197 ResolveIntoPackage::MainField { field: name } => {
2198 if let Some(package_json) =
2199 &*read_package_json(Vc::upcast(FileSource::new(package_json_path.clone())))
2200 .await?
2201 && let Some(field_value) = package_json[name.as_str()].as_str()
2202 {
2203 let normalized_request = RcStr::from(normalize_request(field_value));
2204 if normalized_request.is_empty()
2205 || &*normalized_request == "."
2206 || &*normalized_request == "./"
2207 {
2208 continue;
2209 }
2210 let request = Request::parse_string(normalized_request);
2211
2212 let options = if options_value.fully_specified {
2214 *options.with_fully_specified(false).to_resolved().await?
2215 } else {
2216 options
2217 };
2218 let result =
2219 &*resolve_internal_inline(package_path.clone(), request, options)
2220 .await?
2221 .await?;
2222 if !result.is_unresolvable_ref() {
2225 let mut result: ResolveResultBuilder =
2226 result.with_request_ref(rcstr!(".")).into();
2227 if options_value.collect_affecting_sources {
2228 result.affecting_sources.push(ResolvedVc::upcast(
2229 FileSource::new(package_json_path).to_resolved().await?,
2230 ));
2231 result.affecting_sources.extend(affecting_sources);
2232 }
2233 return Ok(ResolveResult::from(result).cell());
2234 }
2235 };
2236 }
2237 ResolveIntoPackage::ExportsField { .. } => {}
2238 }
2239 }
2240 }
2241
2242 if options_value.fully_specified {
2243 return Ok(ResolveResult::unresolvable_with_affecting_sources(affecting_sources).cell());
2244 }
2245
2246 let pattern = match &options_value.default_files[..] {
2248 [] => {
2249 return Ok(
2250 ResolveResult::unresolvable_with_affecting_sources(affecting_sources).cell(),
2251 );
2252 }
2253 [file] => Pattern::Constant(format!("./{file}").into()),
2254 files => Pattern::Alternatives(
2255 files
2256 .iter()
2257 .map(|file| Pattern::Constant(format!("./{file}").into()))
2258 .collect(),
2259 ),
2260 };
2261
2262 let request = Request::parse(pattern);
2263 let result = resolve_internal_inline(package_path.clone(), request, options)
2264 .await?
2265 .with_request(rcstr!("."));
2266
2267 Ok(if !affecting_sources.is_empty() {
2268 result.with_affecting_sources(ResolvedVc::deref_vec(affecting_sources))
2269 } else {
2270 result
2271 })
2272}
2273
2274#[tracing::instrument(level = Level::TRACE, skip_all)]
2275async fn resolve_relative_request(
2276 lookup_path: FileSystemPath,
2277 request: Vc<Request>,
2278 options: Vc<ResolveOptions>,
2279 options_value: &ResolveOptions,
2280 path_pattern: &Pattern,
2281 query: RcStr,
2282 force_in_lookup_dir: bool,
2283 fragment: RcStr,
2284) -> Result<Vc<ResolveResult>> {
2285 debug_assert!(query.is_empty() || query.starts_with("?"));
2286 debug_assert!(fragment.is_empty() || fragment.starts_with("#"));
2287 let lookup_path_ref = lookup_path.clone();
2289 if let Some(result) = apply_in_package(
2290 lookup_path.clone(),
2291 options,
2292 options_value,
2293 |package_path| {
2294 let request = path_pattern.as_constant_string()?;
2295 let prefix_path = package_path.get_path_to(&lookup_path_ref)?;
2296 let request = normalize_request(&format!("./{prefix_path}/{request}"));
2297 Some(request.into())
2298 },
2299 query.clone(),
2300 fragment.clone(),
2301 )
2302 .await?
2303 {
2304 return Ok(result.into_cell());
2305 }
2306
2307 let mut new_path = path_pattern.clone();
2308
2309 #[derive(Eq, PartialEq, Clone, Hash, Debug)]
2312 enum RequestKeyTransform {
2313 None,
2315 AddedFragment,
2317 AddedExtension {
2320 ext: RcStr,
2322 next: Vec<RequestKeyTransform>,
2325 },
2326 ReplacedExtension {
2327 ext: RcStr,
2330 next: Vec<RequestKeyTransform>,
2333 },
2334 }
2335
2336 impl RequestKeyTransform {
2337 fn undo(
2340 &self,
2341 matched_pattern: &RcStr,
2342 fragment: &RcStr,
2343 pattern: &Pattern,
2344 ) -> impl Iterator<Item = (RcStr, RcStr)> {
2345 let mut result = SmallVec::new();
2346 self.apply_internal(matched_pattern, fragment, pattern, &mut result);
2347 result.into_iter()
2348 }
2349
2350 fn apply_internal(
2351 &self,
2352 matched_pattern: &RcStr,
2353 fragment: &RcStr,
2354 pattern: &Pattern,
2355 result: &mut SmallVec<[(RcStr, RcStr); 2]>,
2356 ) {
2357 match self {
2358 RequestKeyTransform::None => {
2359 if pattern.is_match(matched_pattern.as_str()) {
2360 result.push((matched_pattern.clone(), fragment.clone()));
2361 }
2362 }
2363 RequestKeyTransform::AddedFragment => {
2364 debug_assert!(
2365 !fragment.is_empty(),
2366 "can only have an AddedFragment modification if there was a fragment"
2367 );
2368 if let Some(stripped_pattern) = matched_pattern.strip_suffix(fragment.as_str())
2369 && pattern.is_match(stripped_pattern)
2370 {
2371 result.push((stripped_pattern.into(), RcStr::default()));
2372 }
2373 }
2374 RequestKeyTransform::AddedExtension { ext, next } => {
2375 if let Some(stripped_pattern) = matched_pattern.strip_suffix(ext.as_str()) {
2376 let stripped_pattern: RcStr = stripped_pattern.into();
2377 Self::apply_all(next, &stripped_pattern, fragment, pattern, result);
2378 }
2379 }
2380 RequestKeyTransform::ReplacedExtension { ext, next } => {
2381 if let Some(stripped_pattern) = matched_pattern.strip_suffix(ext.as_str()) {
2382 let replaced_pattern: RcStr = format!(
2383 "{stripped_pattern}{old_ext}",
2384 old_ext = TS_EXTENSION_REPLACEMENTS.reverse.get(ext).unwrap()
2385 )
2386 .into();
2387 Self::apply_all(next, &replaced_pattern, fragment, pattern, result);
2388 }
2389 }
2390 }
2391 }
2392
2393 fn apply_all(
2394 list: &[RequestKeyTransform],
2395 matched_pattern: &RcStr,
2396 fragment: &RcStr,
2397 pattern: &Pattern,
2398 result: &mut SmallVec<[(RcStr, RcStr); 2]>,
2399 ) {
2400 list.iter()
2401 .for_each(|pm| pm.apply_internal(matched_pattern, fragment, pattern, result));
2402 }
2403 }
2404
2405 let mut modifications = Vec::new();
2406 modifications.push(RequestKeyTransform::None);
2407
2408 if !fragment.is_empty() {
2412 modifications.push(RequestKeyTransform::AddedFragment);
2413 new_path.push(Pattern::Alternatives(vec![
2414 Pattern::Constant(RcStr::default()),
2415 Pattern::Constant(fragment.clone()),
2416 ]));
2417 }
2418
2419 if !options_value.fully_specified {
2420 modifications =
2422 modifications
2423 .iter()
2424 .cloned()
2425 .chain(options_value.extensions.iter().map(|ext| {
2426 RequestKeyTransform::AddedExtension {
2427 ext: ext.clone(),
2428 next: modifications.clone(),
2429 }
2430 }))
2431 .collect();
2432 new_path.push(Pattern::Alternatives(
2437 once(Pattern::Constant(RcStr::default()))
2438 .chain(
2439 options_value
2440 .extensions
2441 .iter()
2442 .map(|ext| Pattern::Constant(ext.clone())),
2443 )
2444 .collect(),
2445 ));
2446 new_path.normalize();
2447 };
2448
2449 struct ExtensionReplacements {
2450 forward: FxHashMap<RcStr, SmallVec<[RcStr; 3]>>,
2451 reverse: FxHashMap<RcStr, RcStr>,
2452 }
2453 static TS_EXTENSION_REPLACEMENTS: Lazy<ExtensionReplacements> = Lazy::new(|| {
2454 let mut forward = FxHashMap::default();
2455 forward.insert(
2456 rcstr!(".js"),
2457 SmallVec::from_vec(vec![rcstr!(".ts"), rcstr!(".tsx"), rcstr!(".js")]),
2458 );
2459
2460 forward.insert(
2461 rcstr!(".mjs"),
2462 SmallVec::from_vec(vec![rcstr!(".mts"), rcstr!(".mjs")]),
2463 );
2464
2465 forward.insert(
2466 rcstr!(".cjs"),
2467 SmallVec::from_vec(vec![rcstr!(".cts"), rcstr!(".cjs")]),
2468 );
2469 let reverse = forward
2470 .iter()
2471 .flat_map(|(k, v)| v.iter().map(|v: &RcStr| (v.clone(), k.clone())))
2472 .collect::<FxHashMap<_, _>>();
2473 ExtensionReplacements { forward, reverse }
2474 });
2475
2476 if options_value.enable_typescript_with_output_extension {
2477 let mut replaced_extensions = SmallVec::<[RcStr; 4]>::new();
2479 let replaced = new_path.replace_final_constants(&mut |c: &RcStr| -> Option<Pattern> {
2480 let (base, ext) = c.split_at(c.rfind('.')?);
2481
2482 let (ext, replacements) = TS_EXTENSION_REPLACEMENTS.forward.get_key_value(ext)?;
2483 for replacement in replacements {
2484 if replacement != ext && !replaced_extensions.contains(replacement) {
2485 replaced_extensions.push(replacement.clone());
2486 debug_assert!(replaced_extensions.len() <= replaced_extensions.inline_size());
2487 }
2488 }
2489
2490 let replacements = replacements
2491 .iter()
2492 .cloned()
2493 .map(Pattern::Constant)
2494 .collect();
2495
2496 if base.is_empty() {
2497 Some(Pattern::Alternatives(replacements))
2498 } else {
2499 Some(Pattern::Concatenation(vec![
2500 Pattern::Constant(base.into()),
2501 Pattern::Alternatives(replacements),
2502 ]))
2503 }
2504 });
2505 if replaced {
2506 modifications = modifications
2508 .iter()
2509 .cloned()
2510 .chain(replaced_extensions.iter().map(|ext| {
2511 RequestKeyTransform::ReplacedExtension {
2512 ext: ext.clone(),
2513 next: modifications.clone(),
2514 }
2515 }))
2516 .collect();
2517 new_path.normalize();
2518 }
2519 }
2520
2521 let matches = read_matches(
2522 lookup_path.clone(),
2523 rcstr!(""),
2524 force_in_lookup_dir,
2525 *Pattern::new(new_path.clone()).to_resolved().await?,
2526 )
2527 .await?;
2528
2529 let mut keys = FxHashSet::default();
2533 let results = matches
2534 .iter()
2535 .flat_map(|m| {
2536 if let PatternMatch::File(matched_pattern, path) = m {
2537 Either::Left(
2538 modifications
2539 .iter()
2540 .flat_map(|m| m.undo(matched_pattern, &fragment, path_pattern))
2541 .map(move |result| (result, path)),
2542 )
2543 } else {
2544 Either::Right(empty())
2545 }
2546 })
2547 .filter(move |((matched_pattern, _), _)| keys.insert(matched_pattern.clone()))
2549 .map(|((matched_pattern, fragment), path)| {
2550 resolved(
2551 RequestKey::new(matched_pattern),
2552 path.clone(),
2553 lookup_path.clone(),
2554 request,
2555 options_value,
2556 options,
2557 query.clone(),
2558 fragment,
2559 )
2560 })
2561 .try_join()
2562 .await?;
2563
2564 let mut results: Vec<Vc<ResolveResult>> = results.into_iter().map(|r| r.into_cell()).collect();
2566
2567 for m in matches.iter() {
2569 if let PatternMatch::Directory(matched_pattern, path) = m {
2570 results.push(
2571 resolve_into_folder(path.clone(), options).with_request(matched_pattern.clone()),
2572 );
2573 }
2574 }
2575
2576 Ok(merge_results(results))
2577}
2578
2579#[tracing::instrument(level = Level::TRACE, skip_all)]
2580async fn apply_in_package(
2581 lookup_path: FileSystemPath,
2582 options: Vc<ResolveOptions>,
2583 options_value: &ResolveOptions,
2584 get_request: impl Fn(&FileSystemPath) -> Option<RcStr>,
2585 query: RcStr,
2586 fragment: RcStr,
2587) -> Result<Option<ResolveResultOrCell>> {
2588 for in_package in options_value.in_package.iter() {
2590 let ResolveInPackage::AliasField(field) = in_package else {
2594 continue;
2595 };
2596
2597 let FindContextFileResult::Found(package_json_path, refs) = &*find_context_file(
2598 lookup_path.clone(),
2599 *package_json().to_resolved().await?,
2600 options_value.collect_affecting_sources,
2601 )
2602 .await?
2603 else {
2604 continue;
2605 };
2606
2607 let read =
2608 read_package_json(Vc::upcast(FileSource::new(package_json_path.clone()))).await?;
2609 let Some(package_json) = &*read else {
2610 continue;
2611 };
2612
2613 let Some(field_value) = package_json[field.as_str()].as_object() else {
2614 continue;
2615 };
2616
2617 let package_path = package_json_path.parent();
2618
2619 let Some(request) = get_request(&package_path) else {
2620 continue;
2621 };
2622
2623 let value = if let Some(value) = field_value.get(&*request) {
2624 value
2625 } else if let Some(request) = request.strip_prefix("./") {
2626 let Some(value) = field_value.get(request) else {
2627 continue;
2628 };
2629 value
2630 } else {
2631 continue;
2632 };
2633
2634 let refs = refs.clone();
2635 let request_key = RequestKey::new(request.clone());
2636
2637 if value.as_bool() == Some(false) {
2638 return Ok(Some(ResolveResultOrCell::Value(
2639 ResolveResult::primary_with_affecting_sources(
2640 request_key,
2641 ResolveResultItem::Ignore,
2642 refs,
2643 ),
2644 )));
2645 }
2646
2647 if let Some(value) = value.as_str() {
2648 if value == &*request {
2649 return Ok(None);
2651 }
2652 let mut result = resolve_internal(
2653 package_path,
2654 Request::parse(Pattern::Constant(value.into()))
2655 .with_query(query.clone())
2656 .with_fragment(fragment.clone()),
2657 options,
2658 )
2659 .with_replaced_request_key(value.into(), request_key);
2660 if options_value.collect_affecting_sources && !refs.is_empty() {
2661 result = result.with_affecting_sources(refs.into_iter().map(|src| *src).collect());
2662 }
2663 return Ok(Some(ResolveResultOrCell::Cell(result)));
2664 }
2665
2666 ResolvingIssue {
2667 severity: resolve_error_severity(options).await?,
2668 file_path: package_json_path.clone(),
2669 request_type: format!("alias field ({field})"),
2670 request: Request::parse(Pattern::Constant(request))
2671 .to_resolved()
2672 .await?,
2673 resolve_options: options.to_resolved().await?,
2674 error_message: Some(format!("invalid alias field value: {value}")),
2675 source: None,
2676 }
2677 .resolved_cell()
2678 .emit();
2679
2680 return Ok(Some(ResolveResultOrCell::Value(
2681 ResolveResult::unresolvable_with_affecting_sources(refs),
2682 )));
2683 }
2684 Ok(None)
2685}
2686
2687#[turbo_tasks::value]
2688enum FindSelfReferencePackageResult {
2689 Found {
2690 name: String,
2691 package_path: FileSystemPath,
2692 },
2693 NotFound,
2694}
2695
2696#[turbo_tasks::function]
2697async fn find_self_reference(
2700 lookup_path: FileSystemPath,
2701) -> Result<Vc<FindSelfReferencePackageResult>> {
2702 let package_json_context =
2703 find_context_file(lookup_path, *package_json().to_resolved().await?, false).await?;
2704 if let FindContextFileResult::Found(package_json_path, _refs) = &*package_json_context {
2705 let read =
2706 read_package_json(Vc::upcast(FileSource::new(package_json_path.clone()))).await?;
2707 if let Some(json) = &*read
2708 && json.get("exports").is_some()
2709 && let Some(name) = json["name"].as_str()
2710 {
2711 return Ok(FindSelfReferencePackageResult::Found {
2712 name: name.to_string(),
2713 package_path: package_json_path.parent(),
2714 }
2715 .cell());
2716 }
2717 }
2718 Ok(FindSelfReferencePackageResult::NotFound.cell())
2719}
2720
2721#[tracing::instrument(level = Level::TRACE, skip_all)]
2722async fn resolve_module_request(
2723 lookup_path: FileSystemPath,
2724 request: Vc<Request>,
2725 options: Vc<ResolveOptions>,
2726 options_value: &ResolveOptions,
2727 module: &Pattern,
2728 path: &Pattern,
2729 query: RcStr,
2730 fragment: RcStr,
2731) -> Result<Vc<ResolveResult>> {
2732 if let Some(result) = apply_in_package(
2734 lookup_path.clone(),
2735 options,
2736 options_value,
2737 |_| {
2738 let full_pattern = Pattern::concat([module.clone(), path.clone()]);
2739 full_pattern.as_constant_string().cloned()
2740 },
2741 query.clone(),
2742 fragment.clone(),
2743 )
2744 .await?
2745 {
2746 return Ok(result.into_cell());
2747 }
2748
2749 let mut results = vec![];
2750
2751 if let FindSelfReferencePackageResult::Found { name, package_path } =
2755 &*find_self_reference(lookup_path.clone()).await?
2756 && module.is_match(name)
2757 {
2758 let result = resolve_into_package(
2759 path.clone(),
2760 package_path.clone(),
2761 query.clone(),
2762 fragment.clone(),
2763 options,
2764 );
2765 if !(*result.is_unresolvable().await?) {
2766 return Ok(result);
2767 }
2768 }
2769
2770 let result = find_package(
2771 lookup_path.clone(),
2772 module.clone(),
2773 *resolve_modules_options(options).to_resolved().await?,
2774 options_value.collect_affecting_sources,
2775 )
2776 .await?;
2777
2778 if result.packages.is_empty() {
2779 return Ok(ResolveResult::unresolvable_with_affecting_sources(
2780 result.affecting_sources.clone(),
2781 )
2782 .cell());
2783 }
2784
2785 for item in &result.packages {
2791 match item {
2792 FindPackageItem::PackageDirectory { name, dir } => {
2793 results.push(
2794 resolve_into_package(
2795 path.clone(),
2796 dir.clone(),
2797 query.clone(),
2798 fragment.clone(),
2799 options,
2800 )
2801 .with_replaced_request_key(rcstr!("."), RequestKey::new(name.clone())),
2802 );
2803 }
2804 FindPackageItem::PackageFile { name, file } => {
2805 if path.is_match("") {
2806 let resolved_result = resolved(
2807 RequestKey::new(rcstr!(".")),
2808 file.clone(),
2809 lookup_path.clone(),
2810 request,
2811 options_value,
2812 options,
2813 query.clone(),
2814 fragment.clone(),
2815 )
2816 .await?
2817 .into_cell()
2818 .with_replaced_request_key(rcstr!("."), RequestKey::new(name.clone()));
2819 results.push(resolved_result)
2820 }
2821 }
2822 }
2823 }
2824
2825 let module_result =
2826 merge_results_with_affecting_sources(results, result.affecting_sources.clone());
2827
2828 if options_value.prefer_relative {
2829 let mut module_prefixed = module.clone();
2830 module_prefixed.push_front(rcstr!("./").into());
2831 let pattern = Pattern::concat([module_prefixed.clone(), rcstr!("/").into(), path.clone()]);
2832 let relative = Request::relative(pattern, query, fragment, true)
2833 .to_resolved()
2834 .await?;
2835 let relative_result = Box::pin(resolve_internal_inline(
2836 lookup_path.clone(),
2837 *relative,
2838 options,
2839 ))
2840 .await?;
2841 let relative_result = relative_result.with_stripped_request_key_prefix(rcstr!("./"));
2842
2843 Ok(merge_results(vec![relative_result, module_result]))
2844 } else {
2845 Ok(module_result)
2846 }
2847}
2848
2849#[turbo_tasks::function]
2850async fn resolve_into_package(
2851 path: Pattern,
2852 package_path: FileSystemPath,
2853 query: RcStr,
2854 fragment: RcStr,
2855 options: ResolvedVc<ResolveOptions>,
2856) -> Result<Vc<ResolveResult>> {
2857 let options_value = options.await?;
2858 let mut results = Vec::new();
2859
2860 let is_root_match = path.is_match("") || path.is_match("/");
2861 let could_match_others = path.could_match_others("");
2862
2863 let mut export_path_request = path.clone();
2864 export_path_request.push_front(rcstr!(".").into());
2865 for resolve_into_package in options_value.into_package.iter() {
2866 match resolve_into_package {
2867 ResolveIntoPackage::MainField { .. } => {}
2869 ResolveIntoPackage::ExportsField {
2870 conditions,
2871 unspecified_conditions,
2872 } => {
2873 let package_json_path = package_path.join("package.json")?;
2874 let ExportsFieldResult::Some(exports_field) =
2875 &*exports_field(Vc::upcast(FileSource::new(package_json_path.clone()))).await?
2876 else {
2877 continue;
2878 };
2879
2880 results.push(
2881 handle_exports_imports_field(
2882 package_path.clone(),
2883 package_json_path,
2884 *options,
2885 exports_field,
2886 export_path_request.clone(),
2887 conditions,
2888 unspecified_conditions,
2889 query,
2890 )
2891 .await?,
2892 );
2893
2894 return Ok(merge_results(results));
2897 }
2898 }
2899 }
2900
2901 if is_root_match {
2903 results.push(resolve_into_folder(
2904 package_path.clone(),
2905 options.with_fully_specified(false),
2906 ));
2907 }
2908
2909 if could_match_others {
2910 let mut new_pat = path.clone();
2911 new_pat.push_front(rcstr!(".").into());
2912
2913 let relative = Request::relative(new_pat, query, fragment, true)
2914 .to_resolved()
2915 .await?;
2916 results.push(resolve_internal_inline(package_path.clone(), *relative, *options).await?);
2917 }
2918
2919 Ok(merge_results(results))
2920}
2921
2922#[tracing::instrument(level = Level::TRACE, skip_all)]
2923async fn resolve_import_map_result(
2924 result: &ImportMapResult,
2925 lookup_path: FileSystemPath,
2926 original_lookup_path: FileSystemPath,
2927 original_request: Vc<Request>,
2928 options: Vc<ResolveOptions>,
2929 query: RcStr,
2930) -> Result<Option<ResolveResultOrCell>> {
2931 Ok(match result {
2932 ImportMapResult::Result(result) => Some(ResolveResultOrCell::Cell(**result)),
2933 ImportMapResult::Alias(request, alias_lookup_path) => {
2934 let request_vc: Vc<Request> = **request;
2935 let request = if request_vc.query().await?.is_empty() && !query.is_empty() {
2937 request_vc.with_query(query.clone())
2938 } else {
2939 request_vc
2940 };
2941 let lookup_path = alias_lookup_path.clone().unwrap_or(lookup_path);
2942
2943 let request_pattern = request.request_pattern();
2945 let original_pattern = original_request.request_pattern();
2946
2947 if *request_pattern.await? == *original_pattern.await?
2948 && lookup_path == original_lookup_path
2949 {
2950 None
2951 } else {
2952 Some(ResolveResultOrCell::Cell(
2953 resolve_internal(lookup_path, request, options)
2954 .with_replaced_request_key_pattern(request_pattern, original_pattern),
2955 ))
2956 }
2957 }
2958 ImportMapResult::External {
2959 name,
2960 ty,
2961 traced,
2962 target,
2963 } => Some(ResolveResultOrCell::Value(ResolveResult::primary(
2964 ResolveResultItem::External {
2965 name: name.clone(),
2966 ty: *ty,
2967 traced: *traced,
2968 target: target.clone(),
2969 },
2970 ))),
2971 ImportMapResult::AliasExternal {
2972 name,
2973 ty,
2974 traced,
2975 lookup_dir: alias_lookup_path,
2976 } => {
2977 let request = Request::parse_string(name.clone());
2978
2979 if *request.to_resolved().await? == original_request
2981 && *alias_lookup_path == original_lookup_path
2982 {
2983 None
2984 } else {
2985 let is_external_resolvable = !resolve_internal(
2986 alias_lookup_path.clone(),
2987 request,
2988 match ty {
2989 ExternalType::CommonJs => {
2991 node_cjs_resolve_options(alias_lookup_path.root().owned().await?)
2992 }
2993 ExternalType::EcmaScriptModule => {
2994 node_esm_resolve_options(alias_lookup_path.root().owned().await?)
2995 }
2996 ExternalType::Script | ExternalType::Url | ExternalType::Global => options,
2997 },
2998 )
2999 .await?
3000 .is_unresolvable_ref();
3001 if is_external_resolvable {
3002 Some(ResolveResultOrCell::Value(ResolveResult::primary(
3003 ResolveResultItem::External {
3004 name: name.clone(),
3005 ty: *ty,
3006 traced: *traced,
3007 target: None,
3008 },
3009 )))
3010 } else {
3011 None
3012 }
3013 }
3014 }
3015 ImportMapResult::Alternatives(list) => {
3016 let results = list
3017 .iter()
3018 .map(|result| {
3019 resolve_import_map_result(
3020 result,
3021 lookup_path.clone(),
3022 original_lookup_path.clone(),
3023 original_request,
3024 options,
3025 query.clone(),
3026 )
3027 })
3028 .try_join()
3029 .await?;
3030
3031 let cells: Vec<Vc<ResolveResult>> = results
3033 .into_iter()
3034 .flatten()
3035 .map(|r| r.into_cell())
3036 .collect();
3037 Some(ResolveResultOrCell::Cell(merge_results(cells)))
3038 }
3039 ImportMapResult::NoEntry => None,
3040 ImportMapResult::Error(issue) => Some(ResolveResultOrCell::Value(ResolveResult::primary(
3041 ResolveResultItem::Error(*issue),
3042 ))),
3043 })
3044}
3045
3046enum ResolveResultOrCell {
3049 Cell(Vc<ResolveResult>),
3050 Value(ResolveResult),
3051}
3052
3053impl ResolveResultOrCell {
3054 fn into_cell(self) -> Vc<ResolveResult> {
3055 match self {
3056 ResolveResultOrCell::Cell(vc) => vc,
3057 ResolveResultOrCell::Value(value) => value.cell(),
3058 }
3059 }
3060
3061 async fn into_cell_if_resolvable(self) -> Result<Option<Vc<ResolveResult>>> {
3062 match self {
3063 ResolveResultOrCell::Cell(resolved_result) => {
3064 if !*resolved_result.is_unresolvable().await? {
3065 return Ok(Some(resolved_result));
3066 }
3067 }
3068 ResolveResultOrCell::Value(resolve_result) => {
3069 if !resolve_result.is_unresolvable_ref() {
3070 return Ok(Some(resolve_result.cell()));
3071 }
3072 }
3073 }
3074 Ok(None)
3075 }
3076}
3077
3078#[tracing::instrument(level = Level::TRACE, skip_all)]
3079async fn resolved(
3080 request_key: RequestKey,
3081 fs_path: FileSystemPath,
3082 original_context: FileSystemPath,
3083 original_request: Vc<Request>,
3084 options_value: &ResolveOptions,
3085 options: Vc<ResolveOptions>,
3086 query: RcStr,
3087 fragment: RcStr,
3088) -> Result<ResolveResultOrCell> {
3089 let result = &*fs_path.realpath_with_links().await?;
3090 let path = match &result.path_result {
3091 Ok(path) => path,
3092 Err(e) => bail!(e.as_error_message(&fs_path, result).await?),
3093 };
3094
3095 let path_ref = path.clone();
3096 if let Some(result) = apply_in_package(
3098 path.parent(),
3099 options,
3100 options_value,
3101 |package_path| package_path.get_relative_path_to(&path_ref),
3102 query.clone(),
3103 fragment.clone(),
3104 )
3105 .await?
3106 {
3107 return Ok(result);
3108 }
3109
3110 if let Some(resolved_map) = options_value.resolved_map {
3111 let result = resolved_map
3112 .lookup(path.clone(), original_context.clone(), original_request)
3113 .await?;
3114
3115 let resolved_result = resolve_import_map_result(
3116 &result,
3117 path.parent(),
3118 original_context.clone(),
3119 original_request,
3120 options,
3121 query.clone(),
3122 )
3123 .await?;
3124
3125 if let Some(result) = resolved_result {
3126 return Ok(result);
3127 }
3128 }
3129 let source = ResolvedVc::upcast(
3130 FileSource::new_with_query_and_fragment(path.clone(), query, fragment)
3131 .to_resolved()
3132 .await?,
3133 );
3134 Ok(ResolveResultOrCell::Value(
3135 if options_value.collect_affecting_sources {
3136 ResolveResult::source_with_affecting_sources(
3137 request_key,
3138 source,
3139 result
3140 .symlinks
3141 .iter()
3142 .map(|symlink| async move {
3143 anyhow::Ok(ResolvedVc::upcast(
3144 FileSource::new(symlink.clone()).to_resolved().await?,
3145 ))
3146 })
3147 .try_join()
3148 .await?,
3149 )
3150 } else {
3151 ResolveResult::source_with_key(request_key, source)
3152 },
3153 ))
3154}
3155
3156async fn handle_exports_imports_field(
3157 package_path: FileSystemPath,
3158 package_json_path: FileSystemPath,
3159 options: Vc<ResolveOptions>,
3160 exports_imports_field: &AliasMap<SubpathValue>,
3161 mut path: Pattern,
3162 conditions: &BTreeMap<RcStr, ConditionValue>,
3163 unspecified_conditions: &ConditionValue,
3164 query: RcStr,
3165) -> Result<Vc<ResolveResult>> {
3166 let mut results = Vec::new();
3167 let mut conditions_state = FxHashMap::default();
3168
3169 if !query.is_empty() {
3170 path.push(query.into());
3171 }
3172 let req = path;
3173
3174 let values = exports_imports_field.lookup(&req);
3175 for value in values {
3176 let value = value?;
3177 if value.output.add_results(
3178 value.prefix,
3179 value.key,
3180 conditions,
3181 unspecified_conditions,
3182 &mut conditions_state,
3183 &mut results,
3184 ) {
3185 break;
3187 }
3188 }
3189
3190 let mut resolved_results = Vec::new();
3191 for ReplacedSubpathValueResult {
3192 result_path,
3193 conditions,
3194 map_prefix,
3195 map_key,
3196 } in results
3197 {
3198 if let Some(result_path) = result_path.with_normalized_path() {
3199 let request = *Request::parse(Pattern::Concatenation(vec![
3200 Pattern::Constant(rcstr!("./")),
3201 result_path.clone(),
3202 ]))
3203 .to_resolved()
3204 .await?;
3205
3206 let resolve_result = Box::pin(resolve_internal_inline(
3207 package_path.clone(),
3208 request,
3209 options,
3210 ))
3211 .await?;
3212
3213 let resolve_result = if let Some(req) = req.as_constant_string() {
3214 resolve_result.with_request(req.clone())
3215 } else {
3216 match map_key {
3217 AliasKey::Exact => resolve_result.with_request(map_prefix.clone().into()),
3218 AliasKey::Wildcard { .. } => {
3219 let mut old_request_key = result_path;
3229 old_request_key.push_front(rcstr!("./").into());
3231 let new_request_key = req.clone();
3232
3233 resolve_result.with_replaced_request_key_pattern(
3234 Pattern::new(old_request_key),
3235 Pattern::new(new_request_key),
3236 )
3237 }
3238 }
3239 };
3240
3241 let resolve_result = if !conditions.is_empty() {
3242 let resolve_result = resolve_result.await?.with_conditions(&conditions);
3243 resolve_result.cell()
3244 } else {
3245 resolve_result
3246 };
3247 resolved_results.push(resolve_result);
3248 }
3249 }
3250
3251 Ok(merge_results_with_affecting_sources(
3253 resolved_results,
3254 vec![ResolvedVc::upcast(
3255 FileSource::new(package_json_path).to_resolved().await?,
3256 )],
3257 ))
3258}
3259
3260async fn resolve_package_internal_with_imports_field(
3265 file_path: FileSystemPath,
3266 request: Vc<Request>,
3267 resolve_options: Vc<ResolveOptions>,
3268 pattern: &Pattern,
3269 conditions: &BTreeMap<RcStr, ConditionValue>,
3270 unspecified_conditions: &ConditionValue,
3271) -> Result<Vc<ResolveResult>> {
3272 let Pattern::Constant(specifier) = pattern else {
3273 bail!("PackageInternal requests can only be Constant strings");
3274 };
3275 if specifier == "#" || specifier.starts_with("#/") || specifier.ends_with('/') {
3277 ResolvingIssue {
3278 severity: resolve_error_severity(resolve_options).await?,
3279 file_path: file_path.clone(),
3280 request_type: format!("package imports request: `{specifier}`"),
3281 request: request.to_resolved().await?,
3282 resolve_options: resolve_options.to_resolved().await?,
3283 error_message: None,
3284 source: None,
3285 }
3286 .resolved_cell()
3287 .emit();
3288 return Ok(ResolveResult::unresolvable().cell());
3289 }
3290
3291 let imports_result = imports_field(file_path).await?;
3292 let (imports, package_json_path) = match &*imports_result {
3293 ImportsFieldResult::Some(i, p) => (i, p.clone()),
3294 ImportsFieldResult::None => return Ok(ResolveResult::unresolvable().cell()),
3295 };
3296
3297 handle_exports_imports_field(
3298 package_json_path.parent(),
3299 package_json_path.clone(),
3300 resolve_options,
3301 imports,
3302 Pattern::Constant(specifier.clone()),
3303 conditions,
3304 unspecified_conditions,
3305 RcStr::default(),
3306 )
3307 .await
3308}
3309
3310#[derive(
3314 Serialize,
3315 Deserialize,
3316 Debug,
3317 Clone,
3318 PartialEq,
3319 Eq,
3320 Hash,
3321 TraceRawVcs,
3322 TaskInput,
3323 NonLocalValue,
3324 Encode,
3325 Decode,
3326)]
3327pub enum ModulePart {
3328 Evaluation,
3331 Export(RcStr),
3333 RenamedExport {
3335 original_export: RcStr,
3336 export: RcStr,
3337 },
3338 RenamedNamespace { export: RcStr },
3340 Internal(u32),
3342 Locals,
3344 Exports,
3346 Facade,
3349}
3350
3351impl ModulePart {
3352 pub fn evaluation() -> Self {
3353 ModulePart::Evaluation
3354 }
3355
3356 pub fn export(export: RcStr) -> Self {
3357 ModulePart::Export(export)
3358 }
3359
3360 pub fn renamed_export(original_export: RcStr, export: RcStr) -> Self {
3361 ModulePart::RenamedExport {
3362 original_export,
3363 export,
3364 }
3365 }
3366
3367 pub fn renamed_namespace(export: RcStr) -> Self {
3368 ModulePart::RenamedNamespace { export }
3369 }
3370
3371 pub fn internal(id: u32) -> Self {
3372 ModulePart::Internal(id)
3373 }
3374
3375 pub fn locals() -> Self {
3376 ModulePart::Locals
3377 }
3378
3379 pub fn exports() -> Self {
3380 ModulePart::Exports
3381 }
3382
3383 pub fn facade() -> Self {
3384 ModulePart::Facade
3385 }
3386}
3387
3388impl Display for ModulePart {
3389 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
3390 match self {
3391 ModulePart::Evaluation => f.write_str("module evaluation"),
3392 ModulePart::Export(export) => write!(f, "export {export}"),
3393 ModulePart::RenamedExport {
3394 original_export,
3395 export,
3396 } => write!(f, "export {original_export} as {export}"),
3397 ModulePart::RenamedNamespace { export } => {
3398 write!(f, "export * as {export}")
3399 }
3400 ModulePart::Internal(id) => write!(f, "internal part {id}"),
3401 ModulePart::Locals => f.write_str("locals"),
3402 ModulePart::Exports => f.write_str("exports"),
3403 ModulePart::Facade => f.write_str("facade"),
3404 }
3405 }
3406}
3407#[cfg(test)]
3408mod tests {
3409 use std::{
3410 fs::{File, create_dir_all},
3411 io::Write,
3412 };
3413
3414 use turbo_rcstr::{RcStr, rcstr};
3415 use turbo_tasks::{TryJoinIterExt, Vc};
3416 use turbo_tasks_backend::{BackendOptions, TurboTasksBackend, noop_backing_storage};
3417 use turbo_tasks_fs::{DiskFileSystem, FileSystem, FileSystemPath};
3418
3419 use crate::{
3420 resolve::{
3421 ResolveResult, ResolveResultItem, node::node_esm_resolve_options, parse::Request,
3422 pattern::Pattern,
3423 },
3424 source::Source,
3425 };
3426
3427 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
3428 async fn test_explicit_js_resolves_to_ts() {
3429 resolve_relative_request_test(TestParams {
3430 files: vec!["foo.js", "foo.ts"],
3431 pattern: rcstr!("./foo.js").into(),
3432 enable_typescript_with_output_extension: true,
3433 fully_specified: false,
3434 custom_extensions: None,
3435 expected: vec![("./foo.js", "foo.ts")],
3436 })
3437 .await;
3438 }
3439
3440 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
3441 async fn test_implicit_request_ts_priority() {
3442 resolve_relative_request_test(TestParams {
3443 files: vec!["foo.js", "foo.ts"],
3444 pattern: rcstr!("./foo").into(),
3445 enable_typescript_with_output_extension: true,
3446 fully_specified: false,
3447 custom_extensions: None,
3448 expected: vec![("./foo", "foo.ts")],
3449 })
3450 .await;
3451 }
3452
3453 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
3454 async fn test_ts_priority_over_json() {
3455 resolve_relative_request_test(TestParams {
3456 files: vec!["posts.json", "posts.ts"],
3457 pattern: rcstr!("./posts").into(),
3458 enable_typescript_with_output_extension: true,
3459 fully_specified: false,
3460 custom_extensions: None,
3461 expected: vec![("./posts", "posts.ts")],
3462 })
3463 .await;
3464 }
3465
3466 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
3467 async fn test_only_js_file_no_ts() {
3468 resolve_relative_request_test(TestParams {
3469 files: vec!["bar.js"],
3470 pattern: rcstr!("./bar.js").into(),
3471 enable_typescript_with_output_extension: true,
3472 fully_specified: false,
3473 custom_extensions: None,
3474 expected: vec![("./bar.js", "bar.js")],
3475 })
3476 .await;
3477 }
3478
3479 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
3480 async fn test_explicit_ts_request() {
3481 resolve_relative_request_test(TestParams {
3482 files: vec!["foo.js", "foo.ts"],
3483 pattern: rcstr!("./foo.ts").into(),
3484 enable_typescript_with_output_extension: true,
3485 fully_specified: false,
3486 custom_extensions: None,
3487 expected: vec![("./foo.ts", "foo.ts")],
3488 })
3489 .await;
3490 }
3491
3492 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
3494 async fn test_fragment() {
3495 resolve_relative_request_test(TestParams {
3496 files: vec!["client.ts"],
3497 pattern: rcstr!("./client#frag").into(),
3498 enable_typescript_with_output_extension: true,
3499 fully_specified: false,
3500 custom_extensions: None,
3501 expected: vec![("./client", "client.ts")],
3502 })
3503 .await;
3504 }
3505
3506 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
3507 async fn test_fragment_as_part_of_filename() {
3508 resolve_relative_request_test(TestParams {
3510 files: vec!["client#component.js", "client#component.ts"],
3511 pattern: rcstr!("./client#component.js").into(),
3512 enable_typescript_with_output_extension: true,
3513 fully_specified: false,
3514 custom_extensions: None,
3515 expected: vec![("./client", "client#component.ts")],
3518 })
3519 .await;
3520 }
3521
3522 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
3523 async fn test_fragment_with_ts_priority() {
3524 resolve_relative_request_test(TestParams {
3526 files: vec!["page#section.js", "page#section.ts"],
3527 pattern: rcstr!("./page#section").into(),
3528 enable_typescript_with_output_extension: true,
3529 fully_specified: false,
3530 custom_extensions: None,
3531 expected: vec![("./page", "page#section.ts")],
3532 })
3533 .await;
3534 }
3535
3536 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
3537 async fn test_query() {
3538 resolve_relative_request_test(TestParams {
3539 files: vec!["client.ts", "client.js"],
3540 pattern: rcstr!("./client?q=s").into(),
3541 enable_typescript_with_output_extension: true,
3542 fully_specified: false,
3543 custom_extensions: None,
3544 expected: vec![("./client", "client.ts")],
3545 })
3546 .await;
3547 }
3548
3549 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
3551 async fn test_dynamic_pattern_with_js_extension() {
3552 resolve_relative_request_test(TestParams {
3556 files: vec!["src/foo.js", "src/foo.ts", "src/bar.js"],
3557 pattern: Pattern::Concatenation(vec![
3558 Pattern::Constant(rcstr!("./src/")),
3559 Pattern::Dynamic,
3560 Pattern::Constant(rcstr!(".js")),
3561 ]),
3562 enable_typescript_with_output_extension: true,
3563 fully_specified: false,
3564 custom_extensions: None,
3565 expected: vec![
3566 ("./src/foo.js", "src/foo.ts"),
3567 ("./src/bar.js", "src/bar.js"),
3568 ],
3569 })
3570 .await;
3571 }
3572
3573 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
3574 async fn test_dynamic_pattern_without_extension() {
3575 resolve_relative_request_test(TestParams {
3579 files: vec!["src/foo.js", "src/foo.ts", "src/bar.js"],
3580 pattern: Pattern::Concatenation(vec![
3581 Pattern::Constant(rcstr!("./src/")),
3582 Pattern::Dynamic,
3583 ]),
3584 enable_typescript_with_output_extension: true,
3585 fully_specified: false,
3586 custom_extensions: None,
3587 expected: vec![
3588 ("./src/bar.js", "src/bar.js"),
3589 ("./src/bar", "src/bar.js"),
3590 ("./src/foo.js", "src/foo.js"),
3596 ("./src/foo", "src/foo.js"),
3597 ("./src/foo.ts", "src/foo.ts"),
3598 ],
3599 })
3600 .await;
3601 }
3602
3603 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
3606 async fn test_custom_extensions_web_before_default() {
3607 resolve_relative_request_test(TestParams {
3608 files: vec!["Component.web.tsx", "Component.tsx"],
3609 pattern: rcstr!("./Component").into(),
3610 enable_typescript_with_output_extension: false,
3611 fully_specified: false,
3612 custom_extensions: Some(vec![
3613 rcstr!(".web.tsx"),
3614 rcstr!(".web.ts"),
3615 rcstr!(".web.jsx"),
3616 rcstr!(".web.js"),
3617 rcstr!(".tsx"),
3618 rcstr!(".ts"),
3619 rcstr!(".jsx"),
3620 rcstr!(".js"),
3621 ]),
3622 expected: vec![("./Component", "Component.web.tsx")],
3623 })
3624 .await;
3625 }
3626
3627 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
3629 async fn test_custom_extensions_fallback_when_web_missing() {
3630 resolve_relative_request_test(TestParams {
3631 files: vec!["Component.tsx"],
3632 pattern: rcstr!("./Component").into(),
3633 enable_typescript_with_output_extension: false,
3634 fully_specified: false,
3635 custom_extensions: Some(vec![
3636 rcstr!(".web.tsx"),
3637 rcstr!(".web.ts"),
3638 rcstr!(".web.jsx"),
3639 rcstr!(".web.js"),
3640 rcstr!(".tsx"),
3641 rcstr!(".ts"),
3642 rcstr!(".jsx"),
3643 rcstr!(".js"),
3644 ]),
3645 expected: vec![("./Component", "Component.tsx")],
3646 })
3647 .await;
3648 }
3649
3650 struct TestParams<'a> {
3652 files: Vec<&'a str>,
3653 pattern: Pattern,
3654 enable_typescript_with_output_extension: bool,
3655 fully_specified: bool,
3656 custom_extensions: Option<Vec<RcStr>>,
3658 expected: Vec<(&'a str, &'a str)>,
3659 }
3660
3661 async fn resolve_relative_request_test(
3663 TestParams {
3664 files,
3665 pattern,
3666 enable_typescript_with_output_extension,
3667 fully_specified,
3668 custom_extensions,
3669 expected,
3670 }: TestParams<'_>,
3671 ) {
3672 let scratch = tempfile::tempdir().unwrap();
3673 {
3674 let path = scratch.path();
3675
3676 for file_name in &files {
3677 let file_path = path.join(file_name);
3678 if let Some(parent) = file_path.parent() {
3679 create_dir_all(parent).unwrap();
3680 }
3681 File::create_new(&file_path)
3682 .unwrap()
3683 .write_all(format!("export default '{file_name}'").as_bytes())
3684 .unwrap();
3685 }
3686 }
3687
3688 let path: RcStr = scratch.path().to_str().unwrap().into();
3689 let expected_owned: Vec<(String, String)> = expected
3690 .iter()
3691 .map(|(k, v)| (k.to_string(), v.to_string()))
3692 .collect();
3693
3694 let tt = turbo_tasks::TurboTasks::new(TurboTasksBackend::new(
3695 BackendOptions::default(),
3696 noop_backing_storage(),
3697 ));
3698
3699 let custom_extensions_owned = custom_extensions;
3700
3701 tt.run_once(async move {
3702 #[turbo_tasks::value(transparent)]
3703 struct ResolveRelativeRequestOutput(Vec<(String, String)>);
3704
3705 #[turbo_tasks::function(operation)]
3706 async fn resolve_relative_request_operation(
3707 path: RcStr,
3708 pattern: Pattern,
3709 enable_typescript_with_output_extension: bool,
3710 fully_specified: bool,
3711 custom_extensions: Option<Vec<RcStr>>,
3712 ) -> anyhow::Result<Vc<ResolveRelativeRequestOutput>> {
3713 let fs = DiskFileSystem::new(rcstr!("temp"), Vc::cell(path));
3714 let lookup_path = fs.root().owned().await?;
3715
3716 let result = resolve_relative_helper(
3717 lookup_path,
3718 pattern,
3719 enable_typescript_with_output_extension,
3720 fully_specified,
3721 custom_extensions,
3722 )
3723 .await?;
3724
3725 let results: Vec<(String, String)> = result
3726 .primary
3727 .iter()
3728 .map(async |(k, v)| {
3729 Ok((
3730 k.to_string(),
3731 if let ResolveResultItem::Source(source) = v {
3732 source.ident().await?.path.path.to_string()
3733 } else {
3734 unreachable!()
3735 },
3736 ))
3737 })
3738 .try_join()
3739 .await?;
3740
3741 Ok(Vc::cell(results))
3742 }
3743
3744 let results = resolve_relative_request_operation(
3745 path,
3746 pattern,
3747 enable_typescript_with_output_extension,
3748 fully_specified,
3749 custom_extensions_owned,
3750 )
3751 .read_strongly_consistent()
3752 .await?;
3753
3754 assert_eq!(&*results, &expected_owned);
3755
3756 Ok(())
3757 })
3758 .await
3759 .unwrap();
3760 }
3761
3762 #[turbo_tasks::function]
3763 async fn resolve_relative_helper(
3764 lookup_path: FileSystemPath,
3765 pattern: Pattern,
3766 enable_typescript_with_output_extension: bool,
3767 fully_specified: bool,
3768 custom_extensions: Option<Vec<RcStr>>,
3769 ) -> anyhow::Result<Vc<ResolveResult>> {
3770 let request = Request::parse(pattern.clone());
3771
3772 let extensions = custom_extensions
3773 .unwrap_or_else(|| vec![rcstr!(".ts"), rcstr!(".js"), rcstr!(".json")]);
3774 let mut options_value = node_esm_resolve_options(lookup_path.clone())
3775 .with_fully_specified(fully_specified)
3776 .with_extensions(extensions)
3777 .owned()
3778 .await?;
3779 options_value.enable_typescript_with_output_extension =
3780 enable_typescript_with_output_extension;
3781 let options = options_value.clone().cell();
3782 match &*request.await? {
3783 Request::Relative {
3784 path,
3785 query,
3786 force_in_lookup_dir,
3787 fragment,
3788 } => {
3789 super::resolve_relative_request(
3790 lookup_path,
3791 request,
3792 options,
3793 &options_value,
3794 path,
3795 query.clone(),
3796 *force_in_lookup_dir,
3797 fragment.clone(),
3798 )
3799 .await
3800 }
3801 r => panic!("request should be relative, got {r:?}"),
3802 }
3803 }
3804}