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 name: RcStr,
78 ty: ExternalType,
79 traced: Option<ResolvedVc<ModuleResolveResult>>,
80 },
81 Unknown(ResolvedVc<Box<dyn Source>>),
83 Ignore,
84 Error(ResolvedVc<RcStr>),
85 Empty,
86 Custom(u8),
87}
88
89impl ModuleResolveResultItem {
90 async fn as_module(&self) -> Result<Option<ResolvedVc<Box<dyn Module>>>> {
91 Ok(match *self {
92 ModuleResolveResultItem::Module(module) => Some(module),
93 ModuleResolveResultItem::Unknown(source) => {
94 emit_unknown_module_type_error(*source).await?;
95 None
96 }
97 ModuleResolveResultItem::Error(_err) => {
98 None
100 }
101 _ => None,
102 })
103 }
104}
105
106#[turbo_tasks::value]
107#[derive(Debug, Clone, Default, Hash)]
108pub enum ExportUsage {
109 Named(RcStr),
110 #[default]
112 All,
113 Evaluation,
115}
116
117impl Display for ExportUsage {
118 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
119 match self {
120 ExportUsage::Named(name) => write!(f, "export {name}"),
121 ExportUsage::All => write!(f, "all"),
122 ExportUsage::Evaluation => write!(f, "evaluation"),
123 }
124 }
125}
126
127#[turbo_tasks::value_impl]
128impl ExportUsage {
129 #[turbo_tasks::function]
130 pub fn all() -> Vc<Self> {
131 Self::All.cell()
132 }
133
134 #[turbo_tasks::function]
135 pub fn evaluation() -> Vc<Self> {
136 Self::Evaluation.cell()
137 }
138
139 #[turbo_tasks::function]
140 pub fn named(name: RcStr) -> Vc<Self> {
141 Self::Named(name).cell()
142 }
143}
144
145#[turbo_tasks::value(shared)]
146#[derive(Clone)]
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 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 #[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 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 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 #[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 name: RcStr,
510 ty: ExternalType,
511 traced: ExternalTraced,
512 },
513 Ignore,
514 Error(ResolvedVc<RcStr>),
515 Empty,
516 Custom(u8),
517}
518
519#[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 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 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 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 #[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 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 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 #[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 #[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 #[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#[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 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#[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 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 Ok(find_context_file(
1303 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#[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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 for in_package in options_value.in_package.iter() {
2391 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 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]
2491async 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 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 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 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 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 return Ok(merge_results(results));
2696 }
2697 }
2698 }
2699
2700 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 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 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 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 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 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
2952async 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 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#[derive(
3157 Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash, TraceRawVcs, TaskInput, NonLocalValue,
3158)]
3159pub enum ModulePart {
3160 Evaluation,
3163 Export(RcStr),
3165 RenamedExport {
3167 original_export: RcStr,
3168 export: RcStr,
3169 },
3170 RenamedNamespace { export: RcStr },
3172 Internal(u32),
3174 Locals,
3176 Exports,
3178 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}