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
69#[cfg(test)]
70mod tests;
71
72use dashmap::{DashMap, mapref::one::Ref};
73use rustc_hash::FxHashSet;
74use std::{
75 borrow::Cow,
76 cmp::Ordering,
77 ffi::OsStr,
78 fmt,
79 path::{Component, Path, PathBuf},
80 sync::Arc,
81};
82
83#[cfg(feature = "fs_cache")]
84pub use crate::{
85 file_system::{FileMetadata, FileSystem, FileSystemOs},
86 fs_cache::{FsCache, FsCachedPath},
87 package_json_serde::PackageJsonSerde,
88 tsconfig_serde::{CompilerOptionsSerde, ExtendsField, ProjectReferenceSerde, TsConfigSerde},
89};
90
91#[cfg(feature = "fs_cache")]
92pub type FsResolution = Resolution<FsCache<FileSystemOs>>;
93
94pub use crate::{
95 builtins::NODEJS_BUILTINS,
96 cache::{Cache, CachedPath},
97 error::{JSONError, ResolveError, SpecifierError},
98 options::{
99 Alias, AliasValue, EnforceExtension, ResolveOptions, Restriction, TsconfigOptions,
100 TsconfigReferences,
101 },
102 package_json::{
103 ImportsExportsArray, ImportsExportsEntry, ImportsExportsKind, ImportsExportsMap,
104 PackageJson, PackageType,
105 },
106 path::PathUtil,
107 resolution::Resolution,
108 tsconfig::{CompilerOptions, CompilerOptionsPathsMap, ProjectReference, TsConfig},
109};
110use crate::{context::ResolveContext as Ctx, path::SLASH_START, specifier::Specifier};
111
112type ResolveResult<Cp> = Result<Option<Cp>, ResolveError>;
113
114#[derive(Debug, Default, Clone)]
116pub struct ResolveContext {
117 pub file_dependencies: FxHashSet<PathBuf>,
119
120 pub missing_dependencies: FxHashSet<PathBuf>,
122}
123
124#[cfg(feature = "fs_cache")]
126pub type Resolver = ResolverGeneric<FsCache<FileSystemOs>>;
127
128pub struct ResolverGeneric<C: Cache> {
130 options: ResolveOptions,
131 cache: Arc<C>,
132 #[cfg(feature = "yarn_pnp")]
133 pnp_cache: Arc<DashMap<FsCachedPath, Option<pnp::Manifest>>>,
134}
135
136impl<C: Cache> fmt::Debug for ResolverGeneric<C> {
137 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138 self.options.fmt(f)
139 }
140}
141
142impl<C: Cache + Default> Default for ResolverGeneric<C> {
143 fn default() -> Self {
144 Self::new(ResolveOptions::default())
145 }
146}
147
148impl<C: Cache + Default> ResolverGeneric<C> {
149 #[must_use]
150 pub fn new(options: ResolveOptions) -> Self {
151 Self {
152 options: options.sanitize(),
153 cache: Arc::new(C::default()),
154 #[cfg(feature = "yarn_pnp")]
155 pnp_cache: Arc::new(DashMap::default()),
156 }
157 }
158}
159
160impl<C: Cache<Cp = FsCachedPath>> ResolverGeneric<C> {
161 pub fn new_with_cache(cache: Arc<C>, options: ResolveOptions) -> Self {
162 Self {
163 cache,
164 options: options.sanitize(),
165 #[cfg(feature = "yarn_pnp")]
166 pnp_cache: Arc::new(DashMap::default()),
167 }
168 }
169
170 #[must_use]
172 pub fn clone_with_options(&self, options: ResolveOptions) -> Self {
173 Self {
174 options: options.sanitize(),
175 cache: Arc::clone(&self.cache),
176 #[cfg(feature = "yarn_pnp")]
177 pnp_cache: Arc::clone(&self.pnp_cache),
178 }
179 }
180
181 #[must_use]
183 pub const fn options(&self) -> &ResolveOptions {
184 &self.options
185 }
186
187 pub fn clear_cache(&self) {
189 self.cache.clear();
190 }
191
192 pub fn resolve<P: AsRef<Path>>(
204 &self,
205 directory: P,
206 specifier: &str,
207 ) -> Result<Resolution<C>, ResolveError> {
208 let mut ctx = Ctx::default();
209 self.resolve_tracing(directory.as_ref(), specifier, &mut ctx)
210 }
211
212 pub fn resolve_tsconfig<P: AsRef<Path>>(&self, path: P) -> Result<Arc<C::Tc>, ResolveError> {
224 let path = path.as_ref();
225 self.load_tsconfig(true, path, &TsconfigReferences::Auto)
226 }
227
228 pub fn resolve_with_context<P: AsRef<Path>>(
234 &self,
235 directory: P,
236 specifier: &str,
237 resolve_context: &mut ResolveContext,
238 ) -> Result<Resolution<C>, ResolveError> {
239 let mut ctx = Ctx::default();
240 ctx.init_file_dependencies();
241 let result = self.resolve_tracing(directory.as_ref(), specifier, &mut ctx);
242 if let Some(deps) = &mut ctx.file_dependencies {
243 resolve_context.file_dependencies.extend(deps.drain(..));
244 }
245 if let Some(deps) = &mut ctx.missing_dependencies {
246 resolve_context.missing_dependencies.extend(deps.drain(..));
247 }
248 result
249 }
250
251 fn resolve_tracing(
253 &self,
254 directory: &Path,
255 specifier: &str,
256 ctx: &mut Ctx,
257 ) -> Result<Resolution<C>, ResolveError> {
258 let span = tracing::debug_span!("resolve", path = ?directory, specifier = specifier);
259 let _enter = span.enter();
260 let r = self.resolve_impl(directory, specifier, ctx);
261 match &r {
262 Ok(r) => {
263 tracing::debug!(options = ?self.options, path = ?directory, specifier = specifier, ret = ?r.path);
264 }
265 Err(err) => {
266 tracing::debug!(options = ?self.options, path = ?directory, specifier = specifier, err = ?err);
267 }
268 }
269 r
270 }
271
272 fn resolve_impl(
273 &self,
274 path: &Path,
275 specifier: &str,
276 ctx: &mut Ctx,
277 ) -> Result<Resolution<C>, ResolveError> {
278 ctx.with_fully_specified(self.options.fully_specified);
279 let cached_path = self.cache.value(path);
280 let cached_path = self.require(&cached_path, specifier, ctx)?;
281 let path = self.load_realpath(&cached_path)?;
282 self.check_restrictions(&path)?;
284 let package_json =
285 cached_path.find_package_json(&self.options, self.cache.as_ref(), ctx)?;
286 if let Some((_, package_json)) = &package_json {
287 debug_assert!(path.starts_with(package_json.directory()));
289 }
290 Ok(Resolution {
291 path,
292 query: ctx.query.take(),
293 fragment: ctx.fragment.take(),
294 package_json: package_json.map(|(_, p)| p),
295 })
296 }
297
298 fn require(
305 &self,
306 cached_path: &C::Cp,
307 specifier: &str,
308 ctx: &mut Ctx,
309 ) -> Result<C::Cp, ResolveError> {
310 ctx.test_for_infinite_recursion()?;
311
312 let (parsed, try_fragment_as_path) = self.load_parse(cached_path, specifier, ctx)?;
314 if let Some(path) = try_fragment_as_path {
315 return Ok(path);
316 }
317
318 self.require_without_parse(cached_path, parsed.path(), ctx)
319 }
320
321 fn require_without_parse(
322 &self,
323 cached_path: &C::Cp,
324 specifier: &str,
325 ctx: &mut Ctx,
326 ) -> Result<C::Cp, ResolveError> {
327 if let Some(path) = self.load_tsconfig_paths(cached_path, specifier, &mut Ctx::default())? {
329 return Ok(path);
330 }
331
332 if let Some(path) = self.load_alias(cached_path, specifier, &self.options.alias, ctx)? {
334 return Ok(path);
335 }
336
337 let result = match Path::new(specifier).components().next() {
338 Some(Component::RootDir | Component::Prefix(_)) => {
340 self.require_absolute(cached_path, specifier, ctx)
341 }
342 Some(Component::CurDir | Component::ParentDir) => {
344 self.require_relative(cached_path, specifier, ctx)
345 }
346 Some(Component::Normal(_)) if specifier.as_bytes()[0] == b'#' => {
348 self.require_hash(cached_path, specifier, ctx)
349 }
350 _ => {
351 self.require_core(specifier)?;
355
356 self.require_bare(cached_path, specifier, ctx)
360 }
361 };
362
363 result.or_else(|err| {
364 if err.is_ignore() {
365 return Err(err);
366 }
367 self.load_alias(cached_path, specifier, &self.options.fallback, ctx)
369 .and_then(|value| value.ok_or(err))
370 })
371 }
372
373 fn require_core(&self, specifier: &str) -> Result<(), ResolveError> {
377 if self.options.builtin_modules {
378 let is_runtime_module = specifier.starts_with("node:");
379 if is_runtime_module || NODEJS_BUILTINS.binary_search(&specifier).is_ok() {
380 let resolved = if is_runtime_module {
381 specifier.to_string()
382 } else {
383 format!("node:{specifier}")
384 };
385 return Err(ResolveError::Builtin { resolved, is_runtime_module });
386 }
387 }
388 Ok(())
389 }
390
391 fn require_absolute(
392 &self,
393 cached_path: &C::Cp,
394 specifier: &str,
395 ctx: &mut Ctx,
396 ) -> Result<C::Cp, ResolveError> {
397 debug_assert!(
399 Path::new(specifier)
400 .components()
401 .next()
402 .is_some_and(|c| matches!(c, Component::RootDir | Component::Prefix(_)))
403 );
404 if !self.options.prefer_relative && self.options.prefer_absolute {
405 if let Ok(path) = self.load_package_self_or_node_modules(cached_path, specifier, ctx) {
406 return Ok(path);
407 }
408 }
409 if let Some(path) = self.load_roots(cached_path, specifier, ctx) {
410 return Ok(path);
411 }
412 let path = self.cache.value(Path::new(specifier));
415 if let Some(path) = self.load_as_file_or_directory(&path, specifier, ctx)? {
416 return Ok(path);
417 }
418 Err(ResolveError::NotFound(specifier.to_string()))
419 }
420
421 fn require_relative(
423 &self,
424 cached_path: &C::Cp,
425 specifier: &str,
426 ctx: &mut Ctx,
427 ) -> Result<C::Cp, ResolveError> {
428 debug_assert!(Path::new(specifier).components().next().is_some_and(|c| matches!(
430 c,
431 Component::CurDir | Component::ParentDir | Component::Normal(_)
432 )));
433 let cached_path = cached_path.normalize_with(specifier, self.cache.as_ref());
434 if let Some(path) = self.load_as_file_or_directory(&cached_path, specifier, ctx)? {
437 return Ok(path);
438 }
439 Err(ResolveError::NotFound(specifier.to_string()))
441 }
442
443 fn require_hash(
444 &self,
445 cached_path: &C::Cp,
446 specifier: &str,
447 ctx: &mut Ctx,
448 ) -> Result<C::Cp, ResolveError> {
449 debug_assert_eq!(specifier.chars().next(), Some('#'));
450 if let Some(path) = self.load_package_imports(cached_path, specifier, ctx)? {
452 return Ok(path);
453 }
454 self.load_package_self_or_node_modules(cached_path, specifier, ctx)
455 }
456
457 fn require_bare(
458 &self,
459 cached_path: &C::Cp,
460 specifier: &str,
461 ctx: &mut Ctx,
462 ) -> Result<C::Cp, ResolveError> {
463 debug_assert!(
465 Path::new(specifier)
466 .components()
467 .next()
468 .is_some_and(|c| matches!(c, Component::Normal(_)))
469 );
470 if self.options.prefer_relative {
471 if let Ok(path) = self.require_relative(cached_path, specifier, ctx) {
472 return Ok(path);
473 }
474 }
475 self.load_package_self_or_node_modules(cached_path, specifier, ctx)
476 }
477
478 fn load_parse<'s>(
487 &self,
488 cached_path: &C::Cp,
489 specifier: &'s str,
490 ctx: &mut Ctx,
491 ) -> Result<(Specifier<'s>, Option<C::Cp>), ResolveError> {
492 let parsed = Specifier::parse(specifier).map_err(ResolveError::Specifier)?;
493 ctx.with_query_fragment(parsed.query, parsed.fragment);
494
495 if ctx.fragment.is_some() && ctx.query.is_none() {
497 let specifier = parsed.path();
498 let fragment = ctx.fragment.take().unwrap();
499 let path = format!("{specifier}{fragment}");
500 if let Ok(path) = self.require_without_parse(cached_path, &path, ctx) {
501 return Ok((parsed, Some(path)));
502 }
503 ctx.fragment.replace(fragment);
504 }
505 Ok((parsed, None))
506 }
507
508 fn load_package_self_or_node_modules(
509 &self,
510 cached_path: &C::Cp,
511 specifier: &str,
512 ctx: &mut Ctx,
513 ) -> Result<C::Cp, ResolveError> {
514 let (_, subpath) = Self::parse_package_specifier(specifier);
515 if subpath.is_empty() {
516 ctx.with_fully_specified(false);
517 }
518 if let Some(path) = self.load_package_self(cached_path, specifier, ctx)? {
520 return Ok(path);
521 }
522 if let Some(path) = self.load_node_modules(cached_path, specifier, ctx)? {
524 return Ok(path);
525 }
526 Err(ResolveError::NotFound(specifier.to_string()))
528 }
529
530 fn load_package_imports(
532 &self,
533 cached_path: &C::Cp,
534 specifier: &str,
535 ctx: &mut Ctx,
536 ) -> ResolveResult<C::Cp> {
537 let Some((_, package_json)) =
540 cached_path.find_package_json(&self.options, self.cache.as_ref(), ctx)?
541 else {
542 return Ok(None);
543 };
544 if let Some(path) = self.package_imports_resolve(specifier, &package_json, ctx)? {
547 return self.resolve_esm_match(specifier, &path, ctx);
549 }
550 Ok(None)
551 }
552
553 fn load_as_file(&self, cached_path: &C::Cp, ctx: &mut Ctx) -> ResolveResult<C::Cp> {
554 if let Some(path) = self.load_extension_alias(cached_path, ctx)? {
556 return Ok(Some(path));
557 }
558 if self.options.enforce_extension.is_disabled() {
559 if let Some(path) = self.load_alias_or_file(cached_path, ctx)? {
561 return Ok(Some(path));
562 }
563 }
564 if let Some(path) = self.load_extensions(cached_path, &self.options.extensions, ctx)? {
568 return Ok(Some(path));
569 }
570 Ok(None)
571 }
572
573 fn load_as_directory(&self, cached_path: &C::Cp, ctx: &mut Ctx) -> ResolveResult<C::Cp> {
574 if !self.options.description_files.is_empty() {
578 if let Some((_, package_json)) =
580 self.cache.get_package_json(cached_path, &self.options, ctx)?
581 {
582 for main_field in package_json.main_fields(&self.options.main_fields) {
584 let main_field =
586 if main_field.starts_with("./") || main_field.starts_with("../") {
587 Cow::Borrowed(main_field)
588 } else {
589 Cow::Owned(format!("./{main_field}"))
590 };
591
592 let cached_path =
594 cached_path.normalize_with(main_field.as_ref(), self.cache.as_ref());
595 if let Some(path) = self.load_as_file(&cached_path, ctx)? {
597 return Ok(Some(path));
598 }
599 if let Some(path) = self.load_index(&cached_path, ctx)? {
601 return Ok(Some(path));
602 }
603 }
604 }
607 }
608 self.load_index(cached_path, ctx)
610 }
611
612 fn load_as_file_or_directory(
613 &self,
614 cached_path: &C::Cp,
615 specifier: &str,
616 ctx: &mut Ctx,
617 ) -> ResolveResult<C::Cp> {
618 if self.options.resolve_to_context {
619 return Ok(self.cache.is_dir(cached_path, ctx).then(|| cached_path.clone()));
620 }
621 if !specifier.ends_with('/') {
622 if let Some(path) = self.load_as_file(cached_path, ctx)? {
623 return Ok(Some(path));
624 }
625 }
626 if self.cache.is_dir(cached_path, ctx) {
627 if let Some(path) = self.load_as_directory(cached_path, ctx)? {
628 return Ok(Some(path));
629 }
630 }
631 Ok(None)
632 }
633
634 fn load_extensions(
635 &self,
636 path: &C::Cp,
637 extensions: &[String],
638 ctx: &mut Ctx,
639 ) -> ResolveResult<C::Cp> {
640 if ctx.fully_specified {
641 return Ok(None);
642 }
643 for extension in extensions {
644 let cached_path = path.add_extension(extension, self.cache.as_ref());
645 if let Some(path) = self.load_alias_or_file(&cached_path, ctx)? {
646 return Ok(Some(path));
647 }
648 }
649 Ok(None)
650 }
651
652 fn load_realpath(&self, cached_path: &C::Cp) -> Result<PathBuf, ResolveError> {
653 if self.options.symlinks {
654 self.cache.canonicalize(cached_path)
655 } else {
656 Ok(cached_path.to_path_buf())
657 }
658 }
659
660 fn check_restrictions(&self, path: &Path) -> Result<(), ResolveError> {
661 fn is_inside(path: &Path, parent: &Path) -> bool {
663 if !path.starts_with(parent) {
664 return false;
665 }
666 if path.as_os_str().len() == parent.as_os_str().len() {
667 return true;
668 }
669 path.strip_prefix(parent).is_ok_and(|p| p == Path::new("./"))
670 }
671 for restriction in &self.options.restrictions {
672 match restriction {
673 Restriction::Path(restricted_path) => {
674 if !is_inside(path, restricted_path) {
675 return Err(ResolveError::Restriction(
676 path.to_path_buf(),
677 restricted_path.clone(),
678 ));
679 }
680 }
681 Restriction::RegExp(_) => {
682 return Err(ResolveError::Unimplemented("Restriction with regex"));
683 }
684 }
685 }
686 Ok(())
687 }
688
689 fn load_index(&self, cached_path: &C::Cp, ctx: &mut Ctx) -> ResolveResult<C::Cp> {
690 for main_file in &self.options.main_files {
691 let cached_path = cached_path.normalize_with(main_file, self.cache.as_ref());
692 if self.options.enforce_extension.is_disabled() {
693 if let Some(path) = self.load_alias_or_file(&cached_path, ctx)? {
694 return Ok(Some(path));
695 }
696 }
697 if let Some(path) = self.load_extensions(&cached_path, &self.options.extensions, ctx)? {
701 return Ok(Some(path));
702 }
703 }
704 Ok(None)
705 }
706
707 fn load_browser_field_or_alias(
708 &self,
709 cached_path: &C::Cp,
710 ctx: &mut Ctx,
711 ) -> ResolveResult<C::Cp> {
712 if !self.options.alias_fields.is_empty() {
713 if let Some((package_url, package_json)) =
714 cached_path.find_package_json(&self.options, self.cache.as_ref(), ctx)?
715 {
716 if let Some(path) =
717 self.load_browser_field(cached_path, None, &package_url, &package_json, ctx)?
718 {
719 return Ok(Some(path));
720 }
721 }
722 }
723 if !self.options.alias.is_empty() {
726 let alias_specifier = cached_path.path().to_string_lossy();
727 if let Some(path) =
728 self.load_alias(cached_path, &alias_specifier, &self.options.alias, ctx)?
729 {
730 return Ok(Some(path));
731 }
732 }
733 Ok(None)
734 }
735
736 fn load_alias_or_file(&self, cached_path: &C::Cp, ctx: &mut Ctx) -> ResolveResult<C::Cp> {
737 if let Some(path) = self.load_browser_field_or_alias(cached_path, ctx)? {
738 return Ok(Some(path));
739 }
740 if self.cache.is_file(cached_path, ctx) {
741 return Ok(Some(cached_path.clone()));
742 }
743 Ok(None)
744 }
745
746 fn load_node_modules(
747 &self,
748 cached_path: &C::Cp,
749 specifier: &str,
750 ctx: &mut Ctx,
751 ) -> ResolveResult<C::Cp> {
752 #[cfg(feature = "yarn_pnp")]
753 if self.options.enable_pnp {
754 if let Some(resolved_path) = self.load_pnp(cached_path, specifier, ctx)? {
755 return Ok(Some(resolved_path));
756 }
757 }
758
759 let (package_name, subpath) = Self::parse_package_specifier(specifier);
760 for module_name in &self.options.modules {
763 for cached_path in std::iter::successors(Some(cached_path), |p| p.parent()) {
764 if !self.cache.is_dir(cached_path, ctx) {
766 continue;
767 }
768
769 let Some(cached_path) = self.get_module_directory(cached_path, module_name, ctx)
770 else {
771 continue;
772 };
773 if !package_name.is_empty() {
778 let cached_path = cached_path.normalize_with(package_name, self.cache.as_ref());
779 if self.cache.is_dir(&cached_path, ctx) {
781 if let Some(path) =
783 self.load_package_exports(specifier, subpath, &cached_path, ctx)?
784 {
785 return Ok(Some(path));
786 }
787 } else {
788 if !subpath.is_empty() {
790 continue;
791 }
792 if package_name.starts_with('@') {
795 if let Some(path) = cached_path.parent() {
796 if !self.cache.is_dir(path, ctx) {
797 continue;
798 }
799 }
800 }
801 }
802 }
803
804 let cached_path = cached_path.normalize_with(specifier, self.cache.as_ref());
809
810 if self.options.resolve_to_context {
812 return Ok(self.cache.is_dir(&cached_path, ctx).then(|| cached_path.clone()));
813 }
814 if self.cache.is_dir(&cached_path, ctx) {
815 if let Some(path) = self.load_browser_field_or_alias(&cached_path, ctx)? {
816 return Ok(Some(path));
817 }
818 if let Some(path) = self.load_as_directory(&cached_path, ctx)? {
819 return Ok(Some(path));
820 }
821 }
822 if let Some(path) = self.load_as_file(&cached_path, ctx)? {
823 return Ok(Some(path));
824 }
825 if let Some(path) = self.load_as_directory(&cached_path, ctx)? {
826 return Ok(Some(path));
827 }
828 }
829 }
830 Ok(None)
831 }
832
833 #[cfg(feature = "yarn_pnp")]
834 fn find_pnp_manifest(&self, cached_path: &C::Cp) -> Ref<'_, C::Cp, Option<pnp::Manifest>> {
835 let entry = self
836 .pnp_cache
837 .entry(cached_path.clone())
838 .or_insert_with(|| pnp::find_pnp_manifest(cached_path.path()).unwrap());
839
840 entry.downgrade()
841 }
842
843 #[cfg(feature = "yarn_pnp")]
844 fn load_pnp(
845 &self,
846 cached_path: &C::Cp,
847 specifier: &str,
848 ctx: &mut Ctx,
849 ) -> Result<Option<C::Cp>, ResolveError> {
850 let pnp_manifest = self.find_pnp_manifest(cached_path);
851
852 if let Some(pnp_manifest) = pnp_manifest.as_ref() {
853 if specifier == "pnpapi" {
855 return Ok(Some(self.cache.value(pnp_manifest.manifest_path.as_path())));
856 }
857
858 let mut path = cached_path.to_path_buf();
860 path.push("");
861
862 let resolution =
863 pnp::resolve_to_unqualified_via_manifest(pnp_manifest, specifier, path);
864
865 match resolution {
866 Ok(pnp::Resolution::Resolved(path, subpath)) => {
867 let cached_path = self.cache.value(&path);
868 let cached_path_string = cached_path.path().to_string_lossy();
869
870 let pkg_name = cached_path_string.rsplit_once("node_modules/").map_or(
872 "",
873 |last| last.1.strip_suffix("/").unwrap_or(last.1),
875 );
876
877 let inner_request = if pkg_name.is_empty() {
878 subpath.map_or_else(
879 || ".".to_string(),
880 |mut p| {
881 p.insert_str(0, "./");
882 p
883 },
884 )
885 } else {
886 String::from("./") + specifier.strip_prefix(pkg_name).unwrap()
887 };
888
889 let export_resolution = self.load_package_self(&cached_path, specifier, ctx)?;
890 if export_resolution.is_some() {
892 return Ok(export_resolution);
893 }
894
895 let inner_resolver = self.clone_with_options(self.options().clone());
896
897 let Ok(inner_resolution) = inner_resolver.resolve(&path, &inner_request) else {
899 return Err(ResolveError::NotFound(specifier.to_string()));
900 };
901
902 Ok(Some(self.cache.value(inner_resolution.path())))
903 }
904
905 Ok(pnp::Resolution::Skipped) => Ok(None),
906 Err(_) => Err(ResolveError::NotFound(specifier.to_string())),
907 }
908 } else {
909 Ok(None)
910 }
911 }
912
913 fn get_module_directory(
914 &self,
915 cached_path: &C::Cp,
916 module_name: &str,
917 ctx: &mut Ctx,
918 ) -> Option<C::Cp> {
919 if module_name == "node_modules" {
920 cached_path.cached_node_modules(self.cache.as_ref(), ctx)
921 } else if cached_path.path().components().next_back()
922 == Some(Component::Normal(OsStr::new(module_name)))
923 {
924 Some(cached_path.clone())
925 } else {
926 cached_path.module_directory(module_name, self.cache.as_ref(), ctx)
927 }
928 }
929
930 fn load_package_exports(
931 &self,
932 specifier: &str,
933 subpath: &str,
934 cached_path: &C::Cp,
935 ctx: &mut Ctx,
936 ) -> ResolveResult<C::Cp> {
937 let Some((_, package_json)) =
940 self.cache.get_package_json(cached_path, &self.options, ctx)?
941 else {
942 return Ok(None);
943 };
944 for exports in package_json.exports_fields(&self.options.exports_fields) {
950 if let Some(path) =
951 self.package_exports_resolve(cached_path, &format!(".{subpath}"), &exports, ctx)?
952 {
953 return self.resolve_esm_match(specifier, &path, ctx);
955 }
956 }
957 Ok(None)
958 }
959
960 fn load_package_self(
961 &self,
962 cached_path: &C::Cp,
963 specifier: &str,
964 ctx: &mut Ctx,
965 ) -> ResolveResult<C::Cp> {
966 let Some((package_url, package_json)) =
969 cached_path.find_package_json(&self.options, self.cache.as_ref(), ctx)?
970 else {
971 return Ok(None);
972 };
973 if let Some(subpath) = package_json
976 .name()
977 .and_then(|package_name| Self::strip_package_name(specifier, package_name))
978 {
979 for exports in package_json.exports_fields(&self.options.exports_fields) {
985 if let Some(cached_path) = self.package_exports_resolve(
986 &package_url,
987 &format!(".{subpath}"),
988 &exports,
989 ctx,
990 )? {
991 return self.resolve_esm_match(specifier, &cached_path, ctx);
993 }
994 }
995 }
996 self.load_browser_field(cached_path, Some(specifier), &package_url, &package_json, ctx)
997 }
998
999 fn resolve_esm_match(
1001 &self,
1002 specifier: &str,
1003 cached_path: &C::Cp,
1004 ctx: &mut Ctx,
1005 ) -> ResolveResult<C::Cp> {
1006 if let Some(path) = self.load_as_file_or_directory(cached_path, "", ctx)? {
1011 return Ok(Some(path));
1012 }
1013
1014 let mut path_str = cached_path.path().to_str();
1015
1016 while let Some(s) = path_str {
1020 if let Some((before, _)) = s.rsplit_once('?') {
1021 if (self.load_as_file_or_directory(
1022 &self.cache.value(Path::new(before)),
1023 "",
1024 ctx,
1025 )?)
1026 .is_some()
1027 {
1028 return Ok(Some(cached_path.clone()));
1029 }
1030 path_str = Some(before);
1031 } else {
1032 break;
1033 }
1034 }
1035
1036 Err(ResolveError::NotFound(specifier.to_string()))
1038 }
1039
1040 fn load_browser_field(
1042 &self,
1043 cached_path: &C::Cp,
1044 module_specifier: Option<&str>,
1045 package_url: &C::Cp,
1046 package_json: &C::Pj,
1047 ctx: &mut Ctx,
1048 ) -> ResolveResult<C::Cp> {
1049 let path = cached_path.path();
1050 let Some(new_specifier) = package_json.resolve_browser_field(
1051 path,
1052 module_specifier,
1053 &self.options.alias_fields,
1054 )?
1055 else {
1056 return Ok(None);
1057 };
1058 if module_specifier.is_some_and(|s| s == new_specifier) {
1060 return Ok(None);
1061 }
1062 if ctx.resolving_alias.as_ref().is_some_and(|s| s == new_specifier) {
1063 if new_specifier.strip_prefix("./").filter(|s| path.ends_with(Path::new(s))).is_some() {
1065 return if self.cache.is_file(cached_path, ctx) {
1066 Ok(Some(cached_path.clone()))
1067 } else {
1068 Err(ResolveError::NotFound(new_specifier.to_string()))
1069 };
1070 }
1071 return Err(ResolveError::Recursion);
1072 }
1073 ctx.with_resolving_alias(new_specifier.to_string());
1074 ctx.with_fully_specified(false);
1075 self.require(package_url, new_specifier, ctx).map(Some)
1076 }
1077
1078 fn load_alias(
1080 &self,
1081 cached_path: &C::Cp,
1082 specifier: &str,
1083 aliases: &Alias,
1084 ctx: &mut Ctx,
1085 ) -> ResolveResult<C::Cp> {
1086 for (alias_key_raw, specifiers) in aliases {
1087 let mut alias_key_has_wildcard = false;
1088 let alias_key = if let Some(alias_key) = alias_key_raw.strip_suffix('$') {
1089 if alias_key != specifier {
1090 continue;
1091 }
1092 alias_key
1093 } else if alias_key_raw.contains('*') {
1094 alias_key_has_wildcard = true;
1095 alias_key_raw
1096 } else {
1097 let strip_package_name = Self::strip_package_name(specifier, alias_key_raw);
1098 if strip_package_name.is_none() {
1099 continue;
1100 }
1101 alias_key_raw
1102 };
1103 let mut should_stop = false;
1107 for r in specifiers {
1108 match r {
1109 AliasValue::Path(alias_value) => {
1110 if let Some(path) = self.load_alias_value(
1111 cached_path,
1112 alias_key,
1113 alias_key_has_wildcard,
1114 alias_value,
1115 specifier,
1116 ctx,
1117 &mut should_stop,
1118 )? {
1119 return Ok(Some(path));
1120 }
1121 }
1122 AliasValue::Ignore => {
1123 let cached_path =
1124 cached_path.normalize_with(alias_key, self.cache.as_ref());
1125 return Err(ResolveError::Ignored(cached_path.to_path_buf()));
1126 }
1127 }
1128 }
1129 if should_stop {
1130 return Err(ResolveError::MatchedAliasNotFound(
1131 specifier.to_string(),
1132 alias_key.to_string(),
1133 ));
1134 }
1135 }
1136 Ok(None)
1137 }
1138
1139 #[allow(clippy::too_many_arguments)]
1140 fn load_alias_value(
1141 &self,
1142 cached_path: &C::Cp,
1143 alias_key: &str,
1144 alias_key_has_wild_card: bool,
1145 alias_value: &str,
1146 request: &str,
1147 ctx: &mut Ctx,
1148 should_stop: &mut bool,
1149 ) -> ResolveResult<C::Cp> {
1150 if request != alias_value
1151 && !request.strip_prefix(alias_value).is_some_and(|prefix| prefix.starts_with('/'))
1152 {
1153 let new_specifier = if alias_key_has_wild_card {
1154 let Some(alias_key) = alias_key.split_once('*').and_then(|(prefix, suffix)| {
1156 request
1157 .strip_prefix(prefix)
1158 .and_then(|specifier| specifier.strip_suffix(suffix))
1159 }) else {
1160 return Ok(None);
1161 };
1162 if alias_value.contains('*') {
1163 Cow::Owned(alias_value.replacen('*', alias_key, 1))
1164 } else {
1165 Cow::Borrowed(alias_value)
1166 }
1167 } else {
1168 let tail = &request[alias_key.len()..];
1169 if tail.is_empty() {
1170 Cow::Borrowed(alias_value)
1171 } else {
1172 let alias_path = Path::new(alias_value).normalize();
1173 let cached_alias_path = self.cache.value(&alias_path);
1175 if self.cache.is_file(&cached_alias_path, ctx) {
1176 return Ok(None);
1177 }
1178 let tail = tail.trim_start_matches(SLASH_START);
1180 if tail.is_empty() {
1181 Cow::Borrowed(alias_value)
1182 } else {
1183 let normalized = alias_path.normalize_with(tail);
1184 Cow::Owned(normalized.to_string_lossy().to_string())
1185 }
1186 }
1187 };
1188
1189 *should_stop = true;
1190 ctx.with_fully_specified(false);
1191 return match self.require(cached_path, new_specifier.as_ref(), ctx) {
1192 Err(ResolveError::NotFound(_) | ResolveError::MatchedAliasNotFound(_, _)) => {
1193 Ok(None)
1194 }
1195 Ok(path) => return Ok(Some(path)),
1196 Err(err) => return Err(err),
1197 };
1198 }
1199 Ok(None)
1200 }
1201
1202 fn load_extension_alias(&self, cached_path: &C::Cp, ctx: &mut Ctx) -> ResolveResult<C::Cp> {
1211 if self.options.extension_alias.is_empty() {
1212 return Ok(None);
1213 }
1214 let Some(path_extension) = cached_path.path().extension() else {
1215 return Ok(None);
1216 };
1217 let Some((_, extensions)) = self
1218 .options
1219 .extension_alias
1220 .iter()
1221 .find(|(ext, _)| OsStr::new(ext.trim_start_matches('.')) == path_extension)
1222 else {
1223 return Ok(None);
1224 };
1225 let path = cached_path.path();
1226 let Some(filename) = path.file_name() else { return Ok(None) };
1227 let path_without_extension = path.with_extension("");
1228
1229 ctx.with_fully_specified(true);
1230 for extension in extensions {
1231 let mut path_with_extension = path_without_extension.clone().into_os_string();
1232 path_with_extension.reserve_exact(extension.len());
1233 path_with_extension.push(extension);
1234 let cached_path = self.cache.value(Path::new(&path_with_extension));
1235 if let Some(path) = self.load_alias_or_file(&cached_path, ctx)? {
1236 ctx.with_fully_specified(false);
1237 return Ok(Some(path));
1238 }
1239 }
1240 if !self.cache.is_file(cached_path, ctx) {
1242 ctx.with_fully_specified(false);
1243 return Ok(None);
1244 }
1245 let dir = path.parent().unwrap().to_path_buf();
1247 let filename_without_extension = Path::new(filename).with_extension("");
1248 let filename_without_extension = filename_without_extension.to_string_lossy();
1249 let files = extensions
1250 .iter()
1251 .map(|ext| format!("{filename_without_extension}{ext}"))
1252 .collect::<Vec<_>>()
1253 .join(",");
1254 Err(ResolveError::ExtensionAlias(filename.to_string_lossy().to_string(), files, dir))
1255 }
1256
1257 fn load_roots(&self, cached_path: &C::Cp, specifier: &str, ctx: &mut Ctx) -> Option<C::Cp> {
1264 if self.options.roots.is_empty() {
1265 return None;
1266 }
1267 if let Some(specifier) = specifier.strip_prefix(SLASH_START) {
1268 if specifier.is_empty() {
1269 if self.options.roots.iter().any(|root| root.as_path() == cached_path.path()) {
1270 if let Ok(path) = self.require_relative(cached_path, "./", ctx) {
1271 return Some(path);
1272 }
1273 }
1274 } else {
1275 for root in &self.options.roots {
1276 let cached_path = self.cache.value(root);
1277 if let Ok(path) = self.require_relative(&cached_path, specifier, ctx) {
1278 return Some(path);
1279 }
1280 }
1281 }
1282 }
1283 None
1284 }
1285
1286 fn load_tsconfig(
1287 &self,
1288 root: bool,
1289 path: &Path,
1290 references: &TsconfigReferences,
1291 ) -> Result<Arc<C::Tc>, ResolveError> {
1292 self.cache.get_tsconfig(root, path, |tsconfig| {
1293 let directory = self.cache.value(tsconfig.directory());
1294 tracing::trace!(tsconfig = ?tsconfig, "load_tsconfig");
1295
1296 let extended_tsconfig_paths = tsconfig
1298 .extends()
1299 .map(|specifier| self.get_extended_tsconfig_path(&directory, tsconfig, specifier))
1300 .collect::<Result<Vec<_>, _>>()?;
1301 for extended_tsconfig_path in extended_tsconfig_paths {
1302 let extended_tsconfig = self.load_tsconfig(
1303 false,
1304 &extended_tsconfig_path,
1305 &TsconfigReferences::Disabled,
1306 )?;
1307 tsconfig.extend_tsconfig(&extended_tsconfig);
1308 }
1309
1310 if tsconfig.load_references(references) {
1311 let path = tsconfig.path().to_path_buf();
1312 let directory = tsconfig.directory().to_path_buf();
1313 for reference in tsconfig.references_mut() {
1314 let reference_tsconfig_path = directory.normalize_with(reference.path());
1315 let tsconfig = self.cache.get_tsconfig(
1316 true,
1317 &reference_tsconfig_path,
1318 |reference_tsconfig| {
1319 if reference_tsconfig.path() == path {
1320 return Err(ResolveError::TsconfigSelfReference(
1321 reference_tsconfig.path().to_path_buf(),
1322 ));
1323 }
1324 Ok(())
1325 },
1326 )?;
1327 reference.set_tsconfig(tsconfig);
1328 }
1329 }
1330 Ok(())
1331 })
1332 }
1333
1334 fn load_tsconfig_paths(
1335 &self,
1336 cached_path: &C::Cp,
1337 specifier: &str,
1338 ctx: &mut Ctx,
1339 ) -> ResolveResult<C::Cp> {
1340 let Some(tsconfig_options) = &self.options.tsconfig else {
1341 return Ok(None);
1342 };
1343 let tsconfig = self.load_tsconfig(
1344 true,
1345 &tsconfig_options.config_file,
1346 &tsconfig_options.references,
1347 )?;
1348 let paths = tsconfig.resolve(cached_path.path(), specifier);
1349 for path in paths {
1350 let cached_path = self.cache.value(&path);
1351 if let Ok(path) = self.require_relative(&cached_path, ".", ctx) {
1352 return Ok(Some(path));
1353 }
1354 }
1355 Ok(None)
1356 }
1357
1358 fn get_extended_tsconfig_path(
1359 &self,
1360 directory: &C::Cp,
1361 tsconfig: &C::Tc,
1362 specifier: &str,
1363 ) -> Result<PathBuf, ResolveError> {
1364 match specifier.as_bytes().first() {
1365 None => Err(ResolveError::Specifier(SpecifierError::Empty(specifier.to_string()))),
1366 Some(b'/') => Ok(PathBuf::from(specifier)),
1367 Some(b'.') => Ok(tsconfig.directory().normalize_with(specifier)),
1368 _ => self
1369 .clone_with_options(ResolveOptions {
1370 description_files: vec![],
1371 extensions: vec![".json".into()],
1372 main_files: vec!["tsconfig.json".into()],
1373 ..ResolveOptions::default()
1374 })
1375 .load_package_self_or_node_modules(directory, specifier, &mut Ctx::default())
1376 .map(|p| p.to_path_buf())
1377 .map_err(|err| match err {
1378 ResolveError::NotFound(_) => {
1379 ResolveError::TsconfigNotFound(PathBuf::from(specifier))
1380 }
1381 _ => err,
1382 }),
1383 }
1384 }
1385
1386 fn package_resolve(
1388 &self,
1389 cached_path: &C::Cp,
1390 specifier: &str,
1391 ctx: &mut Ctx,
1392 ) -> ResolveResult<C::Cp> {
1393 let (package_name, subpath) = Self::parse_package_specifier(specifier);
1394
1395 self.require_core(package_name)?;
1398
1399 for module_name in &self.options.modules {
1401 for cached_path in std::iter::successors(Some(cached_path), |p| p.parent()) {
1402 let Some(cached_path) = self.get_module_directory(cached_path, module_name, ctx)
1404 else {
1405 continue;
1406 };
1407 let cached_path = cached_path.normalize_with(package_name, self.cache.as_ref());
1409 if self.cache.is_dir(&cached_path, ctx) {
1412 if let Some((_, package_json)) =
1414 self.cache.get_package_json(&cached_path, &self.options, ctx)?
1415 {
1416 for exports in package_json.exports_fields(&self.options.exports_fields) {
1419 if let Some(path) = self.package_exports_resolve(
1420 &cached_path,
1421 &format!(".{subpath}"),
1422 &exports,
1423 ctx,
1424 )? {
1425 return Ok(Some(path));
1426 }
1427 }
1428 if subpath == "." {
1430 for main_field in package_json.main_fields(&self.options.main_fields) {
1432 let cached_path =
1434 cached_path.normalize_with(main_field, self.cache.as_ref());
1435 if self.cache.is_file(&cached_path, ctx) {
1436 return Ok(Some(cached_path));
1437 }
1438 }
1439 }
1440 }
1441 let subpath = format!(".{subpath}");
1442 ctx.with_fully_specified(false);
1443 return self.require(&cached_path, &subpath, ctx).map(Some);
1444 }
1445 }
1446 }
1447
1448 Err(ResolveError::NotFound(specifier.to_string()))
1449 }
1450
1451 fn package_exports_resolve<'a, Io: ImportsExportsEntry<'a>>(
1453 &self,
1454 package_url: &C::Cp,
1455 subpath: &str,
1456 exports: &Io,
1457 ctx: &mut Ctx,
1458 ) -> ResolveResult<C::Cp> {
1459 let conditions = &self.options.condition_names;
1460 if let Some(map) = exports.as_map() {
1462 let mut has_dot = false;
1463 let mut without_dot = false;
1464 for key in map.keys() {
1465 let starts_with_dot_or_hash = key.starts_with(['.', '#']);
1466 has_dot = has_dot || starts_with_dot_or_hash;
1467 without_dot = without_dot || !starts_with_dot_or_hash;
1468 if has_dot && without_dot {
1469 return Err(ResolveError::InvalidPackageConfig(
1470 package_url.path().join("package.json"),
1471 ));
1472 }
1473 }
1474 }
1475 if subpath == "." {
1478 if ctx.query.is_some() || ctx.fragment.is_some() {
1482 let query = ctx.query.clone().unwrap_or_default();
1483 let fragment = ctx.fragment.clone().unwrap_or_default();
1484 return Err(ResolveError::PackagePathNotExported(
1485 format!("./{}{query}{fragment}", subpath.trim_start_matches('.')),
1486 package_url.path().join("package.json"),
1487 ));
1488 }
1489 let main_export = match exports.kind() {
1491 ImportsExportsKind::String | ImportsExportsKind::Array => {
1493 Some(Cow::Borrowed(exports))
1495 }
1496 _ => exports.as_map().and_then(|map| {
1498 map.get(".").map_or_else(
1499 || {
1500 if map.keys().any(|key| key.starts_with("./") || key.starts_with('#')) {
1501 None
1502 } else {
1503 Some(Cow::Borrowed(exports))
1504 }
1505 },
1506 |entry| Some(Cow::Owned(entry)),
1507 )
1508 }),
1509 };
1510 if let Some(main_export) = main_export {
1512 let resolved = self.package_target_resolve(
1514 package_url,
1515 ".",
1516 main_export.as_ref(),
1517 None,
1518 false,
1519 conditions,
1520 ctx,
1521 )?;
1522 if let Some(path) = resolved {
1524 return Ok(Some(path));
1525 }
1526 }
1527 }
1528 if let Some(exports) = exports.as_map() {
1530 let match_key = &subpath;
1533 if let Some(path) = self.package_imports_exports_resolve(
1535 match_key,
1536 &exports,
1537 package_url,
1538 false,
1539 conditions,
1540 ctx,
1541 )? {
1542 return Ok(Some(path));
1544 }
1545 }
1546 Err(ResolveError::PackagePathNotExported(
1548 subpath.to_string(),
1549 package_url.path().join("package.json"),
1550 ))
1551 }
1552
1553 fn package_imports_resolve(
1555 &self,
1556 specifier: &str,
1557 package_json: &C::Pj,
1558 ctx: &mut Ctx,
1559 ) -> Result<Option<C::Cp>, ResolveError> {
1560 debug_assert!(specifier.starts_with('#'), "{specifier}");
1562 let mut has_imports = false;
1572 for imports in package_json.imports_fields(&self.options.imports_fields) {
1573 if !has_imports {
1574 has_imports = true;
1575 if specifier == "#" || specifier.starts_with("#/") {
1577 return Err(ResolveError::InvalidModuleSpecifier(
1578 specifier.to_string(),
1579 package_json.path().to_path_buf(),
1580 ));
1581 }
1582 }
1583 if let Some(path) = self.package_imports_exports_resolve(
1584 specifier,
1585 &imports,
1586 &self.cache.value(package_json.directory()),
1587 true,
1588 &self.options.condition_names,
1589 ctx,
1590 )? {
1591 return Ok(Some(path));
1593 }
1594 }
1595
1596 if has_imports {
1598 Err(ResolveError::PackageImportNotDefined(
1599 specifier.to_string(),
1600 package_json.path().to_path_buf(),
1601 ))
1602 } else {
1603 Ok(None)
1604 }
1605 }
1606
1607 fn package_imports_exports_resolve<'a, Io: ImportsExportsMap<'a>>(
1609 &self,
1610 match_key: &str,
1611 match_obj: &Io,
1612 package_url: &C::Cp,
1613 is_imports: bool,
1614 conditions: &[String],
1615 ctx: &mut Ctx,
1616 ) -> ResolveResult<C::Cp> {
1617 if match_key.ends_with('/') {
1620 return Ok(None);
1621 }
1622 if !match_key.contains('*') {
1624 if let Some(target) = match_obj.get(match_key) {
1626 return self.package_target_resolve(
1628 package_url,
1629 match_key,
1630 &target,
1631 None,
1632 is_imports,
1633 conditions,
1634 ctx,
1635 );
1636 }
1637 }
1638
1639 let mut best_target = None;
1640 let mut best_match = "";
1641 let mut best_key = "";
1642 for (expansion_key, target) in match_obj.iter() {
1645 if expansion_key.starts_with("./") || expansion_key.starts_with('#') {
1646 if let Some((pattern_base, pattern_trailer)) = expansion_key.split_once('*') {
1648 if match_key.starts_with(pattern_base)
1650 && !pattern_trailer.contains('*')
1652 && (pattern_trailer.is_empty()
1654 || (match_key.len() >= expansion_key.len()
1655 && match_key.ends_with(pattern_trailer)))
1656 && Self::pattern_key_compare(best_key, expansion_key).is_gt()
1657 {
1658 best_target = Some(target);
1660 best_match =
1662 &match_key[pattern_base.len()..match_key.len() - pattern_trailer.len()];
1663 best_key = expansion_key;
1664 }
1665 } else if expansion_key.ends_with('/')
1666 && match_key.starts_with(expansion_key)
1667 && Self::pattern_key_compare(best_key, expansion_key).is_gt()
1668 {
1669 best_target = Some(target);
1671 best_match = &match_key[expansion_key.len()..];
1672 best_key = expansion_key;
1673 }
1674 }
1675 }
1676 if let Some(best_target) = best_target {
1677 return self.package_target_resolve(
1679 package_url,
1680 best_key,
1681 &best_target,
1682 Some(best_match),
1683 is_imports,
1684 conditions,
1685 ctx,
1686 );
1687 }
1688 Ok(None)
1690 }
1691
1692 #[allow(clippy::too_many_arguments)]
1694 fn package_target_resolve<'a, Io: ImportsExportsEntry<'a>>(
1695 &self,
1696 package_url: &C::Cp,
1697 target_key: &str,
1698 target: &Io,
1699 pattern_match: Option<&str>,
1700 is_imports: bool,
1701 conditions: &[String],
1702 ctx: &mut Ctx,
1703 ) -> ResolveResult<C::Cp> {
1704 fn normalize_string_target<'a>(
1705 target_key: &'a str,
1706 target: &'a str,
1707 pattern_match: Option<&'a str>,
1708 package_url: &impl CachedPath,
1709 ) -> Result<Cow<'a, str>, ResolveError> {
1710 let target = if let Some(pattern_match) = pattern_match {
1711 if !target_key.contains('*') && !target.contains('*') {
1712 if target_key.ends_with('/') && target.ends_with('/') {
1715 Cow::Owned(format!("{target}{pattern_match}"))
1716 } else {
1717 return Err(ResolveError::InvalidPackageConfigDirectory(
1718 package_url.path().join("package.json"),
1719 ));
1720 }
1721 } else {
1722 Cow::Owned(target.replace('*', pattern_match))
1723 }
1724 } else {
1725 Cow::Borrowed(target)
1726 };
1727 Ok(target)
1728 }
1729
1730 if let Some(target) = target.as_string() {
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 for (key, target_value) in target.iter() {
1772 if key == "default" || conditions.iter().any(|condition| condition == key) {
1774 let resolved = self.package_target_resolve(
1777 package_url,
1778 target_key,
1779 &target_value,
1780 pattern_match,
1781 is_imports,
1782 conditions,
1783 ctx,
1784 );
1785 if let Some(path) = resolved? {
1787 return Ok(Some(path));
1789 }
1790 }
1791 }
1792 return Ok(None);
1794 }
1795 else if let Some(targets) = target.as_array() {
1797 if targets.is_empty() {
1799 return Err(ResolveError::PackagePathNotExported(
1801 pattern_match.unwrap_or(".").to_string(),
1802 package_url.path().join("package.json"),
1803 ));
1804 }
1805 for (i, target_value) in targets.iter().enumerate() {
1807 let resolved = self.package_target_resolve(
1809 package_url,
1810 target_key,
1811 &target_value,
1812 pattern_match,
1813 is_imports,
1814 conditions,
1815 ctx,
1816 );
1817
1818 if resolved.is_err() && i == targets.len() {
1819 return resolved;
1820 }
1821
1822 if let Ok(Some(path)) = resolved {
1824 return Ok(Some(path));
1826 }
1827 }
1828 }
1831 Ok(None)
1833 }
1835
1836 fn parse_package_specifier(specifier: &str) -> (&str, &str) {
1839 let mut separator_index = specifier.as_bytes().iter().position(|b| *b == b'/');
1840 if specifier.starts_with('@') {
1843 if separator_index.is_none() || specifier.is_empty() {
1845 } else if let Some(index) = &separator_index {
1847 separator_index = specifier.as_bytes()[*index + 1..]
1848 .iter()
1849 .position(|b| *b == b'/')
1850 .map(|i| i + *index + 1);
1851 }
1852 }
1853 let package_name =
1854 separator_index.map_or(specifier, |separator_index| &specifier[..separator_index]);
1855
1856 let package_subpath =
1867 separator_index.map_or("", |separator_index| &specifier[separator_index..]);
1868 (package_name, package_subpath)
1869 }
1870
1871 fn pattern_key_compare(key_a: &str, key_b: &str) -> Ordering {
1873 if key_a.is_empty() {
1874 return Ordering::Greater;
1875 }
1876 debug_assert!(key_a.ends_with('/') || key_a.match_indices('*').count() == 1, "{key_a}");
1878 debug_assert!(key_b.ends_with('/') || key_b.match_indices('*').count() == 1, "{key_b}");
1880 let a_pos = key_a.chars().position(|c| c == '*');
1882 let base_length_a = a_pos.map_or(key_a.len(), |p| p + 1);
1883 let b_pos = key_b.chars().position(|c| c == '*');
1885 let base_length_b = b_pos.map_or(key_b.len(), |p| p + 1);
1886 if base_length_a > base_length_b {
1888 return Ordering::Less;
1889 }
1890 if base_length_b > base_length_a {
1892 return Ordering::Greater;
1893 }
1894 if !key_a.contains('*') {
1896 return Ordering::Greater;
1897 }
1898 if !key_b.contains('*') {
1900 return Ordering::Less;
1901 }
1902 if key_a.len() > key_b.len() {
1904 return Ordering::Less;
1905 }
1906 if key_b.len() > key_a.len() {
1908 return Ordering::Greater;
1909 }
1910 Ordering::Equal
1912 }
1913
1914 fn strip_package_name<'a>(specifier: &'a str, package_name: &'a str) -> Option<&'a str> {
1915 specifier
1916 .strip_prefix(package_name)
1917 .filter(|tail| tail.is_empty() || tail.starts_with(SLASH_START))
1918 }
1919}