diff options
author | David Sherret <dsherret@users.noreply.github.com> | 2024-02-27 13:30:21 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-02-27 18:30:21 +0000 |
commit | e9fe71acb53c8856754ef892c463253cb96087ce (patch) | |
tree | dedffed4cbee198a064bd3ff81058f951bf0b401 /cli/resolver.rs | |
parent | 8d5c2313495014a6af842e21da802e01e11b8e08 (diff) |
fix(unstable): sloppy imports should resolve .d.ts files during types resolution (#22602)
Diffstat (limited to 'cli/resolver.rs')
-rw-r--r-- | cli/resolver.rs | 205 |
1 files changed, 152 insertions, 53 deletions
diff --git a/cli/resolver.rs b/cli/resolver.rs index 5bb9e66d0..de85992a7 100644 --- a/cli/resolver.rs +++ b/cli/resolver.rs @@ -522,6 +522,7 @@ impl Resolver for CliGraphResolver { sloppy_imports_resolver, specifier, referrer_range, + mode, ) }) } else { @@ -646,12 +647,22 @@ fn sloppy_imports_resolve( resolver: &SloppyImportsResolver, specifier: ModuleSpecifier, referrer_range: &deno_graph::Range, + mode: ResolutionMode, ) -> ModuleSpecifier { - let resolution = resolver.resolve(&specifier); + let resolution = resolver.resolve(&specifier, mode); + if mode.is_types() { + // don't bother warning for types resolution because + // we already probably warned during execution resolution + match resolution { + SloppyImportsResolution::None(_) => return specifier, // avoid a clone + _ => return resolution.into_specifier().into_owned(), + } + } + let hint_message = match &resolution { SloppyImportsResolution::JsToTs(to_specifier) => { - let from_media_type = MediaType::from_specifier(&specifier); let to_media_type = MediaType::from_specifier(to_specifier); + let from_media_type = MediaType::from_specifier(&specifier); format!( "update {} extension to {}", from_media_type.as_ts_extension(), @@ -677,7 +688,7 @@ fn sloppy_imports_resolve( log::warn!( "{} Sloppy module resolution {}\n at {}", crate::colors::yellow("Warning"), - crate::colors::gray(format!("(hint: {})", hint_message)), + crate::colors::gray(format!("(hint: {})", hint_message)).to_string(), if referrer_range.end == deno_graph::Position::zeroed() { // not worth showing the range in this case crate::colors::cyan(referrer_range.specifier.as_str()).to_string() @@ -928,8 +939,9 @@ impl SloppyImportsResolver { pub fn resolve_with_fs<'a>( fs: &dyn FileSystem, specifier: &'a ModuleSpecifier, + mode: ResolutionMode, ) -> SloppyImportsResolution<'a> { - Self::resolve_with_stat_sync(specifier, |path| { + Self::resolve_with_stat_sync(specifier, mode, |path| { fs.stat_sync(path) .ok() .and_then(|stat| SloppyImportsFsEntry::from_fs_stat(&stat)) @@ -938,8 +950,38 @@ impl SloppyImportsResolver { pub fn resolve_with_stat_sync( specifier: &ModuleSpecifier, + mode: ResolutionMode, stat_sync: impl Fn(&Path) -> Option<SloppyImportsFsEntry>, ) -> SloppyImportsResolution { + fn path_without_ext( + path: &Path, + media_type: MediaType, + ) -> Option<Cow<str>> { + let old_path_str = path.to_string_lossy(); + match media_type { + MediaType::Unknown => Some(old_path_str), + _ => old_path_str + .strip_suffix(media_type.as_ts_extension()) + .map(|s| Cow::Owned(s.to_string())), + } + } + + fn media_types_to_paths( + path_no_ext: &str, + probe_media_type_types: Vec<MediaType>, + ) -> Vec<PathBuf> { + probe_media_type_types + .into_iter() + .map(|media_type| { + PathBuf::from(format!( + "{}{}", + path_no_ext, + media_type.as_ts_extension() + )) + }) + .collect::<Vec<_>>() + } + if specifier.scheme() != "file" { return SloppyImportsResolution::None(specifier); } @@ -951,27 +993,79 @@ impl SloppyImportsResolver { let mut is_no_ext_resolution = false; let probe_paths = match (stat_sync)(&path) { Some(SloppyImportsFsEntry::File) => { - return SloppyImportsResolution::None(specifier); + if mode.is_types() { + let media_type = MediaType::from_specifier(specifier); + // attempt to resolve the .d.ts file before the .js file + let probe_media_type_types = match media_type { + MediaType::JavaScript => { + vec![MediaType::Dts, MediaType::JavaScript] + } + MediaType::Mjs => { + vec![MediaType::Dmts, MediaType::Dts, MediaType::Mjs] + } + MediaType::Cjs => { + vec![MediaType::Dcts, MediaType::Dts, MediaType::Cjs] + } + _ => return SloppyImportsResolution::None(specifier), + }; + let Some(path_no_ext) = path_without_ext(&path, media_type) else { + return SloppyImportsResolution::None(specifier); + }; + media_types_to_paths(&path_no_ext, probe_media_type_types) + } else { + return SloppyImportsResolution::None(specifier); + } } Some(SloppyImportsFsEntry::Dir) => { is_dir_resolution = true; // try to resolve at the index file - vec![ - path.join("index.ts"), - path.join("index.js"), - path.join("index.mts"), - path.join("index.mjs"), - path.join("index.tsx"), - path.join("index.jsx"), - ] + if mode.is_types() { + vec![ + path.join("index.ts"), + path.join("index.mts"), + path.join("index.d.ts"), + path.join("index.d.mts"), + path.join("index.js"), + path.join("index.mjs"), + path.join("index.tsx"), + path.join("index.jsx"), + ] + } else { + vec![ + path.join("index.ts"), + path.join("index.mts"), + path.join("index.tsx"), + path.join("index.js"), + path.join("index.mjs"), + path.join("index.jsx"), + ] + } } None => { let media_type = MediaType::from_specifier(specifier); let probe_media_type_types = match media_type { - MediaType::JavaScript => vec![MediaType::TypeScript, MediaType::Tsx], + MediaType::JavaScript => { + if mode.is_types() { + vec![MediaType::TypeScript, MediaType::Tsx, MediaType::Dts] + } else { + vec![MediaType::TypeScript, MediaType::Tsx] + } + } MediaType::Jsx => vec![MediaType::Tsx], - MediaType::Mjs => vec![MediaType::Mts], - MediaType::Cjs => vec![MediaType::Cts], + MediaType::Mjs => { + if mode.is_types() { + vec![MediaType::Mts, MediaType::Dmts, MediaType::Dts] + } else { + vec![MediaType::Mts] + } + } + MediaType::Cjs => { + if mode.is_types() { + vec![MediaType::Cts, MediaType::Dcts, MediaType::Dts] + } else { + vec![MediaType::Cts] + } + } MediaType::TypeScript | MediaType::Mts | MediaType::Cts @@ -987,34 +1081,34 @@ impl SloppyImportsResolver { } MediaType::Unknown => { is_no_ext_resolution = true; - vec![ - MediaType::TypeScript, - MediaType::JavaScript, - MediaType::Tsx, - MediaType::Jsx, - MediaType::Mts, - MediaType::Mjs, - ] + if mode.is_types() { + vec![ + MediaType::TypeScript, + MediaType::Tsx, + MediaType::Mts, + MediaType::Dts, + MediaType::Dmts, + MediaType::Dcts, + MediaType::JavaScript, + MediaType::Jsx, + MediaType::Mjs, + ] + } else { + vec![ + MediaType::TypeScript, + MediaType::JavaScript, + MediaType::Tsx, + MediaType::Jsx, + MediaType::Mts, + MediaType::Mjs, + ] + } } }; - let old_path_str = path.to_string_lossy(); - let old_path_str = match media_type { - MediaType::Unknown => old_path_str, - _ => match old_path_str.strip_suffix(media_type.as_ts_extension()) { - Some(s) => Cow::Borrowed(s), - None => return SloppyImportsResolution::None(specifier), - }, + let Some(path_no_ext) = path_without_ext(&path, media_type) else { + return SloppyImportsResolution::None(specifier); }; - probe_media_type_types - .into_iter() - .map(|media_type| { - PathBuf::from(format!( - "{}{}", - old_path_str, - media_type.as_ts_extension() - )) - }) - .collect::<Vec<_>>() + media_types_to_paths(&path_no_ext, probe_media_type_types) } }; @@ -1038,8 +1132,9 @@ impl SloppyImportsResolver { pub fn resolve<'a>( &self, specifier: &'a ModuleSpecifier, + mode: ResolutionMode, ) -> SloppyImportsResolution<'a> { - Self::resolve_with_stat_sync(specifier, |path| { + Self::resolve_with_stat_sync(specifier, mode, |path| { self.stat_cache.stat_sync(path) }) } @@ -1117,17 +1212,21 @@ mod test { #[test] fn test_unstable_sloppy_imports() { fn resolve(specifier: &ModuleSpecifier) -> SloppyImportsResolution { - SloppyImportsResolver::resolve_with_stat_sync(specifier, |path| { - RealFs.stat_sync(path).ok().and_then(|stat| { - if stat.is_file { - Some(SloppyImportsFsEntry::File) - } else if stat.is_directory { - Some(SloppyImportsFsEntry::Dir) - } else { - None - } - }) - }) + SloppyImportsResolver::resolve_with_stat_sync( + specifier, + ResolutionMode::Execution, + |path| { + RealFs.stat_sync(path).ok().and_then(|stat| { + if stat.is_file { + Some(SloppyImportsFsEntry::File) + } else if stat.is_directory { + Some(SloppyImportsFsEntry::Dir) + } else { + None + } + }) + }, + ) } let context = TestContext::default(); |