1#![cfg_attr(feature = "document-features", doc = document_features::document_features!())]
42#![cfg_attr(docsrs, feature(doc_auto_cfg))]
43#![doc = include_str!("../examples/resolver.rs")]
48mod builtins;
51mod cache;
52pub mod context;
53mod error;
54#[cfg(feature = "fs_cache")]
55mod file_system;
56#[cfg(feature = "fs_cache")]
57mod fs_cache;
58mod options;
59mod package_json;
60#[cfg(feature = "fs_cache")]
61mod package_json_serde;
62mod path;
63mod resolution;
64mod specifier;
65mod tsconfig;
66#[cfg(feature = "fs_cache")]
67mod tsconfig_serde;
68#[cfg(target_os = "windows")]
69mod windows;
70
71#[cfg(test)]
72mod tests;
73
74use dashmap::{DashMap, mapref::one::Ref};
75use rustc_hash::FxHashSet;
76use std::{
77 borrow::Cow,
78 cmp::Ordering,
79 ffi::OsStr,
80 fmt,
81 path::{Component, Path, PathBuf},
82 sync::Arc,
83};
84
85#[cfg(feature = "fs_cache")]
86pub use crate::{
87 file_system::{FileMetadata, FileSystem, FileSystemOs},
88 fs_cache::{FsCache, FsCachedPath},
89 package_json_serde::PackageJsonSerde,
90 tsconfig_serde::{CompilerOptionsSerde, ExtendsField, ProjectReferenceSerde, TsConfigSerde},
91};
92
93#[cfg(feature = "fs_cache")]
94pub type FsResolution = Resolution<FsCache<FileSystemOs>>;
95
96pub use crate::{
97 builtins::NODEJS_BUILTINS,
98 cache::{Cache, CachedPath},
99 error::{JSONError, ResolveError, SpecifierError},
100 options::{
101 Alias, AliasValue, EnforceExtension, ResolveOptions, Restriction, TsconfigOptions,
102 TsconfigReferences,
103 },
104 package_json::{
105 ImportsExportsArray, ImportsExportsEntry, ImportsExportsKind, ImportsExportsMap,
106 PackageJson, PackageType,
107 },
108 path::PathUtil,
109 resolution::Resolution,
110 tsconfig::{CompilerOptions, CompilerOptionsPathsMap, ProjectReference, TsConfig},
111};
112use crate::{context::ResolveContext as Ctx, path::SLASH_START, specifier::Specifier};
113
114type ResolveResult<Cp> = Result<Option<Cp>, ResolveError>;
115
116#[derive(Debug, Default, Clone)]
118pub struct ResolveContext {
119 pub file_dependencies: FxHashSet<PathBuf>,
121
122 pub missing_dependencies: FxHashSet<PathBuf>,
124}
125
126#[cfg(feature = "fs_cache")]
128pub type Resolver = ResolverGeneric<FsCache<FileSystemOs>>;
129
130pub struct ResolverGeneric<C: Cache> {
132 options: ResolveOptions,
133 cache: Arc<C>,
134 #[cfg(feature = "yarn_pnp")]
135 pnp_cache: Arc<DashMap<FsCachedPath, Option<pnp::Manifest>>>,
136}
137
138impl<C: Cache> fmt::Debug for ResolverGeneric<C> {
139 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140 self.options.fmt(f)
141 }
142}
143
144impl<C: Cache + Default> Default for ResolverGeneric<C> {
145 fn default() -> Self {
146 Self::new(ResolveOptions::default())
147 }
148}
149
150impl<C: Cache + Default> ResolverGeneric<C> {
151 #[must_use]
152 pub fn new(options: ResolveOptions) -> Self {
153 Self {
154 options: options.sanitize(),
155 cache: Arc::new(C::default()),
156 #[cfg(feature = "yarn_pnp")]
157 pnp_cache: Arc::new(DashMap::default()),
158 }
159 }
160}
161
162impl<C: Cache<Cp = FsCachedPath>> ResolverGeneric<C> {
163 pub fn new_with_cache(cache: Arc<C>, options: ResolveOptions) -> Self {
164 Self {
165 cache,
166 options: options.sanitize(),
167 #[cfg(feature = "yarn_pnp")]
168 pnp_cache: Arc::new(DashMap::default()),
169 }
170 }
171
172 #[must_use]
174 pub fn clone_with_options(&self, options: ResolveOptions) -> Self {
175 Self {
176 options: options.sanitize(),
177 cache: Arc::clone(&self.cache),
178 #[cfg(feature = "yarn_pnp")]
179 pnp_cache: Arc::clone(&self.pnp_cache),
180 }
181 }
182
183 #[must_use]
185 pub const fn options(&self) -> &ResolveOptions {
186 &self.options
187 }
188
189 pub fn clear_cache(&self) {
191 self.cache.clear();
192 }
193
194 pub fn resolve<P: AsRef<Path>>(
206 &self,
207 directory: P,
208 specifier: &str,
209 ) -> Result<Resolution<C>, ResolveError> {
210 let mut ctx = Ctx::default();
211 self.resolve_tracing(directory.as_ref(), specifier, &mut ctx)
212 }
213
214 pub fn resolve_tsconfig<P: AsRef<Path>>(&self, path: P) -> Result<Arc<C::Tc>, ResolveError> {
226 let path = path.as_ref();
227 self.load_tsconfig(true, path, &TsconfigReferences::Auto)
228 }
229
230 pub fn resolve_with_context<P: AsRef<Path>>(
236 &self,
237 directory: P,
238 specifier: &str,
239 resolve_context: &mut ResolveContext,
240 ) -> Result<Resolution<C>, ResolveError> {
241 let mut ctx = Ctx::default();
242 ctx.init_file_dependencies();
243 let result = self.resolve_tracing(directory.as_ref(), specifier, &mut ctx);
244 if let Some(deps) = &mut ctx.file_dependencies {
245 resolve_context.file_dependencies.extend(deps.drain(..));
246 }
247 if let Some(deps) = &mut ctx.missing_dependencies {
248 resolve_context.missing_dependencies.extend(deps.drain(..));
249 }
250 result
251 }
252
253 fn resolve_tracing(
255 &self,
256 directory: &Path,
257 specifier: &str,
258 ctx: &mut Ctx,
259 ) -> Result<Resolution<C>, ResolveError> {
260 let span = tracing::debug_span!("resolve", path = ?directory, specifier = specifier);
261 let _enter = span.enter();
262 let r = self.resolve_impl(directory, specifier, ctx);
263 match &r {
264 Ok(r) => {
265 tracing::debug!(options = ?self.options, path = ?directory, specifier = specifier, ret = ?r.path);
266 }
267 Err(err) => {
268 tracing::debug!(options = ?self.options, path = ?directory, specifier = specifier, err = ?err);
269 }
270 }
271 r
272 }
273
274 fn resolve_impl(
275 &self,
276 path: &Path,
277 specifier: &str,
278 ctx: &mut Ctx,
279 ) -> Result<Resolution<C>, ResolveError> {
280 ctx.with_fully_specified(self.options.fully_specified);
281 let cached_path = self.cache.value(path);
282 let cached_path = self.require(&cached_path, specifier, ctx)?;
283 let path = self.load_realpath(&cached_path)?;
284 self.check_restrictions(&path)?;
286 let package_json =
287 cached_path.find_package_json(&self.options, self.cache.as_ref(), ctx)?;
288 if let Some((_, package_json)) = &package_json {
289 debug_assert!(path.starts_with(package_json.directory()));
291 }
292 Ok(Resolution {
293 path,
294 query: ctx.query.take(),
295 fragment: ctx.fragment.take(),
296 package_json: package_json.map(|(_, p)| p),
297 })
298 }
299
300 fn require(
307 &self,
308 cached_path: &C::Cp,
309 specifier: &str,
310 ctx: &mut Ctx,
311 ) -> Result<C::Cp, ResolveError> {
312 ctx.test_for_infinite_recursion()?;
313
314 let (parsed, try_fragment_as_path) = self.load_parse(cached_path, specifier, ctx)?;
316 if let Some(path) = try_fragment_as_path {
317 return Ok(path);
318 }
319
320 self.require_without_parse(cached_path, parsed.path(), ctx)
321 }
322
323 fn require_without_parse(
324 &self,
325 cached_path: &C::Cp,
326 specifier: &str,
327 ctx: &mut Ctx,
328 ) -> Result<C::Cp, ResolveError> {
329 if let Some(path) = self.load_tsconfig_paths(cached_path, specifier, &mut Ctx::default())? {
331 return Ok(path);
332 }
333
334 if let Some(path) = self.load_alias(cached_path, specifier, &self.options.alias, ctx)? {
336 return Ok(path);
337 }
338
339 let result = match Path::new(specifier).components().next() {
340 Some(Component::RootDir | Component::Prefix(_)) => {
342 self.require_absolute(cached_path, specifier, ctx)
343 }
344 Some(Component::CurDir | Component::ParentDir) => {
346 self.require_relative(cached_path, specifier, ctx)
347 }
348 Some(Component::Normal(_)) if specifier.as_bytes()[0] == b'#' => {
350 self.require_hash(cached_path, specifier, ctx)
351 }
352 _ => {
353 self.require_core(specifier)?;
357
358 self.require_bare(cached_path, specifier, ctx)
362 }
363 };
364
365 result.or_else(|err| {
366 if err.is_ignore() {
367 return Err(err);
368 }
369 self.load_alias(cached_path, specifier, &self.options.fallback, ctx)
371 .and_then(|value| value.ok_or(err))
372 })
373 }
374
375 fn require_core(&self, specifier: &str) -> Result<(), ResolveError> {
379 if self.options.builtin_modules {
380 let is_runtime_module = specifier.starts_with("node:");
381 if is_runtime_module || NODEJS_BUILTINS.binary_search(&specifier).is_ok() {
382 let resolved = if is_runtime_module {
383 specifier.to_string()
384 } else {
385 format!("node:{specifier}")
386 };
387 return Err(ResolveError::Builtin { resolved, is_runtime_module });
388 }
389 }
390 Ok(())
391 }
392
393 fn require_absolute(
394 &self,
395 cached_path: &C::Cp,
396 specifier: &str,
397 ctx: &mut Ctx,
398 ) -> Result<C::Cp, ResolveError> {
399 debug_assert!(
401 Path::new(specifier)
402 .components()
403 .next()
404 .is_some_and(|c| matches!(c, Component::RootDir | Component::Prefix(_)))
405 );
406 if !self.options.prefer_relative && self.options.prefer_absolute {
407 if let Ok(path) = self.load_package_self_or_node_modules(cached_path, specifier, ctx) {
408 return Ok(path);
409 }
410 }
411 if let Some(path) = self.load_roots(cached_path, specifier, ctx) {
412 return Ok(path);
413 }
414 let path = self.cache.value(Path::new(specifier));
417 if let Some(path) = self.load_as_file_or_directory(&path, specifier, ctx)? {
418 return Ok(path);
419 }
420 Err(ResolveError::NotFound(specifier.to_string()))
421 }
422
423 fn require_relative(
425 &self,
426 cached_path: &C::Cp,
427 specifier: &str,
428 ctx: &mut Ctx,
429 ) -> Result<C::Cp, ResolveError> {
430 debug_assert!(Path::new(specifier).components().next().is_some_and(|c| matches!(
432 c,
433 Component::CurDir | Component::ParentDir | Component::Normal(_)
434 )));
435 let cached_path = cached_path.normalize_with(specifier, self.cache.as_ref());
436 if let Some(path) = self.load_as_file_or_directory(&cached_path, specifier, ctx)? {
439 return Ok(path);
440 }
441 Err(ResolveError::NotFound(specifier.to_string()))
443 }
444
445 fn require_hash(
446 &self,
447 cached_path: &C::Cp,
448 specifier: &str,
449 ctx: &mut Ctx,
450 ) -> Result<C::Cp, ResolveError> {
451 debug_assert_eq!(specifier.chars().next(), Some('#'));
452 if let Some(path) = self.load_package_imports(cached_path, specifier, ctx)? {
454 return Ok(path);
455 }
456 self.load_package_self_or_node_modules(cached_path, specifier, ctx)
457 }
458
459 fn require_bare(
460 &self,
461 cached_path: &C::Cp,
462 specifier: &str,
463 ctx: &mut Ctx,
464 ) -> Result<C::Cp, ResolveError> {
465 debug_assert!(
467 Path::new(specifier)
468 .components()
469 .next()
470 .is_some_and(|c| matches!(c, Component::Normal(_)))
471 );
472 if self.options.prefer_relative {
473 if let Ok(path) = self.require_relative(cached_path, specifier, ctx) {
474 return Ok(path);
475 }
476 }
477 self.load_package_self_or_node_modules(cached_path, specifier, ctx)
478 }
479
480 fn load_parse<'s>(
489 &self,
490 cached_path: &C::Cp,
491 specifier: &'s str,
492 ctx: &mut Ctx,
493 ) -> Result<(Specifier<'s>, Option<C::Cp>), ResolveError> {
494 let parsed = Specifier::parse(specifier).map_err(ResolveError::Specifier)?;
495 ctx.with_query_fragment(parsed.query, parsed.fragment);
496
497 if ctx.fragment.is_some() && ctx.query.is_none() {
499 let specifier = parsed.path();
500 let fragment = ctx.fragment.take().unwrap();
501 let path = format!("{specifier}{fragment}");
502 if let Ok(path) = self.require_without_parse(cached_path, &path, ctx) {
503 return Ok((parsed, Some(path)));
504 }
505 ctx.fragment.replace(fragment);
506 }
507 Ok((parsed, None))
508 }
509
510 fn load_package_self_or_node_modules(
511 &self,
512 cached_path: &C::Cp,
513 specifier: &str,
514 ctx: &mut Ctx,
515 ) -> Result<C::Cp, ResolveError> {
516 let (package_name, subpath) = Self::parse_package_specifier(specifier);
517 if subpath.is_empty() {
518 ctx.with_fully_specified(false);
519 }
520 if let Some(path) = self.load_package_self(cached_path, specifier, ctx)? {
522 return Ok(path);
523 }
524 if let Some(path) =
526 self.load_node_modules(cached_path, specifier, package_name, subpath, ctx)?
527 {
528 return Ok(path);
529 }
530 Err(ResolveError::NotFound(specifier.to_string()))
532 }
533
534 fn load_package_imports(
536 &self,
537 cached_path: &C::Cp,
538 specifier: &str,
539 ctx: &mut Ctx,
540 ) -> ResolveResult<C::Cp> {
541 let Some((_, package_json)) =
544 cached_path.find_package_json(&self.options, self.cache.as_ref(), ctx)?
545 else {
546 return Ok(None);
547 };
548 if let Some(path) = self.package_imports_resolve(specifier, &package_json, ctx)? {
551 return self.resolve_esm_match(specifier, &path, ctx);
553 }
554 Ok(None)
555 }
556
557 fn load_as_file(&self, cached_path: &C::Cp, ctx: &mut Ctx) -> ResolveResult<C::Cp> {
558 if let Some(path) = self.load_extension_alias(cached_path, ctx)? {
560 return Ok(Some(path));
561 }
562 if self.options.enforce_extension.is_disabled() {
563 if let Some(path) = self.load_alias_or_file(cached_path, ctx)? {
565 return Ok(Some(path));
566 }
567 }
568 if let Some(path) = self.load_extensions(cached_path, &self.options.extensions, ctx)? {
572 return Ok(Some(path));
573 }
574 Ok(None)
575 }
576
577 fn load_as_directory(&self, cached_path: &C::Cp, ctx: &mut Ctx) -> ResolveResult<C::Cp> {
578 if !self.options.description_files.is_empty() {
582 if let Some((_, package_json)) =
584 self.cache.get_package_json(cached_path, &self.options, ctx)?
585 {
586 for main_field in package_json.main_fields(&self.options.main_fields) {
588 let main_field =
590 if main_field.starts_with("./") || main_field.starts_with("../") {
591 Cow::Borrowed(main_field)
592 } else {
593 Cow::Owned(format!("./{main_field}"))
594 };
595
596 let cached_path =
598 cached_path.normalize_with(main_field.as_ref(), self.cache.as_ref());
599 if let Some(path) = self.load_as_file(&cached_path, ctx)? {
601 return Ok(Some(path));
602 }
603 if let Some(path) = self.load_index(&cached_path, ctx)? {
605 return Ok(Some(path));
606 }
607 }
608 }
611 }
612 self.load_index(cached_path, ctx)
614 }
615
616 fn load_as_file_or_directory(
617 &self,
618 cached_path: &C::Cp,
619 specifier: &str,
620 ctx: &mut Ctx,
621 ) -> ResolveResult<C::Cp> {
622 if self.options.resolve_to_context {
623 return Ok(self.cache.is_dir(cached_path, ctx).then(|| cached_path.clone()));
624 }
625 if !specifier.ends_with('/') {
626 if let Some(path) = self.load_as_file(cached_path, ctx)? {
627 return Ok(Some(path));
628 }
629 }
630 if self.cache.is_dir(cached_path, ctx) {
631 if let Some(path) = self.load_as_directory(cached_path, ctx)? {
632 return Ok(Some(path));
633 }
634 }
635 Ok(None)
636 }
637
638 fn load_extensions(
639 &self,
640 path: &C::Cp,
641 extensions: &[String],
642 ctx: &mut Ctx,
643 ) -> ResolveResult<C::Cp> {
644 if ctx.fully_specified {
645 return Ok(None);
646 }
647 for extension in extensions {
648 let cached_path = path.add_extension(extension, self.cache.as_ref());
649 if let Some(path) = self.load_alias_or_file(&cached_path, ctx)? {
650 return Ok(Some(path));
651 }
652 }
653 Ok(None)
654 }
655
656 fn load_realpath(&self, cached_path: &C::Cp) -> Result<PathBuf, ResolveError> {
657 if self.options.symlinks {
658 self.cache.canonicalize(cached_path)
659 } else {
660 Ok(cached_path.to_path_buf())
661 }
662 }
663
664 fn check_restrictions(&self, path: &Path) -> Result<(), ResolveError> {
665 fn is_inside(path: &Path, parent: &Path) -> bool {
667 if !path.starts_with(parent) {
668 return false;
669 }
670 if path.as_os_str().len() == parent.as_os_str().len() {
671 return true;
672 }
673 path.strip_prefix(parent).is_ok_and(|p| p == Path::new("./"))
674 }
675 for restriction in &self.options.restrictions {
676 match restriction {
677 Restriction::Path(restricted_path) => {
678 if !is_inside(path, restricted_path) {
679 return Err(ResolveError::Restriction(
680 path.to_path_buf(),
681 restricted_path.clone(),
682 ));
683 }
684 }
685 Restriction::RegExp(_) => {
686 return Err(ResolveError::Unimplemented("Restriction with regex"));
687 }
688 }
689 }
690 Ok(())
691 }
692
693 fn load_index(&self, cached_path: &C::Cp, ctx: &mut Ctx) -> ResolveResult<C::Cp> {
694 for main_file in &self.options.main_files {
695 let cached_path = cached_path.normalize_with(main_file, self.cache.as_ref());
696 if self.options.enforce_extension.is_disabled() {
697 if let Some(path) = self.load_alias_or_file(&cached_path, ctx)? {
698 return Ok(Some(path));
699 }
700 }
701 if let Some(path) = self.load_extensions(&cached_path, &self.options.extensions, ctx)? {
705 return Ok(Some(path));
706 }
707 }
708 Ok(None)
709 }
710
711 fn load_browser_field_or_alias(
712 &self,
713 cached_path: &C::Cp,
714 ctx: &mut Ctx,
715 ) -> ResolveResult<C::Cp> {
716 if !self.options.alias_fields.is_empty() {
717 if let Some((package_url, package_json)) =
718 cached_path.find_package_json(&self.options, self.cache.as_ref(), ctx)?
719 {
720 if let Some(path) =
721 self.load_browser_field(cached_path, None, &package_url, &package_json, ctx)?
722 {
723 return Ok(Some(path));
724 }
725 }
726 }
727 if !self.options.alias.is_empty() {
730 let alias_specifier = cached_path.path().to_string_lossy();
731 if let Some(path) =
732 self.load_alias(cached_path, &alias_specifier, &self.options.alias, ctx)?
733 {
734 return Ok(Some(path));
735 }
736 }
737 Ok(None)
738 }
739
740 fn load_alias_or_file(&self, cached_path: &C::Cp, ctx: &mut Ctx) -> ResolveResult<C::Cp> {
741 if let Some(path) = self.load_browser_field_or_alias(cached_path, ctx)? {
742 return Ok(Some(path));
743 }
744 if self.cache.is_file(cached_path, ctx) {
745 return Ok(Some(cached_path.clone()));
746 }
747 Ok(None)
748 }
749
750 fn load_node_modules(
751 &self,
752 cached_path: &C::Cp,
753 specifier: &str,
754 package_name: &str,
755 subpath: &str,
756 ctx: &mut Ctx,
757 ) -> ResolveResult<C::Cp> {
758 #[cfg(feature = "yarn_pnp")]
759 if self.options.enable_pnp {
760 if let Some(resolved_path) = self.load_pnp(cached_path, specifier, ctx)? {
761 return Ok(Some(resolved_path));
762 }
763 }
764
765 for module_name in &self.options.modules {
768 for cached_path in std::iter::successors(Some(cached_path), |p| p.parent()) {
769 if !self.cache.is_dir(cached_path, ctx) {
771 continue;
772 }
773
774 let Some(cached_path) = self.get_module_directory(cached_path, module_name, ctx)
775 else {
776 continue;
777 };
778 if !package_name.is_empty() {
783 let cached_path = cached_path.normalize_with(package_name, self.cache.as_ref());
784 if self.cache.is_dir(&cached_path, ctx) {
786 if let Some(path) =
788 self.load_package_exports(specifier, subpath, &cached_path, ctx)?
789 {
790 return Ok(Some(path));
791 }
792 } else {
793 if !subpath.is_empty() {
795 continue;
796 }
797 if package_name.starts_with('@') {
800 if let Some(path) = cached_path.parent() {
801 if !self.cache.is_dir(path, ctx) {
802 continue;
803 }
804 }
805 }
806 }
807 }
808
809 let cached_path = cached_path.normalize_with(specifier, self.cache.as_ref());
814
815 if self.options.resolve_to_context {
817 return Ok(self.cache.is_dir(&cached_path, ctx).then(|| cached_path.clone()));
818 }
819 if self.cache.is_dir(&cached_path, ctx) {
820 if let Some(path) = self.load_browser_field_or_alias(&cached_path, ctx)? {
821 return Ok(Some(path));
822 }
823 if let Some(path) = self.load_as_directory(&cached_path, ctx)? {
824 return Ok(Some(path));
825 }
826 }
827 if let Some(path) = self.load_as_file(&cached_path, ctx)? {
828 return Ok(Some(path));
829 }
830 if let Some(path) = self.load_as_directory(&cached_path, ctx)? {
831 return Ok(Some(path));
832 }
833 }
834 }
835 Ok(None)
836 }
837
838 #[cfg(feature = "yarn_pnp")]
839 fn find_pnp_manifest(&self, cached_path: &C::Cp) -> Ref<'_, C::Cp, Option<pnp::Manifest>> {
840 let entry = self
841 .pnp_cache
842 .entry(cached_path.clone())
843 .or_insert_with(|| pnp::find_pnp_manifest(cached_path.path()).unwrap());
844
845 entry.downgrade()
846 }
847
848 #[cfg(feature = "yarn_pnp")]
849 fn load_pnp(
850 &self,
851 cached_path: &C::Cp,
852 specifier: &str,
853 ctx: &mut Ctx,
854 ) -> Result<Option<C::Cp>, ResolveError> {
855 let pnp_manifest = self.find_pnp_manifest(cached_path);
856
857 if let Some(pnp_manifest) = pnp_manifest.as_ref() {
858 if specifier == "pnpapi" {
860 return Ok(Some(self.cache.value(pnp_manifest.manifest_path.as_path())));
861 }
862
863 let mut path = cached_path.to_path_buf();
865 path.push("");
866
867 let resolution =
868 pnp::resolve_to_unqualified_via_manifest(pnp_manifest, specifier, path);
869
870 match resolution {
871 Ok(pnp::Resolution::Resolved(path, subpath)) => {
872 let cached_path = self.cache.value(&path);
873 let cached_path_string = cached_path.path().to_string_lossy();
874
875 let export_resolution = self.load_package_self(&cached_path, specifier, ctx)?;
876 if export_resolution.is_some() {
878 return Ok(export_resolution);
879 }
880
881 let pkg_name = cached_path_string.rsplit_once("node_modules/").map_or(
883 "",
884 |last| last.1.strip_suffix("/").unwrap_or(last.1),
886 );
887
888 let inner_request = if pkg_name.is_empty() {
889 subpath.map_or_else(
890 || ".".to_string(),
891 |mut p| {
892 p.insert_str(0, "./");
893 p
894 },
895 )
896 } else {
897 let inner_specifier = specifier.strip_prefix(pkg_name).unwrap();
898 String::from("./")
899 + inner_specifier.strip_prefix("/").unwrap_or(inner_specifier)
900 };
901
902 if let Ok(Some(result)) = self.load_as_directory(
905 &self.cache.value(&path.join(inner_request.clone()).normalize()),
906 ctx,
907 ) {
908 return Ok(Some(result));
909 }
910
911 let inner_resolver = self.clone_with_options(self.options().clone());
912
913 let Ok(inner_resolution) = inner_resolver.resolve(&path, &inner_request) else {
915 return Err(ResolveError::NotFound(specifier.to_string()));
916 };
917
918 Ok(Some(self.cache.value(inner_resolution.path())))
919 }
920
921 Ok(pnp::Resolution::Skipped) => Ok(None),
922 Err(_) => Err(ResolveError::NotFound(specifier.to_string())),
923 }
924 } else {
925 Ok(None)
926 }
927 }
928
929 fn get_module_directory(
930 &self,
931 cached_path: &C::Cp,
932 module_name: &str,
933 ctx: &mut Ctx,
934 ) -> Option<C::Cp> {
935 if module_name == "node_modules" {
936 cached_path.cached_node_modules(self.cache.as_ref(), ctx)
937 } else if cached_path.path().components().next_back()
938 == Some(Component::Normal(OsStr::new(module_name)))
939 {
940 Some(cached_path.clone())
941 } else {
942 cached_path.module_directory(module_name, self.cache.as_ref(), ctx)
943 }
944 }
945
946 fn load_package_exports(
947 &self,
948 specifier: &str,
949 subpath: &str,
950 cached_path: &C::Cp,
951 ctx: &mut Ctx,
952 ) -> ResolveResult<C::Cp> {
953 let Some((_, package_json)) =
956 self.cache.get_package_json(cached_path, &self.options, ctx)?
957 else {
958 return Ok(None);
959 };
960 for exports in package_json.exports_fields(&self.options.exports_fields) {
966 if let Some(path) =
967 self.package_exports_resolve(cached_path, &format!(".{subpath}"), &exports, ctx)?
968 {
969 return self.resolve_esm_match(specifier, &path, ctx);
971 }
972 }
973 Ok(None)
974 }
975
976 fn load_package_self(
977 &self,
978 cached_path: &C::Cp,
979 specifier: &str,
980 ctx: &mut Ctx,
981 ) -> ResolveResult<C::Cp> {
982 let Some((package_url, package_json)) =
985 cached_path.find_package_json(&self.options, self.cache.as_ref(), ctx)?
986 else {
987 return Ok(None);
988 };
989 if let Some(subpath) = package_json
992 .name()
993 .and_then(|package_name| Self::strip_package_name(specifier, package_name))
994 {
995 for exports in package_json.exports_fields(&self.options.exports_fields) {
1001 if let Some(cached_path) = self.package_exports_resolve(
1002 &package_url,
1003 &format!(".{subpath}"),
1004 &exports,
1005 ctx,
1006 )? {
1007 return self.resolve_esm_match(specifier, &cached_path, ctx);
1009 }
1010 }
1011 }
1012 self.load_browser_field(cached_path, Some(specifier), &package_url, &package_json, ctx)
1013 }
1014
1015 fn resolve_esm_match(
1017 &self,
1018 specifier: &str,
1019 cached_path: &C::Cp,
1020 ctx: &mut Ctx,
1021 ) -> ResolveResult<C::Cp> {
1022 if let Some(path) = self.load_as_file_or_directory(cached_path, "", ctx)? {
1027 return Ok(Some(path));
1028 }
1029
1030 Err(ResolveError::NotFound(specifier.to_string()))
1032 }
1033
1034 fn load_browser_field(
1036 &self,
1037 cached_path: &C::Cp,
1038 module_specifier: Option<&str>,
1039 package_url: &C::Cp,
1040 package_json: &C::Pj,
1041 ctx: &mut Ctx,
1042 ) -> ResolveResult<C::Cp> {
1043 let path = cached_path.path();
1044 let Some(new_specifier) = package_json.resolve_browser_field(
1045 path,
1046 module_specifier,
1047 &self.options.alias_fields,
1048 )?
1049 else {
1050 return Ok(None);
1051 };
1052 if module_specifier.is_some_and(|s| s == new_specifier) {
1054 return Ok(None);
1055 }
1056 if ctx.resolving_alias.as_ref().is_some_and(|s| s == new_specifier) {
1057 if new_specifier.strip_prefix("./").filter(|s| path.ends_with(Path::new(s))).is_some() {
1059 return if self.cache.is_file(cached_path, ctx) {
1060 Ok(Some(cached_path.clone()))
1061 } else {
1062 Err(ResolveError::NotFound(new_specifier.to_string()))
1063 };
1064 }
1065 return Err(ResolveError::Recursion);
1066 }
1067 ctx.with_resolving_alias(new_specifier.to_string());
1068 ctx.with_fully_specified(false);
1069 self.require(package_url, new_specifier, ctx).map(Some)
1070 }
1071
1072 fn load_alias(
1074 &self,
1075 cached_path: &C::Cp,
1076 specifier: &str,
1077 aliases: &Alias,
1078 ctx: &mut Ctx,
1079 ) -> ResolveResult<C::Cp> {
1080 for (alias_key_raw, specifiers) in aliases {
1081 let mut alias_key_has_wildcard = false;
1082 let alias_key = if let Some(alias_key) = alias_key_raw.strip_suffix('$') {
1083 if alias_key != specifier {
1084 continue;
1085 }
1086 alias_key
1087 } else if alias_key_raw.contains('*') {
1088 alias_key_has_wildcard = true;
1089 alias_key_raw
1090 } else {
1091 let strip_package_name = Self::strip_package_name(specifier, alias_key_raw);
1092 if strip_package_name.is_none() {
1093 continue;
1094 }
1095 alias_key_raw
1096 };
1097 let mut should_stop = false;
1101 for r in specifiers {
1102 match r {
1103 AliasValue::Path(alias_value) => {
1104 if let Some(path) = self.load_alias_value(
1105 cached_path,
1106 alias_key,
1107 alias_key_has_wildcard,
1108 alias_value,
1109 specifier,
1110 ctx,
1111 &mut should_stop,
1112 )? {
1113 return Ok(Some(path));
1114 }
1115 }
1116 AliasValue::Ignore => {
1117 let cached_path =
1118 cached_path.normalize_with(alias_key, self.cache.as_ref());
1119 return Err(ResolveError::Ignored(cached_path.to_path_buf()));
1120 }
1121 }
1122 }
1123 if should_stop {
1124 return Err(ResolveError::MatchedAliasNotFound(
1125 specifier.to_string(),
1126 alias_key.to_string(),
1127 ));
1128 }
1129 }
1130 Ok(None)
1131 }
1132
1133 #[allow(clippy::too_many_arguments)]
1134 fn load_alias_value(
1135 &self,
1136 cached_path: &C::Cp,
1137 alias_key: &str,
1138 alias_key_has_wild_card: bool,
1139 alias_value: &str,
1140 request: &str,
1141 ctx: &mut Ctx,
1142 should_stop: &mut bool,
1143 ) -> ResolveResult<C::Cp> {
1144 if request != alias_value
1145 && !request.strip_prefix(alias_value).is_some_and(|prefix| prefix.starts_with('/'))
1146 {
1147 let new_specifier = if alias_key_has_wild_card {
1148 let Some(alias_key) = alias_key.split_once('*').and_then(|(prefix, suffix)| {
1150 request
1151 .strip_prefix(prefix)
1152 .and_then(|specifier| specifier.strip_suffix(suffix))
1153 }) else {
1154 return Ok(None);
1155 };
1156 if alias_value.contains('*') {
1157 Cow::Owned(alias_value.replacen('*', alias_key, 1))
1158 } else {
1159 Cow::Borrowed(alias_value)
1160 }
1161 } else {
1162 let tail = &request[alias_key.len()..];
1163 if tail.is_empty() {
1164 Cow::Borrowed(alias_value)
1165 } else {
1166 let alias_path = Path::new(alias_value).normalize();
1167 let cached_alias_path = self.cache.value(&alias_path);
1169 if self.cache.is_file(&cached_alias_path, ctx) {
1170 return Ok(None);
1171 }
1172 let tail = tail.trim_start_matches(SLASH_START);
1174 if tail.is_empty() {
1175 Cow::Borrowed(alias_value)
1176 } else {
1177 let normalized = alias_path.normalize_with(tail);
1178 Cow::Owned(normalized.to_string_lossy().to_string())
1179 }
1180 }
1181 };
1182
1183 *should_stop = true;
1184 ctx.with_fully_specified(false);
1185 return match self.require(cached_path, new_specifier.as_ref(), ctx) {
1186 Err(ResolveError::NotFound(_) | ResolveError::MatchedAliasNotFound(_, _)) => {
1187 Ok(None)
1188 }
1189 Ok(path) => return Ok(Some(path)),
1190 Err(err) => return Err(err),
1191 };
1192 }
1193 Ok(None)
1194 }
1195
1196 fn load_extension_alias(&self, cached_path: &C::Cp, ctx: &mut Ctx) -> ResolveResult<C::Cp> {
1205 if self.options.extension_alias.is_empty() {
1206 return Ok(None);
1207 }
1208 let Some(path_extension) = cached_path.path().extension() else {
1209 return Ok(None);
1210 };
1211 let Some((_, extensions)) = self
1212 .options
1213 .extension_alias
1214 .iter()
1215 .find(|(ext, _)| OsStr::new(ext.trim_start_matches('.')) == path_extension)
1216 else {
1217 return Ok(None);
1218 };
1219 let path = cached_path.path();
1220 let Some(filename) = path.file_name() else { return Ok(None) };
1221 let path_without_extension = path.with_extension("");
1222
1223 ctx.with_fully_specified(true);
1224 for extension in extensions {
1225 let mut path_with_extension = path_without_extension.clone().into_os_string();
1226 path_with_extension.reserve_exact(extension.len());
1227 path_with_extension.push(extension);
1228 let cached_path = self.cache.value(Path::new(&path_with_extension));
1229 if let Some(path) = self.load_alias_or_file(&cached_path, ctx)? {
1230 ctx.with_fully_specified(false);
1231 return Ok(Some(path));
1232 }
1233 }
1234 if !self.cache.is_file(cached_path, ctx) {
1236 ctx.with_fully_specified(false);
1237 return Ok(None);
1238 }
1239 let dir = path.parent().unwrap().to_path_buf();
1241 let filename_without_extension = Path::new(filename).with_extension("");
1242 let filename_without_extension = filename_without_extension.to_string_lossy();
1243 let files = extensions
1244 .iter()
1245 .map(|ext| format!("{filename_without_extension}{ext}"))
1246 .collect::<Vec<_>>()
1247 .join(",");
1248 Err(ResolveError::ExtensionAlias(filename.to_string_lossy().to_string(), files, dir))
1249 }
1250
1251 fn load_roots(&self, cached_path: &C::Cp, specifier: &str, ctx: &mut Ctx) -> Option<C::Cp> {
1258 if self.options.roots.is_empty() {
1259 return None;
1260 }
1261 if let Some(specifier) = specifier.strip_prefix(SLASH_START) {
1262 if specifier.is_empty() {
1263 if self.options.roots.iter().any(|root| root.as_path() == cached_path.path()) {
1264 if let Ok(path) = self.require_relative(cached_path, "./", ctx) {
1265 return Some(path);
1266 }
1267 }
1268 } else {
1269 for root in &self.options.roots {
1270 let cached_path = self.cache.value(root);
1271 if let Ok(path) = self.require_relative(&cached_path, specifier, ctx) {
1272 return Some(path);
1273 }
1274 }
1275 }
1276 }
1277 None
1278 }
1279
1280 fn load_tsconfig(
1281 &self,
1282 root: bool,
1283 path: &Path,
1284 references: &TsconfigReferences,
1285 ) -> Result<Arc<C::Tc>, ResolveError> {
1286 self.cache.get_tsconfig(root, path, |tsconfig| {
1287 let directory = self.cache.value(tsconfig.directory());
1288 tracing::trace!(tsconfig = ?tsconfig, "load_tsconfig");
1289
1290 let extended_tsconfig_paths = tsconfig
1292 .extends()
1293 .map(|specifier| self.get_extended_tsconfig_path(&directory, tsconfig, specifier))
1294 .collect::<Result<Vec<_>, _>>()?;
1295 for extended_tsconfig_path in extended_tsconfig_paths {
1296 let extended_tsconfig = self.load_tsconfig(
1297 false,
1298 &extended_tsconfig_path,
1299 &TsconfigReferences::Disabled,
1300 )?;
1301 tsconfig.extend_tsconfig(&extended_tsconfig);
1302 }
1303
1304 if tsconfig.load_references(references) {
1305 let path = tsconfig.path().to_path_buf();
1306 let directory = tsconfig.directory().to_path_buf();
1307 for reference in tsconfig.references_mut() {
1308 let reference_tsconfig_path = directory.normalize_with(reference.path());
1309 let tsconfig = self.cache.get_tsconfig(
1310 true,
1311 &reference_tsconfig_path,
1312 |reference_tsconfig| {
1313 if reference_tsconfig.path() == path {
1314 return Err(ResolveError::TsconfigSelfReference(
1315 reference_tsconfig.path().to_path_buf(),
1316 ));
1317 }
1318 Ok(())
1319 },
1320 )?;
1321 reference.set_tsconfig(tsconfig);
1322 }
1323 }
1324 Ok(())
1325 })
1326 }
1327
1328 fn load_tsconfig_paths(
1329 &self,
1330 cached_path: &C::Cp,
1331 specifier: &str,
1332 ctx: &mut Ctx,
1333 ) -> ResolveResult<C::Cp> {
1334 let Some(tsconfig_options) = &self.options.tsconfig else {
1335 return Ok(None);
1336 };
1337 let tsconfig = self.load_tsconfig(
1338 true,
1339 &tsconfig_options.config_file,
1340 &tsconfig_options.references,
1341 )?;
1342 let paths = tsconfig.resolve(cached_path.path(), specifier);
1343 for path in paths {
1344 let cached_path = self.cache.value(&path);
1345 if let Ok(path) = self.require_relative(&cached_path, ".", ctx) {
1346 return Ok(Some(path));
1347 }
1348 }
1349 Ok(None)
1350 }
1351
1352 fn get_extended_tsconfig_path(
1353 &self,
1354 directory: &C::Cp,
1355 tsconfig: &C::Tc,
1356 specifier: &str,
1357 ) -> Result<PathBuf, ResolveError> {
1358 match specifier.as_bytes().first() {
1359 None => Err(ResolveError::Specifier(SpecifierError::Empty(specifier.to_string()))),
1360 Some(b'/') => Ok(PathBuf::from(specifier)),
1361 Some(b'.') => Ok(tsconfig.directory().normalize_with(specifier)),
1362 _ => self
1363 .clone_with_options(ResolveOptions {
1364 description_files: vec![],
1365 extensions: vec![".json".into()],
1366 main_files: vec!["tsconfig.json".into()],
1367 ..ResolveOptions::default()
1368 })
1369 .load_package_self_or_node_modules(directory, specifier, &mut Ctx::default())
1370 .map(|p| p.to_path_buf())
1371 .map_err(|err| match err {
1372 ResolveError::NotFound(_) => {
1373 ResolveError::TsconfigNotFound(PathBuf::from(specifier))
1374 }
1375 _ => err,
1376 }),
1377 }
1378 }
1379
1380 fn package_resolve(
1382 &self,
1383 cached_path: &C::Cp,
1384 specifier: &str,
1385 ctx: &mut Ctx,
1386 ) -> ResolveResult<C::Cp> {
1387 let (package_name, subpath) = Self::parse_package_specifier(specifier);
1388
1389 self.require_core(package_name)?;
1392
1393 for module_name in &self.options.modules {
1395 for cached_path in std::iter::successors(Some(cached_path), |p| p.parent()) {
1396 let Some(cached_path) = self.get_module_directory(cached_path, module_name, ctx)
1398 else {
1399 continue;
1400 };
1401 let cached_path = cached_path.normalize_with(package_name, self.cache.as_ref());
1403 if self.cache.is_dir(&cached_path, ctx) {
1406 if let Some((_, package_json)) =
1408 self.cache.get_package_json(&cached_path, &self.options, ctx)?
1409 {
1410 for exports in package_json.exports_fields(&self.options.exports_fields) {
1413 if let Some(path) = self.package_exports_resolve(
1414 &cached_path,
1415 &format!(".{subpath}"),
1416 &exports,
1417 ctx,
1418 )? {
1419 return Ok(Some(path));
1420 }
1421 }
1422 if subpath == "." {
1424 for main_field in package_json.main_fields(&self.options.main_fields) {
1426 let cached_path =
1428 cached_path.normalize_with(main_field, self.cache.as_ref());
1429 if self.cache.is_file(&cached_path, ctx) {
1430 return Ok(Some(cached_path));
1431 }
1432 }
1433 }
1434 }
1435 let subpath = format!(".{subpath}");
1436 ctx.with_fully_specified(false);
1437 return self.require(&cached_path, &subpath, ctx).map(Some);
1438 }
1439 }
1440 }
1441
1442 Err(ResolveError::NotFound(specifier.to_string()))
1443 }
1444
1445 fn package_exports_resolve<'a, Io: ImportsExportsEntry<'a>>(
1447 &self,
1448 package_url: &C::Cp,
1449 subpath: &str,
1450 exports: &Io,
1451 ctx: &mut Ctx,
1452 ) -> ResolveResult<C::Cp> {
1453 let conditions = &self.options.condition_names;
1454 if let Some(map) = exports.as_map() {
1456 let mut has_dot = false;
1457 let mut without_dot = false;
1458 for key in map.keys() {
1459 let starts_with_dot_or_hash = key.starts_with(['.', '#']);
1460 has_dot = has_dot || starts_with_dot_or_hash;
1461 without_dot = without_dot || !starts_with_dot_or_hash;
1462 if has_dot && without_dot {
1463 return Err(ResolveError::InvalidPackageConfig(
1464 package_url.path().join("package.json"),
1465 ));
1466 }
1467 }
1468 }
1469 if subpath == "." {
1472 if ctx.query.is_some() || ctx.fragment.is_some() {
1476 let query = ctx.query.clone().unwrap_or_default();
1477 let fragment = ctx.fragment.clone().unwrap_or_default();
1478 return Err(ResolveError::PackagePathNotExported(
1479 format!("./{}{query}{fragment}", subpath.trim_start_matches('.')),
1480 package_url.path().join("package.json"),
1481 ));
1482 }
1483 let main_export = match exports.kind() {
1485 ImportsExportsKind::String | ImportsExportsKind::Array => {
1487 Some(Cow::Borrowed(exports))
1489 }
1490 _ => exports.as_map().and_then(|map| {
1492 map.get(".").map_or_else(
1493 || {
1494 if map.keys().any(|key| key.starts_with("./") || key.starts_with('#')) {
1495 None
1496 } else {
1497 Some(Cow::Borrowed(exports))
1498 }
1499 },
1500 |entry| Some(Cow::Owned(entry)),
1501 )
1502 }),
1503 };
1504 if let Some(main_export) = main_export {
1506 let resolved = self.package_target_resolve(
1508 package_url,
1509 ".",
1510 main_export.as_ref(),
1511 None,
1512 false,
1513 conditions,
1514 ctx,
1515 )?;
1516 if let Some(path) = resolved {
1518 return Ok(Some(path));
1519 }
1520 }
1521 }
1522 if let Some(exports) = exports.as_map() {
1524 let match_key = &subpath;
1527 if let Some(path) = self.package_imports_exports_resolve(
1529 match_key,
1530 &exports,
1531 package_url,
1532 false,
1533 conditions,
1534 ctx,
1535 )? {
1536 return Ok(Some(path));
1538 }
1539 }
1540 Err(ResolveError::PackagePathNotExported(
1542 subpath.to_string(),
1543 package_url.path().join("package.json"),
1544 ))
1545 }
1546
1547 fn package_imports_resolve(
1549 &self,
1550 specifier: &str,
1551 package_json: &C::Pj,
1552 ctx: &mut Ctx,
1553 ) -> Result<Option<C::Cp>, ResolveError> {
1554 debug_assert!(specifier.starts_with('#'), "{specifier}");
1556 let mut has_imports = false;
1566 for imports in package_json.imports_fields(&self.options.imports_fields) {
1567 if !has_imports {
1568 has_imports = true;
1569 if specifier == "#" || specifier.starts_with("#/") {
1571 return Err(ResolveError::InvalidModuleSpecifier(
1572 specifier.to_string(),
1573 package_json.path().to_path_buf(),
1574 ));
1575 }
1576 }
1577 if let Some(path) = self.package_imports_exports_resolve(
1578 specifier,
1579 &imports,
1580 &self.cache.value(package_json.directory()),
1581 true,
1582 &self.options.condition_names,
1583 ctx,
1584 )? {
1585 return Ok(Some(path));
1587 }
1588 }
1589
1590 if has_imports {
1592 Err(ResolveError::PackageImportNotDefined(
1593 specifier.to_string(),
1594 package_json.path().to_path_buf(),
1595 ))
1596 } else {
1597 Ok(None)
1598 }
1599 }
1600
1601 fn package_imports_exports_resolve<'a, Io: ImportsExportsMap<'a>>(
1603 &self,
1604 match_key: &str,
1605 match_obj: &Io,
1606 package_url: &C::Cp,
1607 is_imports: bool,
1608 conditions: &[String],
1609 ctx: &mut Ctx,
1610 ) -> ResolveResult<C::Cp> {
1611 if match_key.ends_with('/') {
1614 return Ok(None);
1615 }
1616 if !match_key.contains('*') {
1618 if let Some(target) = match_obj.get(match_key) {
1620 return self.package_target_resolve(
1622 package_url,
1623 match_key,
1624 &target,
1625 None,
1626 is_imports,
1627 conditions,
1628 ctx,
1629 );
1630 }
1631 }
1632
1633 let mut best_target = None;
1634 let mut best_match = "";
1635 let mut best_key = "";
1636 for (expansion_key, target) in match_obj.iter() {
1639 if expansion_key.starts_with("./") || expansion_key.starts_with('#') {
1640 if let Some((pattern_base, pattern_trailer)) = expansion_key.split_once('*') {
1642 if match_key.starts_with(pattern_base)
1644 && !pattern_trailer.contains('*')
1646 && (pattern_trailer.is_empty()
1648 || (match_key.len() >= expansion_key.len()
1649 && match_key.ends_with(pattern_trailer)))
1650 && Self::pattern_key_compare(best_key, expansion_key).is_gt()
1651 {
1652 best_target = Some(target);
1654 best_match =
1656 &match_key[pattern_base.len()..match_key.len() - pattern_trailer.len()];
1657 best_key = expansion_key;
1658 }
1659 } else if expansion_key.ends_with('/')
1660 && match_key.starts_with(expansion_key)
1661 && Self::pattern_key_compare(best_key, expansion_key).is_gt()
1662 {
1663 best_target = Some(target);
1665 best_match = &match_key[expansion_key.len()..];
1666 best_key = expansion_key;
1667 }
1668 }
1669 }
1670 if let Some(best_target) = best_target {
1671 return self.package_target_resolve(
1673 package_url,
1674 best_key,
1675 &best_target,
1676 Some(best_match),
1677 is_imports,
1678 conditions,
1679 ctx,
1680 );
1681 }
1682 Ok(None)
1684 }
1685
1686 #[allow(clippy::too_many_arguments)]
1688 fn package_target_resolve<'a, Io: ImportsExportsEntry<'a>>(
1689 &self,
1690 package_url: &C::Cp,
1691 target_key: &str,
1692 target: &Io,
1693 pattern_match: Option<&str>,
1694 is_imports: bool,
1695 conditions: &[String],
1696 ctx: &mut Ctx,
1697 ) -> ResolveResult<C::Cp> {
1698 fn normalize_string_target<'a>(
1699 target_key: &'a str,
1700 target: &'a str,
1701 pattern_match: Option<&'a str>,
1702 package_url: &impl CachedPath,
1703 ) -> Result<Cow<'a, str>, ResolveError> {
1704 let target = if let Some(pattern_match) = pattern_match {
1705 if !target_key.contains('*') && !target.contains('*') {
1706 if target_key.ends_with('/') && target.ends_with('/') {
1709 Cow::Owned(format!("{target}{pattern_match}"))
1710 } else {
1711 return Err(ResolveError::InvalidPackageConfigDirectory(
1712 package_url.path().join("package.json"),
1713 ));
1714 }
1715 } else {
1716 Cow::Owned(target.replace('*', pattern_match))
1717 }
1718 } else {
1719 Cow::Borrowed(target)
1720 };
1721 Ok(target)
1722 }
1723
1724 if let Some(target) = target.as_string() {
1726 let parsed = Specifier::parse(target).map_err(ResolveError::Specifier)?;
1729 ctx.with_query_fragment(parsed.query, parsed.fragment);
1730 let target = parsed.path();
1731
1732 if !target.starts_with("./") {
1734 if !is_imports || target.starts_with("../") || target.starts_with('/') {
1736 return Err(ResolveError::InvalidPackageTarget(
1738 (*target).to_string(),
1739 target_key.to_string(),
1740 package_url.path().join("package.json"),
1741 ));
1742 }
1743 let target =
1746 normalize_string_target(target_key, target, pattern_match, package_url)?;
1747 return self.package_resolve(package_url, &target, ctx);
1749 }
1750
1751 let target = normalize_string_target(target_key, target, pattern_match, package_url)?;
1756 if Path::new(target.as_ref()).is_invalid_exports_target() {
1757 return Err(ResolveError::InvalidPackageTarget(
1758 target.to_string(),
1759 target_key.to_string(),
1760 package_url.path().join("package.json"),
1761 ));
1762 }
1763 return Ok(Some(package_url.normalize_with(target.as_ref(), self.cache.as_ref())));
1766 }
1767 else if let Some(target) = target.as_map() {
1769 let mut conditions_with_default = conditions.to_vec();
1770 if !conditions_with_default.iter().any(|condition| condition == "default") {
1772 conditions_with_default.push("default".into());
1773 }
1774 for condition in conditions_with_default {
1777 if let Some((_, target_value)) = target.iter().find(|(key, _)| condition == *key) {
1779 let resolved = self.package_target_resolve(
1782 package_url,
1783 target_key,
1784 &target_value,
1785 pattern_match,
1786 is_imports,
1787 conditions,
1788 ctx,
1789 );
1790 if let Some(path) = resolved? {
1792 return Ok(Some(path));
1794 }
1795 }
1796 }
1797 return Ok(None);
1799 }
1800 else if let Some(targets) = target.as_array() {
1802 if targets.is_empty() {
1804 return Err(ResolveError::PackagePathNotExported(
1806 pattern_match.unwrap_or(".").to_string(),
1807 package_url.path().join("package.json"),
1808 ));
1809 }
1810 for (i, target_value) in targets.iter().enumerate() {
1812 let resolved = self.package_target_resolve(
1814 package_url,
1815 target_key,
1816 &target_value,
1817 pattern_match,
1818 is_imports,
1819 conditions,
1820 ctx,
1821 );
1822
1823 if resolved.is_err() && i == targets.len() {
1824 return resolved;
1825 }
1826
1827 if let Ok(Some(path)) = resolved {
1829 return Ok(Some(path));
1831 }
1832 }
1833 }
1836 Ok(None)
1838 }
1840
1841 fn parse_package_specifier(specifier: &str) -> (&str, &str) {
1844 let mut separator_index = specifier.as_bytes().iter().position(|b| *b == b'/');
1845 if specifier.starts_with('@') {
1848 if separator_index.is_none() || specifier.is_empty() {
1850 } else if let Some(index) = &separator_index {
1852 separator_index = specifier.as_bytes()[*index + 1..]
1853 .iter()
1854 .position(|b| *b == b'/')
1855 .map(|i| i + *index + 1);
1856 }
1857 }
1858 let package_name =
1859 separator_index.map_or(specifier, |separator_index| &specifier[..separator_index]);
1860
1861 let package_subpath =
1872 separator_index.map_or("", |separator_index| &specifier[separator_index..]);
1873 (package_name, package_subpath)
1874 }
1875
1876 fn pattern_key_compare(key_a: &str, key_b: &str) -> Ordering {
1878 if key_a.is_empty() {
1879 return Ordering::Greater;
1880 }
1881 debug_assert!(key_a.ends_with('/') || key_a.match_indices('*').count() == 1, "{key_a}");
1883 debug_assert!(key_b.ends_with('/') || key_b.match_indices('*').count() == 1, "{key_b}");
1885 let a_pos = key_a.chars().position(|c| c == '*');
1887 let base_length_a = a_pos.map_or(key_a.len(), |p| p + 1);
1888 let b_pos = key_b.chars().position(|c| c == '*');
1890 let base_length_b = b_pos.map_or(key_b.len(), |p| p + 1);
1891 if base_length_a > base_length_b {
1893 return Ordering::Less;
1894 }
1895 if base_length_b > base_length_a {
1897 return Ordering::Greater;
1898 }
1899 if !key_a.contains('*') {
1901 return Ordering::Greater;
1902 }
1903 if !key_b.contains('*') {
1905 return Ordering::Less;
1906 }
1907 if key_a.len() > key_b.len() {
1909 return Ordering::Less;
1910 }
1911 if key_b.len() > key_a.len() {
1913 return Ordering::Greater;
1914 }
1915 Ordering::Equal
1917 }
1918
1919 fn strip_package_name<'a>(specifier: &'a str, package_name: &'a str) -> Option<&'a str> {
1920 specifier
1921 .strip_prefix(package_name)
1922 .filter(|tail| tail.is_empty() || tail.starts_with(SLASH_START))
1923 }
1924}