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