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