turbopack_core/resolve/
mod.rs

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