diff --git a/crates/rspack_core/src/resolver/mod.rs b/crates/rspack_core/src/resolver/mod.rs index 24312000caa..694e4041008 100644 --- a/crates/rspack_core/src/resolver/mod.rs +++ b/crates/rspack_core/src/resolver/mod.rs @@ -1,6 +1,7 @@ mod factory; mod resolver_impl; - +use std::borrow::Borrow; +use std::fs; use std::{fmt, path::PathBuf}; use once_cell::sync::Lazy; @@ -169,6 +170,101 @@ which tries to resolve these kind of requests in the current directory too.", } } + // try to resolve relative path with extension + if RELATIVE_PATH_REGEX.is_match(args.specifier) { + let connected_path = base_dir.join(args.specifier); + let normalized_path = connected_path.absolutize(); + + let mut is_resolving_dir = false; // whether the request is to resolve a directory or not + + let file_name = normalized_path.file_name(); + let parent_path = match fs::metadata(&normalized_path) { + Ok(metadata) => { + // if the path is not directory, we need to resolve the parent directory + if !metadata.is_dir() { + normalized_path.parent() + } else { + is_resolving_dir = true; + Some(normalized_path.borrow()) + } + } + Err(_) => normalized_path.parent(), + }; + + if file_name.is_some() && parent_path.is_some() { + let file_name = file_name.expect("fail to get the filename of the current resolved module"); + let parent_path = + parent_path.expect("fail to get the parent path of the current resolved module"); + + // read the files in the parent directory + let files = fs::read_dir(parent_path); + match files { + Ok(files) => { + let mut requested_names = vec![file_name + .to_str() + .map(|f| f.to_string()) + .unwrap_or_default()]; + if is_resolving_dir { + // The request maybe is like `./` or `./dir` to resolve the main file (e.g.: index) in directory + // So we need to check them. + let main_files = dep + .resolve_options + .as_deref() + .or(Some(&plugin_driver.options.resolve)) + .and_then(|o| o.main_files.as_ref().cloned()) + .unwrap_or_default(); + + requested_names.extend(main_files); + } + + let suggestions = files + .into_iter() + .filter_map(|file| { + file.ok().and_then(|file| { + file.path().file_stem().and_then(|file_stem| { + if requested_names.contains(&file_stem.to_string_lossy().to_string()) { + let mut suggestion = file.path().relative(args.context.as_path()); + + if !suggestion.to_string_lossy().starts_with('.') { + suggestion = PathBuf::from(format!("./{}", suggestion.to_string_lossy())); + } + Some(suggestion) + } else { + None + } + }) + }) + }) + .collect::>(); + + if suggestions.is_empty() { + return None; + } + + let mut hint: Vec = vec![]; + for suggestion in suggestions { + let suggestion_ext = suggestion + .extension() + .map(|e| e.to_string_lossy()) + .unwrap_or_default(); + let suggestion_path = suggestion.to_string_lossy(); + let specifier = args.specifier; + + hint.push(format!( + "Found the module '{suggestion_path}' exists, but its extension is not listed in the `resolve.extensions`. Here are some possible solutions: + +1. add the extension `\".{suggestion_ext}\"` to `resolve.extensions` in your rspack configuration +2. use '{suggestion_path}' instead of '{specifier}' +")); + } + + return Some(hint.join("\n")); + } + Err(_) => return None, + } + } + } + None } diff --git a/packages/rspack/tests/diagnostics/builtins/recoverable_syntax_error/stats.err b/packages/rspack/tests/diagnostics/builtins/recoverable_syntax_error/stats.err index 398ee5d6805..70fee710e3e 100644 --- a/packages/rspack/tests/diagnostics/builtins/recoverable_syntax_error/stats.err +++ b/packages/rspack/tests/diagnostics/builtins/recoverable_syntax_error/stats.err @@ -1 +1,5 @@ ERROR in × Resolve error: Can't resolve './' in '/tests/diagnostics/builtins/recoverable_syntax_error' + help: Found the module './index.tsx' exists, but its extension is not listed in the `resolve.extensions`. Here are some possible solutions: + + 1. add the extension `".tsx"` to `resolve.extensions` in your rspack configuration + 2. use './index.tsx' instead of './' diff --git a/packages/rspack/tests/diagnostics/factorize/resolve-extensions-error/a.txt b/packages/rspack/tests/diagnostics/factorize/resolve-extensions-error/a.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/rspack/tests/diagnostics/factorize/resolve-extensions-error/err/index.js b/packages/rspack/tests/diagnostics/factorize/resolve-extensions-error/err/index.js new file mode 100644 index 00000000000..67b430d716b --- /dev/null +++ b/packages/rspack/tests/diagnostics/factorize/resolve-extensions-error/err/index.js @@ -0,0 +1 @@ +import a from '../a'; \ No newline at end of file diff --git a/packages/rspack/tests/diagnostics/factorize/resolve-extensions-error/index.js b/packages/rspack/tests/diagnostics/factorize/resolve-extensions-error/index.js new file mode 100644 index 00000000000..43732393d34 --- /dev/null +++ b/packages/rspack/tests/diagnostics/factorize/resolve-extensions-error/index.js @@ -0,0 +1,3 @@ +import './err'; +import './test'; +import a from './a'; \ No newline at end of file diff --git a/packages/rspack/tests/diagnostics/factorize/resolve-extensions-error/stats.err b/packages/rspack/tests/diagnostics/factorize/resolve-extensions-error/stats.err new file mode 100644 index 00000000000..3ad4d7a10fe --- /dev/null +++ b/packages/rspack/tests/diagnostics/factorize/resolve-extensions-error/stats.err @@ -0,0 +1,36 @@ +ERROR in ./err/index.js + × Resolve error: Can't resolve '../a' in '/tests/diagnostics/factorize/resolve-extensions-error/err' + ╭──── + 1 │ import a from '../a'; + · ────── + ╰──── + help: Found the module '../a.txt' exists, but its extension is not listed in the `resolve.extensions`. Here are some possible solutions: + + 1. add the extension `".txt"` to `resolve.extensions` in your rspack configuration + 2. use '../a.txt' instead of '../a' + +ERROR in ./index.js + × Resolve error: Can't resolve './test' in '/tests/diagnostics/factorize/resolve-extensions-error' + ╭─[1:1] + 1 │ import './err'; + 2 │ import './test'; + · ──────── + 3 │ import a from './a'; + ╰──── + help: Found the module './test/index.txt' exists, but its extension is not listed in the `resolve.extensions`. Here are some possible solutions: + + 1. add the extension `".txt"` to `resolve.extensions` in your rspack configuration + 2. use './test/index.txt' instead of './test' + +ERROR in ./index.js + × Resolve error: Can't resolve './a' in '/tests/diagnostics/factorize/resolve-extensions-error' + ╭─[1:1] + 1 │ import './err'; + 2 │ import './test'; + 3 │ import a from './a'; + · ───── + ╰──── + help: Found the module './a.txt' exists, but its extension is not listed in the `resolve.extensions`. Here are some possible solutions: + + 1. add the extension `".txt"` to `resolve.extensions` in your rspack configuration + 2. use './a.txt' instead of './a' diff --git a/packages/rspack/tests/diagnostics/factorize/resolve-extensions-error/test/index.txt b/packages/rspack/tests/diagnostics/factorize/resolve-extensions-error/test/index.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/rspack/tests/diagnostics/factorize/resolve-extensions-error/webpack.config.js b/packages/rspack/tests/diagnostics/factorize/resolve-extensions-error/webpack.config.js new file mode 100644 index 00000000000..631f375658e --- /dev/null +++ b/packages/rspack/tests/diagnostics/factorize/resolve-extensions-error/webpack.config.js @@ -0,0 +1,2 @@ +module.exports = { +}