1#![cfg_attr(feature = "document-features", doc = document_features::document_features!())]
42#![doc = include_str!("../examples/resolver.rs")]
47mod builtins;
50mod cache;
51mod context;
52mod error;
53mod file_system;
54mod options;
55mod package_json;
56mod path;
57mod resolution;
58mod specifier;
59mod tsconfig;
60mod tsconfig_resolver;
61#[cfg(target_os = "windows")]
62mod windows;
63
64#[cfg(test)]
65mod tests;
66
67pub use crate::{
68 builtins::NODEJS_BUILTINS,
69 cache::{Cache, CachedPath},
70 error::{JSONError, ResolveError, SpecifierError},
71 file_system::{FileMetadata, FileSystem, FileSystemOs},
72 options::{
73 Alias, AliasValue, EnforceExtension, ResolveOptions, Restriction, TsconfigDiscovery,
74 TsconfigOptions, TsconfigReferences,
75 },
76 package_json::{
77 ImportsExportsArray, ImportsExportsEntry, ImportsExportsKind, ImportsExportsMap,
78 PackageJson, PackageType, SideEffects,
79 },
80 path::PathUtil,
81 resolution::{ModuleType, Resolution},
82 tsconfig::{
83 CompilerOptions, CompilerOptionsPathsMap, ExtendsField, ProjectReference, TsConfig,
84 },
85};
86
87use std::{
88 borrow::Cow,
89 cmp::Ordering,
90 ffi::OsStr,
91 fmt, iter,
92 path::{Component, Path, PathBuf},
93 sync::Arc,
94};
95
96use rustc_hash::FxHashSet;
97
98use crate::{context::ResolveContext as Ctx, path::SLASH_START, specifier::Specifier};
99
100type ResolveResult = Result<Option<CachedPath>, ResolveError>;
101
102#[derive(Debug, Default, Clone)]
104pub struct ResolveContext {
105 pub file_dependencies: FxHashSet<PathBuf>,
107
108 pub missing_dependencies: FxHashSet<PathBuf>,
110}
111
112pub type Resolver = ResolverGeneric<FileSystemOs>;
114
115pub struct ResolverGeneric<Fs> {
117 options: ResolveOptions,
118 cache: Arc<Cache<Fs>>,
119}
120
121impl<Fs> fmt::Debug for ResolverGeneric<Fs> {
122 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123 self.options.fmt(f)
124 }
125}
126
127impl<Fs: FileSystem> Default for ResolverGeneric<Fs> {
128 fn default() -> Self {
129 Self::new(ResolveOptions::default())
130 }
131}
132
133impl<Fs: FileSystem> ResolverGeneric<Fs> {
134 #[must_use]
135 pub fn new(options: ResolveOptions) -> Self {
136 cfg_if::cfg_if! {
137 if #[cfg(feature = "yarn_pnp")] {
138 let fs = Fs::new(options.yarn_pnp);
139 } else {
140 let fs = Fs::new();
141 }
142 }
143 let cache = Arc::new(Cache::new(fs));
144 Self { options: options.sanitize(), cache }
145 }
146}
147
148impl<Fs: FileSystem> ResolverGeneric<Fs> {
149 pub fn new_with_file_system(file_system: Fs, options: ResolveOptions) -> Self {
150 Self { cache: Arc::new(Cache::new(file_system)), options: options.sanitize() }
151 }
152
153 #[must_use]
155 pub fn clone_with_options(&self, options: ResolveOptions) -> Self {
156 Self { options: options.sanitize(), cache: Arc::clone(&self.cache) }
157 }
158
159 #[must_use]
161 pub const fn options(&self) -> &ResolveOptions {
162 &self.options
163 }
164
165 pub fn clear_cache(&self) {
167 self.cache.clear();
168 }
169
170 pub fn resolve<P: AsRef<Path>>(
185 &self,
186 directory: P,
187 specifier: &str,
188 ) -> Result<Resolution, ResolveError> {
189 let mut ctx = Ctx::default();
190 let path = directory.as_ref();
191 let tsconfig = match &self.options.tsconfig {
192 Some(TsconfigDiscovery::Manual(o)) => self.find_tsconfig_manual(o)?,
193 _ => None,
194 };
195 self.resolve_tracing(path, specifier, tsconfig.as_deref(), &mut ctx)
196 }
197
198 pub fn resolve_file<P: AsRef<Path>>(
210 &self,
211 file: P,
212 specifier: &str,
213 ) -> Result<Resolution, ResolveError> {
214 self.resolve_file_impl(file.as_ref(), specifier)
215 }
216
217 fn resolve_file_impl(&self, path: &Path, specifier: &str) -> Result<Resolution, ResolveError> {
218 let mut ctx = Ctx::default();
219 let dir = path.parent().unwrap();
220 let tsconfig = self.find_tsconfig(path)?;
221 self.resolve_tracing(dir, specifier, tsconfig.as_deref(), &mut ctx)
222 }
223
224 pub fn resolve_with_context<P: AsRef<Path>>(
230 &self,
231 directory: P,
232 specifier: &str,
233 tsconfig: Option<&TsConfig>,
234 resolve_context: &mut ResolveContext,
235 ) -> Result<Resolution, ResolveError> {
236 let mut ctx = Ctx::default();
237 ctx.init_file_dependencies();
238 let result = self.resolve_tracing(directory.as_ref(), specifier, tsconfig, &mut ctx);
239 if let Some(deps) = &mut ctx.file_dependencies {
240 resolve_context.file_dependencies.extend(deps.drain(..));
241 }
242 if let Some(deps) = &mut ctx.missing_dependencies {
243 resolve_context.missing_dependencies.extend(deps.drain(..));
244 }
245 result
246 }
247
248 fn resolve_tracing(
250 &self,
251 directory: &Path,
252 specifier: &str,
253 tsconfig: Option<&TsConfig>,
254 ctx: &mut Ctx,
255 ) -> Result<Resolution, ResolveError> {
256 let span = tracing::debug_span!("resolve", path = ?directory, specifier = specifier);
257 let _enter = span.enter();
258 let r = self.resolve_impl(directory, specifier, tsconfig, ctx);
259 match &r {
260 Ok(r) => {
261 tracing::debug!(options = ?self.options, path = ?directory, specifier = specifier, ret = ?r.path);
262 }
263 Err(err) => {
264 tracing::debug!(options = ?self.options, path = ?directory, specifier = specifier, err = ?err);
265 }
266 }
267 r
268 }
269
270 fn resolve_impl(
271 &self,
272 path: &Path,
273 specifier: &str,
274 tsconfig: Option<&TsConfig>,
275 ctx: &mut Ctx,
276 ) -> Result<Resolution, ResolveError> {
277 ctx.with_fully_specified(self.options.fully_specified);
278
279 let cached_path = self.cache.value(path);
280 let cached_path = self.require(&cached_path, specifier, tsconfig, ctx)?;
281 let path = self.load_realpath(&cached_path)?;
282
283 let package_json = self.find_package_json_for_a_package(&cached_path, ctx)?;
284 if let Some(package_json) = &package_json {
285 debug_assert!(path.starts_with(package_json.directory()));
287 }
288 let module_type = self.esm_file_format(&cached_path, ctx)?;
289
290 Ok(Resolution {
291 path,
292 query: ctx.query.take(),
293 fragment: ctx.fragment.take(),
294 package_json,
295 module_type,
296 })
297 }
298
299 fn find_package_json_for_a_package(
300 &self,
301 cached_path: &CachedPath,
302 ctx: &mut Ctx,
303 ) -> Result<Option<Arc<PackageJson>>, ResolveError> {
304 let inside_node_modules = cached_path.inside_node_modules();
308 if inside_node_modules {
309 let mut last = None;
310 for cp in iter::successors(Some(cached_path.clone()), CachedPath::parent) {
311 if cp.is_node_modules() {
312 break;
313 }
314 if self.cache.is_dir(&cp, ctx)
315 && let Some(package_json) =
316 self.cache.get_package_json(&cp, &self.options, ctx)?
317 {
318 last = Some(package_json);
319 }
320 }
321 Ok(last)
322 } else {
323 self.cache.find_package_json(cached_path, &self.options, ctx)
324 }
325 }
326
327 fn require(
334 &self,
335 cached_path: &CachedPath,
336 specifier: &str,
337 tsconfig: Option<&TsConfig>,
338 ctx: &mut Ctx,
339 ) -> Result<CachedPath, ResolveError> {
340 ctx.test_for_infinite_recursion()?;
341
342 let (parsed, try_fragment_as_path) =
344 self.load_parse(cached_path, specifier, tsconfig, ctx)?;
345 if let Some(path) = try_fragment_as_path {
346 return Ok(path);
347 }
348
349 self.require_without_parse(cached_path, parsed.path(), tsconfig, ctx)
350 }
351
352 fn require_without_parse(
353 &self,
354 cached_path: &CachedPath,
355 specifier: &str,
356 tsconfig: Option<&TsConfig>,
357 ctx: &mut Ctx,
358 ) -> Result<CachedPath, ResolveError> {
359 if let Some(path) = self.load_tsconfig_paths(cached_path, specifier, tsconfig)? {
361 return Ok(path);
362 }
363
364 if let Some(path) =
366 self.load_alias(cached_path, specifier, &self.options.alias, tsconfig, ctx)?
367 {
368 return Ok(path);
369 }
370
371 cfg_if::cfg_if! {
372 if #[cfg(not(target_arch = "wasm32"))] {
373 let specifier = resolve_file_protocol(specifier)?;
374 let specifier = specifier.as_ref();
375 }
376 };
377
378 let result = match Path::new(&specifier).components().next() {
379 Some(Component::RootDir | Component::Prefix(_)) => {
381 self.require_absolute(cached_path, specifier, tsconfig, ctx)
382 }
383 Some(Component::CurDir | Component::ParentDir) => {
385 self.require_relative(cached_path, specifier, tsconfig, ctx)
386 }
387 Some(Component::Normal(_)) if specifier.as_bytes()[0] == b'#' => {
389 self.require_hash(cached_path, specifier, tsconfig, ctx)
390 }
391 _ => {
392 self.require_core(specifier)?;
396
397 self.require_bare(cached_path, specifier, tsconfig, ctx)
401 }
402 };
403
404 result.or_else(|err| {
405 if err.is_ignore() {
406 return Err(err);
407 }
408 self.load_alias(cached_path, specifier, &self.options.fallback, tsconfig, ctx)
410 .and_then(|value| value.ok_or(err))
411 })
412 }
413
414 fn require_core(&self, specifier: &str) -> Result<(), ResolveError> {
418 if self.options.builtin_modules {
419 let is_runtime_module = specifier.starts_with("node:");
420 if is_runtime_module || NODEJS_BUILTINS.binary_search(&specifier).is_ok() {
421 let resolved = if is_runtime_module {
422 specifier.to_string()
423 } else {
424 format!("node:{specifier}")
425 };
426 return Err(ResolveError::Builtin { resolved, is_runtime_module });
427 }
428 }
429 Ok(())
430 }
431
432 fn require_absolute(
433 &self,
434 cached_path: &CachedPath,
435 specifier: &str,
436 tsconfig: Option<&TsConfig>,
437 ctx: &mut Ctx,
438 ) -> Result<CachedPath, ResolveError> {
439 debug_assert!(
441 Path::new(specifier)
442 .components()
443 .next()
444 .is_some_and(|c| matches!(c, Component::RootDir | Component::Prefix(_)))
445 );
446 if !self.options.prefer_relative
447 && self.options.prefer_absolute
448 && let Ok(path) =
449 self.load_package_self_or_node_modules(cached_path, specifier, tsconfig, ctx)
450 {
451 return Ok(path);
452 }
453 if let Some(path) = self.load_roots(cached_path, specifier, tsconfig, ctx) {
454 return Ok(path);
455 }
456 let path = self.cache.value(Path::new(specifier));
459 if let Some(path) = self.load_as_file_or_directory(&path, specifier, tsconfig, ctx)? {
460 return Ok(path);
461 }
462 Err(ResolveError::NotFound(specifier.to_string()))
463 }
464
465 fn require_relative(
467 &self,
468 cached_path: &CachedPath,
469 specifier: &str,
470 tsconfig: Option<&TsConfig>,
471 ctx: &mut Ctx,
472 ) -> Result<CachedPath, ResolveError> {
473 debug_assert!(Path::new(specifier).components().next().is_some_and(|c| matches!(
475 c,
476 Component::CurDir | Component::ParentDir | Component::Normal(_)
477 )));
478 let cached_path = cached_path.normalize_with(specifier, self.cache.as_ref());
479 if let Some(path) = self.load_as_file_or_directory(
482 &cached_path,
483 if specifier == "." { "./" } else { specifier },
485 tsconfig,
486 ctx,
487 )? {
488 return Ok(path);
489 }
490 Err(ResolveError::NotFound(specifier.to_string()))
492 }
493
494 fn require_hash(
495 &self,
496 cached_path: &CachedPath,
497 specifier: &str,
498 tsconfig: Option<&TsConfig>,
499 ctx: &mut Ctx,
500 ) -> Result<CachedPath, ResolveError> {
501 debug_assert_eq!(specifier.chars().next(), Some('#'));
502 self.load_package_imports(cached_path, specifier, tsconfig, ctx)?
504 .map_or_else(|| Err(ResolveError::NotFound(specifier.to_string())), Ok)
505 }
506
507 fn require_bare(
508 &self,
509 cached_path: &CachedPath,
510 specifier: &str,
511 tsconfig: Option<&TsConfig>,
512 ctx: &mut Ctx,
513 ) -> Result<CachedPath, ResolveError> {
514 debug_assert!(
516 Path::new(specifier)
517 .components()
518 .next()
519 .is_some_and(|c| matches!(c, Component::Normal(_)))
520 );
521 if self.options.prefer_relative
522 && let Ok(path) = self.require_relative(cached_path, specifier, tsconfig, ctx)
523 {
524 return Ok(path);
525 }
526 self.load_package_self_or_node_modules(cached_path, specifier, tsconfig, ctx)
527 }
528
529 fn load_parse<'s>(
538 &self,
539 cached_path: &CachedPath,
540 specifier: &'s str,
541 tsconfig: Option<&TsConfig>,
542 ctx: &mut Ctx,
543 ) -> Result<(Specifier<'s>, Option<CachedPath>), ResolveError> {
544 let parsed = Specifier::parse(specifier).map_err(ResolveError::Specifier)?;
545 ctx.with_query_fragment(parsed.query, parsed.fragment);
546
547 if ctx.fragment.is_some() && ctx.query.is_none() {
549 let specifier = parsed.path();
550 let fragment = ctx.fragment.take().unwrap();
551 let path = format!("{specifier}{fragment}");
552 if let Ok(path) = self.require_without_parse(cached_path, &path, tsconfig, ctx) {
553 return Ok((parsed, Some(path)));
554 }
555 ctx.fragment.replace(fragment);
556 }
557 Ok((parsed, None))
558 }
559
560 fn load_package_self_or_node_modules(
561 &self,
562 cached_path: &CachedPath,
563 specifier: &str,
564 tsconfig: Option<&TsConfig>,
565 ctx: &mut Ctx,
566 ) -> Result<CachedPath, ResolveError> {
567 let (package_name, subpath) = Self::parse_package_specifier(specifier);
568 if subpath.is_empty() {
569 ctx.with_fully_specified(false);
570 }
571 if let Some(path) = self.load_package_self(cached_path, specifier, tsconfig, ctx)? {
573 return Ok(path);
574 }
575 if let Some(path) =
577 self.load_node_modules(cached_path, specifier, package_name, subpath, tsconfig, ctx)?
578 {
579 return Ok(path);
580 }
581
582 if specifier.contains("/../..") || specifier.contains("../../") {
588 let path = Path::new(specifier).normalize_relative();
589 let mut owned = path.to_string_lossy().into_owned();
590
591 if specifier.ends_with('/') {
592 owned += "/";
593 }
594
595 let specifier_owned = Some(owned);
596 let normalized_specifier = specifier_owned.as_deref().unwrap();
597
598 let (package_name, subpath) = Self::parse_package_specifier(normalized_specifier);
599
600 if package_name == ".."
601 && let Some(path) = self.load_node_modules(
602 cached_path,
603 normalized_specifier,
604 package_name,
605 subpath,
606 tsconfig,
607 ctx,
608 )?
609 {
610 return Ok(path);
611 }
612 }
613
614 Err(ResolveError::NotFound(specifier.to_string()))
616 }
617
618 fn load_package_imports(
620 &self,
621 cached_path: &CachedPath,
622 specifier: &str,
623 tsconfig: Option<&TsConfig>,
624 ctx: &mut Ctx,
625 ) -> ResolveResult {
626 let Some(package_json) = self.cache.find_package_json(cached_path, &self.options, ctx)?
629 else {
630 return Ok(None);
631 };
632 if let Some(path) = self.package_imports_resolve(specifier, &package_json, tsconfig, ctx)? {
635 return self.resolve_esm_match(specifier, &path, tsconfig, ctx);
637 }
638 Ok(None)
639 }
640
641 fn load_as_file(
642 &self,
643 cached_path: &CachedPath,
644 tsconfig: Option<&TsConfig>,
645 ctx: &mut Ctx,
646 ) -> ResolveResult {
647 if let Some(path) = self.load_extension_alias(cached_path, tsconfig, ctx)? {
649 return Ok(Some(path));
650 }
651 if self.options.enforce_extension.is_disabled() {
652 if let Some(path) = self.load_alias_or_file(cached_path, tsconfig, ctx)? {
654 return Ok(Some(path));
655 }
656 }
657 if let Some(path) =
661 self.load_extensions(cached_path, &self.options.extensions, tsconfig, ctx)?
662 {
663 return Ok(Some(path));
664 }
665 Ok(None)
666 }
667
668 fn load_as_directory(
669 &self,
670 cached_path: &CachedPath,
671 tsconfig: Option<&TsConfig>,
672 ctx: &mut Ctx,
673 ) -> ResolveResult {
674 if let Some(package_json) = self.cache.get_package_json(cached_path, &self.options, ctx)? {
677 for main_field in package_json.main_fields(&self.options.main_fields) {
679 let main_field = if main_field.starts_with("./") || main_field.starts_with("../") {
681 Cow::Borrowed(main_field)
682 } else {
683 Cow::Owned(format!("./{main_field}"))
684 };
685
686 let cached_path =
688 cached_path.normalize_with(main_field.as_ref(), self.cache.as_ref());
689 if let Some(path) = self.load_as_file(&cached_path, tsconfig, ctx)? {
691 return Ok(Some(path));
692 }
693 if let Some(path) = self.load_index(&cached_path, tsconfig, ctx)? {
695 return Ok(Some(path));
696 }
697 }
698 if self.options.allow_package_exports_in_directory_resolve {
707 for exports in package_json.exports_fields(&self.options.exports_fields) {
708 if let Some(path) =
709 self.package_exports_resolve(cached_path, ".", &exports, tsconfig, ctx)?
710 {
711 return Ok(Some(path));
712 }
713 }
714 }
715 }
716
717 self.load_index(cached_path, tsconfig, ctx)
719 }
720
721 fn load_as_file_or_directory(
722 &self,
723 cached_path: &CachedPath,
724 specifier: &str,
725 tsconfig: Option<&TsConfig>,
726 ctx: &mut Ctx,
727 ) -> ResolveResult {
728 if self.options.resolve_to_context {
729 return Ok(self.cache.is_dir(cached_path, ctx).then(|| cached_path.clone()));
730 }
731 if !specifier.ends_with('/')
732 && let Some(path) = self.load_as_file(cached_path, tsconfig, ctx)?
733 {
734 return Ok(Some(path));
735 }
736 if self.cache.is_dir(cached_path, ctx)
737 && let Some(path) = self.load_as_directory(cached_path, tsconfig, ctx)?
738 {
739 return Ok(Some(path));
740 }
741 Ok(None)
742 }
743
744 fn load_extensions(
745 &self,
746 path: &CachedPath,
747 extensions: &[String],
748 tsconfig: Option<&TsConfig>,
749 ctx: &mut Ctx,
750 ) -> ResolveResult {
751 if ctx.fully_specified {
752 return Ok(None);
753 }
754 for extension in extensions {
755 let cached_path = path.add_extension(extension, self.cache.as_ref());
756 if let Some(path) = self.load_alias_or_file(&cached_path, tsconfig, ctx)? {
757 return Ok(Some(path));
758 }
759 }
760 Ok(None)
761 }
762
763 fn load_realpath(&self, cached_path: &CachedPath) -> Result<PathBuf, ResolveError> {
764 if self.options.symlinks {
765 self.cache.canonicalize(cached_path)
766 } else {
767 Ok(cached_path.to_path_buf())
768 }
769 }
770
771 fn check_restrictions(&self, path: &Path) -> bool {
772 fn is_inside(path: &Path, parent: &Path) -> bool {
774 if !path.starts_with(parent) {
775 return false;
776 }
777 if path.as_os_str().len() == parent.as_os_str().len() {
778 return true;
779 }
780 path.strip_prefix(parent).is_ok_and(|p| p == Path::new("./"))
781 }
782 for restriction in &self.options.restrictions {
783 match restriction {
784 Restriction::Path(restricted_path) => {
785 if !is_inside(path, restricted_path) {
786 return false;
787 }
788 }
789 Restriction::Fn(f) => {
790 if !f(path) {
791 return false;
792 }
793 }
794 }
795 }
796 true
797 }
798
799 fn load_index(
800 &self,
801 cached_path: &CachedPath,
802 tsconfig: Option<&TsConfig>,
803 ctx: &mut Ctx,
804 ) -> ResolveResult {
805 for main_file in &self.options.main_files {
806 let cached_path = cached_path.normalize_with(main_file, self.cache.as_ref());
807 if self.options.enforce_extension.is_disabled()
808 && let Some(path) = self.load_browser_field_or_alias(&cached_path, tsconfig, ctx)?
809 && self.check_restrictions(path.path())
810 {
811 return Ok(Some(path));
812 }
813 if let Some(path) =
817 self.load_extensions(&cached_path, &self.options.extensions, tsconfig, ctx)?
818 {
819 return Ok(Some(path));
820 }
821 }
822 Ok(None)
823 }
824
825 fn load_browser_field_or_alias(
826 &self,
827 cached_path: &CachedPath,
828 tsconfig: Option<&TsConfig>,
829 ctx: &mut Ctx,
830 ) -> ResolveResult {
831 if !self.options.alias_fields.is_empty()
832 && let Some(package_json) =
833 self.cache.find_package_json(cached_path, &self.options, ctx)?
834 && let Some(path) = self.load_browser_field(cached_path, None, &package_json, ctx)?
835 {
836 return Ok(Some(path));
837 }
838 if !self.options.alias.is_empty() {
841 let alias_specifier = cached_path.path().to_string_lossy();
842 if let Some(path) =
843 self.load_alias(cached_path, &alias_specifier, &self.options.alias, tsconfig, ctx)?
844 {
845 return Ok(Some(path));
846 }
847 }
848 Ok(None)
849 }
850
851 fn load_alias_or_file(
852 &self,
853 cached_path: &CachedPath,
854 tsconfig: Option<&TsConfig>,
855 ctx: &mut Ctx,
856 ) -> ResolveResult {
857 if let Some(path) = self.load_browser_field_or_alias(cached_path, tsconfig, ctx)? {
858 return Ok(Some(path));
859 }
860 if self.cache.is_file(cached_path, ctx) && self.check_restrictions(cached_path.path()) {
861 return Ok(Some(cached_path.clone()));
862 }
863 Ok(None)
864 }
865
866 fn load_node_modules(
867 &self,
868 cached_path: &CachedPath,
869 specifier: &str,
870 package_name: &str,
871 subpath: &str,
872 tsconfig: Option<&TsConfig>,
873 ctx: &mut Ctx,
874 ) -> ResolveResult {
875 #[cfg(feature = "yarn_pnp")]
876 if self.options.yarn_pnp
877 && let Some(resolved_path) = self.load_pnp(cached_path, specifier, tsconfig, ctx)?
878 {
879 return Ok(Some(resolved_path));
880 }
881
882 for module_name in &self.options.modules {
885 for cached_path in std::iter::successors(Some(cached_path.clone()), CachedPath::parent)
886 {
887 if !self.cache.is_dir(&cached_path, ctx) {
889 continue;
890 }
891
892 let Some(cached_path) = self.get_module_directory(&cached_path, module_name, ctx)
893 else {
894 continue;
895 };
896 if !package_name.is_empty() {
901 let cached_path = cached_path.normalize_with(package_name, self.cache.as_ref());
902 if self.cache.is_dir(&cached_path, ctx) {
904 if let Some(path) = self.load_package_exports(
906 specifier,
907 subpath,
908 &cached_path,
909 tsconfig,
910 ctx,
911 )? {
912 return Ok(Some(path));
913 }
914 } else {
915 if !subpath.is_empty() {
917 continue;
918 }
919 if package_name.starts_with('@')
922 && let Some(path) = cached_path.parent().as_ref()
923 && !self.cache.is_dir(path, ctx)
924 {
925 continue;
926 }
927 }
928 }
929
930 let cached_path = cached_path.normalize_with(specifier, self.cache.as_ref());
935
936 if self.options.resolve_to_context {
937 return Ok(self.cache.is_dir(&cached_path, ctx).then(|| cached_path.clone()));
938 }
939
940 if self.cache.is_dir(&cached_path, ctx) {
942 if let Some(path) =
943 self.load_browser_field_or_alias(&cached_path, tsconfig, ctx)?
944 {
945 return Ok(Some(path));
946 }
947 if let Some(path) = self.load_as_directory(&cached_path, tsconfig, ctx)? {
948 return Ok(Some(path));
949 }
950 } else if let Some(path) = self.load_as_file(&cached_path, tsconfig, ctx)? {
951 return Ok(Some(path));
952 }
953 }
954 }
955 Ok(None)
956 }
957
958 #[cfg(feature = "yarn_pnp")]
959 fn load_pnp(
960 &self,
961 cached_path: &CachedPath,
962 specifier: &str,
963 tsconfig: Option<&TsConfig>,
964 ctx: &mut Ctx,
965 ) -> Result<Option<CachedPath>, ResolveError> {
966 let pnp_manifest = self.cache.get_yarn_pnp_manifest(self.options.cwd.as_deref())?;
967
968 if specifier == "pnpapi" {
970 return Ok(Some(self.cache.value(pnp_manifest.manifest_path.as_path())));
971 }
972
973 let mut path = cached_path.to_path_buf();
975 path.push("");
976
977 let resolution = pnp::resolve_to_unqualified_via_manifest(pnp_manifest, specifier, &path);
978
979 match resolution {
980 Ok(pnp::Resolution::Resolved(path, subpath)) => {
981 let cached_path = self.cache.value(&path);
982 let cached_path_string = cached_path.path().to_string_lossy();
983
984 let export_resolution =
985 self.load_package_self(&cached_path, specifier, tsconfig, ctx)?;
986 if export_resolution.is_some() {
988 return Ok(export_resolution);
989 }
990
991 let pkg_name = cached_path_string.rsplit_once("node_modules/").map_or(
993 "",
994 |(_, last)| last.strip_suffix('/').unwrap_or(last),
996 );
997
998 let inner_request = if pkg_name.is_empty() {
999 subpath.map_or_else(
1000 || ".".to_string(),
1001 |mut p| {
1002 p.insert_str(0, "./");
1003 p
1004 },
1005 )
1006 } else {
1007 let (first, rest) = specifier.split_once('/').unwrap_or((specifier, ""));
1008 let pkg_name = if first.starts_with('@') {
1012 &format!("{first}/{}", rest.split_once('/').unwrap_or((rest, "")).0)
1013 } else {
1014 first
1015 };
1016 let inner_specifier = specifier.strip_prefix(pkg_name).unwrap();
1017 String::from("./")
1018 + inner_specifier.strip_prefix("/").unwrap_or(inner_specifier)
1019 };
1020
1021 if let Ok(Some(result)) = self.load_as_directory(
1024 &self.cache.value(&path.join(inner_request.clone()).normalize()),
1025 tsconfig,
1026 ctx,
1027 ) {
1028 return Ok(Some(result));
1029 }
1030
1031 let cached_path = self.cache.value(&path);
1033 let Ok(inner_resolution) = self.require(&cached_path, &inner_request, None, ctx)
1034 else {
1035 return Err(ResolveError::NotFound(specifier.to_string()));
1036 };
1037
1038 Ok(Some(self.cache.value(inner_resolution.path())))
1039 }
1040
1041 Ok(pnp::Resolution::Skipped) => Ok(None),
1042 Err(_) => Err(ResolveError::NotFound(specifier.to_string())),
1043 }
1044 }
1045
1046 fn get_module_directory(
1047 &self,
1048 cached_path: &CachedPath,
1049 module_name: &str,
1050 ctx: &mut Ctx,
1051 ) -> Option<CachedPath> {
1052 if module_name == "node_modules" {
1053 cached_path.cached_node_modules(self.cache.as_ref(), ctx)
1054 } else if cached_path.path().components().next_back()
1055 == Some(Component::Normal(OsStr::new(module_name)))
1056 {
1057 Some(cached_path.clone())
1058 } else {
1059 cached_path.module_directory(module_name, self.cache.as_ref(), ctx)
1060 }
1061 }
1062
1063 fn load_package_exports(
1064 &self,
1065 specifier: &str,
1066 subpath: &str,
1067 cached_path: &CachedPath,
1068 tsconfig: Option<&TsConfig>,
1069 ctx: &mut Ctx,
1070 ) -> ResolveResult {
1071 let Some(package_json) = self.cache.get_package_json(cached_path, &self.options, ctx)?
1074 else {
1075 return Ok(None);
1076 };
1077 for exports in package_json.exports_fields(&self.options.exports_fields) {
1083 if let Some(path) = self.package_exports_resolve(
1084 cached_path,
1085 &format!(".{subpath}"),
1086 &exports,
1087 tsconfig,
1088 ctx,
1089 )? {
1090 return self.resolve_esm_match(specifier, &path, tsconfig, ctx);
1092 }
1093 }
1094 Ok(None)
1095 }
1096
1097 fn load_package_self(
1098 &self,
1099 cached_path: &CachedPath,
1100 specifier: &str,
1101 tsconfig: Option<&TsConfig>,
1102 ctx: &mut Ctx,
1103 ) -> ResolveResult {
1104 let Some(package_json) = self.cache.find_package_json(cached_path, &self.options, ctx)?
1107 else {
1108 return Ok(None);
1109 };
1110 if let Some(subpath) = package_json
1113 .name()
1114 .and_then(|package_name| Self::strip_package_name(specifier, package_name))
1115 {
1116 let package_url = self.cache.value(package_json.path.parent().unwrap());
1122 for exports in package_json.exports_fields(&self.options.exports_fields) {
1123 if let Some(cached_path) = self.package_exports_resolve(
1124 &package_url,
1125 &format!(".{subpath}"),
1126 &exports,
1127 tsconfig,
1128 ctx,
1129 )? {
1130 return self.resolve_esm_match(specifier, &cached_path, tsconfig, ctx);
1132 }
1133 }
1134 }
1135 self.load_browser_field(cached_path, Some(specifier), &package_json, ctx)
1136 }
1137
1138 fn resolve_esm_match(
1140 &self,
1141 specifier: &str,
1142 cached_path: &CachedPath,
1143 tsconfig: Option<&TsConfig>,
1144 ctx: &mut Ctx,
1145 ) -> ResolveResult {
1146 if let Some(path) = self.load_as_file_or_directory(cached_path, "", tsconfig, ctx)? {
1151 return Ok(Some(path));
1152 }
1153
1154 Err(ResolveError::NotFound(specifier.to_string()))
1156 }
1157
1158 fn load_browser_field(
1160 &self,
1161 cached_path: &CachedPath,
1162 module_specifier: Option<&str>,
1163 package_json: &PackageJson,
1164 ctx: &mut Ctx,
1165 ) -> ResolveResult {
1166 let path = cached_path.path();
1167 let Some(new_specifier) = package_json.resolve_browser_field(
1168 path,
1169 module_specifier,
1170 &self.options.alias_fields,
1171 )?
1172 else {
1173 return Ok(None);
1174 };
1175 if module_specifier.is_some_and(|s| s == new_specifier) {
1177 return Ok(None);
1178 }
1179 if ctx.resolving_alias.as_ref().is_some_and(|s| s == new_specifier) {
1180 if new_specifier.strip_prefix("./").filter(|s| path.ends_with(Path::new(s))).is_some() {
1182 return if self.cache.is_file(cached_path, ctx) {
1183 if self.check_restrictions(cached_path.path()) {
1184 Ok(Some(cached_path.clone()))
1185 } else {
1186 Ok(None)
1187 }
1188 } else {
1189 Err(ResolveError::NotFound(new_specifier.to_string()))
1190 };
1191 }
1192 return Err(ResolveError::Recursion);
1193 }
1194 ctx.with_resolving_alias(new_specifier.to_string());
1195 ctx.with_fully_specified(false);
1196 let package_url = self.cache.value(package_json.path().parent().unwrap());
1197 self.require(&package_url, new_specifier, None, ctx).map(Some)
1198 }
1199
1200 fn load_alias(
1202 &self,
1203 cached_path: &CachedPath,
1204 specifier: &str,
1205 aliases: &Alias,
1206 tsconfig: Option<&TsConfig>,
1207 ctx: &mut Ctx,
1208 ) -> ResolveResult {
1209 for (alias_key_raw, specifiers) in aliases {
1210 let mut alias_key_has_wildcard = false;
1211 let alias_key = if let Some(alias_key) = alias_key_raw.strip_suffix('$') {
1212 if alias_key != specifier {
1213 continue;
1214 }
1215 alias_key
1216 } else if alias_key_raw.contains('*') {
1217 alias_key_has_wildcard = true;
1218 alias_key_raw
1219 } else {
1220 let strip_package_name = Self::strip_package_name(specifier, alias_key_raw);
1221 if strip_package_name.is_none() {
1222 continue;
1223 }
1224 alias_key_raw
1225 };
1226 let mut should_stop = false;
1230 for r in specifiers {
1231 match r {
1232 AliasValue::Path(alias_value) => {
1233 if let Some(path) = self.load_alias_value(
1234 cached_path,
1235 alias_key,
1236 alias_key_has_wildcard,
1237 alias_value,
1238 specifier,
1239 tsconfig,
1240 ctx,
1241 &mut should_stop,
1242 )? {
1243 return Ok(Some(path));
1244 }
1245 }
1246 AliasValue::Ignore => {
1247 let cached_path =
1248 cached_path.normalize_with(alias_key, self.cache.as_ref());
1249 return Err(ResolveError::Ignored(cached_path.to_path_buf()));
1250 }
1251 }
1252 }
1253 if should_stop {
1254 return Err(ResolveError::MatchedAliasNotFound(
1255 specifier.to_string(),
1256 alias_key.to_string(),
1257 ));
1258 }
1259 }
1260 Ok(None)
1261 }
1262
1263 fn load_alias_value(
1264 &self,
1265 cached_path: &CachedPath,
1266 alias_key: &str,
1267 alias_key_has_wild_card: bool,
1268 alias_value: &str,
1269 request: &str,
1270 tsconfig: Option<&TsConfig>,
1271 ctx: &mut Ctx,
1272 should_stop: &mut bool,
1273 ) -> ResolveResult {
1274 if request != alias_value
1275 && !request.strip_prefix(alias_value).is_some_and(|prefix| prefix.starts_with('/'))
1276 {
1277 let new_specifier = if alias_key_has_wild_card {
1278 let Some(alias_key) = alias_key.split_once('*').and_then(|(prefix, suffix)| {
1280 request
1281 .strip_prefix(prefix)
1282 .and_then(|specifier| specifier.strip_suffix(suffix))
1283 }) else {
1284 return Ok(None);
1285 };
1286 if alias_value.contains('*') {
1287 Cow::Owned(alias_value.replacen('*', alias_key, 1))
1288 } else {
1289 Cow::Borrowed(alias_value)
1290 }
1291 } else {
1292 let tail = &request[alias_key.len()..];
1293 if tail.is_empty() {
1294 Cow::Borrowed(alias_value)
1295 } else {
1296 let alias_path = Path::new(alias_value).normalize();
1297 let cached_alias_path = self.cache.value(&alias_path);
1299 if self.cache.is_file(&cached_alias_path, ctx) {
1300 return Ok(None);
1301 }
1302 let tail = tail.trim_start_matches(SLASH_START);
1304 if tail.is_empty() {
1305 Cow::Borrowed(alias_value)
1306 } else {
1307 let normalized = alias_path.normalize_with(tail);
1308 Cow::Owned(normalized.to_string_lossy().to_string())
1309 }
1310 }
1311 };
1312
1313 *should_stop = true;
1314 ctx.with_fully_specified(false);
1315 return match self.require(cached_path, new_specifier.as_ref(), tsconfig, ctx) {
1316 Err(ResolveError::NotFound(_) | ResolveError::MatchedAliasNotFound(_, _)) => {
1317 Ok(None)
1318 }
1319 Ok(path) => return Ok(Some(path)),
1320 Err(err) => return Err(err),
1321 };
1322 }
1323 Ok(None)
1324 }
1325
1326 fn load_extension_alias(
1335 &self,
1336 cached_path: &CachedPath,
1337 tsconfig: Option<&TsConfig>,
1338 ctx: &mut Ctx,
1339 ) -> ResolveResult {
1340 if self.options.extension_alias.is_empty() {
1341 return Ok(None);
1342 }
1343 let Some(path_extension) = cached_path.path().extension() else {
1344 return Ok(None);
1345 };
1346 let Some((_, extensions)) = self
1347 .options
1348 .extension_alias
1349 .iter()
1350 .find(|(ext, _)| OsStr::new(ext.trim_start_matches('.')) == path_extension)
1351 else {
1352 return Ok(None);
1353 };
1354 let path = cached_path.path();
1355 let Some(filename) = path.file_name() else { return Ok(None) };
1356 ctx.with_fully_specified(true);
1357 for extension in extensions {
1358 let cached_path = cached_path.replace_extension(extension, self.cache.as_ref());
1359 if let Some(path) = self.load_alias_or_file(&cached_path, tsconfig, ctx)? {
1360 ctx.with_fully_specified(false);
1361 return Ok(Some(path));
1362 }
1363 }
1364 if !self.cache.is_file(cached_path, ctx) {
1366 ctx.with_fully_specified(false);
1367 return Ok(None);
1368 } else if !self.check_restrictions(cached_path.path()) {
1369 return Ok(None);
1370 }
1371 let dir = path.parent().unwrap().to_path_buf();
1373 let filename_without_extension = Path::new(filename).with_extension("");
1374 let filename_without_extension = filename_without_extension.to_string_lossy();
1375 let files = extensions
1376 .iter()
1377 .map(|ext| format!("{filename_without_extension}{ext}"))
1378 .collect::<Vec<_>>()
1379 .join(",");
1380 Err(ResolveError::ExtensionAlias(filename.to_string_lossy().to_string(), files, dir))
1381 }
1382
1383 fn load_roots(
1390 &self,
1391 cached_path: &CachedPath,
1392 specifier: &str,
1393 tsconfig: Option<&TsConfig>,
1394 ctx: &mut Ctx,
1395 ) -> Option<CachedPath> {
1396 if self.options.roots.is_empty() {
1397 return None;
1398 }
1399 if let Some(specifier) = specifier.strip_prefix(SLASH_START) {
1400 if specifier.is_empty() {
1401 if self.options.roots.iter().any(|root| root.as_path() == cached_path.path())
1402 && let Ok(path) = self.require_relative(cached_path, "./", tsconfig, ctx)
1403 {
1404 return Some(path);
1405 }
1406 } else {
1407 for root in &self.options.roots {
1408 let cached_path = self.cache.value(root);
1409 if let Ok(path) = self.require_relative(&cached_path, specifier, tsconfig, ctx)
1410 {
1411 return Some(path);
1412 }
1413 }
1414 }
1415 }
1416 None
1417 }
1418
1419 fn package_resolve(
1421 &self,
1422 cached_path: &CachedPath,
1423 specifier: &str,
1424 tsconfig: Option<&TsConfig>,
1425 ctx: &mut Ctx,
1426 ) -> ResolveResult {
1427 let (package_name, subpath) = Self::parse_package_specifier(specifier);
1428
1429 self.require_core(package_name)?;
1432
1433 for module_name in &self.options.modules {
1435 for cached_path in std::iter::successors(Some(cached_path.clone()), CachedPath::parent)
1436 {
1437 let Some(cached_path) = self.get_module_directory(&cached_path, module_name, ctx)
1439 else {
1440 continue;
1441 };
1442 let cached_path = cached_path.normalize_with(package_name, self.cache.as_ref());
1444 if self.cache.is_dir(&cached_path, ctx) {
1447 if let Some(package_json) =
1449 self.cache.get_package_json(&cached_path, &self.options, ctx)?
1450 {
1451 for exports in package_json.exports_fields(&self.options.exports_fields) {
1454 if let Some(path) = self.package_exports_resolve(
1455 &cached_path,
1456 &format!(".{subpath}"),
1457 &exports,
1458 tsconfig,
1459 ctx,
1460 )? {
1461 return Ok(Some(path));
1462 }
1463 }
1464 if subpath == "." {
1466 for main_field in package_json.main_fields(&self.options.main_fields) {
1468 let cached_path =
1470 cached_path.normalize_with(main_field, self.cache.as_ref());
1471 if self.cache.is_file(&cached_path, ctx)
1472 && self.check_restrictions(cached_path.path())
1473 {
1474 return Ok(Some(cached_path));
1475 }
1476 }
1477 }
1478 }
1479 let subpath = format!(".{subpath}");
1480 ctx.with_fully_specified(false);
1481 return self.require(&cached_path, &subpath, tsconfig, ctx).map(Some);
1482 }
1483 }
1484 }
1485
1486 Err(ResolveError::NotFound(specifier.to_string()))
1487 }
1488
1489 fn package_exports_resolve(
1491 &self,
1492 package_url: &CachedPath,
1493 subpath: &str,
1494 exports: &ImportsExportsEntry<'_>,
1495 tsconfig: Option<&TsConfig>,
1496 ctx: &mut Ctx,
1497 ) -> ResolveResult {
1498 let conditions = &self.options.condition_names;
1499 if let Some(map) = exports.as_map() {
1501 let mut has_dot = false;
1502 let mut without_dot = false;
1503 for key in map.keys() {
1504 let starts_with_dot_or_hash = key.starts_with(['.', '#']);
1505 has_dot = has_dot || starts_with_dot_or_hash;
1506 without_dot = without_dot || !starts_with_dot_or_hash;
1507 if has_dot && without_dot {
1508 return Err(ResolveError::InvalidPackageConfig(
1509 package_url.path().join("package.json"),
1510 ));
1511 }
1512 }
1513 }
1514 if subpath == "." {
1517 let main_export = match exports.kind() {
1519 ImportsExportsKind::String | ImportsExportsKind::Array => {
1521 Some(Cow::Borrowed(exports))
1523 }
1524 _ => exports.as_map().and_then(|map| {
1526 map.get(".").map_or_else(
1527 || {
1528 if map.keys().any(|key| key.starts_with("./") || key.starts_with('#')) {
1529 None
1530 } else {
1531 Some(Cow::Borrowed(exports))
1532 }
1533 },
1534 |entry| Some(Cow::Owned(entry)),
1535 )
1536 }),
1537 };
1538 if let Some(main_export) = main_export {
1540 let resolved = self.package_target_resolve(
1542 package_url,
1543 ".",
1544 main_export.as_ref(),
1545 None,
1546 false,
1547 conditions,
1548 tsconfig,
1549 ctx,
1550 )?;
1551 if let Some(path) = resolved {
1553 return Ok(Some(path));
1554 }
1555 }
1556 }
1557 if let Some(exports) = exports.as_map() {
1559 let match_key = &subpath;
1562 if let Some(path) = self.package_imports_exports_resolve(
1564 match_key,
1565 &exports,
1566 package_url,
1567 false,
1568 conditions,
1569 tsconfig,
1570 ctx,
1571 )? {
1572 return Ok(Some(path));
1574 }
1575 }
1576 Err(ResolveError::PackagePathNotExported {
1578 subpath: subpath.to_string(),
1579 package_path: package_url.path().to_path_buf(),
1580 package_json_path: package_url.path().join("package.json"),
1581 conditions: self.options.condition_names.clone().into(),
1582 })
1583 }
1584
1585 fn package_imports_resolve(
1587 &self,
1588 specifier: &str,
1589 package_json: &PackageJson,
1590 tsconfig: Option<&TsConfig>,
1591 ctx: &mut Ctx,
1592 ) -> Result<Option<CachedPath>, ResolveError> {
1593 debug_assert!(specifier.starts_with('#'), "{specifier}");
1595 let mut has_imports = false;
1605 for imports in package_json.imports_fields(&self.options.imports_fields) {
1606 if !has_imports {
1607 has_imports = true;
1608 if specifier == "#" || specifier.starts_with("#/") {
1610 return Err(ResolveError::InvalidModuleSpecifier(
1611 specifier.to_string(),
1612 package_json.path().to_path_buf(),
1613 ));
1614 }
1615 }
1616 if let Some(path) = self.package_imports_exports_resolve(
1617 specifier,
1618 &imports,
1619 &self.cache.value(package_json.directory()),
1620 true,
1621 &self.options.condition_names,
1622 tsconfig,
1623 ctx,
1624 )? {
1625 return Ok(Some(path));
1627 }
1628 }
1629
1630 if has_imports {
1632 Err(ResolveError::PackageImportNotDefined(
1633 specifier.to_string(),
1634 package_json.path().to_path_buf(),
1635 ))
1636 } else {
1637 Ok(None)
1638 }
1639 }
1640
1641 fn package_imports_exports_resolve(
1643 &self,
1644 match_key: &str,
1645 match_obj: &ImportsExportsMap<'_>,
1646 package_url: &CachedPath,
1647 is_imports: bool,
1648 conditions: &[String],
1649 tsconfig: Option<&TsConfig>,
1650 ctx: &mut Ctx,
1651 ) -> ResolveResult {
1652 if match_key.ends_with('/') {
1655 return Ok(None);
1656 }
1657 if !match_key.contains('*') {
1659 if let Some(target) = match_obj.get(match_key) {
1661 return self.package_target_resolve(
1663 package_url,
1664 match_key,
1665 &target,
1666 None,
1667 is_imports,
1668 conditions,
1669 tsconfig,
1670 ctx,
1671 );
1672 }
1673 }
1674
1675 let mut best_target = None;
1676 let mut best_match = "";
1677 let mut best_key = "";
1678 for (expansion_key, target) in match_obj.iter() {
1681 if expansion_key.starts_with("./") || expansion_key.starts_with('#') {
1682 if let Some((pattern_base, pattern_trailer)) = expansion_key.split_once('*') {
1684 if match_key.starts_with(pattern_base)
1686 && !pattern_trailer.contains('*')
1688 && (pattern_trailer.is_empty()
1690 || (match_key.len() >= expansion_key.len()
1691 && match_key.ends_with(pattern_trailer)))
1692 && Self::pattern_key_compare(best_key, expansion_key).is_gt()
1693 {
1694 best_target = Some(target);
1696 best_match =
1698 &match_key[pattern_base.len()..match_key.len() - pattern_trailer.len()];
1699 best_key = expansion_key;
1700 }
1701 } else if expansion_key.ends_with('/')
1702 && match_key.starts_with(expansion_key)
1703 && Self::pattern_key_compare(best_key, expansion_key).is_gt()
1704 {
1705 best_target = Some(target);
1707 best_match = &match_key[expansion_key.len()..];
1708 best_key = expansion_key;
1709 }
1710 }
1711 }
1712 if let Some(best_target) = best_target {
1713 return self.package_target_resolve(
1715 package_url,
1716 best_key,
1717 &best_target,
1718 Some(best_match),
1719 is_imports,
1720 conditions,
1721 tsconfig,
1722 ctx,
1723 );
1724 }
1725 Ok(None)
1727 }
1728
1729 fn package_target_resolve(
1731 &self,
1732 package_url: &CachedPath,
1733 target_key: &str,
1734 target: &ImportsExportsEntry<'_>,
1735 pattern_match: Option<&str>,
1736 is_imports: bool,
1737 conditions: &[String],
1738 tsconfig: Option<&TsConfig>,
1739 ctx: &mut Ctx,
1740 ) -> ResolveResult {
1741 fn normalize_string_target<'a>(
1742 target_key: &'a str,
1743 target: &'a str,
1744 pattern_match: Option<&'a str>,
1745 package_url: &CachedPath,
1746 ) -> Result<Cow<'a, str>, ResolveError> {
1747 let target = if let Some(pattern_match) = pattern_match {
1748 if !target_key.contains('*') && !target.contains('*') {
1749 if target_key.ends_with('/') && target.ends_with('/') {
1752 Cow::Owned(format!("{target}{pattern_match}"))
1753 } else {
1754 return Err(ResolveError::InvalidPackageConfigDirectory(
1755 package_url.path().join("package.json"),
1756 ));
1757 }
1758 } else {
1759 Cow::Owned(target.replace('*', pattern_match))
1760 }
1761 } else {
1762 Cow::Borrowed(target)
1763 };
1764 Ok(target)
1765 }
1766
1767 if let Some(target) = target.as_string() {
1769 let parsed = Specifier::parse(target).map_err(ResolveError::Specifier)?;
1772 ctx.with_query_fragment(parsed.query, parsed.fragment);
1773 let target = parsed.path();
1774
1775 if !target.starts_with("./") {
1777 if !is_imports || target.starts_with("../") || target.starts_with('/') {
1779 return Err(ResolveError::InvalidPackageTarget(
1781 (*target).to_string(),
1782 target_key.to_string(),
1783 package_url.path().join("package.json"),
1784 ));
1785 }
1786 let target =
1789 normalize_string_target(target_key, target, pattern_match, package_url)?;
1790 return self.package_resolve(package_url, &target, tsconfig, ctx);
1792 }
1793
1794 let target = normalize_string_target(target_key, target, pattern_match, package_url)?;
1799 if Path::new(target.as_ref()).is_invalid_exports_target() {
1800 return Err(ResolveError::InvalidPackageTarget(
1801 target.to_string(),
1802 target_key.to_string(),
1803 package_url.path().join("package.json"),
1804 ));
1805 }
1806 return Ok(Some(package_url.normalize_with(target.as_ref(), self.cache.as_ref())));
1809 }
1810 else if let Some(target) = target.as_map() {
1812 for (key, target_value) in target.iter() {
1815 if key == "default" || conditions.iter().any(|condition| condition == key) {
1817 let resolved = self.package_target_resolve(
1820 package_url,
1821 target_key,
1822 &target_value,
1823 pattern_match,
1824 is_imports,
1825 conditions,
1826 tsconfig,
1827 ctx,
1828 );
1829 if let Some(path) = resolved? {
1831 return Ok(Some(path));
1833 }
1834 }
1835 }
1836 return Ok(None);
1838 }
1839 else if let Some(targets) = target.as_array() {
1841 if targets.is_empty() {
1843 return Err(ResolveError::PackagePathNotExported {
1845 subpath: pattern_match.unwrap_or(".").to_string(),
1846 package_path: package_url.path().to_path_buf(),
1847 package_json_path: package_url.path().join("package.json"),
1848 conditions: self.options.condition_names.clone().into(),
1849 });
1850 }
1851 for (i, target_value) in targets.iter().enumerate() {
1853 let resolved = self.package_target_resolve(
1855 package_url,
1856 target_key,
1857 &target_value,
1858 pattern_match,
1859 is_imports,
1860 conditions,
1861 tsconfig,
1862 ctx,
1863 );
1864
1865 if resolved.is_err() && i == targets.len() {
1866 return resolved;
1867 }
1868
1869 if let Ok(Some(path)) = resolved {
1871 return Ok(Some(path));
1873 }
1874 }
1875 }
1878 Ok(None)
1880 }
1882
1883 fn parse_package_specifier(specifier: &str) -> (&str, &str) {
1886 let mut separator_index = specifier.as_bytes().iter().position(|b| *b == b'/');
1887 if specifier.starts_with('@') {
1890 if separator_index.is_none() || specifier.is_empty() {
1892 } else if let Some(index) = &separator_index {
1894 separator_index = specifier.as_bytes()[*index + 1..]
1895 .iter()
1896 .position(|b| *b == b'/')
1897 .map(|i| i + *index + 1);
1898 }
1899 }
1900 let package_name =
1901 separator_index.map_or(specifier, |separator_index| &specifier[..separator_index]);
1902
1903 let package_subpath =
1914 separator_index.map_or("", |separator_index| &specifier[separator_index..]);
1915 (package_name, package_subpath)
1916 }
1917
1918 fn pattern_key_compare(key_a: &str, key_b: &str) -> Ordering {
1920 if key_a.is_empty() {
1921 return Ordering::Greater;
1922 }
1923 debug_assert!(key_a.ends_with('/') || key_a.match_indices('*').count() == 1, "{key_a}");
1925 debug_assert!(key_b.ends_with('/') || key_b.match_indices('*').count() == 1, "{key_b}");
1927 let a_pos = key_a.bytes().position(|c| c == b'*');
1929 let base_length_a = a_pos.map_or(key_a.len(), |p| p + 1);
1930 let b_pos = key_b.bytes().position(|c| c == b'*');
1932 let base_length_b = b_pos.map_or(key_b.len(), |p| p + 1);
1933 if base_length_a > base_length_b {
1935 return Ordering::Less;
1936 }
1937 if base_length_b > base_length_a {
1939 return Ordering::Greater;
1940 }
1941 if a_pos.is_none() {
1943 return Ordering::Greater;
1944 }
1945 if b_pos.is_none() {
1947 return Ordering::Less;
1948 }
1949 if key_a.len() > key_b.len() {
1951 return Ordering::Less;
1952 }
1953 if key_b.len() > key_a.len() {
1955 return Ordering::Greater;
1956 }
1957 Ordering::Equal
1959 }
1960
1961 fn strip_package_name<'a>(specifier: &'a str, package_name: &'a str) -> Option<&'a str> {
1962 specifier
1963 .strip_prefix(package_name)
1964 .filter(|tail| tail.is_empty() || tail.starts_with(SLASH_START))
1965 }
1966
1967 fn esm_file_format(
1971 &self,
1972 cached_path: &CachedPath,
1973 ctx: &mut Ctx,
1974 ) -> Result<Option<ModuleType>, ResolveError> {
1975 if !self.options.module_type {
1976 return Ok(None);
1977 }
1978 let ext = cached_path.path().extension().and_then(|ext| ext.to_str());
1980 match ext {
1981 Some("mjs" | "mts") => Ok(Some(ModuleType::Module)),
1984 Some("cjs" | "cts") => Ok(Some(ModuleType::CommonJs)),
1987 Some("json") => Ok(Some(ModuleType::Json)),
1990 Some("wasm") => Ok(Some(ModuleType::Wasm)),
1993 Some("node") => Ok(Some(ModuleType::Addon)),
1996 Some("js" | "ts") => {
2000 let package_json = self.cache.find_package_json(cached_path, &self.options, ctx)?;
2003 if let Some(package_json) = package_json {
2005 if let Some(ty) = package_json.r#type() {
2008 return Ok(Some(match ty {
2009 PackageType::Module => ModuleType::Module,
2010 PackageType::CommonJs => ModuleType::CommonJs,
2011 }));
2012 }
2013 }
2014 Ok(None)
2015 }
2016 _ => Ok(None),
2018 }
2019 }
2020}
2021
2022#[cfg(not(target_arch = "wasm32"))]
2023fn resolve_file_protocol(specifier: &str) -> Result<Cow<'_, str>, ResolveError> {
2024 if specifier.starts_with("file://") {
2025 url::Url::parse(specifier)
2026 .map_err(|_| ())
2027 .and_then(|url| {
2028 url.to_file_path().map(|path| {
2029 let mut result = path.to_string_lossy().to_string();
2030 if let Some(query) = url.query() {
2032 result.push('?');
2033 result.push_str(query);
2034 }
2035 if let Some(fragment) = url.fragment() {
2036 result.push('#');
2037 result.push_str(fragment);
2038 }
2039 Cow::Owned(result)
2040 })
2041 })
2042 .map_err(|()| ResolveError::PathNotSupported(PathBuf::from(specifier)))
2043 } else {
2044 Ok(Cow::Borrowed(specifier))
2045 }
2046}
2047
2048pub(crate) fn replace_bom_with_whitespace(s: &mut [u8]) {
2051 if s.starts_with(b"\xEF\xBB\xBF") {
2052 s[0] = b' ';
2053 s[1] = b' ';
2054 s[2] = b' ';
2055 }
2056}