1use std::{
2 borrow::Cow,
3 collections::BTreeMap,
4 fmt::{Display, Formatter, Write},
5 future::Future,
6 iter::once,
7};
8
9use anyhow::{Result, bail};
10use rustc_hash::{FxHashMap, FxHashSet};
11use serde::{Deserialize, Serialize};
12use tracing::{Instrument, Level};
13use turbo_rcstr::{RcStr, rcstr};
14use turbo_tasks::{
15 FxIndexMap, FxIndexSet, NonLocalValue, ReadRef, ResolvedVc, SliceMap, TaskInput,
16 TryFlatJoinIterExt, TryJoinIterExt, ValueToString, Vc, trace::TraceRawVcs,
17};
18use turbo_tasks_fs::{FileSystemEntryType, FileSystemPath};
19use turbo_unix_path::normalize_request;
20
21use self::{
22 options::{
23 ConditionValue, ImportMapResult, ResolveInPackage, ResolveIntoPackage, ResolveModules,
24 ResolveModulesOptions, ResolveOptions, resolve_modules_options,
25 },
26 origin::{ResolveOrigin, ResolveOriginExt},
27 parse::Request,
28 pattern::Pattern,
29 plugin::BeforeResolvePlugin,
30 remap::{ExportsField, ImportsField},
31};
32use crate::{
33 context::AssetContext,
34 data_uri_source::DataUriSource,
35 file_source::FileSource,
36 issue::{
37 IssueExt, IssueSource, module::emit_unknown_module_type_error, resolve::ResolvingIssue,
38 },
39 module::{Module, Modules, OptionModule},
40 output::{OutputAsset, OutputAssets},
41 package_json::{PackageJsonIssue, read_package_json},
42 raw_module::RawModule,
43 reference_type::ReferenceType,
44 resolve::{
45 alias_map::AliasKey,
46 node::{node_cjs_resolve_options, node_esm_resolve_options},
47 parse::stringify_data_uri,
48 pattern::{PatternMatch, read_matches},
49 plugin::AfterResolvePlugin,
50 remap::ReplacedSubpathValueResult,
51 },
52 source::{OptionSource, Source, Sources},
53};
54
55mod alias_map;
56pub mod node;
57pub mod options;
58pub mod origin;
59pub mod parse;
60pub mod pattern;
61pub mod plugin;
62pub(crate) mod remap;
63
64pub use alias_map::{
65 AliasMap, AliasMapIntoIter, AliasMapLookupIterator, AliasMatch, AliasPattern, AliasTemplate,
66};
67pub use remap::{ResolveAliasMap, SubpathValue};
68
69use crate::{error::PrettyPrintError, issue::IssueSeverity};
70
71#[turbo_tasks::value(shared)]
72#[derive(Clone, Debug)]
73pub enum ModuleResolveResultItem {
74 Module(ResolvedVc<Box<dyn Module>>),
75 OutputAsset(ResolvedVc<Box<dyn OutputAsset>>),
76 External {
77 name: RcStr,
79 ty: ExternalType,
80 },
81 Unknown(ResolvedVc<Box<dyn Source>>),
83 Ignore,
84 Error(ResolvedVc<RcStr>),
85 Empty,
86 Custom(u8),
87}
88
89impl ModuleResolveResultItem {
90 async fn as_module(&self) -> Result<Option<ResolvedVc<Box<dyn Module>>>> {
91 Ok(match *self {
92 ModuleResolveResultItem::Module(module) => Some(module),
93 ModuleResolveResultItem::Unknown(source) => {
94 emit_unknown_module_type_error(*source).await?;
95 None
96 }
97 ModuleResolveResultItem::Error(_err) => {
98 None
100 }
101 _ => None,
102 })
103 }
104}
105
106#[turbo_tasks::value]
107#[derive(Debug, Clone, Default, Hash)]
108pub enum ExportUsage {
109 Named(RcStr),
110 #[default]
112 All,
113 Evaluation,
115}
116
117impl Display for ExportUsage {
118 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
119 match self {
120 ExportUsage::Named(name) => write!(f, "export {name}"),
121 ExportUsage::All => write!(f, "all"),
122 ExportUsage::Evaluation => write!(f, "evaluation"),
123 }
124 }
125}
126
127#[turbo_tasks::value_impl]
128impl ExportUsage {
129 #[turbo_tasks::function]
130 pub fn all() -> Vc<Self> {
131 Self::All.cell()
132 }
133
134 #[turbo_tasks::function]
135 pub fn evaluation() -> Vc<Self> {
136 Self::Evaluation.cell()
137 }
138
139 #[turbo_tasks::function]
140 pub fn named(name: RcStr) -> Vc<Self> {
141 Self::Named(name).cell()
142 }
143}
144
145#[turbo_tasks::value(shared)]
146#[derive(Clone, Debug)]
147pub struct ModuleResolveResult {
148 pub primary: SliceMap<RequestKey, ModuleResolveResultItem>,
149 pub affecting_sources: Box<[ResolvedVc<Box<dyn Source>>]>,
152}
153
154impl ModuleResolveResult {
155 pub fn unresolvable() -> ResolvedVc<Self> {
156 ModuleResolveResult {
157 primary: Default::default(),
158 affecting_sources: Default::default(),
159 }
160 .resolved_cell()
161 }
162
163 pub fn module(module: ResolvedVc<Box<dyn Module>>) -> ResolvedVc<Self> {
164 Self::module_with_key(RequestKey::default(), module)
165 }
166
167 pub fn module_with_key(
168 request_key: RequestKey,
169 module: ResolvedVc<Box<dyn Module>>,
170 ) -> ResolvedVc<Self> {
171 ModuleResolveResult {
172 primary: vec![(request_key, ModuleResolveResultItem::Module(module))]
173 .into_boxed_slice(),
174 affecting_sources: Default::default(),
175 }
176 .resolved_cell()
177 }
178
179 pub fn output_asset(
180 request_key: RequestKey,
181 output_asset: ResolvedVc<Box<dyn OutputAsset>>,
182 ) -> ResolvedVc<Self> {
183 ModuleResolveResult {
184 primary: vec![(
185 request_key,
186 ModuleResolveResultItem::OutputAsset(output_asset),
187 )]
188 .into_boxed_slice(),
189 affecting_sources: Default::default(),
190 }
191 .resolved_cell()
192 }
193
194 pub fn modules(
195 modules: impl IntoIterator<Item = (RequestKey, ResolvedVc<Box<dyn Module>>)>,
196 ) -> ResolvedVc<Self> {
197 ModuleResolveResult {
198 primary: modules
199 .into_iter()
200 .map(|(k, v)| (k, ModuleResolveResultItem::Module(v)))
201 .collect(),
202 affecting_sources: Default::default(),
203 }
204 .resolved_cell()
205 }
206
207 pub fn modules_with_affecting_sources(
208 modules: impl IntoIterator<Item = (RequestKey, ResolvedVc<Box<dyn Module>>)>,
209 affecting_sources: Vec<ResolvedVc<Box<dyn Source>>>,
210 ) -> ResolvedVc<Self> {
211 ModuleResolveResult {
212 primary: modules
213 .into_iter()
214 .map(|(k, v)| (k, ModuleResolveResultItem::Module(v)))
215 .collect(),
216 affecting_sources: affecting_sources.into_boxed_slice(),
217 }
218 .resolved_cell()
219 }
220}
221
222impl ModuleResolveResult {
223 pub fn primary_modules_raw_iter(
225 &self,
226 ) -> impl Iterator<Item = ResolvedVc<Box<dyn Module>>> + '_ {
227 self.primary.iter().filter_map(|(_, item)| match *item {
228 ModuleResolveResultItem::Module(a) => Some(a),
229 _ => None,
230 })
231 }
232
233 pub fn affecting_sources_iter(&self) -> impl Iterator<Item = ResolvedVc<Box<dyn Source>>> + '_ {
234 self.affecting_sources.iter().copied()
235 }
236
237 pub fn is_unresolvable_ref(&self) -> bool {
238 self.primary.is_empty()
239 }
240}
241
242pub struct ModuleResolveResultBuilder {
243 pub primary: FxIndexMap<RequestKey, ModuleResolveResultItem>,
244 pub affecting_sources: Vec<ResolvedVc<Box<dyn Source>>>,
245}
246
247impl From<ModuleResolveResultBuilder> for ModuleResolveResult {
248 fn from(v: ModuleResolveResultBuilder) -> Self {
249 ModuleResolveResult {
250 primary: v.primary.into_iter().collect(),
251 affecting_sources: v.affecting_sources.into_boxed_slice(),
252 }
253 }
254}
255impl From<ModuleResolveResult> for ModuleResolveResultBuilder {
256 fn from(v: ModuleResolveResult) -> Self {
257 ModuleResolveResultBuilder {
258 primary: IntoIterator::into_iter(v.primary).collect(),
259 affecting_sources: v.affecting_sources.into_vec(),
260 }
261 }
262}
263impl ModuleResolveResultBuilder {
264 pub fn merge_alternatives(&mut self, other: &ModuleResolveResult) {
265 for (k, v) in other.primary.iter() {
266 if !self.primary.contains_key(k) {
267 self.primary.insert(k.clone(), v.clone());
268 }
269 }
270 let set = self
271 .affecting_sources
272 .iter()
273 .copied()
274 .collect::<FxHashSet<_>>();
275 self.affecting_sources.extend(
276 other
277 .affecting_sources
278 .iter()
279 .filter(|source| !set.contains(source))
280 .copied(),
281 );
282 }
283}
284
285#[turbo_tasks::value_impl]
286impl ModuleResolveResult {
287 #[turbo_tasks::function]
288 pub async fn alternatives(results: Vec<Vc<ModuleResolveResult>>) -> Result<Vc<Self>> {
289 if results.len() == 1 {
290 return Ok(results.into_iter().next().unwrap());
291 }
292 let mut iter = results.into_iter().try_join().await?.into_iter();
293 if let Some(current) = iter.next() {
294 let mut current: ModuleResolveResultBuilder = ReadRef::into_owned(current).into();
295 for result in iter {
296 let other = &*result;
298 current.merge_alternatives(other);
299 }
300 Ok(Self::cell(current.into()))
301 } else {
302 Ok(*ModuleResolveResult::unresolvable())
303 }
304 }
305
306 #[turbo_tasks::function]
307 pub fn is_unresolvable(&self) -> Vc<bool> {
308 Vc::cell(self.is_unresolvable_ref())
309 }
310
311 #[turbo_tasks::function]
312 pub async fn first_module(&self) -> Result<Vc<OptionModule>> {
313 for (_, item) in self.primary.iter() {
314 if let Some(module) = item.as_module().await? {
315 return Ok(Vc::cell(Some(module)));
316 }
317 }
318 Ok(Vc::cell(None))
319 }
320
321 #[turbo_tasks::function]
324 pub async fn primary_modules(&self) -> Result<Vc<Modules>> {
325 let mut set = FxIndexSet::default();
326 for (_, item) in self.primary.iter() {
327 if let Some(module) = item.as_module().await? {
328 set.insert(module);
329 }
330 }
331 Ok(Vc::cell(set.into_iter().collect()))
332 }
333
334 #[turbo_tasks::function]
335 pub fn primary_output_assets(&self) -> Vc<OutputAssets> {
336 Vc::cell(
337 self.primary
338 .iter()
339 .filter_map(|(_, item)| match item {
340 &ModuleResolveResultItem::OutputAsset(a) => Some(a),
341 _ => None,
342 })
343 .collect(),
344 )
345 }
346}
347
348#[derive(
349 Copy,
350 Clone,
351 Debug,
352 PartialEq,
353 Eq,
354 TaskInput,
355 Hash,
356 NonLocalValue,
357 TraceRawVcs,
358 Serialize,
359 Deserialize,
360)]
361pub enum ExternalTraced {
362 Untraced,
363 Traced,
364}
365
366impl Display for ExternalTraced {
367 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
368 match self {
369 ExternalTraced::Untraced => write!(f, "untraced"),
370 ExternalTraced::Traced => write!(f, "traced"),
371 }
372 }
373}
374
375#[derive(
376 Copy,
377 Clone,
378 Debug,
379 Eq,
380 PartialEq,
381 Hash,
382 Serialize,
383 Deserialize,
384 TraceRawVcs,
385 TaskInput,
386 NonLocalValue,
387)]
388pub enum ExternalType {
389 Url,
390 CommonJs,
391 EcmaScriptModule,
392 Global,
393 Script,
394}
395
396impl Display for ExternalType {
397 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
398 match self {
399 ExternalType::CommonJs => write!(f, "commonjs"),
400 ExternalType::EcmaScriptModule => write!(f, "esm"),
401 ExternalType::Url => write!(f, "url"),
402 ExternalType::Global => write!(f, "global"),
403 ExternalType::Script => write!(f, "script"),
404 }
405 }
406}
407
408#[turbo_tasks::value(shared)]
409#[derive(Debug, Clone)]
410pub enum ResolveResultItem {
411 Source(ResolvedVc<Box<dyn Source>>),
412 External {
413 name: RcStr,
415 ty: ExternalType,
416 traced: ExternalTraced,
417 },
418 Ignore,
419 Error(ResolvedVc<RcStr>),
420 Empty,
421 Custom(u8),
422}
423
424#[derive(Clone, Debug, Default, Hash, TaskInput)]
431#[turbo_tasks::value]
432pub struct RequestKey {
433 pub request: Option<RcStr>,
434 pub conditions: BTreeMap<String, bool>,
435}
436
437impl Display for RequestKey {
438 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
439 if let Some(request) = &self.request {
440 write!(f, "{request}")?;
441 } else {
442 write!(f, "<default>")?;
443 }
444 if !self.conditions.is_empty() {
445 write!(f, " (")?;
446 for (i, (k, v)) in self.conditions.iter().enumerate() {
447 if i > 0 {
448 write!(f, ", ")?;
449 }
450 write!(f, "{k}={v}")?;
451 }
452 write!(f, ")")?;
453 }
454 Ok(())
455 }
456}
457
458impl RequestKey {
459 pub fn new(request: RcStr) -> Self {
460 RequestKey {
461 request: Some(request),
462 ..Default::default()
463 }
464 }
465}
466
467#[turbo_tasks::value(shared)]
468#[derive(Clone)]
469pub struct ResolveResult {
470 pub primary: SliceMap<RequestKey, ResolveResultItem>,
471 pub affecting_sources: Box<[ResolvedVc<Box<dyn Source>>]>,
474}
475
476#[turbo_tasks::value_impl]
477impl ValueToString for ResolveResult {
478 #[turbo_tasks::function]
479 async fn to_string(&self) -> Result<Vc<RcStr>> {
480 let mut result = String::new();
481 if self.is_unresolvable_ref() {
482 result.push_str("unresolvable");
483 }
484 for (i, (request, item)) in self.primary.iter().enumerate() {
485 if i > 0 {
486 result.push_str(", ");
487 }
488 write!(result, "{request} -> ").unwrap();
489 match item {
490 ResolveResultItem::Source(a) => {
491 result.push_str(&a.ident().to_string().await?);
492 }
493 ResolveResultItem::External {
494 name: s,
495 ty,
496 traced,
497 } => {
498 result.push_str("external ");
499 result.push_str(s);
500 write!(result, " ({ty}, {traced})")?;
501 }
502 ResolveResultItem::Ignore => {
503 result.push_str("ignore");
504 }
505 ResolveResultItem::Empty => {
506 result.push_str("empty");
507 }
508 ResolveResultItem::Error(_) => {
509 result.push_str("error");
510 }
511 ResolveResultItem::Custom(_) => {
512 result.push_str("custom");
513 }
514 }
515 result.push('\n');
516 }
517 if !self.affecting_sources.is_empty() {
518 result.push_str(" (affecting sources: ");
519 for (i, source) in self.affecting_sources.iter().enumerate() {
520 if i > 0 {
521 result.push_str(", ");
522 }
523 result.push_str(&source.ident().to_string().await?);
524 }
525 result.push(')');
526 }
527 Ok(Vc::cell(result.into()))
528 }
529}
530
531impl ResolveResult {
532 pub fn unresolvable() -> ResolvedVc<Self> {
533 ResolveResult {
534 primary: Default::default(),
535 affecting_sources: Default::default(),
536 }
537 .resolved_cell()
538 }
539
540 pub fn unresolvable_with_affecting_sources(
541 affecting_sources: Vec<ResolvedVc<Box<dyn Source>>>,
542 ) -> ResolvedVc<Self> {
543 ResolveResult {
544 primary: Default::default(),
545 affecting_sources: affecting_sources.into_boxed_slice(),
546 }
547 .resolved_cell()
548 }
549
550 pub fn primary(result: ResolveResultItem) -> ResolvedVc<Self> {
551 Self::primary_with_key(RequestKey::default(), result)
552 }
553
554 pub fn primary_with_key(
555 request_key: RequestKey,
556 result: ResolveResultItem,
557 ) -> ResolvedVc<Self> {
558 ResolveResult {
559 primary: vec![(request_key, result)].into_boxed_slice(),
560 affecting_sources: Default::default(),
561 }
562 .resolved_cell()
563 }
564
565 pub fn primary_with_affecting_sources(
566 request_key: RequestKey,
567 result: ResolveResultItem,
568 affecting_sources: Vec<ResolvedVc<Box<dyn Source>>>,
569 ) -> ResolvedVc<Self> {
570 ResolveResult {
571 primary: vec![(request_key, result)].into_boxed_slice(),
572 affecting_sources: affecting_sources.into_boxed_slice(),
573 }
574 .resolved_cell()
575 }
576
577 pub fn source(source: ResolvedVc<Box<dyn Source>>) -> ResolvedVc<Self> {
578 Self::source_with_key(RequestKey::default(), source).resolved_cell()
579 }
580
581 fn source_with_key(request_key: RequestKey, source: ResolvedVc<Box<dyn Source>>) -> Self {
582 ResolveResult {
583 primary: vec![(request_key, ResolveResultItem::Source(source))].into_boxed_slice(),
584 affecting_sources: Default::default(),
585 }
586 }
587
588 fn source_with_affecting_sources(
589 request_key: RequestKey,
590 source: ResolvedVc<Box<dyn Source>>,
591 affecting_sources: Vec<ResolvedVc<Box<dyn Source>>>,
592 ) -> Self {
593 ResolveResult {
594 primary: vec![(request_key, ResolveResultItem::Source(source))].into_boxed_slice(),
595 affecting_sources: affecting_sources.into_boxed_slice(),
596 }
597 }
598}
599
600impl ResolveResult {
601 pub fn get_affecting_sources(&self) -> impl Iterator<Item = ResolvedVc<Box<dyn Source>>> + '_ {
604 self.affecting_sources.iter().copied()
605 }
606
607 pub fn is_unresolvable_ref(&self) -> bool {
608 self.primary.is_empty()
609 }
610
611 pub async fn map_module<A, AF>(&self, source_fn: A) -> Result<ModuleResolveResult>
612 where
613 A: Fn(ResolvedVc<Box<dyn Source>>) -> AF,
614 AF: Future<Output = Result<ModuleResolveResultItem>>,
615 {
616 Ok(ModuleResolveResult {
617 primary: self
618 .primary
619 .iter()
620 .map(|(request, item)| {
621 let asset_fn = &source_fn;
622 let request = request.clone();
623 let item = item.clone();
624 async move {
625 Ok((
626 request,
627 match item {
628 ResolveResultItem::Source(source) => asset_fn(source).await?,
629 ResolveResultItem::External { name, ty, traced } => {
630 if traced == ExternalTraced::Traced {
631 bail!("map_module doesn't handle traced externals");
633 }
634 ModuleResolveResultItem::External { name, ty }
635 }
636 ResolveResultItem::Ignore => ModuleResolveResultItem::Ignore,
637 ResolveResultItem::Empty => ModuleResolveResultItem::Empty,
638 ResolveResultItem::Error(e) => ModuleResolveResultItem::Error(e),
639 ResolveResultItem::Custom(u8) => {
640 ModuleResolveResultItem::Custom(u8)
641 }
642 },
643 ))
644 }
645 })
646 .try_join()
647 .await?
648 .into_iter()
649 .collect(),
650 affecting_sources: self.affecting_sources.clone(),
651 })
652 }
653
654 pub async fn map_primary_items<A, AF>(&self, item_fn: A) -> Result<ModuleResolveResult>
655 where
656 A: Fn(ResolveResultItem) -> AF,
657 AF: Future<Output = Result<ModuleResolveResultItem>>,
658 {
659 Ok(ModuleResolveResult {
660 primary: self
661 .primary
662 .iter()
663 .map(|(request, item)| {
664 let asset_fn = &item_fn;
665 let request = request.clone();
666 let item = item.clone();
667 async move { Ok((request, asset_fn(item).await?)) }
668 })
669 .try_join()
670 .await?
671 .into_iter()
672 .collect(),
673 affecting_sources: self.affecting_sources.clone(),
674 })
675 }
676
677 fn with_request_ref(&self, request: RcStr) -> Self {
680 let new_primary = self
681 .primary
682 .iter()
683 .map(|(k, v)| {
684 (
685 RequestKey {
686 request: Some(request.clone()),
687 conditions: k.conditions.clone(),
688 },
689 v.clone(),
690 )
691 })
692 .collect();
693 ResolveResult {
694 primary: new_primary,
695 affecting_sources: self.affecting_sources.clone(),
696 }
697 }
698
699 pub fn add_conditions(&mut self, conditions: impl IntoIterator<Item = (RcStr, bool)>) {
700 let mut primary = std::mem::take(&mut self.primary);
701 for (k, v) in conditions {
702 for (key, _) in primary.iter_mut() {
703 key.conditions.insert(k.to_string(), v);
704 }
705 }
706 self.primary = IntoIterator::into_iter(primary)
708 .collect::<FxIndexMap<_, _>>()
709 .into_iter()
710 .collect::<Vec<_>>()
711 .into_boxed_slice();
712 }
713}
714
715struct ResolveResultBuilder {
716 primary: FxIndexMap<RequestKey, ResolveResultItem>,
717 affecting_sources: Vec<ResolvedVc<Box<dyn Source>>>,
718}
719
720impl From<ResolveResultBuilder> for ResolveResult {
721 fn from(v: ResolveResultBuilder) -> Self {
722 ResolveResult {
723 primary: v.primary.into_iter().collect(),
724 affecting_sources: v.affecting_sources.into_boxed_slice(),
725 }
726 }
727}
728impl From<ResolveResult> for ResolveResultBuilder {
729 fn from(v: ResolveResult) -> Self {
730 ResolveResultBuilder {
731 primary: IntoIterator::into_iter(v.primary).collect(),
732 affecting_sources: v.affecting_sources.into_vec(),
733 }
734 }
735}
736impl ResolveResultBuilder {
737 pub fn merge_alternatives(&mut self, other: &ResolveResult) {
738 for (k, v) in other.primary.iter() {
739 if !self.primary.contains_key(k) {
740 self.primary.insert(k.clone(), v.clone());
741 }
742 }
743 let set = self
744 .affecting_sources
745 .iter()
746 .copied()
747 .collect::<FxHashSet<_>>();
748 self.affecting_sources.extend(
749 other
750 .affecting_sources
751 .iter()
752 .filter(|source| !set.contains(source))
753 .copied(),
754 );
755 }
756}
757
758#[turbo_tasks::value_impl]
759impl ResolveResult {
760 #[turbo_tasks::function]
761 pub async fn as_raw_module_result(&self) -> Result<Vc<ModuleResolveResult>> {
762 Ok(self
763 .map_module(|asset| async move {
764 Ok(ModuleResolveResultItem::Module(ResolvedVc::upcast(
765 RawModule::new(*asset).to_resolved().await?,
766 )))
767 })
768 .await?
769 .cell())
770 }
771
772 #[turbo_tasks::function]
773 fn with_affecting_sources(
774 &self,
775 sources: Vec<ResolvedVc<Box<dyn Source>>>,
776 ) -> Result<Vc<Self>> {
777 Ok(Self {
778 primary: self.primary.clone(),
779 affecting_sources: self
780 .affecting_sources
781 .iter()
782 .copied()
783 .chain(sources)
784 .collect(),
785 }
786 .cell())
787 }
788
789 #[turbo_tasks::function]
790 async fn alternatives(results: Vec<Vc<ResolveResult>>) -> Result<Vc<Self>> {
791 if results.len() == 1 {
792 return Ok(results.into_iter().next().unwrap());
793 }
794 let mut iter = results.into_iter().try_join().await?.into_iter();
795 if let Some(current) = iter.next() {
796 let mut current: ResolveResultBuilder = ReadRef::into_owned(current).into();
797 for result in iter {
798 let other = &*result;
800 current.merge_alternatives(other);
801 }
802 Ok(Self::cell(current.into()))
803 } else {
804 Ok(*ResolveResult::unresolvable())
805 }
806 }
807
808 #[turbo_tasks::function]
809 async fn alternatives_with_affecting_sources(
810 results: Vec<Vc<ResolveResult>>,
811 affecting_sources: Vec<ResolvedVc<Box<dyn Source>>>,
812 ) -> Result<Vc<Self>> {
813 debug_assert!(
814 !affecting_sources.is_empty(),
815 "Caller should not call this function if there are no affecting sources"
816 );
817 if results.len() == 1 {
818 return Ok(results
819 .into_iter()
820 .next()
821 .unwrap()
822 .with_affecting_sources(affecting_sources.into_iter().map(|src| *src).collect()));
823 }
824 let mut iter = results.into_iter().try_join().await?.into_iter();
825 if let Some(current) = iter.next() {
826 let mut current: ResolveResultBuilder = ReadRef::into_owned(current).into();
827 for result in iter {
828 let other = &*result;
830 current.merge_alternatives(other);
831 }
832 current.affecting_sources.extend(affecting_sources);
833 Ok(Self::cell(current.into()))
834 } else {
835 Ok(*ResolveResult::unresolvable_with_affecting_sources(
836 affecting_sources,
837 ))
838 }
839 }
840
841 #[turbo_tasks::function]
842 pub fn is_unresolvable(&self) -> Vc<bool> {
843 Vc::cell(self.is_unresolvable_ref())
844 }
845
846 #[turbo_tasks::function]
847 pub fn first_source(&self) -> Vc<OptionSource> {
848 Vc::cell(self.primary.iter().find_map(|(_, item)| {
849 if let &ResolveResultItem::Source(a) = item {
850 Some(a)
851 } else {
852 None
853 }
854 }))
855 }
856
857 #[turbo_tasks::function]
858 pub fn primary_sources(&self) -> Vc<Sources> {
859 Vc::cell(
860 self.primary
861 .iter()
862 .filter_map(|(_, item)| {
863 if let &ResolveResultItem::Source(a) = item {
864 Some(a)
865 } else {
866 None
867 }
868 })
869 .collect(),
870 )
871 }
872
873 #[turbo_tasks::function]
878 fn with_replaced_request_key(
879 &self,
880 old_request_key: RcStr,
881 request_key: RequestKey,
882 ) -> Result<Vc<Self>> {
883 let new_primary = self
884 .primary
885 .iter()
886 .filter_map(|(k, v)| {
887 let remaining = k.request.as_ref()?.strip_prefix(&*old_request_key)?;
888 Some((
889 RequestKey {
890 request: request_key
891 .request
892 .as_ref()
893 .map(|r| format!("{r}{remaining}").into()),
894 conditions: request_key.conditions.clone(),
895 },
896 v.clone(),
897 ))
898 })
899 .collect();
900 Ok(ResolveResult {
901 primary: new_primary,
902 affecting_sources: self.affecting_sources.clone(),
903 }
904 .cell())
905 }
906
907 #[turbo_tasks::function]
911 fn with_stripped_request_key_prefix(&self, prefix: RcStr) -> Result<Vc<Self>> {
912 let new_primary = self
913 .primary
914 .iter()
915 .filter_map(|(k, v)| {
916 let remaining = k.request.as_ref()?.strip_prefix(&*prefix)?;
917 Some((
918 RequestKey {
919 request: Some(remaining.into()),
920 conditions: k.conditions.clone(),
921 },
922 v.clone(),
923 ))
924 })
925 .collect();
926 Ok(ResolveResult {
927 primary: new_primary,
928 affecting_sources: self.affecting_sources.clone(),
929 }
930 .cell())
931 }
932
933 #[turbo_tasks::function]
938 async fn with_replaced_request_key_pattern(
939 &self,
940 old_request_key: Vc<Pattern>,
941 request_key: Vc<Pattern>,
942 ) -> Result<Vc<Self>> {
943 let old_request_key = &*old_request_key.await?;
944 let request_key = &*request_key.await?;
945
946 let new_primary = self
947 .primary
948 .iter()
949 .map(|(k, v)| {
950 (
951 RequestKey {
952 request: k
953 .request
954 .as_ref()
955 .and_then(|r| old_request_key.match_apply_template(r, request_key))
956 .map(Into::into),
957 conditions: k.conditions.clone(),
958 },
959 v.clone(),
960 )
961 })
962 .collect();
963 Ok(ResolveResult {
964 primary: new_primary,
965 affecting_sources: self.affecting_sources.clone(),
966 }
967 .cell())
968 }
969
970 #[turbo_tasks::function]
973 fn with_request(&self, request: RcStr) -> Vc<Self> {
974 let new_primary = self
975 .primary
976 .iter()
977 .map(|(k, v)| {
978 (
979 RequestKey {
980 request: Some(request.clone()),
981 conditions: k.conditions.clone(),
982 },
983 v.clone(),
984 )
985 })
986 .collect();
987 ResolveResult {
988 primary: new_primary,
989 affecting_sources: self.affecting_sources.clone(),
990 }
991 .cell()
992 }
993}
994
995#[turbo_tasks::value(transparent)]
996pub struct ResolveResultOption(Option<ResolvedVc<ResolveResult>>);
997
998#[turbo_tasks::value_impl]
999impl ResolveResultOption {
1000 #[turbo_tasks::function]
1001 pub fn some(result: ResolvedVc<ResolveResult>) -> Vc<Self> {
1002 ResolveResultOption(Some(result)).cell()
1003 }
1004
1005 #[turbo_tasks::function]
1006 pub fn none() -> Vc<Self> {
1007 ResolveResultOption(None).cell()
1008 }
1009}
1010
1011async fn exists(
1012 fs_path: &FileSystemPath,
1013 refs: Option<&mut Vec<ResolvedVc<Box<dyn Source>>>>,
1014) -> Result<Option<FileSystemPath>> {
1015 type_exists(fs_path, FileSystemEntryType::File, refs).await
1016}
1017
1018async fn dir_exists(
1019 fs_path: &FileSystemPath,
1020 refs: Option<&mut Vec<ResolvedVc<Box<dyn Source>>>>,
1021) -> Result<Option<FileSystemPath>> {
1022 type_exists(fs_path, FileSystemEntryType::Directory, refs).await
1023}
1024
1025async fn type_exists(
1026 fs_path: &FileSystemPath,
1027 ty: FileSystemEntryType,
1028 refs: Option<&mut Vec<ResolvedVc<Box<dyn Source>>>>,
1029) -> Result<Option<FileSystemPath>> {
1030 let path = realpath(fs_path, refs).await?;
1031 Ok(if *path.get_type().await? == ty {
1032 Some(path)
1033 } else {
1034 None
1035 })
1036}
1037
1038async fn realpath(
1039 fs_path: &FileSystemPath,
1040 refs: Option<&mut Vec<ResolvedVc<Box<dyn Source>>>>,
1041) -> Result<FileSystemPath> {
1042 let result = fs_path.realpath_with_links().await?;
1043 if let Some(refs) = refs {
1044 refs.extend(
1045 result
1046 .symlinks
1047 .iter()
1048 .map(|path| async move {
1049 Ok(ResolvedVc::upcast(
1050 FileSource::new(path.clone()).to_resolved().await?,
1051 ))
1052 })
1053 .try_join()
1054 .await?,
1055 );
1056 }
1057 match &result.path_result {
1058 Ok(path) => Ok(path.clone()),
1059 Err(e) => bail!(e.as_error_message(fs_path, &result)),
1060 }
1061}
1062
1063#[turbo_tasks::value(shared)]
1064enum ExportsFieldResult {
1065 Some(#[turbo_tasks(debug_ignore, trace_ignore)] ExportsField),
1066 None,
1067}
1068
1069#[turbo_tasks::function]
1072async fn exports_field(
1073 package_json_path: ResolvedVc<Box<dyn Source>>,
1074) -> Result<Vc<ExportsFieldResult>> {
1075 let read = read_package_json(*package_json_path).await?;
1076 let package_json = match &*read {
1077 Some(json) => json,
1078 None => return Ok(ExportsFieldResult::None.cell()),
1079 };
1080
1081 let Some(exports) = package_json.get("exports") else {
1082 return Ok(ExportsFieldResult::None.cell());
1083 };
1084 match exports.try_into() {
1085 Ok(exports) => Ok(ExportsFieldResult::Some(exports).cell()),
1086 Err(err) => {
1087 PackageJsonIssue {
1088 error_message: err.to_string().into(),
1089 source: IssueSource::from_source_only(package_json_path),
1091 }
1092 .resolved_cell()
1093 .emit();
1094 Ok(ExportsFieldResult::None.cell())
1095 }
1096 }
1097}
1098
1099#[turbo_tasks::value(shared)]
1100enum ImportsFieldResult {
1101 Some(
1102 #[turbo_tasks(debug_ignore, trace_ignore)] ImportsField,
1103 FileSystemPath,
1104 ),
1105 None,
1106}
1107
1108#[turbo_tasks::function]
1111async fn imports_field(lookup_path: FileSystemPath) -> Result<Vc<ImportsFieldResult>> {
1112 let package_json_context = find_context_file(lookup_path, package_json(), false).await?;
1114 let FindContextFileResult::Found(package_json_path, _refs) = &*package_json_context else {
1115 return Ok(ImportsFieldResult::None.cell());
1116 };
1117 let source = Vc::upcast::<Box<dyn Source>>(FileSource::new(package_json_path.clone()))
1118 .to_resolved()
1119 .await?;
1120
1121 let read = read_package_json(*source).await?;
1122 let package_json = match &*read {
1123 Some(json) => json,
1124 None => return Ok(ImportsFieldResult::None.cell()),
1125 };
1126
1127 let Some(imports) = package_json.get("imports") else {
1128 return Ok(ImportsFieldResult::None.cell());
1129 };
1130 match imports.try_into() {
1131 Ok(imports) => Ok(ImportsFieldResult::Some(imports, package_json_path.clone()).cell()),
1132 Err(err) => {
1133 PackageJsonIssue {
1134 error_message: err.to_string().into(),
1135 source: IssueSource::from_source_only(source),
1137 }
1138 .resolved_cell()
1139 .emit();
1140 Ok(ImportsFieldResult::None.cell())
1141 }
1142 }
1143}
1144
1145#[turbo_tasks::function]
1146pub fn package_json() -> Vc<Vec<RcStr>> {
1147 Vc::cell(vec![rcstr!("package.json")])
1148}
1149
1150#[turbo_tasks::value(shared)]
1151pub enum FindContextFileResult {
1152 Found(FileSystemPath, Vec<ResolvedVc<Box<dyn Source>>>),
1153 NotFound(Vec<ResolvedVc<Box<dyn Source>>>),
1154}
1155
1156#[turbo_tasks::function]
1157pub async fn find_context_file(
1158 lookup_path: FileSystemPath,
1159 names: Vc<Vec<RcStr>>,
1160 collect_affecting_sources: bool,
1161) -> Result<Vc<FindContextFileResult>> {
1162 let mut refs = Vec::new();
1163 for name in &*names.await? {
1164 let fs_path = lookup_path.join(name)?;
1165 if let Some(fs_path) = exists(
1166 &fs_path,
1167 if collect_affecting_sources {
1168 Some(&mut refs)
1169 } else {
1170 None
1171 },
1172 )
1173 .await?
1174 {
1175 return Ok(FindContextFileResult::Found(fs_path, refs).cell());
1176 }
1177 }
1178 if lookup_path.is_root() {
1179 return Ok(FindContextFileResult::NotFound(refs).cell());
1180 }
1181 if refs.is_empty() {
1182 Ok(find_context_file(
1184 lookup_path.parent(),
1185 names,
1186 collect_affecting_sources,
1187 ))
1188 } else {
1189 let parent_result =
1190 find_context_file(lookup_path.parent(), names, collect_affecting_sources).await?;
1191 Ok(match &*parent_result {
1192 FindContextFileResult::Found(p, r) => {
1193 refs.extend(r.iter().copied());
1194 FindContextFileResult::Found(p.clone(), refs)
1195 }
1196 FindContextFileResult::NotFound(r) => {
1197 refs.extend(r.iter().copied());
1198 FindContextFileResult::NotFound(refs)
1199 }
1200 }
1201 .cell())
1202 }
1203}
1204
1205#[turbo_tasks::function]
1208pub async fn find_context_file_or_package_key(
1209 lookup_path: FileSystemPath,
1210 names: Vc<Vec<RcStr>>,
1211 package_key: RcStr,
1212) -> Result<Vc<FindContextFileResult>> {
1213 let package_json_path = lookup_path.join("package.json")?;
1214 if let Some(package_json_path) = exists(&package_json_path, None).await?
1215 && let Some(json) =
1216 &*read_package_json(Vc::upcast(FileSource::new(package_json_path.clone()))).await?
1217 && json.get(&*package_key).is_some()
1218 {
1219 return Ok(FindContextFileResult::Found(package_json_path, Vec::new()).cell());
1220 }
1221 for name in &*names.await? {
1222 let fs_path = lookup_path.join(name)?;
1223 if let Some(fs_path) = exists(&fs_path, None).await? {
1224 return Ok(FindContextFileResult::Found(fs_path, Vec::new()).cell());
1225 }
1226 }
1227 if lookup_path.is_root() {
1228 return Ok(FindContextFileResult::NotFound(Vec::new()).cell());
1229 }
1230
1231 Ok(find_context_file(lookup_path.parent(), names, false))
1232}
1233
1234#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, Debug, NonLocalValue)]
1235enum FindPackageItem {
1236 PackageDirectory { name: RcStr, dir: FileSystemPath },
1237 PackageFile { name: RcStr, file: FileSystemPath },
1238}
1239
1240#[turbo_tasks::value]
1241#[derive(Debug)]
1242struct FindPackageResult {
1243 packages: Vec<FindPackageItem>,
1244 affecting_sources: Vec<ResolvedVc<Box<dyn Source>>>,
1246}
1247
1248#[turbo_tasks::function]
1249async fn find_package(
1250 lookup_path: FileSystemPath,
1251 package_name: Pattern,
1252 options: Vc<ResolveModulesOptions>,
1253 collect_affecting_sources: bool,
1254) -> Result<Vc<FindPackageResult>> {
1255 let mut packages = vec![];
1256 let mut affecting_sources = vec![];
1257 let options = options.await?;
1258 let package_name_cell = Pattern::new(package_name.clone());
1259
1260 fn get_package_name(basepath: &FileSystemPath, package_dir: &FileSystemPath) -> Result<RcStr> {
1261 if let Some(name) = basepath.get_path_to(package_dir) {
1262 Ok(name.into())
1263 } else {
1264 bail!("Package directory {package_dir} is not inside the lookup path {basepath}");
1265 }
1266 }
1267
1268 for resolve_modules in &options.modules {
1269 match resolve_modules {
1270 ResolveModules::Nested(root, names) => {
1271 let mut lookup_path = lookup_path.clone();
1272 let mut lookup_path_value = lookup_path.clone();
1273 while lookup_path_value.is_inside_ref(root) {
1274 for name in names.iter() {
1275 let fs_path = lookup_path.join(name)?;
1276 if let Some(fs_path) = dir_exists(
1277 &fs_path,
1278 collect_affecting_sources.then_some(&mut affecting_sources),
1279 )
1280 .await?
1281 {
1282 let matches =
1283 read_matches(fs_path.clone(), rcstr!(""), true, package_name_cell)
1284 .await?;
1285 for m in &*matches {
1286 if let PatternMatch::Directory(_, package_dir) = m {
1287 packages.push(FindPackageItem::PackageDirectory {
1288 name: get_package_name(&fs_path, package_dir)?,
1289 dir: realpath(
1290 package_dir,
1291 collect_affecting_sources
1292 .then_some(&mut affecting_sources),
1293 )
1294 .await?,
1295 });
1296 }
1297 }
1298 }
1299 }
1300 lookup_path = lookup_path.parent();
1301 let new_context_value = lookup_path.clone();
1302 if new_context_value == lookup_path_value {
1303 break;
1304 }
1305 lookup_path_value = new_context_value;
1306 }
1307 }
1308 ResolveModules::Path {
1309 dir,
1310 excluded_extensions,
1311 } => {
1312 let matches =
1313 read_matches(dir.clone(), rcstr!(""), true, package_name_cell).await?;
1314 for m in &*matches {
1315 match m {
1316 PatternMatch::Directory(_, package_dir) => {
1317 packages.push(FindPackageItem::PackageDirectory {
1318 name: get_package_name(dir, package_dir)?,
1319 dir: realpath(
1320 package_dir,
1321 collect_affecting_sources.then_some(&mut affecting_sources),
1322 )
1323 .await?,
1324 });
1325 }
1326 PatternMatch::File(_, package_file) => {
1327 packages.push(FindPackageItem::PackageFile {
1328 name: get_package_name(dir, package_file)?,
1329 file: realpath(
1330 package_file,
1331 collect_affecting_sources.then_some(&mut affecting_sources),
1332 )
1333 .await?,
1334 });
1335 }
1336 }
1337 }
1338
1339 let excluded_extensions = excluded_extensions.await?;
1340 let mut package_name_with_extensions = package_name.clone();
1341 package_name_with_extensions.push(Pattern::alternatives(
1342 options
1343 .extensions
1344 .iter()
1345 .filter(|ext| !excluded_extensions.contains(*ext))
1346 .cloned()
1347 .map(Pattern::from),
1348 ));
1349 let package_name_with_extensions = Pattern::new(package_name_with_extensions);
1350
1351 let matches =
1352 read_matches(dir.clone(), rcstr!(""), true, package_name_with_extensions)
1353 .await?;
1354 for m in matches {
1355 if let PatternMatch::File(_, package_file) = m {
1356 packages.push(FindPackageItem::PackageFile {
1357 name: get_package_name(dir, package_file)?,
1358 file: realpath(
1359 package_file,
1360 collect_affecting_sources.then_some(&mut affecting_sources),
1361 )
1362 .await?,
1363 });
1364 }
1365 }
1366 }
1367 }
1368 }
1369 Ok(FindPackageResult::cell(FindPackageResult {
1370 packages,
1371 affecting_sources,
1372 }))
1373}
1374
1375fn merge_results(results: Vec<Vc<ResolveResult>>) -> Vc<ResolveResult> {
1376 match results.len() {
1377 0 => *ResolveResult::unresolvable(),
1378 1 => results.into_iter().next().unwrap(),
1379 _ => ResolveResult::alternatives(results),
1380 }
1381}
1382
1383fn merge_results_with_affecting_sources(
1384 results: Vec<Vc<ResolveResult>>,
1385 affecting_sources: Vec<ResolvedVc<Box<dyn Source>>>,
1386) -> Vc<ResolveResult> {
1387 if affecting_sources.is_empty() {
1388 return merge_results(results);
1389 }
1390 match results.len() {
1391 0 => *ResolveResult::unresolvable_with_affecting_sources(affecting_sources),
1392 1 => results
1393 .into_iter()
1394 .next()
1395 .unwrap()
1396 .with_affecting_sources(affecting_sources.into_iter().map(|src| *src).collect()),
1397 _ => ResolveResult::alternatives_with_affecting_sources(
1398 results,
1399 affecting_sources.into_iter().map(|src| *src).collect(),
1400 ),
1401 }
1402}
1403
1404#[turbo_tasks::function]
1406pub async fn resolve_raw(
1407 lookup_dir: FileSystemPath,
1408 path: Vc<Pattern>,
1409 collect_affecting_sources: bool,
1410 force_in_lookup_dir: bool,
1411) -> Result<Vc<ResolveResult>> {
1412 async fn to_result(
1413 request: RcStr,
1414 path: &FileSystemPath,
1415 collect_affecting_sources: bool,
1416 ) -> Result<ResolveResult> {
1417 let result = &*path.realpath_with_links().await?;
1418 let path = match &result.path_result {
1419 Ok(path) => path,
1420 Err(e) => bail!(e.as_error_message(path, result)),
1421 };
1422 let request_key = RequestKey::new(request);
1423 let source = ResolvedVc::upcast(FileSource::new(path.clone()).to_resolved().await?);
1424 Ok(if collect_affecting_sources {
1425 ResolveResult::source_with_affecting_sources(
1426 request_key,
1427 source,
1428 result
1429 .symlinks
1430 .iter()
1431 .map(|symlink| {
1432 Vc::upcast::<Box<dyn Source>>(FileSource::new(symlink.clone()))
1433 .to_resolved()
1434 })
1435 .try_join()
1436 .await?,
1437 )
1438 } else {
1439 ResolveResult::source_with_key(request_key, source)
1440 })
1441 }
1442
1443 async fn collect_matches(
1444 matches: &[PatternMatch],
1445 collect_affecting_sources: bool,
1446 ) -> Result<Vec<Vc<ResolveResult>>> {
1447 Ok(matches
1448 .iter()
1449 .map(|m| async move {
1450 Ok(if let PatternMatch::File(request, path) = m {
1451 Some(to_result(request.clone(), path, collect_affecting_sources).await?)
1452 } else {
1453 None
1454 })
1455 })
1456 .try_flat_join()
1457 .await?
1458 .into_iter()
1461 .map(|res| res.cell())
1462 .collect())
1463 }
1464
1465 let mut results = Vec::new();
1466
1467 let pat = path.await?;
1468 if let Some(pat) = pat
1469 .filter_could_match("/ROOT/")
1470 .and_then(|pat| pat.filter_could_not_match("/ROOT/fsd8nz8og54z"))
1473 {
1474 let path = Pattern::new(pat);
1475 let matches = read_matches(
1476 lookup_dir.root().owned().await?,
1477 rcstr!("/ROOT/"),
1478 true,
1479 path,
1480 )
1481 .await?;
1482 results.extend(
1483 collect_matches(&matches, collect_affecting_sources)
1484 .await?
1485 .into_iter(),
1486 );
1487 }
1488
1489 {
1490 let matches =
1491 read_matches(lookup_dir.clone(), rcstr!(""), force_in_lookup_dir, path).await?;
1492
1493 results.extend(
1494 collect_matches(&matches, collect_affecting_sources)
1495 .await?
1496 .into_iter(),
1497 );
1498 }
1499
1500 Ok(merge_results(results))
1501}
1502
1503#[turbo_tasks::function]
1504pub async fn resolve(
1505 lookup_path: FileSystemPath,
1506 reference_type: ReferenceType,
1507 request: Vc<Request>,
1508 options: Vc<ResolveOptions>,
1509) -> Result<Vc<ResolveResult>> {
1510 resolve_inline(lookup_path, reference_type, request, options).await
1511}
1512
1513pub async fn resolve_inline(
1514 lookup_path: FileSystemPath,
1515 reference_type: ReferenceType,
1516 request: Vc<Request>,
1517 options: Vc<ResolveOptions>,
1518) -> Result<Vc<ResolveResult>> {
1519 let span = tracing::info_span!(
1520 "resolving",
1521 lookup_path = display(lookup_path.value_to_string().await?),
1522 name = tracing::field::Empty,
1523 reference_type = display(&reference_type),
1524 );
1525 if !span.is_disabled() {
1526 span.record("name", request.to_string().await?.as_str());
1528 }
1529
1530 async {
1531 let before_plugins_result = handle_before_resolve_plugins(
1532 lookup_path.clone(),
1533 reference_type.clone(),
1534 request,
1535 options,
1536 )
1537 .await?;
1538
1539 let raw_result = match before_plugins_result {
1540 Some(result) => result,
1541 None => {
1542 resolve_internal(lookup_path.clone(), request, options)
1543 .resolve()
1544 .await?
1545 }
1546 };
1547
1548 let result =
1549 handle_after_resolve_plugins(lookup_path, reference_type, request, options, raw_result)
1550 .await?;
1551 Ok(result)
1552 }
1553 .instrument(span)
1554 .await
1555}
1556
1557#[turbo_tasks::function]
1558pub async fn url_resolve(
1559 origin: Vc<Box<dyn ResolveOrigin>>,
1560 request: Vc<Request>,
1561 reference_type: ReferenceType,
1562 issue_source: Option<IssueSource>,
1563 is_optional: bool,
1564) -> Result<Vc<ModuleResolveResult>> {
1565 let resolve_options = origin.resolve_options(reference_type.clone()).await?;
1566 let rel_request = request.as_relative();
1567 let rel_result = resolve(
1568 origin.origin_path().await?.parent(),
1569 reference_type.clone(),
1570 rel_request,
1571 resolve_options,
1572 );
1573 let result = if *rel_result.is_unresolvable().await? && rel_request.resolve().await? != request
1574 {
1575 let result = resolve(
1576 origin.origin_path().await?.parent(),
1577 reference_type.clone(),
1578 request,
1579 resolve_options,
1580 );
1581 if resolve_options.await?.collect_affecting_sources {
1582 result.with_affecting_sources(
1583 rel_result
1584 .await?
1585 .get_affecting_sources()
1586 .map(|src| *src)
1587 .collect(),
1588 )
1589 } else {
1590 result
1591 }
1592 } else {
1593 rel_result
1594 };
1595 let result = origin
1596 .asset_context()
1597 .process_resolve_result(result, reference_type.clone());
1598 handle_resolve_error(
1599 result,
1600 reference_type,
1601 origin.origin_path().owned().await?,
1602 request,
1603 resolve_options,
1604 is_optional,
1605 issue_source,
1606 )
1607 .await
1608}
1609
1610#[tracing::instrument(level = "trace", skip_all)]
1611async fn handle_before_resolve_plugins(
1612 lookup_path: FileSystemPath,
1613 reference_type: ReferenceType,
1614 request: Vc<Request>,
1615 options: Vc<ResolveOptions>,
1616) -> Result<Option<Vc<ResolveResult>>> {
1617 for plugin in &options.await?.before_resolve_plugins {
1618 let condition = plugin.before_resolve_condition().resolve().await?;
1619 if !*condition.matches(request).await? {
1620 continue;
1621 }
1622
1623 if let Some(result) = *plugin
1624 .before_resolve(lookup_path.clone(), reference_type.clone(), request)
1625 .await?
1626 {
1627 return Ok(Some(*result));
1628 }
1629 }
1630 Ok(None)
1631}
1632
1633#[tracing::instrument(level = "trace", skip_all)]
1634async fn handle_after_resolve_plugins(
1635 lookup_path: FileSystemPath,
1636 reference_type: ReferenceType,
1637 request: Vc<Request>,
1638 options: Vc<ResolveOptions>,
1639 result: Vc<ResolveResult>,
1640) -> Result<Vc<ResolveResult>> {
1641 async fn apply_plugins_to_path(
1642 path: FileSystemPath,
1643 lookup_path: FileSystemPath,
1644 reference_type: ReferenceType,
1645 request: Vc<Request>,
1646 options: Vc<ResolveOptions>,
1647 ) -> Result<Option<Vc<ResolveResult>>> {
1648 for plugin in &options.await?.after_resolve_plugins {
1649 let after_resolve_condition = plugin.after_resolve_condition().resolve().await?;
1650 if *after_resolve_condition.matches(path.clone()).await?
1651 && let Some(result) = *plugin
1652 .after_resolve(
1653 path.clone(),
1654 lookup_path.clone(),
1655 reference_type.clone(),
1656 request,
1657 )
1658 .await?
1659 {
1660 return Ok(Some(*result));
1661 }
1662 }
1663 Ok(None)
1664 }
1665
1666 let mut changed = false;
1667 let result_value = result.await?;
1668
1669 let mut new_primary = FxIndexMap::default();
1670 let mut new_affecting_sources = Vec::new();
1671
1672 for (key, primary) in result_value.primary.iter() {
1673 if let &ResolveResultItem::Source(source) = primary {
1674 let path = source.ident().path().owned().await?;
1675 if let Some(new_result) = apply_plugins_to_path(
1676 path.clone(),
1677 lookup_path.clone(),
1678 reference_type.clone(),
1679 request,
1680 options,
1681 )
1682 .await?
1683 {
1684 let new_result = new_result.await?;
1685 changed = true;
1686 new_primary.extend(
1687 new_result
1688 .primary
1689 .iter()
1690 .map(|(_, item)| (key.clone(), item.clone())),
1691 );
1692 new_affecting_sources.extend(new_result.affecting_sources.iter().copied());
1693 } else {
1694 new_primary.insert(key.clone(), primary.clone());
1695 }
1696 } else {
1697 new_primary.insert(key.clone(), primary.clone());
1698 }
1699 }
1700
1701 if !changed {
1702 return Ok(result);
1703 }
1704
1705 let mut affecting_sources = result_value.affecting_sources.to_vec();
1706 affecting_sources.append(&mut new_affecting_sources);
1707
1708 Ok(ResolveResult {
1709 primary: new_primary.into_iter().collect(),
1710 affecting_sources: affecting_sources.into_boxed_slice(),
1711 }
1712 .cell())
1713}
1714
1715#[turbo_tasks::function]
1716async fn resolve_internal(
1717 lookup_path: FileSystemPath,
1718 request: ResolvedVc<Request>,
1719 options: ResolvedVc<ResolveOptions>,
1720) -> Result<Vc<ResolveResult>> {
1721 resolve_internal_inline(lookup_path.clone(), *request, *options).await
1722}
1723
1724async fn resolve_internal_inline(
1725 lookup_path: FileSystemPath,
1726 request: Vc<Request>,
1727 options: Vc<ResolveOptions>,
1728) -> Result<Vc<ResolveResult>> {
1729 let span = tracing::info_span!(
1730 "internal resolving",
1731 lookup_path = display(lookup_path.value_to_string().await?),
1732 name = tracing::field::Empty
1733 );
1734 if !span.is_disabled() {
1735 span.record("name", request.to_string().await?.as_str());
1737 }
1738
1739 async move {
1740 let options_value: &ResolveOptions = &*options.await?;
1741
1742 let request_value = request.await?;
1743
1744 let mut has_alias = false;
1746 if let Some(import_map) = &options_value.import_map {
1747 let request_parts = match &*request_value {
1748 Request::Alternatives { requests } => requests.as_slice(),
1749 _ => &[request.to_resolved().await?],
1750 };
1751 for &request in request_parts {
1752 let result = import_map
1753 .await?
1754 .lookup(lookup_path.clone(), *request)
1755 .await?;
1756 if !matches!(result, ImportMapResult::NoEntry) {
1757 has_alias = true;
1758 let resolved_result = resolve_import_map_result(
1759 &result,
1760 lookup_path.clone(),
1761 lookup_path.clone(),
1762 *request,
1763 options,
1764 request.query().owned().await?,
1765 )
1766 .await?;
1767 if let Some(result) = resolved_result
1774 && !*result.is_unresolvable().await?
1775 {
1776 return Ok(result);
1777 }
1778 }
1779 }
1780 }
1781
1782 let result = match &*request_value {
1783 Request::Dynamic => *ResolveResult::unresolvable(),
1784 Request::Alternatives { requests } => {
1785 let results = requests
1786 .iter()
1787 .map(|req| async {
1788 resolve_internal_inline(lookup_path.clone(), **req, options).await
1789 })
1790 .try_join()
1791 .await?;
1792
1793 merge_results(results)
1794 }
1795 Request::Raw {
1796 path,
1797 query,
1798 force_in_lookup_dir,
1799 fragment,
1800 } => {
1801 let mut results = Vec::new();
1802 let matches = read_matches(
1803 lookup_path.clone(),
1804 rcstr!(""),
1805 *force_in_lookup_dir,
1806 Pattern::new(path.clone()).resolve().await?,
1807 )
1808 .await?;
1809
1810 for m in matches.iter() {
1811 match m {
1812 PatternMatch::File(matched_pattern, path) => {
1813 results.push(
1814 resolved(
1815 RequestKey::new(matched_pattern.clone()),
1816 path.clone(),
1817 lookup_path.clone(),
1818 request,
1819 options_value,
1820 options,
1821 query.clone(),
1822 fragment.clone(),
1823 )
1824 .await?,
1825 );
1826 }
1827 PatternMatch::Directory(matched_pattern, path) => {
1828 results.push(
1829 resolve_into_folder(path.clone(), options)
1830 .with_request(matched_pattern.clone()),
1831 );
1832 }
1833 }
1834 }
1835
1836 merge_results(results)
1837 }
1838 Request::Relative {
1839 path,
1840 query,
1841 force_in_lookup_dir,
1842 fragment,
1843 } => {
1844 resolve_relative_request(
1845 lookup_path.clone(),
1846 request,
1847 options,
1848 options_value,
1849 path,
1850 query.clone(),
1851 *force_in_lookup_dir,
1852 fragment.clone(),
1853 )
1854 .await?
1855 }
1856 Request::Module {
1857 module,
1858 path,
1859 query,
1860 fragment,
1861 } => {
1862 resolve_module_request(
1863 lookup_path.clone(),
1864 request,
1865 options,
1866 options_value,
1867 module,
1868 path,
1869 query.clone(),
1870 fragment.clone(),
1871 )
1872 .await?
1873 }
1874 Request::ServerRelative {
1875 path,
1876 query,
1877 fragment,
1878 } => {
1879 let mut new_pat = path.clone();
1880 new_pat.push_front(rcstr!(".").into());
1881 let relative = Request::relative(new_pat, query.clone(), fragment.clone(), true);
1882
1883 if !has_alias {
1884 ResolvingIssue {
1885 severity: error_severity(options).await?,
1886 request_type: "server relative import: not implemented yet".to_string(),
1887 request: relative.to_resolved().await?,
1888 file_path: lookup_path.clone(),
1889 resolve_options: options.to_resolved().await?,
1890 error_message: Some(
1891 "server relative imports are not implemented yet. Please try an \
1892 import relative to the file you are importing from."
1893 .to_string(),
1894 ),
1895 source: None,
1896 }
1897 .resolved_cell()
1898 .emit();
1899 }
1900
1901 Box::pin(resolve_internal_inline(
1902 lookup_path.root().owned().await?,
1903 relative,
1904 options,
1905 ))
1906 .await?
1907 }
1908 Request::Windows {
1909 path: _,
1910 query: _,
1911 fragment: _,
1912 } => {
1913 if !has_alias {
1914 ResolvingIssue {
1915 severity: error_severity(options).await?,
1916 request_type: "windows import: not implemented yet".to_string(),
1917 request: request.to_resolved().await?,
1918 file_path: lookup_path.clone(),
1919 resolve_options: options.to_resolved().await?,
1920 error_message: Some("windows imports are not implemented yet".to_string()),
1921 source: None,
1922 }
1923 .resolved_cell()
1924 .emit();
1925 }
1926
1927 *ResolveResult::unresolvable()
1928 }
1929 Request::Empty => *ResolveResult::unresolvable(),
1930 Request::PackageInternal { path } => {
1931 let (conditions, unspecified_conditions) = options_value
1932 .in_package
1933 .iter()
1934 .find_map(|item| match item {
1935 ResolveInPackage::ImportsField {
1936 conditions,
1937 unspecified_conditions,
1938 } => Some((Cow::Borrowed(conditions), *unspecified_conditions)),
1939 _ => None,
1940 })
1941 .unwrap_or_else(|| (Default::default(), ConditionValue::Unset));
1942 resolve_package_internal_with_imports_field(
1943 lookup_path.clone(),
1944 request,
1945 options,
1946 path,
1947 &conditions,
1948 &unspecified_conditions,
1949 )
1950 .await?
1951 }
1952 Request::DataUri {
1953 media_type,
1954 encoding,
1955 data,
1956 } => {
1957 let uri: RcStr = stringify_data_uri(media_type, encoding, *data)
1959 .await?
1960 .into();
1961 if options_value.parse_data_uris {
1962 *ResolveResult::primary_with_key(
1963 RequestKey::new(uri.clone()),
1964 ResolveResultItem::Source(ResolvedVc::upcast(
1965 DataUriSource::new(
1966 media_type.clone(),
1967 encoding.clone(),
1968 **data,
1969 lookup_path.clone(),
1970 )
1971 .to_resolved()
1972 .await?,
1973 )),
1974 )
1975 } else {
1976 *ResolveResult::primary_with_key(
1977 RequestKey::new(uri.clone()),
1978 ResolveResultItem::External {
1979 name: uri,
1980 ty: ExternalType::Url,
1981 traced: ExternalTraced::Untraced,
1982 },
1983 )
1984 }
1985 }
1986 Request::Uri {
1987 protocol,
1988 remainder,
1989 query: _,
1990 fragment: _,
1991 } => {
1992 let uri: RcStr = format!("{protocol}{remainder}").into();
1993 *ResolveResult::primary_with_key(
1994 RequestKey::new(uri.clone()),
1995 ResolveResultItem::External {
1996 name: uri,
1997 ty: ExternalType::Url,
1998 traced: ExternalTraced::Untraced,
1999 },
2000 )
2001 }
2002 Request::Unknown { path } => {
2003 if !has_alias {
2004 ResolvingIssue {
2005 severity: error_severity(options).await?,
2006 request_type: format!("unknown import: `{}`", path.describe_as_string()),
2007 request: request.to_resolved().await?,
2008 file_path: lookup_path.clone(),
2009 resolve_options: options.to_resolved().await?,
2010 error_message: None,
2011 source: None,
2012 }
2013 .resolved_cell()
2014 .emit();
2015 }
2016 *ResolveResult::unresolvable()
2017 }
2018 };
2019
2020 if !matches!(*request_value, Request::Alternatives { .. }) {
2023 if let Some(import_map) = &options_value.fallback_import_map
2025 && *result.is_unresolvable().await?
2026 {
2027 let result = import_map
2028 .await?
2029 .lookup(lookup_path.clone(), request)
2030 .await?;
2031 let resolved_result = resolve_import_map_result(
2032 &result,
2033 lookup_path.clone(),
2034 lookup_path.clone(),
2035 request,
2036 options,
2037 request.query().owned().await?,
2038 )
2039 .await?;
2040 if let Some(result) = resolved_result
2041 && !*result.is_unresolvable().await?
2042 {
2043 return Ok(result);
2044 }
2045 }
2046 }
2047
2048 Ok(result)
2049 }
2050 .instrument(span)
2051 .await
2052}
2053
2054#[turbo_tasks::function]
2055async fn resolve_into_folder(
2056 package_path: FileSystemPath,
2057 options: Vc<ResolveOptions>,
2058) -> Result<Vc<ResolveResult>> {
2059 let options_value = options.await?;
2060
2061 let mut affecting_sources = vec![];
2062 if let Some(package_json_path) = exists(
2063 &package_path.join("package.json")?,
2064 if options_value.collect_affecting_sources {
2065 Some(&mut affecting_sources)
2066 } else {
2067 None
2068 },
2069 )
2070 .await?
2071 {
2072 for resolve_into_package in options_value.into_package.iter() {
2073 match resolve_into_package {
2074 ResolveIntoPackage::MainField { field: name } => {
2075 if let Some(package_json) =
2076 &*read_package_json(Vc::upcast(FileSource::new(package_json_path.clone())))
2077 .await?
2078 && let Some(field_value) = package_json[name.as_str()].as_str()
2079 {
2080 let normalized_request = RcStr::from(normalize_request(field_value));
2081 if normalized_request.is_empty()
2082 || &*normalized_request == "."
2083 || &*normalized_request == "./"
2084 {
2085 continue;
2086 }
2087 let request = Request::parse_string(normalized_request);
2088
2089 let options = if options_value.fully_specified {
2091 options.with_fully_specified(false).resolve().await?
2092 } else {
2093 options
2094 };
2095 let result =
2096 &*resolve_internal_inline(package_path.clone(), request, options)
2097 .await?
2098 .await?;
2099 if !result.is_unresolvable_ref() {
2102 let mut result: ResolveResultBuilder =
2103 result.with_request_ref(rcstr!(".")).into();
2104 if options_value.collect_affecting_sources {
2105 result.affecting_sources.push(ResolvedVc::upcast(
2106 FileSource::new(package_json_path).to_resolved().await?,
2107 ));
2108 result.affecting_sources.extend(affecting_sources);
2109 }
2110 return Ok(ResolveResult::from(result).cell());
2111 }
2112 };
2113 }
2114 ResolveIntoPackage::ExportsField { .. } => {}
2115 }
2116 }
2117 }
2118
2119 if options_value.fully_specified {
2120 return Ok(*ResolveResult::unresolvable_with_affecting_sources(
2121 affecting_sources,
2122 ));
2123 }
2124
2125 let pattern = match &options_value.default_files[..] {
2127 [] => {
2128 return Ok(*ResolveResult::unresolvable_with_affecting_sources(
2129 affecting_sources,
2130 ));
2131 }
2132 [file] => Pattern::Constant(format!("./{file}").into()),
2133 files => Pattern::Alternatives(
2134 files
2135 .iter()
2136 .map(|file| Pattern::Constant(format!("./{file}").into()))
2137 .collect(),
2138 ),
2139 };
2140
2141 let request = Request::parse(pattern);
2142 let result = resolve_internal_inline(package_path.clone(), request, options)
2143 .await?
2144 .with_request(rcstr!("."));
2145
2146 Ok(if !affecting_sources.is_empty() {
2147 result.with_affecting_sources(ResolvedVc::deref_vec(affecting_sources))
2148 } else {
2149 result
2150 })
2151}
2152
2153#[tracing::instrument(level = Level::TRACE, skip_all)]
2154async fn resolve_relative_request(
2155 lookup_path: FileSystemPath,
2156 request: Vc<Request>,
2157 options: Vc<ResolveOptions>,
2158 options_value: &ResolveOptions,
2159 path_pattern: &Pattern,
2160 query: RcStr,
2161 force_in_lookup_dir: bool,
2162 fragment: RcStr,
2163) -> Result<Vc<ResolveResult>> {
2164 let lookup_path_ref = lookup_path.clone();
2166 if let Some(result) = apply_in_package(
2167 lookup_path.clone(),
2168 options,
2169 options_value,
2170 |package_path| {
2171 let request = path_pattern.as_constant_string()?;
2172 let prefix_path = package_path.get_path_to(&lookup_path_ref)?;
2173 let request = normalize_request(&format!("./{prefix_path}/{request}"));
2174 Some(request.into())
2175 },
2176 query.clone(),
2177 fragment.clone(),
2178 )
2179 .await?
2180 {
2181 return Ok(result);
2182 }
2183
2184 let mut new_path = path_pattern.clone();
2185
2186 if !fragment.is_empty() {
2187 new_path.push(Pattern::Alternatives(vec![
2188 Pattern::Constant(RcStr::default()),
2189 Pattern::Constant(fragment.clone()),
2190 ]));
2191 }
2192
2193 if !options_value.fully_specified {
2194 new_path.push(Pattern::Alternatives(
2197 once(Pattern::Constant(RcStr::default()))
2198 .chain(
2199 options_value
2200 .extensions
2201 .iter()
2202 .map(|ext| Pattern::Constant(ext.clone())),
2203 )
2204 .collect(),
2205 ));
2206 new_path.normalize();
2207 };
2208
2209 if options_value.enable_typescript_with_output_extension {
2210 new_path.replace_final_constants(&|c: &RcStr| -> Option<Pattern> {
2211 let (base, replacement) = match c.rsplit_once(".") {
2212 Some((base, "js")) => (
2213 base,
2214 vec![
2215 Pattern::Constant(rcstr!(".ts")),
2216 Pattern::Constant(rcstr!(".tsx")),
2217 Pattern::Constant(rcstr!(".js")),
2218 ],
2219 ),
2220 Some((base, "mjs")) => (
2221 base,
2222 vec![
2223 Pattern::Constant(rcstr!(".mts")),
2224 Pattern::Constant(rcstr!(".mjs")),
2225 ],
2226 ),
2227 Some((base, "cjs")) => (
2228 base,
2229 vec![
2230 Pattern::Constant(rcstr!(".cts")),
2231 Pattern::Constant(rcstr!(".cjs")),
2232 ],
2233 ),
2234 _ => {
2235 return None;
2236 }
2237 };
2238 if base.is_empty() {
2239 Some(Pattern::Alternatives(replacement))
2240 } else {
2241 Some(Pattern::Concatenation(vec![
2242 Pattern::Constant(base.into()),
2243 Pattern::Alternatives(replacement),
2244 ]))
2245 }
2246 });
2247 new_path.normalize();
2248 }
2249
2250 let mut results = Vec::new();
2251 let matches = read_matches(
2252 lookup_path.clone(),
2253 rcstr!(""),
2254 force_in_lookup_dir,
2255 Pattern::new(new_path).resolve().await?,
2256 )
2257 .await?;
2258
2259 for m in matches.iter() {
2260 if let PatternMatch::File(matched_pattern, path) = m {
2261 let mut pushed = false;
2262 if !options_value.fully_specified {
2263 for ext in options_value.extensions.iter() {
2264 let Some(matched_pattern) = matched_pattern.strip_suffix(&**ext) else {
2265 continue;
2266 };
2267
2268 if !fragment.is_empty() {
2269 if let Some(matched_pattern) = matched_pattern
2272 .strip_suffix(fragment.as_str())
2273 .and_then(|s| s.strip_suffix('#'))
2274 {
2275 results.push(
2276 resolved(
2277 RequestKey::new(matched_pattern.into()),
2278 path.clone(),
2279 lookup_path.clone(),
2280 request,
2281 options_value,
2282 options,
2283 query.clone(),
2284 RcStr::default(),
2285 )
2286 .await?,
2287 );
2288 pushed = true;
2289 }
2290 }
2291 if !pushed && path_pattern.is_match(matched_pattern) {
2292 results.push(
2293 resolved(
2294 RequestKey::new(matched_pattern.into()),
2295 path.clone(),
2296 lookup_path.clone(),
2297 request,
2298 options_value,
2299 options,
2300 query.clone(),
2301 fragment.clone(),
2302 )
2303 .await?,
2304 );
2305 pushed = true;
2306 }
2307 }
2308 }
2309 if !fragment.is_empty() {
2310 if let Some(matched_pattern) = matched_pattern.strip_suffix(fragment.as_str()) {
2312 results.push(
2313 resolved(
2314 RequestKey::new(matched_pattern.into()),
2315 path.clone(),
2316 lookup_path.clone(),
2317 request,
2318 options_value,
2319 options,
2320 query.clone(),
2321 RcStr::default(),
2322 )
2323 .await?,
2324 );
2325 pushed = true;
2326 }
2327 }
2328
2329 if !pushed || path_pattern.is_match(matched_pattern) {
2330 results.push(
2331 resolved(
2332 RequestKey::new(matched_pattern.clone()),
2333 path.clone(),
2334 lookup_path.clone(),
2335 request,
2336 options_value,
2337 options,
2338 query.clone(),
2339 fragment.clone(),
2340 )
2341 .await?,
2342 );
2343 }
2344 }
2345 }
2346 for m in matches.iter() {
2348 if let PatternMatch::Directory(matched_pattern, path) = m {
2349 results.push(
2350 resolve_into_folder(path.clone(), options).with_request(matched_pattern.clone()),
2351 );
2352 }
2353 }
2354
2355 Ok(merge_results(results))
2356}
2357
2358#[tracing::instrument(level = Level::TRACE, skip_all)]
2359async fn apply_in_package(
2360 lookup_path: FileSystemPath,
2361 options: Vc<ResolveOptions>,
2362 options_value: &ResolveOptions,
2363 get_request: impl Fn(&FileSystemPath) -> Option<RcStr>,
2364 query: RcStr,
2365 fragment: RcStr,
2366) -> Result<Option<Vc<ResolveResult>>> {
2367 for in_package in options_value.in_package.iter() {
2369 let ResolveInPackage::AliasField(field) = in_package else {
2373 continue;
2374 };
2375
2376 let FindContextFileResult::Found(package_json_path, refs) = &*find_context_file(
2377 lookup_path.clone(),
2378 package_json().resolve().await?,
2379 options_value.collect_affecting_sources,
2380 )
2381 .await?
2382 else {
2383 continue;
2384 };
2385
2386 let read =
2387 read_package_json(Vc::upcast(FileSource::new(package_json_path.clone()))).await?;
2388 let Some(package_json) = &*read else {
2389 continue;
2390 };
2391
2392 let Some(field_value) = package_json[field.as_str()].as_object() else {
2393 continue;
2394 };
2395
2396 let package_path = package_json_path.parent();
2397
2398 let Some(request) = get_request(&package_path) else {
2399 continue;
2400 };
2401
2402 let value = if let Some(value) = field_value.get(&*request) {
2403 value
2404 } else if let Some(request) = request.strip_prefix("./") {
2405 let Some(value) = field_value.get(request) else {
2406 continue;
2407 };
2408 value
2409 } else {
2410 continue;
2411 };
2412
2413 let refs = refs.clone();
2414 let request_key = RequestKey::new(request.clone());
2415
2416 if value.as_bool() == Some(false) {
2417 return Ok(Some(*ResolveResult::primary_with_affecting_sources(
2418 request_key,
2419 ResolveResultItem::Ignore,
2420 refs,
2421 )));
2422 }
2423
2424 if let Some(value) = value.as_str() {
2425 if value == &*request {
2426 return Ok(None);
2428 }
2429 let mut result = resolve_internal(
2430 package_path,
2431 Request::parse(Pattern::Constant(value.into()))
2432 .with_query(query.clone())
2433 .with_fragment(fragment.clone()),
2434 options,
2435 )
2436 .with_replaced_request_key(value.into(), request_key);
2437 if options_value.collect_affecting_sources && !refs.is_empty() {
2438 result = result.with_affecting_sources(refs.into_iter().map(|src| *src).collect());
2439 }
2440 return Ok(Some(result));
2441 }
2442
2443 ResolvingIssue {
2444 severity: error_severity(options).await?,
2445 file_path: package_json_path.clone(),
2446 request_type: format!("alias field ({field})"),
2447 request: Request::parse(Pattern::Constant(request))
2448 .to_resolved()
2449 .await?,
2450 resolve_options: options.to_resolved().await?,
2451 error_message: Some(format!("invalid alias field value: {value}")),
2452 source: None,
2453 }
2454 .resolved_cell()
2455 .emit();
2456
2457 return Ok(Some(*ResolveResult::unresolvable_with_affecting_sources(
2458 refs,
2459 )));
2460 }
2461 Ok(None)
2462}
2463
2464#[turbo_tasks::value]
2465enum FindSelfReferencePackageResult {
2466 Found {
2467 name: String,
2468 package_path: FileSystemPath,
2469 },
2470 NotFound,
2471}
2472
2473#[turbo_tasks::function]
2474async fn find_self_reference(
2477 lookup_path: FileSystemPath,
2478) -> Result<Vc<FindSelfReferencePackageResult>> {
2479 let package_json_context = find_context_file(lookup_path, package_json(), false).await?;
2480 if let FindContextFileResult::Found(package_json_path, _refs) = &*package_json_context {
2481 let read =
2482 read_package_json(Vc::upcast(FileSource::new(package_json_path.clone()))).await?;
2483 if let Some(json) = &*read
2484 && json.get("exports").is_some()
2485 && let Some(name) = json["name"].as_str()
2486 {
2487 return Ok(FindSelfReferencePackageResult::Found {
2488 name: name.to_string(),
2489 package_path: package_json_path.parent(),
2490 }
2491 .cell());
2492 }
2493 }
2494 Ok(FindSelfReferencePackageResult::NotFound.cell())
2495}
2496
2497#[tracing::instrument(level = Level::TRACE, skip_all)]
2498async fn resolve_module_request(
2499 lookup_path: FileSystemPath,
2500 request: Vc<Request>,
2501 options: Vc<ResolveOptions>,
2502 options_value: &ResolveOptions,
2503 module: &Pattern,
2504 path: &Pattern,
2505 query: RcStr,
2506 fragment: RcStr,
2507) -> Result<Vc<ResolveResult>> {
2508 if let Some(result) = apply_in_package(
2510 lookup_path.clone(),
2511 options,
2512 options_value,
2513 |_| {
2514 let full_pattern = Pattern::concat([module.clone(), path.clone()]);
2515 full_pattern.as_constant_string().cloned()
2516 },
2517 query.clone(),
2518 fragment.clone(),
2519 )
2520 .await?
2521 {
2522 return Ok(result);
2523 }
2524
2525 let mut results = vec![];
2526
2527 if let FindSelfReferencePackageResult::Found { name, package_path } =
2531 &*find_self_reference(lookup_path.clone()).await?
2532 && module.is_match(name)
2533 {
2534 let result = resolve_into_package(
2535 path.clone(),
2536 package_path.clone(),
2537 query.clone(),
2538 fragment.clone(),
2539 options,
2540 );
2541 if !(*result.is_unresolvable().await?) {
2542 return Ok(result);
2543 }
2544 }
2545
2546 let result = find_package(
2547 lookup_path.clone(),
2548 module.clone(),
2549 resolve_modules_options(options).resolve().await?,
2550 options_value.collect_affecting_sources,
2551 )
2552 .await?;
2553
2554 if result.packages.is_empty() {
2555 return Ok(*ResolveResult::unresolvable_with_affecting_sources(
2556 result.affecting_sources.clone(),
2557 ));
2558 }
2559
2560 for item in &result.packages {
2566 match item {
2567 FindPackageItem::PackageDirectory { name, dir } => {
2568 results.push(
2569 resolve_into_package(
2570 path.clone(),
2571 dir.clone(),
2572 query.clone(),
2573 fragment.clone(),
2574 options,
2575 )
2576 .with_replaced_request_key(rcstr!("."), RequestKey::new(name.clone())),
2577 );
2578 }
2579 FindPackageItem::PackageFile { name, file } => {
2580 if path.is_match("") {
2581 let resolved = resolved(
2582 RequestKey::new(rcstr!(".")),
2583 file.clone(),
2584 lookup_path.clone(),
2585 request,
2586 options_value,
2587 options,
2588 query.clone(),
2589 fragment.clone(),
2590 )
2591 .await?
2592 .with_replaced_request_key(rcstr!("."), RequestKey::new(name.clone()));
2593 results.push(resolved)
2594 }
2595 }
2596 }
2597 }
2598
2599 let module_result =
2600 merge_results_with_affecting_sources(results, result.affecting_sources.clone());
2601
2602 if options_value.prefer_relative {
2603 let mut module_prefixed = module.clone();
2604 module_prefixed.push_front(rcstr!("./").into());
2605 let pattern = Pattern::concat([module_prefixed.clone(), rcstr!("/").into(), path.clone()]);
2606 let relative = Request::relative(pattern, query, fragment, true)
2607 .to_resolved()
2608 .await?;
2609 let relative_result = Box::pin(resolve_internal_inline(
2610 lookup_path.clone(),
2611 *relative,
2612 options,
2613 ))
2614 .await?;
2615 let relative_result = relative_result.with_stripped_request_key_prefix(rcstr!("./"));
2616
2617 Ok(merge_results(vec![relative_result, module_result]))
2618 } else {
2619 Ok(module_result)
2620 }
2621}
2622
2623#[turbo_tasks::function]
2624async fn resolve_into_package(
2625 path: Pattern,
2626 package_path: FileSystemPath,
2627 query: RcStr,
2628 fragment: RcStr,
2629 options: ResolvedVc<ResolveOptions>,
2630) -> Result<Vc<ResolveResult>> {
2631 let options_value = options.await?;
2632 let mut results = Vec::new();
2633
2634 let is_root_match = path.is_match("") || path.is_match("/");
2635 let could_match_others = path.could_match_others("");
2636
2637 let mut export_path_request = path.clone();
2638 export_path_request.push_front(rcstr!(".").into());
2639 for resolve_into_package in options_value.into_package.iter() {
2640 match resolve_into_package {
2641 ResolveIntoPackage::MainField { .. } => {}
2643 ResolveIntoPackage::ExportsField {
2644 conditions,
2645 unspecified_conditions,
2646 } => {
2647 let package_json_path = package_path.join("package.json")?;
2648 let ExportsFieldResult::Some(exports_field) =
2649 &*exports_field(Vc::upcast(FileSource::new(package_json_path.clone()))).await?
2650 else {
2651 continue;
2652 };
2653
2654 results.push(
2655 handle_exports_imports_field(
2656 package_path.clone(),
2657 package_json_path,
2658 *options,
2659 exports_field,
2660 export_path_request.clone(),
2661 conditions,
2662 unspecified_conditions,
2663 query,
2664 )
2665 .await?,
2666 );
2667
2668 return Ok(merge_results(results));
2671 }
2672 }
2673 }
2674
2675 if is_root_match {
2677 results.push(resolve_into_folder(
2678 package_path.clone(),
2679 options.with_fully_specified(false),
2680 ));
2681 }
2682
2683 if could_match_others {
2684 let mut new_pat = path.clone();
2685 new_pat.push_front(rcstr!(".").into());
2686
2687 let relative = Request::relative(new_pat, query, fragment, true)
2688 .to_resolved()
2689 .await?;
2690 results.push(resolve_internal_inline(package_path.clone(), *relative, *options).await?);
2691 }
2692
2693 Ok(merge_results(results))
2694}
2695
2696#[tracing::instrument(level = Level::TRACE, skip_all)]
2697async fn resolve_import_map_result(
2698 result: &ImportMapResult,
2699 lookup_path: FileSystemPath,
2700 original_lookup_path: FileSystemPath,
2701 original_request: Vc<Request>,
2702 options: Vc<ResolveOptions>,
2703 query: RcStr,
2704) -> Result<Option<Vc<ResolveResult>>> {
2705 Ok(match result {
2706 ImportMapResult::Result(result) => Some(**result),
2707 ImportMapResult::Alias(request, alias_lookup_path) => {
2708 let request = **request;
2709 let lookup_path = match alias_lookup_path {
2710 Some(path) => path.clone(),
2711 None => lookup_path,
2712 };
2713 if request == original_request && lookup_path == original_lookup_path {
2715 None
2716 } else {
2717 let result = resolve_internal(lookup_path, request, options);
2718 Some(result.with_replaced_request_key_pattern(
2719 request.request_pattern(),
2720 original_request.request_pattern(),
2721 ))
2722 }
2723 }
2724 ImportMapResult::External(name, ty, traced) => {
2725 Some(*ResolveResult::primary(ResolveResultItem::External {
2726 name: name.clone(),
2727 ty: *ty,
2728 traced: *traced,
2729 }))
2730 }
2731 ImportMapResult::AliasExternal {
2732 name,
2733 ty,
2734 traced,
2735 lookup_dir: alias_lookup_path,
2736 } => {
2737 let request = Request::parse_string(name.clone());
2738
2739 if request.resolve().await? == original_request
2741 && *alias_lookup_path == original_lookup_path
2742 {
2743 None
2744 } else {
2745 let is_external_resolvable = !resolve_internal(
2746 alias_lookup_path.clone(),
2747 request,
2748 match ty {
2749 ExternalType::CommonJs => {
2751 node_cjs_resolve_options(alias_lookup_path.root().owned().await?)
2752 }
2753 ExternalType::EcmaScriptModule => {
2754 node_esm_resolve_options(alias_lookup_path.root().owned().await?)
2755 }
2756 ExternalType::Script | ExternalType::Url | ExternalType::Global => options,
2757 },
2758 )
2759 .await?
2760 .is_unresolvable_ref();
2761 if is_external_resolvable {
2762 Some(*ResolveResult::primary(ResolveResultItem::External {
2763 name: name.clone(),
2764 ty: *ty,
2765 traced: *traced,
2766 }))
2767 } else {
2768 None
2769 }
2770 }
2771 }
2772 ImportMapResult::Alternatives(list) => {
2773 let results = list
2774 .iter()
2775 .map(|result| {
2776 resolve_import_map_result(
2777 result,
2778 lookup_path.clone(),
2779 original_lookup_path.clone(),
2780 original_request,
2781 options,
2782 query.clone(),
2783 )
2784 })
2785 .try_join()
2786 .await?;
2787
2788 Some(merge_results(results.into_iter().flatten().collect()))
2789 }
2790 ImportMapResult::NoEntry => None,
2791 })
2792}
2793
2794#[tracing::instrument(level = Level::TRACE, skip_all)]
2795async fn resolved(
2796 request_key: RequestKey,
2797 fs_path: FileSystemPath,
2798 original_context: FileSystemPath,
2799 original_request: Vc<Request>,
2800 options_value: &ResolveOptions,
2801 options: Vc<ResolveOptions>,
2802 query: RcStr,
2803 fragment: RcStr,
2804) -> Result<Vc<ResolveResult>> {
2805 let result = &*fs_path.realpath_with_links().await?;
2806 let path = match &result.path_result {
2807 Ok(path) => path,
2808 Err(e) => bail!(e.as_error_message(&fs_path, result)),
2809 };
2810
2811 let path_ref = path.clone();
2812 if let Some(result) = apply_in_package(
2814 path.parent(),
2815 options,
2816 options_value,
2817 |package_path| package_path.get_relative_path_to(&path_ref),
2818 query.clone(),
2819 fragment.clone(),
2820 )
2821 .await?
2822 {
2823 return Ok(result);
2824 }
2825
2826 if let Some(resolved_map) = options_value.resolved_map {
2827 let result = resolved_map
2828 .lookup(path.clone(), original_context.clone(), original_request)
2829 .await?;
2830
2831 let resolved_result = resolve_import_map_result(
2832 &result,
2833 path.parent(),
2834 original_context.clone(),
2835 original_request,
2836 options,
2837 query.clone(),
2838 )
2839 .await?;
2840
2841 if let Some(result) = resolved_result {
2842 return Ok(result);
2843 }
2844 }
2845 let source = ResolvedVc::upcast(
2846 FileSource::new_with_query_and_fragment(path.clone(), query, fragment)
2847 .to_resolved()
2848 .await?,
2849 );
2850 Ok(if options_value.collect_affecting_sources {
2851 ResolveResult::source_with_affecting_sources(
2852 request_key,
2853 source,
2854 result
2855 .symlinks
2856 .iter()
2857 .map(|symlink| async move {
2858 anyhow::Ok(ResolvedVc::upcast(
2859 FileSource::new(symlink.clone()).to_resolved().await?,
2860 ))
2861 })
2862 .try_join()
2863 .await?,
2864 )
2865 } else {
2866 ResolveResult::source_with_key(request_key, source)
2867 }
2868 .cell())
2869}
2870
2871async fn handle_exports_imports_field(
2872 package_path: FileSystemPath,
2873 package_json_path: FileSystemPath,
2874 options: Vc<ResolveOptions>,
2875 exports_imports_field: &AliasMap<SubpathValue>,
2876 mut path: Pattern,
2877 conditions: &BTreeMap<RcStr, ConditionValue>,
2878 unspecified_conditions: &ConditionValue,
2879 query: RcStr,
2880) -> Result<Vc<ResolveResult>> {
2881 let mut results = Vec::new();
2882 let mut conditions_state = FxHashMap::default();
2883
2884 if !query.is_empty() {
2885 path.push(query.into());
2886 }
2887 let req = path;
2888
2889 let values = exports_imports_field.lookup(&req);
2890 for value in values {
2891 let value = value?;
2892 if value.output.add_results(
2893 value.prefix,
2894 value.key,
2895 conditions,
2896 unspecified_conditions,
2897 &mut conditions_state,
2898 &mut results,
2899 ) {
2900 break;
2902 }
2903 }
2904
2905 let mut resolved_results = Vec::new();
2906 for ReplacedSubpathValueResult {
2907 result_path,
2908 conditions,
2909 map_prefix,
2910 map_key,
2911 } in results
2912 {
2913 if let Some(result_path) = result_path.with_normalized_path() {
2914 let request = Request::parse(Pattern::Concatenation(vec![
2915 Pattern::Constant(rcstr!("./")),
2916 result_path.clone(),
2917 ]))
2918 .resolve()
2919 .await?;
2920
2921 let resolve_result = Box::pin(resolve_internal_inline(
2922 package_path.clone(),
2923 request,
2924 options,
2925 ))
2926 .await?;
2927
2928 let resolve_result = if let Some(req) = req.as_constant_string() {
2929 resolve_result.with_request(req.clone())
2930 } else {
2931 match map_key {
2932 AliasKey::Exact => resolve_result.with_request(map_prefix.clone().into()),
2933 AliasKey::Wildcard { .. } => {
2934 let mut old_request_key = result_path;
2944 old_request_key.push_front(rcstr!("./").into());
2946 let new_request_key = req.clone();
2947
2948 resolve_result.with_replaced_request_key_pattern(
2949 Pattern::new(old_request_key),
2950 Pattern::new(new_request_key),
2951 )
2952 }
2953 }
2954 };
2955
2956 let resolve_result = if !conditions.is_empty() {
2957 let mut resolve_result = resolve_result.owned().await?;
2958 resolve_result.add_conditions(conditions);
2959 resolve_result.cell()
2960 } else {
2961 resolve_result
2962 };
2963 resolved_results.push(resolve_result);
2964 }
2965 }
2966
2967 Ok(merge_results_with_affecting_sources(
2969 resolved_results,
2970 vec![ResolvedVc::upcast(
2971 FileSource::new(package_json_path).to_resolved().await?,
2972 )],
2973 ))
2974}
2975
2976async fn resolve_package_internal_with_imports_field(
2981 file_path: FileSystemPath,
2982 request: Vc<Request>,
2983 resolve_options: Vc<ResolveOptions>,
2984 pattern: &Pattern,
2985 conditions: &BTreeMap<RcStr, ConditionValue>,
2986 unspecified_conditions: &ConditionValue,
2987) -> Result<Vc<ResolveResult>> {
2988 let Pattern::Constant(specifier) = pattern else {
2989 bail!("PackageInternal requests can only be Constant strings");
2990 };
2991 if specifier == "#" || specifier.starts_with("#/") || specifier.ends_with('/') {
2993 ResolvingIssue {
2994 severity: error_severity(resolve_options).await?,
2995 file_path: file_path.clone(),
2996 request_type: format!("package imports request: `{specifier}`"),
2997 request: request.to_resolved().await?,
2998 resolve_options: resolve_options.to_resolved().await?,
2999 error_message: None,
3000 source: None,
3001 }
3002 .resolved_cell()
3003 .emit();
3004 return Ok(*ResolveResult::unresolvable());
3005 }
3006
3007 let imports_result = imports_field(file_path).await?;
3008 let (imports, package_json_path) = match &*imports_result {
3009 ImportsFieldResult::Some(i, p) => (i, p.clone()),
3010 ImportsFieldResult::None => return Ok(*ResolveResult::unresolvable()),
3011 };
3012
3013 handle_exports_imports_field(
3014 package_json_path.parent(),
3015 package_json_path.clone(),
3016 resolve_options,
3017 imports,
3018 Pattern::Constant(specifier.clone()),
3019 conditions,
3020 unspecified_conditions,
3021 RcStr::default(),
3022 )
3023 .await
3024}
3025
3026pub async fn handle_resolve_error(
3027 result: Vc<ModuleResolveResult>,
3028 reference_type: ReferenceType,
3029 origin_path: FileSystemPath,
3030 request: Vc<Request>,
3031 resolve_options: Vc<ResolveOptions>,
3032 is_optional: bool,
3033 source: Option<IssueSource>,
3034) -> Result<Vc<ModuleResolveResult>> {
3035 async fn is_unresolvable(result: Vc<ModuleResolveResult>) -> Result<bool> {
3036 Ok(*result.resolve().await?.is_unresolvable().await?)
3037 }
3038 Ok(match is_unresolvable(result).await {
3039 Ok(unresolvable) => {
3040 if unresolvable {
3041 emit_unresolvable_issue(
3042 is_optional,
3043 origin_path,
3044 reference_type,
3045 request,
3046 resolve_options,
3047 source,
3048 )
3049 .await?;
3050 }
3051
3052 result
3053 }
3054 Err(err) => {
3055 emit_resolve_error_issue(
3056 is_optional,
3057 origin_path,
3058 reference_type,
3059 request,
3060 resolve_options,
3061 err,
3062 source,
3063 )
3064 .await?;
3065 *ModuleResolveResult::unresolvable()
3066 }
3067 })
3068}
3069
3070pub async fn handle_resolve_source_error(
3071 result: Vc<ResolveResult>,
3072 reference_type: ReferenceType,
3073 origin_path: FileSystemPath,
3074 request: Vc<Request>,
3075 resolve_options: Vc<ResolveOptions>,
3076 is_optional: bool,
3077 source: Option<IssueSource>,
3078) -> Result<Vc<ResolveResult>> {
3079 async fn is_unresolvable(result: Vc<ResolveResult>) -> Result<bool> {
3080 Ok(*result.resolve().await?.is_unresolvable().await?)
3081 }
3082 Ok(match is_unresolvable(result).await {
3083 Ok(unresolvable) => {
3084 if unresolvable {
3085 emit_unresolvable_issue(
3086 is_optional,
3087 origin_path,
3088 reference_type,
3089 request,
3090 resolve_options,
3091 source,
3092 )
3093 .await?;
3094 }
3095
3096 result
3097 }
3098 Err(err) => {
3099 emit_resolve_error_issue(
3100 is_optional,
3101 origin_path,
3102 reference_type,
3103 request,
3104 resolve_options,
3105 err,
3106 source,
3107 )
3108 .await?;
3109 *ResolveResult::unresolvable()
3110 }
3111 })
3112}
3113
3114async fn emit_resolve_error_issue(
3115 is_optional: bool,
3116 origin_path: FileSystemPath,
3117 reference_type: ReferenceType,
3118 request: Vc<Request>,
3119 resolve_options: Vc<ResolveOptions>,
3120 err: anyhow::Error,
3121 source: Option<IssueSource>,
3122) -> Result<()> {
3123 let severity = if is_optional || resolve_options.await?.loose_errors {
3124 IssueSeverity::Warning
3125 } else {
3126 IssueSeverity::Error
3127 };
3128 ResolvingIssue {
3129 severity,
3130 file_path: origin_path.clone(),
3131 request_type: format!("{reference_type} request"),
3132 request: request.to_resolved().await?,
3133 resolve_options: resolve_options.to_resolved().await?,
3134 error_message: Some(format!("{}", PrettyPrintError(&err))),
3135 source,
3136 }
3137 .resolved_cell()
3138 .emit();
3139 Ok(())
3140}
3141
3142async fn emit_unresolvable_issue(
3143 is_optional: bool,
3144 origin_path: FileSystemPath,
3145 reference_type: ReferenceType,
3146 request: Vc<Request>,
3147 resolve_options: Vc<ResolveOptions>,
3148 source: Option<IssueSource>,
3149) -> Result<()> {
3150 let severity = if is_optional || resolve_options.await?.loose_errors {
3151 IssueSeverity::Warning
3152 } else {
3153 IssueSeverity::Error
3154 };
3155 ResolvingIssue {
3156 severity,
3157 file_path: origin_path.clone(),
3158 request_type: format!("{reference_type} request"),
3159 request: request.to_resolved().await?,
3160 resolve_options: resolve_options.to_resolved().await?,
3161 error_message: None,
3162 source,
3163 }
3164 .resolved_cell()
3165 .emit();
3166 Ok(())
3167}
3168
3169async fn error_severity(resolve_options: Vc<ResolveOptions>) -> Result<IssueSeverity> {
3170 Ok(if resolve_options.await?.loose_errors {
3171 IssueSeverity::Warning
3172 } else {
3173 IssueSeverity::Error
3174 })
3175}
3176
3177#[derive(
3181 Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash, TraceRawVcs, TaskInput, NonLocalValue,
3182)]
3183pub enum ModulePart {
3184 Evaluation,
3187 Export(RcStr),
3189 RenamedExport {
3191 original_export: RcStr,
3192 export: RcStr,
3193 },
3194 RenamedNamespace { export: RcStr },
3196 Internal(u32),
3198 Locals,
3200 Exports,
3202 Facade,
3205}
3206
3207impl ModulePart {
3208 pub fn evaluation() -> Self {
3209 ModulePart::Evaluation
3210 }
3211
3212 pub fn export(export: RcStr) -> Self {
3213 ModulePart::Export(export)
3214 }
3215
3216 pub fn renamed_export(original_export: RcStr, export: RcStr) -> Self {
3217 ModulePart::RenamedExport {
3218 original_export,
3219 export,
3220 }
3221 }
3222
3223 pub fn renamed_namespace(export: RcStr) -> Self {
3224 ModulePart::RenamedNamespace { export }
3225 }
3226
3227 pub fn internal(id: u32) -> Self {
3228 ModulePart::Internal(id)
3229 }
3230
3231 pub fn locals() -> Self {
3232 ModulePart::Locals
3233 }
3234
3235 pub fn exports() -> Self {
3236 ModulePart::Exports
3237 }
3238
3239 pub fn facade() -> Self {
3240 ModulePart::Facade
3241 }
3242}
3243
3244impl Display for ModulePart {
3245 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
3246 match self {
3247 ModulePart::Evaluation => f.write_str("module evaluation"),
3248 ModulePart::Export(export) => write!(f, "export {export}"),
3249 ModulePart::RenamedExport {
3250 original_export,
3251 export,
3252 } => write!(f, "export {original_export} as {export}"),
3253 ModulePart::RenamedNamespace { export } => {
3254 write!(f, "export * as {export}")
3255 }
3256 ModulePart::Internal(id) => write!(f, "internal part {id}"),
3257 ModulePart::Locals => f.write_str("locals"),
3258 ModulePart::Exports => f.write_str("exports"),
3259 ModulePart::Facade => f.write_str("facade"),
3260 }
3261 }
3262}