summaryrefslogtreecommitdiff
path: root/cli/resolver.rs
diff options
context:
space:
mode:
authorDavid Sherret <dsherret@users.noreply.github.com>2024-02-27 13:30:21 -0500
committerGitHub <noreply@github.com>2024-02-27 18:30:21 +0000
commite9fe71acb53c8856754ef892c463253cb96087ce (patch)
treededffed4cbee198a064bd3ff81058f951bf0b401 /cli/resolver.rs
parent8d5c2313495014a6af842e21da802e01e11b8e08 (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.rs205
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();