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)
579 }
580
581 pub fn source_with_key(
582 request_key: RequestKey,
583 source: ResolvedVc<Box<dyn Source>>,
584 ) -> ResolvedVc<Self> {
585 ResolveResult {
586 primary: vec![(request_key, ResolveResultItem::Source(source))].into_boxed_slice(),
587 affecting_sources: Default::default(),
588 }
589 .resolved_cell()
590 }
591
592 pub fn source_with_affecting_sources(
593 request_key: RequestKey,
594 source: ResolvedVc<Box<dyn Source>>,
595 affecting_sources: Vec<ResolvedVc<Box<dyn Source>>>,
596 ) -> ResolvedVc<Self> {
597 ResolveResult {
598 primary: vec![(request_key, ResolveResultItem::Source(source))].into_boxed_slice(),
599 affecting_sources: affecting_sources.into_boxed_slice(),
600 }
601 .resolved_cell()
602 }
603}
604
605impl ResolveResult {
606 pub fn get_affecting_sources(&self) -> impl Iterator<Item = ResolvedVc<Box<dyn Source>>> + '_ {
609 self.affecting_sources.iter().copied()
610 }
611
612 pub fn is_unresolvable_ref(&self) -> bool {
613 self.primary.is_empty()
614 }
615
616 pub async fn map_module<A, AF>(&self, source_fn: A) -> Result<ModuleResolveResult>
617 where
618 A: Fn(ResolvedVc<Box<dyn Source>>) -> AF,
619 AF: Future<Output = Result<ModuleResolveResultItem>>,
620 {
621 Ok(ModuleResolveResult {
622 primary: self
623 .primary
624 .iter()
625 .map(|(request, item)| {
626 let asset_fn = &source_fn;
627 let request = request.clone();
628 let item = item.clone();
629 async move {
630 Ok((
631 request,
632 match item {
633 ResolveResultItem::Source(source) => asset_fn(source).await?,
634 ResolveResultItem::External { name, ty, traced } => {
635 if traced == ExternalTraced::Traced {
636 bail!("map_module doesn't handle traced externals");
638 }
639 ModuleResolveResultItem::External { name, ty }
640 }
641 ResolveResultItem::Ignore => ModuleResolveResultItem::Ignore,
642 ResolveResultItem::Empty => ModuleResolveResultItem::Empty,
643 ResolveResultItem::Error(e) => ModuleResolveResultItem::Error(e),
644 ResolveResultItem::Custom(u8) => {
645 ModuleResolveResultItem::Custom(u8)
646 }
647 },
648 ))
649 }
650 })
651 .try_join()
652 .await?
653 .into_iter()
654 .collect(),
655 affecting_sources: self.affecting_sources.clone(),
656 })
657 }
658
659 pub async fn map_primary_items<A, AF>(&self, item_fn: A) -> Result<ModuleResolveResult>
660 where
661 A: Fn(ResolveResultItem) -> AF,
662 AF: Future<Output = Result<ModuleResolveResultItem>>,
663 {
664 Ok(ModuleResolveResult {
665 primary: self
666 .primary
667 .iter()
668 .map(|(request, item)| {
669 let asset_fn = &item_fn;
670 let request = request.clone();
671 let item = item.clone();
672 async move { Ok((request, asset_fn(item).await?)) }
673 })
674 .try_join()
675 .await?
676 .into_iter()
677 .collect(),
678 affecting_sources: self.affecting_sources.clone(),
679 })
680 }
681
682 fn with_request_ref(&self, request: RcStr) -> Self {
685 let new_primary = self
686 .primary
687 .iter()
688 .map(|(k, v)| {
689 (
690 RequestKey {
691 request: Some(request.clone()),
692 conditions: k.conditions.clone(),
693 },
694 v.clone(),
695 )
696 })
697 .collect();
698 ResolveResult {
699 primary: new_primary,
700 affecting_sources: self.affecting_sources.clone(),
701 }
702 }
703
704 pub fn add_conditions(&mut self, conditions: impl IntoIterator<Item = (RcStr, bool)>) {
705 let mut primary = std::mem::take(&mut self.primary);
706 for (k, v) in conditions {
707 for (key, _) in primary.iter_mut() {
708 key.conditions.insert(k.to_string(), v);
709 }
710 }
711 self.primary = IntoIterator::into_iter(primary)
713 .collect::<FxIndexMap<_, _>>()
714 .into_iter()
715 .collect::<Vec<_>>()
716 .into_boxed_slice();
717 }
718}
719
720struct ResolveResultBuilder {
721 primary: FxIndexMap<RequestKey, ResolveResultItem>,
722 affecting_sources: Vec<ResolvedVc<Box<dyn Source>>>,
723}
724
725impl From<ResolveResultBuilder> for ResolveResult {
726 fn from(v: ResolveResultBuilder) -> Self {
727 ResolveResult {
728 primary: v.primary.into_iter().collect(),
729 affecting_sources: v.affecting_sources.into_boxed_slice(),
730 }
731 }
732}
733impl From<ResolveResult> for ResolveResultBuilder {
734 fn from(v: ResolveResult) -> Self {
735 ResolveResultBuilder {
736 primary: IntoIterator::into_iter(v.primary).collect(),
737 affecting_sources: v.affecting_sources.into_vec(),
738 }
739 }
740}
741impl ResolveResultBuilder {
742 pub fn merge_alternatives(&mut self, other: &ResolveResult) {
743 for (k, v) in other.primary.iter() {
744 if !self.primary.contains_key(k) {
745 self.primary.insert(k.clone(), v.clone());
746 }
747 }
748 let set = self
749 .affecting_sources
750 .iter()
751 .copied()
752 .collect::<FxHashSet<_>>();
753 self.affecting_sources.extend(
754 other
755 .affecting_sources
756 .iter()
757 .filter(|source| !set.contains(source))
758 .copied(),
759 );
760 }
761}
762
763#[turbo_tasks::value_impl]
764impl ResolveResult {
765 #[turbo_tasks::function]
766 pub async fn as_raw_module_result(&self) -> Result<Vc<ModuleResolveResult>> {
767 Ok(self
768 .map_module(|asset| async move {
769 Ok(ModuleResolveResultItem::Module(ResolvedVc::upcast(
770 RawModule::new(*asset).to_resolved().await?,
771 )))
772 })
773 .await?
774 .cell())
775 }
776
777 #[turbo_tasks::function]
778 fn with_affecting_sources(
779 &self,
780 sources: Vec<ResolvedVc<Box<dyn Source>>>,
781 ) -> Result<Vc<Self>> {
782 Ok(Self {
783 primary: self.primary.clone(),
784 affecting_sources: self
785 .affecting_sources
786 .iter()
787 .copied()
788 .chain(sources)
789 .collect(),
790 }
791 .cell())
792 }
793
794 #[turbo_tasks::function]
795 async fn alternatives(results: Vec<Vc<ResolveResult>>) -> Result<Vc<Self>> {
796 if results.len() == 1 {
797 return Ok(results.into_iter().next().unwrap());
798 }
799 let mut iter = results.into_iter().try_join().await?.into_iter();
800 if let Some(current) = iter.next() {
801 let mut current: ResolveResultBuilder = ReadRef::into_owned(current).into();
802 for result in iter {
803 let other = &*result;
805 current.merge_alternatives(other);
806 }
807 Ok(Self::cell(current.into()))
808 } else {
809 Ok(*ResolveResult::unresolvable())
810 }
811 }
812
813 #[turbo_tasks::function]
814 async fn alternatives_with_affecting_sources(
815 results: Vec<Vc<ResolveResult>>,
816 affecting_sources: Vec<ResolvedVc<Box<dyn Source>>>,
817 ) -> Result<Vc<Self>> {
818 debug_assert!(
819 !affecting_sources.is_empty(),
820 "Caller should not call this function if there are no affecting sources"
821 );
822 if results.len() == 1 {
823 return Ok(results
824 .into_iter()
825 .next()
826 .unwrap()
827 .with_affecting_sources(affecting_sources.into_iter().map(|src| *src).collect()));
828 }
829 let mut iter = results.into_iter().try_join().await?.into_iter();
830 if let Some(current) = iter.next() {
831 let mut current: ResolveResultBuilder = ReadRef::into_owned(current).into();
832 for result in iter {
833 let other = &*result;
835 current.merge_alternatives(other);
836 }
837 current.affecting_sources.extend(affecting_sources);
838 Ok(Self::cell(current.into()))
839 } else {
840 Ok(*ResolveResult::unresolvable_with_affecting_sources(
841 affecting_sources,
842 ))
843 }
844 }
845
846 #[turbo_tasks::function]
847 pub fn is_unresolvable(&self) -> Vc<bool> {
848 Vc::cell(self.is_unresolvable_ref())
849 }
850
851 #[turbo_tasks::function]
852 pub fn first_source(&self) -> Vc<OptionSource> {
853 Vc::cell(self.primary.iter().find_map(|(_, item)| {
854 if let &ResolveResultItem::Source(a) = item {
855 Some(a)
856 } else {
857 None
858 }
859 }))
860 }
861
862 #[turbo_tasks::function]
863 pub fn primary_sources(&self) -> Vc<Sources> {
864 Vc::cell(
865 self.primary
866 .iter()
867 .filter_map(|(_, item)| {
868 if let &ResolveResultItem::Source(a) = item {
869 Some(a)
870 } else {
871 None
872 }
873 })
874 .collect(),
875 )
876 }
877
878 #[turbo_tasks::function]
883 fn with_replaced_request_key(
884 &self,
885 old_request_key: RcStr,
886 request_key: RequestKey,
887 ) -> Result<Vc<Self>> {
888 let new_primary = self
889 .primary
890 .iter()
891 .filter_map(|(k, v)| {
892 let remaining = k.request.as_ref()?.strip_prefix(&*old_request_key)?;
893 Some((
894 RequestKey {
895 request: request_key
896 .request
897 .as_ref()
898 .map(|r| format!("{r}{remaining}").into()),
899 conditions: request_key.conditions.clone(),
900 },
901 v.clone(),
902 ))
903 })
904 .collect();
905 Ok(ResolveResult {
906 primary: new_primary,
907 affecting_sources: self.affecting_sources.clone(),
908 }
909 .into())
910 }
911
912 #[turbo_tasks::function]
916 fn with_stripped_request_key_prefix(&self, prefix: RcStr) -> Result<Vc<Self>> {
917 let new_primary = self
918 .primary
919 .iter()
920 .filter_map(|(k, v)| {
921 let remaining = k.request.as_ref()?.strip_prefix(&*prefix)?;
922 Some((
923 RequestKey {
924 request: Some(remaining.into()),
925 conditions: k.conditions.clone(),
926 },
927 v.clone(),
928 ))
929 })
930 .collect();
931 Ok(ResolveResult {
932 primary: new_primary,
933 affecting_sources: self.affecting_sources.clone(),
934 }
935 .into())
936 }
937
938 #[turbo_tasks::function]
943 async fn with_replaced_request_key_pattern(
944 &self,
945 old_request_key: Vc<Pattern>,
946 request_key: Vc<Pattern>,
947 ) -> Result<Vc<Self>> {
948 let old_request_key = &*old_request_key.await?;
949 let request_key = &*request_key.await?;
950
951 let new_primary = self
952 .primary
953 .iter()
954 .map(|(k, v)| {
955 (
956 RequestKey {
957 request: k
958 .request
959 .as_ref()
960 .and_then(|r| old_request_key.match_apply_template(r, request_key))
961 .map(Into::into),
962 conditions: k.conditions.clone(),
963 },
964 v.clone(),
965 )
966 })
967 .collect();
968 Ok(ResolveResult {
969 primary: new_primary,
970 affecting_sources: self.affecting_sources.clone(),
971 }
972 .into())
973 }
974
975 #[turbo_tasks::function]
978 fn with_request(&self, request: RcStr) -> Vc<Self> {
979 let new_primary = self
980 .primary
981 .iter()
982 .map(|(k, v)| {
983 (
984 RequestKey {
985 request: Some(request.clone()),
986 conditions: k.conditions.clone(),
987 },
988 v.clone(),
989 )
990 })
991 .collect();
992 ResolveResult {
993 primary: new_primary,
994 affecting_sources: self.affecting_sources.clone(),
995 }
996 .into()
997 }
998}
999
1000#[turbo_tasks::value(transparent)]
1001pub struct ResolveResultOption(Option<ResolvedVc<ResolveResult>>);
1002
1003#[turbo_tasks::value_impl]
1004impl ResolveResultOption {
1005 #[turbo_tasks::function]
1006 pub fn some(result: ResolvedVc<ResolveResult>) -> Vc<Self> {
1007 ResolveResultOption(Some(result)).cell()
1008 }
1009
1010 #[turbo_tasks::function]
1011 pub fn none() -> Vc<Self> {
1012 ResolveResultOption(None).cell()
1013 }
1014}
1015
1016async fn exists(
1017 fs_path: &FileSystemPath,
1018 refs: Option<&mut Vec<ResolvedVc<Box<dyn Source>>>>,
1019) -> Result<Option<FileSystemPath>> {
1020 type_exists(fs_path, FileSystemEntryType::File, refs).await
1021}
1022
1023async fn dir_exists(
1024 fs_path: &FileSystemPath,
1025 refs: Option<&mut Vec<ResolvedVc<Box<dyn Source>>>>,
1026) -> Result<Option<FileSystemPath>> {
1027 type_exists(fs_path, FileSystemEntryType::Directory, refs).await
1028}
1029
1030async fn type_exists(
1031 fs_path: &FileSystemPath,
1032 ty: FileSystemEntryType,
1033 refs: Option<&mut Vec<ResolvedVc<Box<dyn Source>>>>,
1034) -> Result<Option<FileSystemPath>> {
1035 let path = realpath(fs_path, refs).await?;
1036 Ok(if *path.get_type().await? == ty {
1037 Some(path)
1038 } else {
1039 None
1040 })
1041}
1042
1043async fn realpath(
1044 fs_path: &FileSystemPath,
1045 refs: Option<&mut Vec<ResolvedVc<Box<dyn Source>>>>,
1046) -> Result<FileSystemPath> {
1047 let result = fs_path.realpath_with_links().await?;
1048 if let Some(refs) = refs {
1049 refs.extend(
1050 result
1051 .symlinks
1052 .iter()
1053 .map(|path| async move {
1054 Ok(ResolvedVc::upcast(
1055 FileSource::new(path.clone()).to_resolved().await?,
1056 ))
1057 })
1058 .try_join()
1059 .await?,
1060 );
1061 }
1062 match &result.path_result {
1063 Ok(path) => Ok(path.clone()),
1064 Err(e) => bail!(e.as_error_message(fs_path, &result)),
1065 }
1066}
1067
1068#[turbo_tasks::value(shared)]
1069enum ExportsFieldResult {
1070 Some(#[turbo_tasks(debug_ignore, trace_ignore)] ExportsField),
1071 None,
1072}
1073
1074#[turbo_tasks::function]
1077async fn exports_field(
1078 package_json_path: ResolvedVc<Box<dyn Source>>,
1079) -> Result<Vc<ExportsFieldResult>> {
1080 let read = read_package_json(*package_json_path).await?;
1081 let package_json = match &*read {
1082 Some(json) => json,
1083 None => return Ok(ExportsFieldResult::None.cell()),
1084 };
1085
1086 let Some(exports) = package_json.get("exports") else {
1087 return Ok(ExportsFieldResult::None.cell());
1088 };
1089 match exports.try_into() {
1090 Ok(exports) => Ok(ExportsFieldResult::Some(exports).cell()),
1091 Err(err) => {
1092 PackageJsonIssue {
1093 error_message: err.to_string().into(),
1094 source: IssueSource::from_source_only(package_json_path),
1096 }
1097 .resolved_cell()
1098 .emit();
1099 Ok(ExportsFieldResult::None.cell())
1100 }
1101 }
1102}
1103
1104#[turbo_tasks::value(shared)]
1105enum ImportsFieldResult {
1106 Some(
1107 #[turbo_tasks(debug_ignore, trace_ignore)] ImportsField,
1108 FileSystemPath,
1109 ),
1110 None,
1111}
1112
1113#[turbo_tasks::function]
1116async fn imports_field(lookup_path: FileSystemPath) -> Result<Vc<ImportsFieldResult>> {
1117 let package_json_context = find_context_file(lookup_path, package_json(), false).await?;
1119 let FindContextFileResult::Found(package_json_path, _refs) = &*package_json_context else {
1120 return Ok(ImportsFieldResult::None.cell());
1121 };
1122 let source = Vc::upcast::<Box<dyn Source>>(FileSource::new(package_json_path.clone()))
1123 .to_resolved()
1124 .await?;
1125
1126 let read = read_package_json(*source).await?;
1127 let package_json = match &*read {
1128 Some(json) => json,
1129 None => return Ok(ImportsFieldResult::None.cell()),
1130 };
1131
1132 let Some(imports) = package_json.get("imports") else {
1133 return Ok(ImportsFieldResult::None.cell());
1134 };
1135 match imports.try_into() {
1136 Ok(imports) => Ok(ImportsFieldResult::Some(imports, package_json_path.clone()).cell()),
1137 Err(err) => {
1138 PackageJsonIssue {
1139 error_message: err.to_string().into(),
1140 source: IssueSource::from_source_only(source),
1142 }
1143 .resolved_cell()
1144 .emit();
1145 Ok(ImportsFieldResult::None.cell())
1146 }
1147 }
1148}
1149
1150#[turbo_tasks::function]
1151pub fn package_json() -> Vc<Vec<RcStr>> {
1152 Vc::cell(vec![rcstr!("package.json")])
1153}
1154
1155#[turbo_tasks::value(shared)]
1156pub enum FindContextFileResult {
1157 Found(FileSystemPath, Vec<ResolvedVc<Box<dyn Source>>>),
1158 NotFound(Vec<ResolvedVc<Box<dyn Source>>>),
1159}
1160
1161#[turbo_tasks::function]
1162pub async fn find_context_file(
1163 lookup_path: FileSystemPath,
1164 names: Vc<Vec<RcStr>>,
1165 collect_affecting_sources: bool,
1166) -> Result<Vc<FindContextFileResult>> {
1167 let mut refs = Vec::new();
1168 for name in &*names.await? {
1169 let fs_path = lookup_path.join(name)?;
1170 if let Some(fs_path) = exists(
1171 &fs_path,
1172 if collect_affecting_sources {
1173 Some(&mut refs)
1174 } else {
1175 None
1176 },
1177 )
1178 .await?
1179 {
1180 return Ok(FindContextFileResult::Found(fs_path, refs).cell());
1181 }
1182 }
1183 if lookup_path.is_root() {
1184 return Ok(FindContextFileResult::NotFound(refs).cell());
1185 }
1186 if refs.is_empty() {
1187 Ok(find_context_file(
1189 lookup_path.parent(),
1190 names,
1191 collect_affecting_sources,
1192 ))
1193 } else {
1194 let parent_result =
1195 find_context_file(lookup_path.parent(), names, collect_affecting_sources).await?;
1196 Ok(match &*parent_result {
1197 FindContextFileResult::Found(p, r) => {
1198 refs.extend(r.iter().copied());
1199 FindContextFileResult::Found(p.clone(), refs)
1200 }
1201 FindContextFileResult::NotFound(r) => {
1202 refs.extend(r.iter().copied());
1203 FindContextFileResult::NotFound(refs)
1204 }
1205 }
1206 .cell())
1207 }
1208}
1209
1210#[turbo_tasks::function]
1213pub async fn find_context_file_or_package_key(
1214 lookup_path: FileSystemPath,
1215 names: Vc<Vec<RcStr>>,
1216 package_key: RcStr,
1217) -> Result<Vc<FindContextFileResult>> {
1218 let package_json_path = lookup_path.join("package.json")?;
1219 if let Some(package_json_path) = exists(&package_json_path, None).await?
1220 && let Some(json) =
1221 &*read_package_json(Vc::upcast(FileSource::new(package_json_path.clone()))).await?
1222 && json.get(&*package_key).is_some()
1223 {
1224 return Ok(FindContextFileResult::Found(package_json_path, Vec::new()).into());
1225 }
1226 for name in &*names.await? {
1227 let fs_path = lookup_path.join(name)?;
1228 if let Some(fs_path) = exists(&fs_path, None).await? {
1229 return Ok(FindContextFileResult::Found(fs_path, Vec::new()).into());
1230 }
1231 }
1232 if lookup_path.is_root() {
1233 return Ok(FindContextFileResult::NotFound(Vec::new()).into());
1234 }
1235
1236 Ok(find_context_file(lookup_path.parent(), names, false))
1237}
1238
1239#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, Debug, NonLocalValue)]
1240enum FindPackageItem {
1241 PackageDirectory { name: RcStr, dir: FileSystemPath },
1242 PackageFile { name: RcStr, file: FileSystemPath },
1243}
1244
1245#[turbo_tasks::value]
1246#[derive(Debug)]
1247struct FindPackageResult {
1248 packages: Vec<FindPackageItem>,
1249 affecting_sources: Vec<ResolvedVc<Box<dyn Source>>>,
1251}
1252
1253#[turbo_tasks::function]
1254async fn find_package(
1255 lookup_path: FileSystemPath,
1256 package_name: Pattern,
1257 options: Vc<ResolveModulesOptions>,
1258 collect_affecting_sources: bool,
1259) -> Result<Vc<FindPackageResult>> {
1260 let mut packages = vec![];
1261 let mut affecting_sources = vec![];
1262 let options = options.await?;
1263 let package_name_cell = Pattern::new(package_name.clone());
1264
1265 fn get_package_name(basepath: &FileSystemPath, package_dir: &FileSystemPath) -> Result<RcStr> {
1266 if let Some(name) = basepath.get_path_to(package_dir) {
1267 Ok(name.into())
1268 } else {
1269 bail!("Package directory {package_dir} is not inside the lookup path {basepath}");
1270 }
1271 }
1272
1273 for resolve_modules in &options.modules {
1274 match resolve_modules {
1275 ResolveModules::Nested(root, names) => {
1276 let mut lookup_path = lookup_path.clone();
1277 let mut lookup_path_value = lookup_path.clone();
1278 while lookup_path_value.is_inside_ref(root) {
1279 for name in names.iter() {
1280 let fs_path = lookup_path.join(name)?;
1281 if let Some(fs_path) = dir_exists(
1282 &fs_path,
1283 collect_affecting_sources.then_some(&mut affecting_sources),
1284 )
1285 .await?
1286 {
1287 let matches =
1288 read_matches(fs_path.clone(), rcstr!(""), true, package_name_cell)
1289 .await?;
1290 for m in &*matches {
1291 if let PatternMatch::Directory(_, package_dir) = m {
1292 packages.push(FindPackageItem::PackageDirectory {
1293 name: get_package_name(&fs_path, package_dir)?,
1294 dir: realpath(
1295 package_dir,
1296 collect_affecting_sources
1297 .then_some(&mut affecting_sources),
1298 )
1299 .await?,
1300 });
1301 }
1302 }
1303 }
1304 }
1305 lookup_path = lookup_path.parent();
1306 let new_context_value = lookup_path.clone();
1307 if new_context_value == lookup_path_value {
1308 break;
1309 }
1310 lookup_path_value = new_context_value;
1311 }
1312 }
1313 ResolveModules::Path {
1314 dir,
1315 excluded_extensions,
1316 } => {
1317 let matches =
1318 read_matches(dir.clone(), rcstr!(""), true, package_name_cell).await?;
1319 for m in &*matches {
1320 match m {
1321 PatternMatch::Directory(_, package_dir) => {
1322 packages.push(FindPackageItem::PackageDirectory {
1323 name: get_package_name(dir, package_dir)?,
1324 dir: realpath(
1325 package_dir,
1326 collect_affecting_sources.then_some(&mut affecting_sources),
1327 )
1328 .await?,
1329 });
1330 }
1331 PatternMatch::File(_, package_file) => {
1332 packages.push(FindPackageItem::PackageFile {
1333 name: get_package_name(dir, package_file)?,
1334 file: realpath(
1335 package_file,
1336 collect_affecting_sources.then_some(&mut affecting_sources),
1337 )
1338 .await?,
1339 });
1340 }
1341 }
1342 }
1343
1344 let excluded_extensions = excluded_extensions.await?;
1345 let mut package_name_with_extensions = package_name.clone();
1346 package_name_with_extensions.push(Pattern::alternatives(
1347 options
1348 .extensions
1349 .iter()
1350 .filter(|ext| !excluded_extensions.contains(*ext))
1351 .cloned()
1352 .map(Pattern::from),
1353 ));
1354 let package_name_with_extensions = Pattern::new(package_name_with_extensions);
1355
1356 let matches =
1357 read_matches(dir.clone(), rcstr!(""), true, package_name_with_extensions)
1358 .await?;
1359 for m in matches {
1360 if let PatternMatch::File(_, package_file) = m {
1361 packages.push(FindPackageItem::PackageFile {
1362 name: get_package_name(dir, package_file)?,
1363 file: realpath(
1364 package_file,
1365 collect_affecting_sources.then_some(&mut affecting_sources),
1366 )
1367 .await?,
1368 });
1369 }
1370 }
1371 }
1372 }
1373 }
1374 Ok(FindPackageResult::cell(FindPackageResult {
1375 packages,
1376 affecting_sources,
1377 }))
1378}
1379
1380fn merge_results(results: Vec<Vc<ResolveResult>>) -> Vc<ResolveResult> {
1381 match results.len() {
1382 0 => *ResolveResult::unresolvable(),
1383 1 => results.into_iter().next().unwrap(),
1384 _ => ResolveResult::alternatives(results),
1385 }
1386}
1387
1388fn merge_results_with_affecting_sources(
1389 results: Vec<Vc<ResolveResult>>,
1390 affecting_sources: Vec<ResolvedVc<Box<dyn Source>>>,
1391) -> Vc<ResolveResult> {
1392 if affecting_sources.is_empty() {
1393 return merge_results(results);
1394 }
1395 match results.len() {
1396 0 => *ResolveResult::unresolvable_with_affecting_sources(affecting_sources),
1397 1 => results
1398 .into_iter()
1399 .next()
1400 .unwrap()
1401 .with_affecting_sources(affecting_sources.into_iter().map(|src| *src).collect()),
1402 _ => ResolveResult::alternatives_with_affecting_sources(
1403 results,
1404 affecting_sources.into_iter().map(|src| *src).collect(),
1405 ),
1406 }
1407}
1408
1409#[turbo_tasks::function]
1411pub async fn resolve_raw(
1412 lookup_dir: FileSystemPath,
1413 path: Vc<Pattern>,
1414 collect_affecting_sources: bool,
1415 force_in_lookup_dir: bool,
1416) -> Result<Vc<ResolveResult>> {
1417 async fn to_result(
1418 request: RcStr,
1419 path: FileSystemPath,
1420 collect_affecting_sources: bool,
1421 ) -> Result<Vc<ResolveResult>> {
1422 let result = &*path.realpath_with_links().await?;
1423 let path = match &result.path_result {
1424 Ok(path) => path,
1425 Err(e) => bail!(e.as_error_message(&path, result)),
1426 };
1427 let request_key = RequestKey::new(request);
1428 let source = ResolvedVc::upcast(FileSource::new(path.clone()).to_resolved().await?);
1429 Ok(*if collect_affecting_sources {
1430 ResolveResult::source_with_affecting_sources(
1431 request_key,
1432 source,
1433 result
1434 .symlinks
1435 .iter()
1436 .map(|symlink| {
1437 Vc::upcast::<Box<dyn Source>>(FileSource::new(symlink.clone()))
1438 .to_resolved()
1439 })
1440 .try_join()
1441 .await?,
1442 )
1443 } else {
1444 ResolveResult::source_with_key(request_key, source)
1445 })
1446 }
1447
1448 async fn collect_matches(
1449 matches: &[PatternMatch],
1450 collect_affecting_sources: bool,
1451 ) -> Result<Vec<Vc<ResolveResult>>> {
1452 matches
1453 .iter()
1454 .map(|m| async move {
1455 Ok(if let PatternMatch::File(request, path) = m {
1456 Some(to_result(request.clone(), path.clone(), collect_affecting_sources).await?)
1457 } else {
1458 None
1459 })
1460 })
1461 .try_flat_join()
1462 .await
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 if options_value.collect_affecting_sources {
2851 Ok(*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 Ok(*ResolveResult::source_with_key(request_key, source))
2867 }
2868}
2869
2870async fn handle_exports_imports_field(
2871 package_path: FileSystemPath,
2872 package_json_path: FileSystemPath,
2873 options: Vc<ResolveOptions>,
2874 exports_imports_field: &AliasMap<SubpathValue>,
2875 mut path: Pattern,
2876 conditions: &BTreeMap<RcStr, ConditionValue>,
2877 unspecified_conditions: &ConditionValue,
2878 query: RcStr,
2879) -> Result<Vc<ResolveResult>> {
2880 let mut results = Vec::new();
2881 let mut conditions_state = FxHashMap::default();
2882
2883 if !query.is_empty() {
2884 path.push(query.into());
2885 }
2886 let req = path;
2887
2888 let values = exports_imports_field.lookup(&req);
2889 for value in values {
2890 let value = value?;
2891 if value.output.add_results(
2892 value.prefix,
2893 value.key,
2894 conditions,
2895 unspecified_conditions,
2896 &mut conditions_state,
2897 &mut results,
2898 ) {
2899 break;
2901 }
2902 }
2903
2904 let mut resolved_results = Vec::new();
2905 for ReplacedSubpathValueResult {
2906 result_path,
2907 conditions,
2908 map_prefix,
2909 map_key,
2910 } in results
2911 {
2912 if let Some(result_path) = result_path.with_normalized_path() {
2913 let request = Request::parse(Pattern::Concatenation(vec![
2914 Pattern::Constant(rcstr!("./")),
2915 result_path.clone(),
2916 ]))
2917 .resolve()
2918 .await?;
2919
2920 let resolve_result = Box::pin(resolve_internal_inline(
2921 package_path.clone(),
2922 request,
2923 options,
2924 ))
2925 .await?;
2926
2927 let resolve_result = if let Some(req) = req.as_constant_string() {
2928 resolve_result.with_request(req.clone())
2929 } else {
2930 match map_key {
2931 AliasKey::Exact => resolve_result.with_request(map_prefix.clone().into()),
2932 AliasKey::Wildcard { .. } => {
2933 let mut old_request_key = result_path;
2943 old_request_key.push_front(rcstr!("./").into());
2945 let new_request_key = req.clone();
2946
2947 resolve_result.with_replaced_request_key_pattern(
2948 Pattern::new(old_request_key),
2949 Pattern::new(new_request_key),
2950 )
2951 }
2952 }
2953 };
2954
2955 let resolve_result = if !conditions.is_empty() {
2956 let mut resolve_result = resolve_result.owned().await?;
2957 resolve_result.add_conditions(conditions);
2958 resolve_result.cell()
2959 } else {
2960 resolve_result
2961 };
2962 resolved_results.push(resolve_result);
2963 }
2964 }
2965
2966 Ok(merge_results_with_affecting_sources(
2968 resolved_results,
2969 vec![ResolvedVc::upcast(
2970 FileSource::new(package_json_path).to_resolved().await?,
2971 )],
2972 ))
2973}
2974
2975async fn resolve_package_internal_with_imports_field(
2980 file_path: FileSystemPath,
2981 request: Vc<Request>,
2982 resolve_options: Vc<ResolveOptions>,
2983 pattern: &Pattern,
2984 conditions: &BTreeMap<RcStr, ConditionValue>,
2985 unspecified_conditions: &ConditionValue,
2986) -> Result<Vc<ResolveResult>> {
2987 let Pattern::Constant(specifier) = pattern else {
2988 bail!("PackageInternal requests can only be Constant strings");
2989 };
2990 if specifier == "#" || specifier.starts_with("#/") || specifier.ends_with('/') {
2992 ResolvingIssue {
2993 severity: error_severity(resolve_options).await?,
2994 file_path: file_path.clone(),
2995 request_type: format!("package imports request: `{specifier}`"),
2996 request: request.to_resolved().await?,
2997 resolve_options: resolve_options.to_resolved().await?,
2998 error_message: None,
2999 source: None,
3000 }
3001 .resolved_cell()
3002 .emit();
3003 return Ok(*ResolveResult::unresolvable());
3004 }
3005
3006 let imports_result = imports_field(file_path).await?;
3007 let (imports, package_json_path) = match &*imports_result {
3008 ImportsFieldResult::Some(i, p) => (i, p.clone()),
3009 ImportsFieldResult::None => return Ok(*ResolveResult::unresolvable()),
3010 };
3011
3012 handle_exports_imports_field(
3013 package_json_path.parent(),
3014 package_json_path.clone(),
3015 resolve_options,
3016 imports,
3017 Pattern::Constant(specifier.clone()),
3018 conditions,
3019 unspecified_conditions,
3020 RcStr::default(),
3021 )
3022 .await
3023}
3024
3025pub async fn handle_resolve_error(
3026 result: Vc<ModuleResolveResult>,
3027 reference_type: ReferenceType,
3028 origin_path: FileSystemPath,
3029 request: Vc<Request>,
3030 resolve_options: Vc<ResolveOptions>,
3031 is_optional: bool,
3032 source: Option<IssueSource>,
3033) -> Result<Vc<ModuleResolveResult>> {
3034 async fn is_unresolvable(result: Vc<ModuleResolveResult>) -> Result<bool> {
3035 Ok(*result.resolve().await?.is_unresolvable().await?)
3036 }
3037 Ok(match is_unresolvable(result).await {
3038 Ok(unresolvable) => {
3039 if unresolvable {
3040 emit_unresolvable_issue(
3041 is_optional,
3042 origin_path,
3043 reference_type,
3044 request,
3045 resolve_options,
3046 source,
3047 )
3048 .await?;
3049 }
3050
3051 result
3052 }
3053 Err(err) => {
3054 emit_resolve_error_issue(
3055 is_optional,
3056 origin_path,
3057 reference_type,
3058 request,
3059 resolve_options,
3060 err,
3061 source,
3062 )
3063 .await?;
3064 *ModuleResolveResult::unresolvable()
3065 }
3066 })
3067}
3068
3069pub async fn handle_resolve_source_error(
3070 result: Vc<ResolveResult>,
3071 reference_type: ReferenceType,
3072 origin_path: FileSystemPath,
3073 request: Vc<Request>,
3074 resolve_options: Vc<ResolveOptions>,
3075 is_optional: bool,
3076 source: Option<IssueSource>,
3077) -> Result<Vc<ResolveResult>> {
3078 async fn is_unresolvable(result: Vc<ResolveResult>) -> Result<bool> {
3079 Ok(*result.resolve().await?.is_unresolvable().await?)
3080 }
3081 Ok(match is_unresolvable(result).await {
3082 Ok(unresolvable) => {
3083 if unresolvable {
3084 emit_unresolvable_issue(
3085 is_optional,
3086 origin_path,
3087 reference_type,
3088 request,
3089 resolve_options,
3090 source,
3091 )
3092 .await?;
3093 }
3094
3095 result
3096 }
3097 Err(err) => {
3098 emit_resolve_error_issue(
3099 is_optional,
3100 origin_path,
3101 reference_type,
3102 request,
3103 resolve_options,
3104 err,
3105 source,
3106 )
3107 .await?;
3108 *ResolveResult::unresolvable()
3109 }
3110 })
3111}
3112
3113async fn emit_resolve_error_issue(
3114 is_optional: bool,
3115 origin_path: FileSystemPath,
3116 reference_type: ReferenceType,
3117 request: Vc<Request>,
3118 resolve_options: Vc<ResolveOptions>,
3119 err: anyhow::Error,
3120 source: Option<IssueSource>,
3121) -> Result<()> {
3122 let severity = if is_optional || resolve_options.await?.loose_errors {
3123 IssueSeverity::Warning
3124 } else {
3125 IssueSeverity::Error
3126 };
3127 ResolvingIssue {
3128 severity,
3129 file_path: origin_path.clone(),
3130 request_type: format!("{reference_type} request"),
3131 request: request.to_resolved().await?,
3132 resolve_options: resolve_options.to_resolved().await?,
3133 error_message: Some(format!("{}", PrettyPrintError(&err))),
3134 source,
3135 }
3136 .resolved_cell()
3137 .emit();
3138 Ok(())
3139}
3140
3141async fn emit_unresolvable_issue(
3142 is_optional: bool,
3143 origin_path: FileSystemPath,
3144 reference_type: ReferenceType,
3145 request: Vc<Request>,
3146 resolve_options: Vc<ResolveOptions>,
3147 source: Option<IssueSource>,
3148) -> Result<()> {
3149 let severity = if is_optional || resolve_options.await?.loose_errors {
3150 IssueSeverity::Warning
3151 } else {
3152 IssueSeverity::Error
3153 };
3154 ResolvingIssue {
3155 severity,
3156 file_path: origin_path.clone(),
3157 request_type: format!("{reference_type} request"),
3158 request: request.to_resolved().await?,
3159 resolve_options: resolve_options.to_resolved().await?,
3160 error_message: None,
3161 source,
3162 }
3163 .resolved_cell()
3164 .emit();
3165 Ok(())
3166}
3167
3168async fn error_severity(resolve_options: Vc<ResolveOptions>) -> Result<IssueSeverity> {
3169 Ok(if resolve_options.await?.loose_errors {
3170 IssueSeverity::Warning
3171 } else {
3172 IssueSeverity::Error
3173 })
3174}
3175
3176#[derive(
3180 Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash, TraceRawVcs, TaskInput, NonLocalValue,
3181)]
3182pub enum ModulePart {
3183 Evaluation,
3186 Export(RcStr),
3188 RenamedExport {
3190 original_export: RcStr,
3191 export: RcStr,
3192 },
3193 RenamedNamespace { export: RcStr },
3195 Internal(u32),
3197 Locals,
3199 Exports,
3201 Facade,
3204}
3205
3206impl ModulePart {
3207 pub fn evaluation() -> Self {
3208 ModulePart::Evaluation
3209 }
3210
3211 pub fn export(export: RcStr) -> Self {
3212 ModulePart::Export(export)
3213 }
3214
3215 pub fn renamed_export(original_export: RcStr, export: RcStr) -> Self {
3216 ModulePart::RenamedExport {
3217 original_export,
3218 export,
3219 }
3220 }
3221
3222 pub fn renamed_namespace(export: RcStr) -> Self {
3223 ModulePart::RenamedNamespace { export }
3224 }
3225
3226 pub fn internal(id: u32) -> Self {
3227 ModulePart::Internal(id)
3228 }
3229
3230 pub fn locals() -> Self {
3231 ModulePart::Locals
3232 }
3233
3234 pub fn exports() -> Self {
3235 ModulePart::Exports
3236 }
3237
3238 pub fn facade() -> Self {
3239 ModulePart::Facade
3240 }
3241}
3242
3243impl Display for ModulePart {
3244 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
3245 match self {
3246 ModulePart::Evaluation => f.write_str("module evaluation"),
3247 ModulePart::Export(export) => write!(f, "export {export}"),
3248 ModulePart::RenamedExport {
3249 original_export,
3250 export,
3251 } => write!(f, "export {original_export} as {export}"),
3252 ModulePart::RenamedNamespace { export } => {
3253 write!(f, "export * as {export}")
3254 }
3255 ModulePart::Internal(id) => write!(f, "internal part {id}"),
3256 ModulePart::Locals => f.write_str("locals"),
3257 ModulePart::Exports => f.write_str("exports"),
3258 ModulePart::Facade => f.write_str("facade"),
3259 }
3260 }
3261}