turbopack_core/resolve/
mod.rs

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