mirror of
https://github.com/denoland/deno.git
synced 2024-12-22 15:24:46 -05:00
fix: support sloppy resolution to file where directory exists (#22800)
Previously the sloppy resolver could not resolve the following: - foo/bar.ts - foo.ts - index.ts Where `index.ts` contains `import "./foo"`, because it did not consider `foo.ts` a valid target for this directory import. This commit fixes this bug.
This commit is contained in:
parent
93b9d114a4
commit
5d671e079a
1 changed files with 220 additions and 135 deletions
355
cli/resolver.rs
355
cli/resolver.rs
|
@ -1015,15 +1015,19 @@ impl SloppyImportsResolver {
|
||||||
fn media_types_to_paths(
|
fn media_types_to_paths(
|
||||||
path_no_ext: &str,
|
path_no_ext: &str,
|
||||||
probe_media_type_types: Vec<MediaType>,
|
probe_media_type_types: Vec<MediaType>,
|
||||||
) -> Vec<PathBuf> {
|
reason: SloppyImportsResolutionReason,
|
||||||
|
) -> Vec<(PathBuf, SloppyImportsResolutionReason)> {
|
||||||
probe_media_type_types
|
probe_media_type_types
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|media_type| {
|
.map(|media_type| {
|
||||||
PathBuf::from(format!(
|
(
|
||||||
"{}{}",
|
PathBuf::from(format!(
|
||||||
path_no_ext,
|
"{}{}",
|
||||||
media_type.as_ts_extension()
|
path_no_ext,
|
||||||
))
|
media_type.as_ts_extension()
|
||||||
|
)),
|
||||||
|
reason,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
}
|
}
|
||||||
|
@ -1035,138 +1039,205 @@ impl SloppyImportsResolver {
|
||||||
let Ok(path) = specifier_to_file_path(specifier) else {
|
let Ok(path) = specifier_to_file_path(specifier) else {
|
||||||
return SloppyImportsResolution::None(specifier);
|
return SloppyImportsResolution::None(specifier);
|
||||||
};
|
};
|
||||||
let mut is_dir_resolution = false;
|
|
||||||
let mut is_no_ext_resolution = false;
|
|
||||||
let probe_paths = match (stat_sync)(&path) {
|
|
||||||
Some(SloppyImportsFsEntry::File) => {
|
|
||||||
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
|
|
||||||
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 => {
|
|
||||||
if mode.is_types() {
|
|
||||||
vec![MediaType::TypeScript, MediaType::Tsx, MediaType::Dts]
|
|
||||||
} else {
|
|
||||||
vec![MediaType::TypeScript, MediaType::Tsx]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MediaType::Jsx => vec![MediaType::Tsx],
|
|
||||||
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
|
|
||||||
| MediaType::Dts
|
|
||||||
| MediaType::Dmts
|
|
||||||
| MediaType::Dcts
|
|
||||||
| MediaType::Tsx
|
|
||||||
| MediaType::Json
|
|
||||||
| MediaType::Wasm
|
|
||||||
| MediaType::TsBuildInfo
|
|
||||||
| MediaType::SourceMap => {
|
|
||||||
return SloppyImportsResolution::None(specifier)
|
|
||||||
}
|
|
||||||
MediaType::Unknown => {
|
|
||||||
is_no_ext_resolution = true;
|
|
||||||
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 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)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for probe_path in probe_paths {
|
#[derive(Clone, Copy)]
|
||||||
|
enum SloppyImportsResolutionReason {
|
||||||
|
JsToTs,
|
||||||
|
NoExtension,
|
||||||
|
Directory,
|
||||||
|
}
|
||||||
|
|
||||||
|
let probe_paths: Vec<(PathBuf, SloppyImportsResolutionReason)> =
|
||||||
|
match (stat_sync)(&path) {
|
||||||
|
Some(SloppyImportsFsEntry::File) => {
|
||||||
|
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,
|
||||||
|
SloppyImportsResolutionReason::JsToTs,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return SloppyImportsResolution::None(specifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entry @ None | entry @ Some(SloppyImportsFsEntry::Dir) => {
|
||||||
|
let media_type = MediaType::from_specifier(specifier);
|
||||||
|
let probe_media_type_types = match media_type {
|
||||||
|
MediaType::JavaScript => (
|
||||||
|
if mode.is_types() {
|
||||||
|
vec![MediaType::TypeScript, MediaType::Tsx, MediaType::Dts]
|
||||||
|
} else {
|
||||||
|
vec![MediaType::TypeScript, MediaType::Tsx]
|
||||||
|
},
|
||||||
|
SloppyImportsResolutionReason::JsToTs,
|
||||||
|
),
|
||||||
|
MediaType::Jsx => {
|
||||||
|
(vec![MediaType::Tsx], SloppyImportsResolutionReason::JsToTs)
|
||||||
|
}
|
||||||
|
MediaType::Mjs => (
|
||||||
|
if mode.is_types() {
|
||||||
|
vec![MediaType::Mts, MediaType::Dmts, MediaType::Dts]
|
||||||
|
} else {
|
||||||
|
vec![MediaType::Mts]
|
||||||
|
},
|
||||||
|
SloppyImportsResolutionReason::JsToTs,
|
||||||
|
),
|
||||||
|
MediaType::Cjs => (
|
||||||
|
if mode.is_types() {
|
||||||
|
vec![MediaType::Cts, MediaType::Dcts, MediaType::Dts]
|
||||||
|
} else {
|
||||||
|
vec![MediaType::Cts]
|
||||||
|
},
|
||||||
|
SloppyImportsResolutionReason::JsToTs,
|
||||||
|
),
|
||||||
|
MediaType::TypeScript
|
||||||
|
| MediaType::Mts
|
||||||
|
| MediaType::Cts
|
||||||
|
| MediaType::Dts
|
||||||
|
| MediaType::Dmts
|
||||||
|
| MediaType::Dcts
|
||||||
|
| MediaType::Tsx
|
||||||
|
| MediaType::Json
|
||||||
|
| MediaType::Wasm
|
||||||
|
| MediaType::TsBuildInfo
|
||||||
|
| MediaType::SourceMap => {
|
||||||
|
return SloppyImportsResolution::None(specifier)
|
||||||
|
}
|
||||||
|
MediaType::Unknown => (
|
||||||
|
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,
|
||||||
|
]
|
||||||
|
},
|
||||||
|
SloppyImportsResolutionReason::NoExtension,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
let mut probe_paths = match path_without_ext(&path, media_type) {
|
||||||
|
Some(path_no_ext) => media_types_to_paths(
|
||||||
|
&path_no_ext,
|
||||||
|
probe_media_type_types.0,
|
||||||
|
probe_media_type_types.1,
|
||||||
|
),
|
||||||
|
None => vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
if matches!(entry, Some(SloppyImportsFsEntry::Dir)) {
|
||||||
|
// try to resolve at the index file
|
||||||
|
if mode.is_types() {
|
||||||
|
probe_paths.push((
|
||||||
|
path.join("index.ts"),
|
||||||
|
SloppyImportsResolutionReason::Directory,
|
||||||
|
));
|
||||||
|
|
||||||
|
probe_paths.push((
|
||||||
|
path.join("index.mts"),
|
||||||
|
SloppyImportsResolutionReason::Directory,
|
||||||
|
));
|
||||||
|
probe_paths.push((
|
||||||
|
path.join("index.d.ts"),
|
||||||
|
SloppyImportsResolutionReason::Directory,
|
||||||
|
));
|
||||||
|
probe_paths.push((
|
||||||
|
path.join("index.d.mts"),
|
||||||
|
SloppyImportsResolutionReason::Directory,
|
||||||
|
));
|
||||||
|
probe_paths.push((
|
||||||
|
path.join("index.js"),
|
||||||
|
SloppyImportsResolutionReason::Directory,
|
||||||
|
));
|
||||||
|
probe_paths.push((
|
||||||
|
path.join("index.mjs"),
|
||||||
|
SloppyImportsResolutionReason::Directory,
|
||||||
|
));
|
||||||
|
probe_paths.push((
|
||||||
|
path.join("index.tsx"),
|
||||||
|
SloppyImportsResolutionReason::Directory,
|
||||||
|
));
|
||||||
|
probe_paths.push((
|
||||||
|
path.join("index.jsx"),
|
||||||
|
SloppyImportsResolutionReason::Directory,
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
probe_paths.push((
|
||||||
|
path.join("index.ts"),
|
||||||
|
SloppyImportsResolutionReason::Directory,
|
||||||
|
));
|
||||||
|
probe_paths.push((
|
||||||
|
path.join("index.mts"),
|
||||||
|
SloppyImportsResolutionReason::Directory,
|
||||||
|
));
|
||||||
|
probe_paths.push((
|
||||||
|
path.join("index.tsx"),
|
||||||
|
SloppyImportsResolutionReason::Directory,
|
||||||
|
));
|
||||||
|
probe_paths.push((
|
||||||
|
path.join("index.js"),
|
||||||
|
SloppyImportsResolutionReason::Directory,
|
||||||
|
));
|
||||||
|
probe_paths.push((
|
||||||
|
path.join("index.mjs"),
|
||||||
|
SloppyImportsResolutionReason::Directory,
|
||||||
|
));
|
||||||
|
probe_paths.push((
|
||||||
|
path.join("index.jsx"),
|
||||||
|
SloppyImportsResolutionReason::Directory,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if probe_paths.is_empty() {
|
||||||
|
return SloppyImportsResolution::None(specifier);
|
||||||
|
}
|
||||||
|
probe_paths
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (probe_path, reason) in probe_paths {
|
||||||
if (stat_sync)(&probe_path) == Some(SloppyImportsFsEntry::File) {
|
if (stat_sync)(&probe_path) == Some(SloppyImportsFsEntry::File) {
|
||||||
if let Ok(specifier) = ModuleSpecifier::from_file_path(probe_path) {
|
if let Ok(specifier) = ModuleSpecifier::from_file_path(probe_path) {
|
||||||
if is_dir_resolution {
|
match reason {
|
||||||
return SloppyImportsResolution::Directory(specifier);
|
SloppyImportsResolutionReason::JsToTs => {
|
||||||
} else if is_no_ext_resolution {
|
return SloppyImportsResolution::JsToTs(specifier)
|
||||||
return SloppyImportsResolution::NoExtension(specifier);
|
}
|
||||||
} else {
|
SloppyImportsResolutionReason::NoExtension => {
|
||||||
return SloppyImportsResolution::JsToTs(specifier);
|
return SloppyImportsResolution::NoExtension(specifier)
|
||||||
|
}
|
||||||
|
SloppyImportsResolutionReason::Directory => {
|
||||||
|
return SloppyImportsResolution::Directory(specifier)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1339,6 +1410,20 @@ mod test {
|
||||||
SloppyImportsResolution::Directory(index_file.uri_file()),
|
SloppyImportsResolution::Directory(index_file.uri_file()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// both a directory and a file with specifier is present
|
||||||
|
{
|
||||||
|
let api_dir = temp_dir.join("api");
|
||||||
|
api_dir.create_dir_all();
|
||||||
|
let bar_file = api_dir.join("bar.ts");
|
||||||
|
bar_file.write("");
|
||||||
|
let api_file = temp_dir.join("api.ts");
|
||||||
|
api_file.write("");
|
||||||
|
assert_eq!(
|
||||||
|
resolve(&api_dir.uri_file()),
|
||||||
|
SloppyImportsResolution::NoExtension(api_file.uri_file()),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
Loading…
Reference in a new issue