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