turbopack_core/resolve/
mod.rs

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        /// uri, path, reference, etc.
78        name: RcStr,
79        ty: ExternalType,
80    },
81    /// A module could not be created (according to the rules, e.g. no module type as assigned)
82    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                // TODO emit error?
99                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    /// This means the whole content of the module is used.
111    #[default]
112    All,
113    /// Only side effects are used.
114    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    /// Affecting sources are other files that influence the resolve result.  For example,
150    /// traversed symlinks
151    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    /// Returns all module results (but ignoring any errors).
224    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                // For clippy -- This explicit deref is necessary
297                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    /// Returns a set (no duplicates) of primary modules in the result. All
322    /// modules are already resolved Vc.
323    #[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        /// uri, path, reference, etc.
414        name: RcStr,
415        ty: ExternalType,
416        traced: ExternalTraced,
417    },
418    Ignore,
419    Error(ResolvedVc<RcStr>),
420    Empty,
421    Custom(u8),
422}
423
424/// Represents the key for a request that leads to a certain results during
425/// resolving.
426///
427/// A primary factor is the actual request string, but there are
428/// other factors like exports conditions that can affect resolting and become
429/// part of the key (assuming the condition is unknown at compile time)
430#[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    /// Affecting sources are other files that influence the resolve result.  For example,
472    /// traversed symlinks
473    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    /// Returns the affecting sources for this result. Will be empty if affecting sources are
607    /// disabled for this result.
608    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                                        // Should use map_primary_items instead
637                                        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    /// Returns a new [ResolveResult] where all [RequestKey]s are set to the
683    /// passed `request`.
684    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        // Deduplicate
712        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                // For clippy -- This explicit deref is necessary
804                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                // For clippy -- This explicit deref is necessary
834                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    /// Returns a new [ResolveResult] where all [RequestKey]s are updated. The `old_request_key`
879    /// (prefix) is replaced with the `request_key`. It's not expected that the [ResolveResult]
880    /// contains [RequestKey]s that don't have the `old_request_key` prefix, but if there are still
881    /// some, they are discarded.
882    #[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    /// Returns a new [ResolveResult] where all [RequestKey]s are updated. The prefix is removed
913    /// from all [RequestKey]s. It's not expected that the [ResolveResult] contains [RequestKey]s
914    /// without the prefix, but if there are still some, they are discarded.
915    #[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    /// Returns a new [ResolveResult] where all [RequestKey]s are updated. All keys matching
939    /// `old_request_key` are rewritten according to `request_key`. It's not expected that the
940    /// [ResolveResult] contains [RequestKey]s that do not match the `old_request_key` prefix, but
941    /// if there are still some, they are discarded.
942    #[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    /// Returns a new [ResolveResult] where all [RequestKey]s are set to the
976    /// passed `request`.
977    #[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/// Extracts the "exports" field out of the package.json, parsing it into an
1075/// appropriate [AliasMap] for lookups.
1076#[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                // TODO(PACK-4879): add line column information
1095                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/// Extracts the "imports" field out of the nearest package.json, parsing it
1114/// into an appropriate [AliasMap] for lookups.
1115#[turbo_tasks::function]
1116async fn imports_field(lookup_path: FileSystemPath) -> Result<Vc<ImportsFieldResult>> {
1117    // We don't need to collect affecting sources here because we don't use them
1118    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                // TODO(PACK-4879): Add line-column information
1141                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        // Tailcall
1188        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// Same as find_context_file, but also stop for package.json with the specified key
1211// This function never collects affecting sources
1212#[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    // Only populated if collect_affecting_sources is true
1250    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// Resolves the pattern
1410#[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        // Checks if this pattern is more specific than everything, so we test using a random path
1471        // that is unlikely to actually exist
1472        .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        // You can't await multiple times in the span macro call parameters.
1527        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        // You can't await multiple times in the span macro call parameters.
1736        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        // Apply import mappings if provided
1745        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                    // We might have matched an alias in the import map, but there is no guarantee
1768                    // the alias actually resolves to something. For instance, a tsconfig.json
1769                    // `compilerOptions.paths` option might alias "@*" to "./*", which
1770                    // would also match a request to "@emotion/core". Here, we follow what the
1771                    // Typescript resolution algorithm does in case an alias match
1772                    // doesn't resolve to anything: fall back to resolving the request normally.
1773                    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                // Behave like Request::Uri
1958                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        // The individual variants inside the alternative already looked at the fallback import
2021        // map in the recursive `resolve_internal_inline` calls
2022        if !matches!(*request_value, Request::Alternatives { .. }) {
2023            // Apply fallback import mappings if provided
2024            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                        // main field will always resolve not fully specified
2090                        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                        // we are not that strict when a main field fails to resolve
2100                        // we continue to try other alternatives
2101                        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    // fall back to dir/index.[js,ts,...]
2126    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    // Check alias field for aliases first
2165    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        // Add the extensions as alternatives to the path
2195        // read_matches keeps the order of alternatives intact
2196        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 the fragment is not empty, we need to strip it from the matched
2270                        // pattern
2271                        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 the fragment is not empty, we need to strip it from the matched pattern
2311                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    // Directory matches must be resolved AFTER file matches
2347    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    // Check alias field for module aliases first
2368    for in_package in options_value.in_package.iter() {
2369        // resolve_module_request is called when importing a node
2370        // module, not a PackageInternal one, so the imports field
2371        // doesn't apply.
2372        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                // This would be a cycle, so we ignore it
2427                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]
2474/// Finds the nearest folder containing package.json that could be used for a
2475/// self-reference (i.e. has an exports fields).
2476async 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    // Check alias field for module aliases first
2509    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    // Self references, if the nearest package.json has the name of the requested
2528    // module. This should match only using the exports field and no other
2529    // fields/fallbacks.
2530    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    // There may be more than one package with the same name. For instance, in a
2561    // TypeScript project, `compilerOptions.baseUrl` can declare a path where to
2562    // resolve packages. A request to "foo/bar" might resolve to either
2563    // "[baseUrl]/foo/bar" or "[baseUrl]/node_modules/foo/bar", and we'll need to
2564    // try both.
2565    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            // handled by the `resolve_into_folder` call below
2642            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                // other options do not apply anymore when an exports
2669                // field exist
2670                return Ok(merge_results(results));
2671            }
2672        }
2673    }
2674
2675    // apply main field(s) or fallback to index.js if there's no subpath
2676    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            // We must avoid cycles during resolving
2714            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            // We must avoid cycles during resolving
2740            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                        // TODO is that root correct?
2750                        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    // Check alias field for path aliases first
2813    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            // Match found, stop (leveraging the lazy `lookup` iterator).
2900            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                        // - `req` is the user's request (key of the export map)
2934                        // - `result_path` is the final request (value of the export map), so
2935                        //   effectively `'{foo}*{bar}'`
2936
2937                        // Because of the assertion in AliasMapLookupIterator, `req` is of the
2938                        // form:
2939                        // - "prefix...<dynamic>" or
2940                        // - "prefix...<dynamic>...suffix"
2941
2942                        let mut old_request_key = result_path;
2943                        // Remove the Pattern::Constant(rcstr!("./")), from above again
2944                        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    // other options do not apply anymore when an exports field exist
2967    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
2975/// Resolves a `#dep` import using the containing package.json's `imports`
2976/// field. The dep may be a constant string or a pattern, and the values can be
2977/// static strings or conditions like `import` or `require` to handle ESM/CJS
2978/// with differently compiled files.
2979async 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    // https://github.com/nodejs/node/blob/1b177932/lib/internal/modules/esm/resolve.js#L615-L619
2991    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/// ModulePart represents a part of a module.
3177///
3178/// Currently this is used only for ESMs.
3179#[derive(
3180    Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash, TraceRawVcs, TaskInput, NonLocalValue,
3181)]
3182pub enum ModulePart {
3183    /// Represents the side effects of a module. This part is evaluated even if
3184    /// all exports are unused.
3185    Evaluation,
3186    /// Represents an export of a module.
3187    Export(RcStr),
3188    /// Represents a renamed export of a module.
3189    RenamedExport {
3190        original_export: RcStr,
3191        export: RcStr,
3192    },
3193    /// Represents a namespace object of a module exported as named export.
3194    RenamedNamespace { export: RcStr },
3195    /// A pointer to a specific part.
3196    Internal(u32),
3197    /// The local declarations of a module.
3198    Locals,
3199    /// The whole exports of a module.
3200    Exports,
3201    /// A facade of the module behaving like the original, but referencing
3202    /// internal parts.
3203    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}