summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Sherret <dsherret@users.noreply.github.com>2023-12-07 15:59:13 -0500
committerGitHub <noreply@github.com>2023-12-07 15:59:13 -0500
commit78566753c81a26dc1855d8187c8192ffb1ba64e2 (patch)
tree73500bbd29baf164312f5bce58d880300a67a52a
parent3f96e5a29a88afafcef0f17458b2800b2db316ee (diff)
feat: add suggestions to module not found error messages for file urls (#21498)
-rw-r--r--cli/factory.rs4
-rw-r--r--cli/graph_util.rs123
-rw-r--r--cli/lsp/diagnostics.rs18
-rw-r--r--cli/lsp/documents.rs26
-rw-r--r--cli/lsp/language_server.rs1
-rw-r--r--cli/module_loader.rs2
-rw-r--r--cli/resolver.rs88
-rw-r--r--cli/tests/integration/lsp_tests.rs52
-rw-r--r--cli/tests/integration/run_tests.rs13
-rw-r--r--cli/tools/bench/mod.rs7
-rw-r--r--cli/tools/test/mod.rs7
-rw-r--r--cli/tools/vendor/build.rs3
12 files changed, 277 insertions, 67 deletions
diff --git a/cli/factory.rs b/cli/factory.rs
index e4b9c1da8..5db09767c 100644
--- a/cli/factory.rs
+++ b/cli/factory.rs
@@ -39,7 +39,7 @@ use crate::npm::CliNpmResolverManagedPackageJsonInstallerOption;
use crate::npm::CliNpmResolverManagedSnapshotOption;
use crate::resolver::CliGraphResolver;
use crate::resolver::CliGraphResolverOptions;
-use crate::resolver::UnstableSloppyImportsResolver;
+use crate::resolver::SloppyImportsResolver;
use crate::standalone::DenoCompileBinaryWriter;
use crate::tools::check::TypeChecker;
use crate::util::file_watcher::WatcherCommunicator;
@@ -383,7 +383,7 @@ impl CliFactory {
fs: self.fs().clone(),
cjs_resolutions: Some(self.cjs_resolutions().clone()),
sloppy_imports_resolver: if self.options.unstable_sloppy_imports() {
- Some(UnstableSloppyImportsResolver::new(self.fs().clone()))
+ Some(SloppyImportsResolver::new(self.fs().clone()))
} else {
None
},
diff --git a/cli/graph_util.rs b/cli/graph_util.rs
index 2e7766b9f..eba88e4d0 100644
--- a/cli/graph_util.rs
+++ b/cli/graph_util.rs
@@ -12,12 +12,15 @@ use crate::errors::get_error_class_name;
use crate::file_fetcher::FileFetcher;
use crate::npm::CliNpmResolver;
use crate::resolver::CliGraphResolver;
+use crate::resolver::SloppyImportsResolution;
+use crate::resolver::SloppyImportsResolver;
use crate::tools::check;
use crate::tools::check::TypeChecker;
use crate::util::file_watcher::WatcherCommunicator;
use crate::util::sync::TaskQueue;
use crate::util::sync::TaskQueuePermit;
+use deno_ast::MediaType;
use deno_core::anyhow::bail;
use deno_core::anyhow::Context;
use deno_core::error::custom_error;
@@ -58,11 +61,13 @@ pub struct GraphValidOptions {
/// error statically reachable from `roots` and not a dynamic import.
pub fn graph_valid_with_cli_options(
graph: &ModuleGraph,
+ fs: &Arc<dyn FileSystem>,
roots: &[ModuleSpecifier],
options: &CliOptions,
) -> Result<(), AnyError> {
graph_valid(
graph,
+ fs,
roots,
GraphValidOptions {
is_vendoring: false,
@@ -81,6 +86,7 @@ pub fn graph_valid_with_cli_options(
/// for the CLI.
pub fn graph_valid(
graph: &ModuleGraph,
+ fs: &Arc<dyn FileSystem>,
roots: &[ModuleSpecifier],
options: GraphValidOptions,
) -> Result<(), AnyError> {
@@ -109,10 +115,12 @@ pub fn graph_valid(
ModuleGraphError::TypesResolutionError(resolution_error) => {
format!(
"Failed resolving types. {}",
- enhanced_resolution_error_message(resolution_error,)
+ enhanced_resolution_error_message(resolution_error)
)
}
- ModuleGraphError::ModuleError(_) => format!("{error}"),
+ ModuleGraphError::ModuleError(e) => {
+ enhanced_module_error_message(fs, e)
+ }
};
if let Some(range) = error.maybe_range() {
@@ -356,7 +364,12 @@ impl ModuleGraphBuilder {
.await?;
let graph = Arc::new(graph);
- graph_valid_with_cli_options(&graph, &graph.roots, &self.options)?;
+ graph_valid_with_cli_options(
+ &graph,
+ &self.fs,
+ &graph.roots,
+ &self.options,
+ )?;
if let Some(lockfile) = &self.lockfile {
graph_lock_or_exit(&graph, &mut lockfile.lock());
}
@@ -524,6 +537,68 @@ pub fn enhanced_resolution_error_message(error: &ResolutionError) -> String {
message
}
+pub fn enhanced_module_error_message(
+ fs: &Arc<dyn FileSystem>,
+ error: &ModuleError,
+) -> String {
+ let additional_message = match error {
+ ModuleError::Missing(specifier, _) => {
+ maybe_sloppy_imports_suggestion_message(fs, specifier)
+ }
+ _ => None,
+ };
+ if let Some(message) = additional_message {
+ format!(
+ "{} {} or run with --unstable-sloppy-imports",
+ error, message
+ )
+ } else {
+ format!("{}", error)
+ }
+}
+
+pub fn maybe_sloppy_imports_suggestion_message(
+ fs: &Arc<dyn FileSystem>,
+ original_specifier: &ModuleSpecifier,
+) -> Option<String> {
+ let sloppy_imports_resolver = SloppyImportsResolver::new(fs.clone());
+ let resolution = sloppy_imports_resolver.resolve(original_specifier);
+ sloppy_import_resolution_to_suggestion_message(&resolution)
+}
+
+fn sloppy_import_resolution_to_suggestion_message(
+ resolution: &SloppyImportsResolution,
+) -> Option<String> {
+ match resolution {
+ SloppyImportsResolution::None(_) => None,
+ SloppyImportsResolution::JsToTs(specifier) => {
+ let media_type = MediaType::from_specifier(specifier);
+ Some(format!(
+ "Maybe change the extension to '{}'",
+ media_type.as_ts_extension()
+ ))
+ }
+ SloppyImportsResolution::NoExtension(specifier) => {
+ let media_type = MediaType::from_specifier(specifier);
+ Some(format!(
+ "Maybe add a '{}' extension",
+ media_type.as_ts_extension()
+ ))
+ }
+ SloppyImportsResolution::Directory(specifier) => {
+ let file_name = specifier
+ .path()
+ .rsplit_once('/')
+ .map(|(_, file_name)| file_name)
+ .unwrap_or(specifier.path());
+ Some(format!(
+ "Maybe specify path to '{}' file in directory instead",
+ file_name
+ ))
+ }
+ }
+}
+
pub fn get_resolution_error_bare_node_specifier(
error: &ResolutionError,
) -> Option<&str> {
@@ -897,4 +972,46 @@ mod test {
assert_eq!(get_resolution_error_bare_node_specifier(&err), output,);
}
}
+
+ #[test]
+ fn test_sloppy_import_resolution_to_message() {
+ // none
+ let url = ModuleSpecifier::parse("file:///dir/index.js").unwrap();
+ assert_eq!(
+ sloppy_import_resolution_to_suggestion_message(
+ &SloppyImportsResolution::None(&url)
+ ),
+ None,
+ );
+ // directory
+ assert_eq!(
+ sloppy_import_resolution_to_suggestion_message(
+ &SloppyImportsResolution::Directory(
+ ModuleSpecifier::parse("file:///dir/index.js").unwrap()
+ )
+ )
+ .unwrap(),
+ "Maybe specify path to 'index.js' file in directory instead"
+ );
+ // no ext
+ assert_eq!(
+ sloppy_import_resolution_to_suggestion_message(
+ &SloppyImportsResolution::NoExtension(
+ ModuleSpecifier::parse("file:///dir/index.mjs").unwrap()
+ )
+ )
+ .unwrap(),
+ "Maybe add a '.mjs' extension"
+ );
+ // js to ts
+ assert_eq!(
+ sloppy_import_resolution_to_suggestion_message(
+ &SloppyImportsResolution::JsToTs(
+ ModuleSpecifier::parse("file:///dir/index.mts").unwrap()
+ )
+ )
+ .unwrap(),
+ "Maybe change the extension to '.mts'"
+ );
+ }
}
diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs
index 4e4e9b3bb..4dbb4e1dd 100644
--- a/cli/lsp/diagnostics.rs
+++ b/cli/lsp/diagnostics.rs
@@ -37,6 +37,7 @@ use deno_graph::Resolution;
use deno_graph::ResolutionError;
use deno_graph::SpecifierError;
use deno_lint::rules::LintRule;
+use deno_runtime::deno_fs;
use deno_runtime::deno_node;
use deno_runtime::tokio_util::create_basic_runtime;
use deno_semver::npm::NpmPackageReqReference;
@@ -1166,6 +1167,21 @@ impl DenoDiagnostic {
/// Convert to an lsp Diagnostic when the range the diagnostic applies to is
/// provided.
pub fn to_lsp_diagnostic(&self, range: &lsp::Range) -> lsp::Diagnostic {
+ fn no_local_message(specifier: &ModuleSpecifier) -> String {
+ let fs: Arc<dyn deno_fs::FileSystem> = Arc::new(deno_fs::RealFs);
+ let mut message =
+ format!("Unable to load a local module: {}\n", specifier);
+ if let Some(additional_message) =
+ graph_util::maybe_sloppy_imports_suggestion_message(&fs, specifier)
+ {
+ message.push_str(&additional_message);
+ message.push('.');
+ } else {
+ message.push_str("Please check the file path.");
+ }
+ message
+ }
+
let (severity, message, data) = match self {
Self::DenoWarn(message) => (lsp::DiagnosticSeverity::WARNING, message.to_string(), None),
Self::ImportMapRemap { from, to } => (lsp::DiagnosticSeverity::HINT, format!("The import specifier can be remapped to \"{to}\" which will resolve it via the active import map."), Some(json!({ "from": from, "to": to }))),
@@ -1173,7 +1189,7 @@ impl DenoDiagnostic {
Self::NoAttributeType => (lsp::DiagnosticSeverity::ERROR, "The module is a JSON module and not being imported with an import attribute. Consider adding `with { type: \"json\" }` to the import statement.".to_string(), None),
Self::NoCache(specifier) => (lsp::DiagnosticSeverity::ERROR, format!("Uncached or missing remote URL: {specifier}"), Some(json!({ "specifier": specifier }))),
Self::NoCacheNpm(pkg_req, specifier) => (lsp::DiagnosticSeverity::ERROR, format!("Uncached or missing npm package: {}", pkg_req), Some(json!({ "specifier": specifier }))),
- Self::NoLocal(specifier) => (lsp::DiagnosticSeverity::ERROR, format!("Unable to load a local module: {specifier}\n Please check the file path."), None),
+ Self::NoLocal(specifier) => (lsp::DiagnosticSeverity::ERROR, no_local_message(specifier), None),
Self::Redirect { from, to} => (lsp::DiagnosticSeverity::INFORMATION, format!("The import of \"{from}\" was redirected to \"{to}\"."), Some(json!({ "specifier": from, "redirect": to }))),
Self::ResolutionError(err) => (
lsp::DiagnosticSeverity::ERROR,
diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs
index 6687d2208..a341ae207 100644
--- a/cli/lsp/documents.rs
+++ b/cli/lsp/documents.rs
@@ -20,9 +20,9 @@ use crate::lsp::logging::lsp_warn;
use crate::npm::CliNpmResolver;
use crate::resolver::CliGraphResolver;
use crate::resolver::CliGraphResolverOptions;
-use crate::resolver::UnstableSloppyImportsFsEntry;
-use crate::resolver::UnstableSloppyImportsResolution;
-use crate::resolver::UnstableSloppyImportsResolver;
+use crate::resolver::SloppyImportsFsEntry;
+use crate::resolver::SloppyImportsResolution;
+use crate::resolver::SloppyImportsResolver;
use crate::util::glob;
use crate::util::path::specifier_to_file_path;
use crate::util::text_encoding;
@@ -1065,20 +1065,20 @@ impl Documents {
fn resolve_unstable_sloppy_import<'a>(
&self,
specifier: &'a ModuleSpecifier,
- ) -> UnstableSloppyImportsResolution<'a> {
- UnstableSloppyImportsResolver::resolve_with_stat_sync(specifier, |path| {
+ ) -> SloppyImportsResolution<'a> {
+ SloppyImportsResolver::resolve_with_stat_sync(specifier, |path| {
if let Ok(specifier) = ModuleSpecifier::from_file_path(path) {
if self.open_docs.contains_key(&specifier)
|| self.cache.contains(&specifier)
{
- return Some(UnstableSloppyImportsFsEntry::File);
+ return Some(SloppyImportsFsEntry::File);
}
}
path.metadata().ok().and_then(|m| {
if m.is_file() {
- Some(UnstableSloppyImportsFsEntry::File)
+ Some(SloppyImportsFsEntry::File)
} else if m.is_dir() {
- Some(UnstableSloppyImportsFsEntry::Dir)
+ Some(SloppyImportsFsEntry::Dir)
} else {
None
}
@@ -1732,18 +1732,18 @@ impl<'a> OpenDocumentsGraphLoader<'a> {
fn resolve_unstable_sloppy_import<'b>(
&self,
specifier: &'b ModuleSpecifier,
- ) -> UnstableSloppyImportsResolution<'b> {
- UnstableSloppyImportsResolver::resolve_with_stat_sync(specifier, |path| {
+ ) -> SloppyImportsResolution<'b> {
+ SloppyImportsResolver::resolve_with_stat_sync(specifier, |path| {
if let Ok(specifier) = ModuleSpecifier::from_file_path(path) {
if self.open_docs.contains_key(&specifier) {
- return Some(UnstableSloppyImportsFsEntry::File);
+ return Some(SloppyImportsFsEntry::File);
}
}
path.metadata().ok().and_then(|m| {
if m.is_file() {
- Some(UnstableSloppyImportsFsEntry::File)
+ Some(SloppyImportsFsEntry::File)
} else if m.is_dir() {
- Some(UnstableSloppyImportsFsEntry::Dir)
+ Some(SloppyImportsFsEntry::Dir)
} else {
None
}
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs
index 92173b8ad..bc8a10235 100644
--- a/cli/lsp/language_server.rs
+++ b/cli/lsp/language_server.rs
@@ -263,6 +263,7 @@ impl LanguageServer {
.await?;
graph_util::graph_valid(
&graph,
+ factory.fs(),
&roots,
graph_util::GraphValidOptions {
is_vendoring: false,
diff --git a/cli/module_loader.rs b/cli/module_loader.rs
index 92116dc7b..afd2d1999 100644
--- a/cli/module_loader.rs
+++ b/cli/module_loader.rs
@@ -169,7 +169,7 @@ impl ModuleLoadPreparer {
)
.await?;
- graph_valid_with_cli_options(graph, &roots, &self.options)?;
+ graph_valid_with_cli_options(graph, &self.fs, &roots, &self.options)?;
// If there is a lockfile...
if let Some(lockfile) = &self.lockfile {
diff --git a/cli/resolver.rs b/cli/resolver.rs
index 12b86632f..3cf124217 100644
--- a/cli/resolver.rs
+++ b/cli/resolver.rs
@@ -117,7 +117,7 @@ impl MappedSpecifierResolver {
#[derive(Debug)]
pub struct CliGraphResolver {
fs: Arc<dyn FileSystem>,
- sloppy_imports_resolver: Option<UnstableSloppyImportsResolver>,
+ sloppy_imports_resolver: Option<SloppyImportsResolver>,
mapped_specifier_resolver: MappedSpecifierResolver,
maybe_default_jsx_import_source: Option<String>,
maybe_jsx_import_source_module: Option<String>,
@@ -132,7 +132,7 @@ pub struct CliGraphResolver {
pub struct CliGraphResolverOptions<'a> {
pub fs: Arc<dyn FileSystem>,
pub cjs_resolutions: Option<Arc<CjsResolutionStore>>,
- pub sloppy_imports_resolver: Option<UnstableSloppyImportsResolver>,
+ pub sloppy_imports_resolver: Option<SloppyImportsResolver>,
pub node_resolver: Option<Arc<NodeResolver>>,
pub npm_resolver: Option<Arc<dyn CliNpmResolver>>,
pub package_json_deps_provider: Arc<PackageJsonDepsProvider>,
@@ -399,13 +399,13 @@ impl Resolver for CliGraphResolver {
}
fn sloppy_imports_resolve(
- resolver: &UnstableSloppyImportsResolver,
+ resolver: &SloppyImportsResolver,
specifier: ModuleSpecifier,
referrer_range: &deno_graph::Range,
) -> ModuleSpecifier {
let resolution = resolver.resolve(&specifier);
let hint_message = match &resolution {
- UnstableSloppyImportsResolution::JsToTs(to_specifier) => {
+ SloppyImportsResolution::JsToTs(to_specifier) => {
let from_media_type = MediaType::from_specifier(&specifier);
let to_media_type = MediaType::from_specifier(to_specifier);
format!(
@@ -414,11 +414,11 @@ fn sloppy_imports_resolve(
to_media_type.as_ts_extension()
)
}
- UnstableSloppyImportsResolution::NoExtension(to_specifier) => {
+ SloppyImportsResolution::NoExtension(to_specifier) => {
let to_media_type = MediaType::from_specifier(to_specifier);
format!("add {} extension", to_media_type.as_ts_extension())
}
- UnstableSloppyImportsResolution::Directory(to_specifier) => {
+ SloppyImportsResolution::Directory(to_specifier) => {
let file_name = to_specifier
.path()
.rsplit_once('/')
@@ -426,7 +426,7 @@ fn sloppy_imports_resolve(
.unwrap_or(to_specifier.path());
format!("specify path to {} file in directory instead", file_name)
}
- UnstableSloppyImportsResolution::None(_) => return specifier,
+ SloppyImportsResolution::None(_) => return specifier,
};
// show a warning when this happens in order to drive
// the user towards correcting these specifiers
@@ -541,12 +541,12 @@ impl NpmResolver for CliGraphResolver {
}
#[derive(Debug)]
-struct UnstableSloppyImportsStatCache {
+struct SloppyImportsStatCache {
fs: Arc<dyn FileSystem>,
- cache: Mutex<HashMap<PathBuf, Option<UnstableSloppyImportsFsEntry>>>,
+ cache: Mutex<HashMap<PathBuf, Option<SloppyImportsFsEntry>>>,
}
-impl UnstableSloppyImportsStatCache {
+impl SloppyImportsStatCache {
pub fn new(fs: Arc<dyn FileSystem>) -> Self {
Self {
fs,
@@ -554,7 +554,7 @@ impl UnstableSloppyImportsStatCache {
}
}
- pub fn stat_sync(&self, path: &Path) -> Option<UnstableSloppyImportsFsEntry> {
+ pub fn stat_sync(&self, path: &Path) -> Option<SloppyImportsFsEntry> {
// there will only ever be one thread in here at a
// time, so it's ok to hold the lock for so long
let mut cache = self.cache.lock();
@@ -564,9 +564,9 @@ impl UnstableSloppyImportsStatCache {
let entry = self.fs.stat_sync(path).ok().and_then(|stat| {
if stat.is_file {
- Some(UnstableSloppyImportsFsEntry::File)
+ Some(SloppyImportsFsEntry::File)
} else if stat.is_directory {
- Some(UnstableSloppyImportsFsEntry::Dir)
+ Some(SloppyImportsFsEntry::Dir)
} else {
None
}
@@ -577,13 +577,13 @@ impl UnstableSloppyImportsStatCache {
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum UnstableSloppyImportsFsEntry {
+pub enum SloppyImportsFsEntry {
File,
Dir,
}
#[derive(Debug, PartialEq, Eq)]
-pub enum UnstableSloppyImportsResolution<'a> {
+pub enum SloppyImportsResolution<'a> {
/// No sloppy resolution was found.
None(&'a ModuleSpecifier),
/// Ex. `./file.js` to `./file.ts`
@@ -594,7 +594,7 @@ pub enum UnstableSloppyImportsResolution<'a> {
Directory(ModuleSpecifier),
}
-impl<'a> UnstableSloppyImportsResolution<'a> {
+impl<'a> SloppyImportsResolution<'a> {
pub fn into_specifier(self) -> Cow<'a, ModuleSpecifier> {
match self {
Self::None(specifier) => Cow::Borrowed(specifier),
@@ -615,35 +615,35 @@ impl<'a> UnstableSloppyImportsResolution<'a> {
}
#[derive(Debug)]
-pub struct UnstableSloppyImportsResolver {
- stat_cache: UnstableSloppyImportsStatCache,
+pub struct SloppyImportsResolver {
+ stat_cache: SloppyImportsStatCache,
}
-impl UnstableSloppyImportsResolver {
+impl SloppyImportsResolver {
pub fn new(fs: Arc<dyn FileSystem>) -> Self {
Self {
- stat_cache: UnstableSloppyImportsStatCache::new(fs),
+ stat_cache: SloppyImportsStatCache::new(fs),
}
}
pub fn resolve_with_stat_sync(
specifier: &ModuleSpecifier,
- stat_sync: impl Fn(&Path) -> Option<UnstableSloppyImportsFsEntry>,
- ) -> UnstableSloppyImportsResolution {
+ stat_sync: impl Fn(&Path) -> Option<SloppyImportsFsEntry>,
+ ) -> SloppyImportsResolution {
if specifier.scheme() != "file" {
- return UnstableSloppyImportsResolution::None(specifier);
+ return SloppyImportsResolution::None(specifier);
}
let Ok(path) = specifier_to_file_path(specifier) else {
- return UnstableSloppyImportsResolution::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(UnstableSloppyImportsFsEntry::File) => {
- return UnstableSloppyImportsResolution::None(specifier);
+ Some(SloppyImportsFsEntry::File) => {
+ return SloppyImportsResolution::None(specifier);
}
- Some(UnstableSloppyImportsFsEntry::Dir) => {
+ Some(SloppyImportsFsEntry::Dir) => {
is_dir_resolution = true;
// try to resolve at the index file
vec![
@@ -673,7 +673,7 @@ impl UnstableSloppyImportsResolver {
| MediaType::Wasm
| MediaType::TsBuildInfo
| MediaType::SourceMap => {
- return UnstableSloppyImportsResolution::None(specifier)
+ return SloppyImportsResolution::None(specifier)
}
MediaType::Unknown => {
is_no_ext_resolution = true;
@@ -692,7 +692,7 @@ impl UnstableSloppyImportsResolver {
MediaType::Unknown => old_path_str,
_ => match old_path_str.strip_suffix(media_type.as_ts_extension()) {
Some(s) => Cow::Borrowed(s),
- None => return UnstableSloppyImportsResolution::None(specifier),
+ None => return SloppyImportsResolution::None(specifier),
},
};
probe_media_type_types
@@ -709,26 +709,26 @@ impl UnstableSloppyImportsResolver {
};
for probe_path in probe_paths {
- if (stat_sync)(&probe_path) == Some(UnstableSloppyImportsFsEntry::File) {
+ if (stat_sync)(&probe_path) == Some(SloppyImportsFsEntry::File) {
if let Ok(specifier) = ModuleSpecifier::from_file_path(probe_path) {
if is_dir_resolution {
- return UnstableSloppyImportsResolution::Directory(specifier);
+ return SloppyImportsResolution::Directory(specifier);
} else if is_no_ext_resolution {
- return UnstableSloppyImportsResolution::NoExtension(specifier);
+ return SloppyImportsResolution::NoExtension(specifier);
} else {
- return UnstableSloppyImportsResolution::JsToTs(specifier);
+ return SloppyImportsResolution::JsToTs(specifier);
}
}
}
}
- UnstableSloppyImportsResolution::None(specifier)
+ SloppyImportsResolution::None(specifier)
}
pub fn resolve<'a>(
&self,
specifier: &'a ModuleSpecifier,
- ) -> UnstableSloppyImportsResolution<'a> {
+ ) -> SloppyImportsResolution<'a> {
Self::resolve_with_stat_sync(specifier, |path| {
self.stat_cache.stat_sync(path)
})
@@ -806,13 +806,13 @@ mod test {
#[test]
fn test_unstable_sloppy_imports() {
- fn resolve(specifier: &ModuleSpecifier) -> UnstableSloppyImportsResolution {
- UnstableSloppyImportsResolver::resolve_with_stat_sync(specifier, |path| {
+ 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(UnstableSloppyImportsFsEntry::File)
+ Some(SloppyImportsFsEntry::File)
} else if stat.is_directory {
- Some(UnstableSloppyImportsFsEntry::Dir)
+ Some(SloppyImportsFsEntry::Dir)
} else {
None
}
@@ -830,7 +830,7 @@ mod test {
let ts_file_uri = ts_file.uri_file();
assert_eq!(
resolve(&ts_file.uri_file()),
- UnstableSloppyImportsResolution::None(&ts_file_uri),
+ SloppyImportsResolution::None(&ts_file_uri),
);
assert_eq!(
resolve(
@@ -839,7 +839,7 @@ mod test {
.join(&format!("file.{}", ext_from))
.unwrap()
),
- UnstableSloppyImportsResolution::JsToTs(ts_file.uri_file()),
+ SloppyImportsResolution::JsToTs(ts_file.uri_file()),
);
ts_file.remove_file();
}
@@ -855,7 +855,7 @@ mod test {
.join("file") // no ext
.unwrap()
),
- UnstableSloppyImportsResolution::NoExtension(file.uri_file()),
+ SloppyImportsResolution::NoExtension(file.uri_file()),
);
file.remove_file();
}
@@ -869,7 +869,7 @@ mod test {
let js_file_uri = js_file.uri_file();
assert_eq!(
resolve(&js_file.uri_file()),
- UnstableSloppyImportsResolution::None(&js_file_uri),
+ SloppyImportsResolution::None(&js_file_uri),
);
}
@@ -881,7 +881,7 @@ mod test {
index_file.write("");
assert_eq!(
resolve(&routes_dir.uri_file()),
- UnstableSloppyImportsResolution::Directory(index_file.uri_file()),
+ SloppyImportsResolution::Directory(index_file.uri_file()),
);
}
}
diff --git a/cli/tests/integration/lsp_tests.rs b/cli/tests/integration/lsp_tests.rs
index 711d0bcd8..9283b4021 100644
--- a/cli/tests/integration/lsp_tests.rs
+++ b/cli/tests/integration/lsp_tests.rs
@@ -10668,3 +10668,55 @@ fn lsp_sloppy_imports_warn() {
client.shutdown();
}
+
+#[test]
+fn sloppy_imports_not_enabled() {
+ let context = TestContextBuilder::new().use_temp_cwd().build();
+ let temp_dir = context.temp_dir();
+ let temp_dir = temp_dir.path();
+ temp_dir.join("deno.json").write(r#"{}"#);
+ // The enhanced, more helpful error message is only available
+ // when the file exists on the file system at the moment because
+ // it's a little more complicated to hook it up otherwise.
+ temp_dir.join("a.ts").write("export class A {}");
+ let mut client = context.new_lsp_command().build();
+ client.initialize(|builder| {
+ builder.set_root_uri(temp_dir.uri_dir());
+ });
+ let diagnostics = client.did_open(json!({
+ "textDocument": {
+ "uri": temp_dir.join("file.ts").uri_file(),
+ "languageId": "typescript",
+ "version": 1,
+ "text": "import * as a from './a';\nconsole.log(a)\n",
+ },
+ }));
+ assert_eq!(
+ diagnostics.messages_with_source("deno"),
+ lsp::PublishDiagnosticsParams {
+ uri: temp_dir.join("file.ts").uri_file(),
+ diagnostics: vec![lsp::Diagnostic {
+ range: lsp::Range {
+ start: lsp::Position {
+ line: 0,
+ character: 19
+ },
+ end: lsp::Position {
+ line: 0,
+ character: 24
+ }
+ },
+ severity: Some(lsp::DiagnosticSeverity::ERROR),
+ code: Some(lsp::NumberOrString::String("no-local".to_string())),
+ source: Some("deno".to_string()),
+ message: format!(
+ "Unable to load a local module: {}\nMaybe add a '.ts' extension.",
+ temp_dir.join("a").uri_file(),
+ ),
+ ..Default::default()
+ }],
+ version: Some(1),
+ }
+ );
+ client.shutdown();
+}
diff --git a/cli/tests/integration/run_tests.rs b/cli/tests/integration/run_tests.rs
index 4e0bbdfd2..03de97ee7 100644
--- a/cli/tests/integration/run_tests.rs
+++ b/cli/tests/integration/run_tests.rs
@@ -4740,7 +4740,6 @@ itest!(unsafe_proto_flag {
fn test_unstable_sloppy_imports() {
let context = TestContextBuilder::new().use_temp_cwd().build();
let temp_dir = context.temp_dir();
- temp_dir.write("deno.json", r#"{ "unstable": ["sloppy-imports"] }"#);
temp_dir.write("a.ts", "export class A {}");
temp_dir.write("b.js", "export class B {}");
temp_dir.write("c.mts", "export class C {}");
@@ -4771,6 +4770,18 @@ console.log(g.G);
"#,
);
+ // run without sloppy imports
+ context
+ .new_command()
+ .args("run main.ts")
+ .run()
+ .assert_matches_text(r#"error: Module not found "file:///[WILDCARD]/a.js". Maybe change the extension to '.ts' or run with --unstable-sloppy-imports
+ at file:///[WILDCARD]/main.ts:1:20
+"#)
+ .assert_exit_code(1);
+
+ // now run with sloppy imports
+ temp_dir.write("deno.json", r#"{ "unstable": ["sloppy-imports"] }"#);
context
.new_command()
.args("run main.ts")
diff --git a/cli/tools/bench/mod.rs b/cli/tools/bench/mod.rs
index ed6192b3b..b04aa757d 100644
--- a/cli/tools/bench/mod.rs
+++ b/cli/tools/bench/mod.rs
@@ -495,7 +495,12 @@ pub async fn run_benchmarks_with_watch(
let graph = module_graph_builder
.create_graph(graph_kind, bench_modules.clone())
.await?;
- graph_valid_with_cli_options(&graph, &bench_modules, cli_options)?;
+ graph_valid_with_cli_options(
+ &graph,
+ factory.fs(),
+ &bench_modules,
+ cli_options,
+ )?;
let bench_modules_to_reload = if let Some(changed_paths) = changed_paths
{
diff --git a/cli/tools/test/mod.rs b/cli/tools/test/mod.rs
index c69c3115c..5d943d716 100644
--- a/cli/tools/test/mod.rs
+++ b/cli/tools/test/mod.rs
@@ -1280,7 +1280,12 @@ pub async fn run_tests_with_watch(
let graph = module_graph_builder
.create_graph(graph_kind, test_modules.clone())
.await?;
- graph_valid_with_cli_options(&graph, &test_modules, &cli_options)?;
+ graph_valid_with_cli_options(
+ &graph,
+ factory.fs(),
+ &test_modules,
+ &cli_options,
+ )?;
let test_modules_to_reload = if let Some(changed_paths) = changed_paths
{
diff --git a/cli/tools/vendor/build.rs b/cli/tools/vendor/build.rs
index 80dda86cc..62fc0aa9a 100644
--- a/cli/tools/vendor/build.rs
+++ b/cli/tools/vendor/build.rs
@@ -15,6 +15,7 @@ use deno_graph::source::ResolutionMode;
use deno_graph::EsmModule;
use deno_graph::Module;
use deno_graph::ModuleGraph;
+use deno_runtime::deno_fs;
use import_map::ImportMap;
use import_map::SpecifierMap;
@@ -134,8 +135,10 @@ pub async fn build<
}
// surface any errors
+ let fs: Arc<dyn deno_fs::FileSystem> = Arc::new(deno_fs::RealFs);
graph_util::graph_valid(
&graph,
+ &fs,
&graph.roots,
graph_util::GraphValidOptions {
is_vendoring: true,