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