diff options
Diffstat (limited to 'cli')
-rw-r--r-- | cli/Cargo.toml | 6 | ||||
-rw-r--r-- | cli/factory.rs | 18 | ||||
-rw-r--r-- | cli/graph_util.rs | 9 | ||||
-rw-r--r-- | cli/lsp/config.rs | 13 | ||||
-rw-r--r-- | cli/lsp/diagnostics.rs | 11 | ||||
-rw-r--r-- | cli/resolver.rs | 525 | ||||
-rw-r--r-- | cli/tools/lint/rules/mod.rs | 6 | ||||
-rw-r--r-- | cli/tools/lint/rules/no_sloppy_imports.rs | 19 | ||||
-rw-r--r-- | cli/tools/registry/mod.rs | 7 | ||||
-rw-r--r-- | cli/tools/registry/unfurl.rs | 15 |
10 files changed, 105 insertions, 524 deletions
diff --git a/cli/Cargo.toml b/cli/Cargo.toml index ddcf7119f..32e0651b2 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -71,9 +71,10 @@ deno_doc = { version = "0.150.0", features = ["html", "syntect"] } deno_graph = { version = "=0.82.3" } deno_lint = { version = "=0.67.0", features = ["docs"] } deno_lockfile.workspace = true -deno_npm = "=0.25.2" +deno_npm.workspace = true deno_package_json.workspace = true deno_path_util.workspace = true +deno_resolver.workspace = true deno_runtime = { workspace = true, features = ["include_js_files_for_snapshotting"] } deno_semver.workspace = true deno_task_shell = "=0.17.0" @@ -85,7 +86,6 @@ node_resolver.workspace = true anstream = "0.6.14" async-trait.workspace = true -base32.workspace = true base64.workspace = true bincode = "=1.3.3" bytes.workspace = true @@ -96,7 +96,7 @@ clap_complete = "=4.5.24" clap_complete_fig = "=4.5.2" color-print = "0.3.5" console_static_text.workspace = true -dashmap = "5.5.3" +dashmap.workspace = true data-encoding.workspace = true dissimilar = "=1.0.4" dotenvy = "0.15.7" diff --git a/cli/factory.rs b/cli/factory.rs index 8aea635d2..5e525ee32 100644 --- a/cli/factory.rs +++ b/cli/factory.rs @@ -41,8 +41,9 @@ use crate::resolver::CjsResolutionStore; use crate::resolver::CliGraphResolver; use crate::resolver::CliGraphResolverOptions; use crate::resolver::CliNodeResolver; +use crate::resolver::CliSloppyImportsResolver; use crate::resolver::NpmModuleLoader; -use crate::resolver::SloppyImportsResolver; +use crate::resolver::SloppyImportsCachedFs; use crate::standalone::DenoCompileBinaryWriter; use crate::tools::check::TypeChecker; use crate::tools::coverage::CoverageCollector; @@ -186,7 +187,7 @@ struct CliFactoryServices { npm_resolver: Deferred<Arc<dyn CliNpmResolver>>, permission_desc_parser: Deferred<Arc<RuntimePermissionDescriptorParser>>, root_permissions_container: Deferred<PermissionsContainer>, - sloppy_imports_resolver: Deferred<Option<Arc<SloppyImportsResolver>>>, + sloppy_imports_resolver: Deferred<Option<Arc<CliSloppyImportsResolver>>>, text_only_progress_bar: Deferred<ProgressBar>, type_checker: Deferred<Arc<TypeChecker>>, cjs_resolutions: Deferred<Arc<CjsResolutionStore>>, @@ -404,17 +405,16 @@ impl CliFactory { pub fn sloppy_imports_resolver( &self, - ) -> Result<Option<&Arc<SloppyImportsResolver>>, AnyError> { + ) -> Result<Option<&Arc<CliSloppyImportsResolver>>, AnyError> { self .services .sloppy_imports_resolver .get_or_try_init(|| { - Ok( - self - .cli_options()? - .unstable_sloppy_imports() - .then(|| Arc::new(SloppyImportsResolver::new(self.fs().clone()))), - ) + Ok(self.cli_options()?.unstable_sloppy_imports().then(|| { + Arc::new(CliSloppyImportsResolver::new(SloppyImportsCachedFs::new( + self.fs().clone(), + ))) + })) }) .map(|maybe| maybe.as_ref()) } diff --git a/cli/graph_util.rs b/cli/graph_util.rs index 7d03d3c0b..f7194ac11 100644 --- a/cli/graph_util.rs +++ b/cli/graph_util.rs @@ -14,7 +14,8 @@ use crate::errors::get_error_class_name; use crate::file_fetcher::FileFetcher; use crate::npm::CliNpmResolver; use crate::resolver::CliGraphResolver; -use crate::resolver::SloppyImportsResolver; +use crate::resolver::CliSloppyImportsResolver; +use crate::resolver::SloppyImportsCachedFs; use crate::tools::check; use crate::tools::check::TypeChecker; use crate::util::file_watcher::WatcherCommunicator; @@ -31,7 +32,6 @@ use deno_core::error::AnyError; use deno_core::parking_lot::Mutex; use deno_core::ModuleSpecifier; use deno_graph::source::Loader; -use deno_graph::source::ResolutionMode; use deno_graph::source::ResolveError; use deno_graph::GraphKind; use deno_graph::ModuleError; @@ -40,6 +40,7 @@ use deno_graph::ModuleGraphError; use deno_graph::ResolutionError; use deno_graph::SpecifierError; use deno_path_util::url_to_file_path; +use deno_resolver::sloppy_imports::SloppyImportsResolutionMode; use deno_runtime::deno_fs::FileSystem; use deno_runtime::deno_node; use deno_runtime::deno_permissions::PermissionsContainer; @@ -765,8 +766,8 @@ fn enhanced_sloppy_imports_error_message( match error { ModuleError::LoadingErr(specifier, _, ModuleLoadError::Loader(_)) // ex. "Is a directory" error | ModuleError::Missing(specifier, _) => { - let additional_message = SloppyImportsResolver::new(fs.clone()) - .resolve(specifier, ResolutionMode::Execution)? + let additional_message = CliSloppyImportsResolver::new(SloppyImportsCachedFs::new(fs.clone())) + .resolve(specifier, SloppyImportsResolutionMode::Execution)? .as_suggestion_message(); Some(format!( "{} {} or run with --unstable-sloppy-imports", diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs index dcb6120a4..c54de3a23 100644 --- a/cli/lsp/config.rs +++ b/cli/lsp/config.rs @@ -59,7 +59,8 @@ use crate::args::LintOptions; use crate::cache::FastInsecureHasher; use crate::file_fetcher::FileFetcher; use crate::lsp::logging::lsp_warn; -use crate::resolver::SloppyImportsResolver; +use crate::resolver::CliSloppyImportsResolver; +use crate::resolver::SloppyImportsCachedFs; use crate::tools::lint::CliLinter; use crate::tools::lint::CliLinterOptions; use crate::tools::lint::LintRuleProvider; @@ -1181,7 +1182,7 @@ pub struct ConfigData { pub lockfile: Option<Arc<CliLockfile>>, pub npmrc: Option<Arc<ResolvedNpmRc>>, pub resolver: Arc<WorkspaceResolver>, - pub sloppy_imports_resolver: Option<Arc<SloppyImportsResolver>>, + pub sloppy_imports_resolver: Option<Arc<CliSloppyImportsResolver>>, pub import_map_from_settings: Option<ModuleSpecifier>, watched_files: HashMap<ModuleSpecifier, ConfigWatchedFileType>, } @@ -1584,9 +1585,11 @@ impl ConfigData { .is_ok() || member_dir.workspace.has_unstable("sloppy-imports"); let sloppy_imports_resolver = unstable_sloppy_imports.then(|| { - Arc::new(SloppyImportsResolver::new_without_stat_cache(Arc::new( - deno_runtime::deno_fs::RealFs, - ))) + Arc::new(CliSloppyImportsResolver::new( + SloppyImportsCachedFs::new_without_stat_cache(Arc::new( + deno_runtime::deno_fs::RealFs, + )), + )) }); let resolver = Arc::new(resolver); let lint_rule_provider = LintRuleProvider::new( diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs index 1aebaf56f..e57681f3f 100644 --- a/cli/lsp/diagnostics.rs +++ b/cli/lsp/diagnostics.rs @@ -19,8 +19,8 @@ use super::urls::LspUrlMap; use crate::graph_util; use crate::graph_util::enhanced_resolution_error_message; use crate::lsp::lsp_custom::DiagnosticBatchNotificationParams; -use crate::resolver::SloppyImportsResolution; -use crate::resolver::SloppyImportsResolver; +use crate::resolver::CliSloppyImportsResolver; +use crate::resolver::SloppyImportsCachedFs; use crate::tools::lint::CliLinter; use crate::tools::lint::CliLinterOptions; use crate::tools::lint::LintRuleProvider; @@ -40,11 +40,12 @@ use deno_core::unsync::spawn_blocking; use deno_core::unsync::JoinHandle; use deno_core::url::Url; use deno_core::ModuleSpecifier; -use deno_graph::source::ResolutionMode; use deno_graph::source::ResolveError; use deno_graph::Resolution; use deno_graph::ResolutionError; use deno_graph::SpecifierError; +use deno_resolver::sloppy_imports::SloppyImportsResolution; +use deno_resolver::sloppy_imports::SloppyImportsResolutionMode; use deno_runtime::deno_fs; use deno_runtime::deno_node; use deno_runtime::tokio_util::create_basic_runtime; @@ -1263,7 +1264,9 @@ impl DenoDiagnostic { Self::NotInstalledJsr(pkg_req, specifier) => (lsp::DiagnosticSeverity::ERROR, format!("JSR package \"{pkg_req}\" is not installed or doesn't exist."), Some(json!({ "specifier": specifier }))), Self::NotInstalledNpm(pkg_req, specifier) => (lsp::DiagnosticSeverity::ERROR, format!("NPM package \"{pkg_req}\" is not installed or doesn't exist."), Some(json!({ "specifier": specifier }))), Self::NoLocal(specifier) => { - let maybe_sloppy_resolution = SloppyImportsResolver::new(Arc::new(deno_fs::RealFs)).resolve(specifier, ResolutionMode::Execution); + let maybe_sloppy_resolution = CliSloppyImportsResolver::new( + SloppyImportsCachedFs::new(Arc::new(deno_fs::RealFs)) + ).resolve(specifier, SloppyImportsResolutionMode::Execution); let data = maybe_sloppy_resolution.as_ref().map(|res| { json!({ "specifier": specifier, diff --git a/cli/resolver.rs b/cli/resolver.rs index cf4cd8b74..d6e14c39d 100644 --- a/cli/resolver.rs +++ b/cli/resolver.rs @@ -22,7 +22,8 @@ use deno_graph::NpmLoadError; use deno_graph::NpmResolvePkgReqsResult; use deno_npm::resolution::NpmResolutionError; use deno_package_json::PackageJsonDepValue; -use deno_path_util::url_to_file_path; +use deno_resolver::sloppy_imports::SloppyImportsResolutionMode; +use deno_resolver::sloppy_imports::SloppyImportsResolver; use deno_runtime::colors; use deno_runtime::deno_fs; use deno_runtime::deno_fs::FileSystem; @@ -421,13 +422,16 @@ impl CjsResolutionStore { } } +pub type CliSloppyImportsResolver = + SloppyImportsResolver<SloppyImportsCachedFs>; + /// A resolver that takes care of resolution, taking into account loaded /// import map, JSX settings. #[derive(Debug)] pub struct CliGraphResolver { node_resolver: Option<Arc<CliNodeResolver>>, npm_resolver: Option<Arc<dyn CliNpmResolver>>, - sloppy_imports_resolver: Option<Arc<SloppyImportsResolver>>, + sloppy_imports_resolver: Option<Arc<CliSloppyImportsResolver>>, workspace_resolver: Arc<WorkspaceResolver>, maybe_default_jsx_import_source: Option<String>, maybe_default_jsx_import_source_types: Option<String>, @@ -441,7 +445,7 @@ pub struct CliGraphResolver { pub struct CliGraphResolverOptions<'a> { pub node_resolver: Option<Arc<CliNodeResolver>>, pub npm_resolver: Option<Arc<dyn CliNpmResolver>>, - pub sloppy_imports_resolver: Option<Arc<SloppyImportsResolver>>, + pub sloppy_imports_resolver: Option<Arc<CliSloppyImportsResolver>>, pub workspace_resolver: Arc<WorkspaceResolver>, pub bare_node_builtins_enabled: bool, pub maybe_jsx_import_source_config: Option<JsxImportSourceConfig>, @@ -565,7 +569,15 @@ impl Resolver for CliGraphResolver { if let Some(sloppy_imports_resolver) = &self.sloppy_imports_resolver { Ok( sloppy_imports_resolver - .resolve(&specifier, mode) + .resolve( + &specifier, + match mode { + ResolutionMode::Execution => { + SloppyImportsResolutionMode::Execution + } + ResolutionMode::Types => SloppyImportsResolutionMode::Types, + }, + ) .map(|s| s.into_specifier()) .unwrap_or(specifier), ) @@ -847,96 +859,18 @@ impl<'a> deno_graph::source::NpmResolver for WorkerCliNpmGraphResolver<'a> { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum SloppyImportsFsEntry { - File, - Dir, -} - -impl SloppyImportsFsEntry { - pub fn from_fs_stat( - stat: &deno_runtime::deno_io::fs::FsStat, - ) -> Option<SloppyImportsFsEntry> { - if stat.is_file { - Some(SloppyImportsFsEntry::File) - } else if stat.is_directory { - Some(SloppyImportsFsEntry::Dir) - } else { - None - } - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum SloppyImportsResolution { - /// Ex. `./file.js` to `./file.ts` - JsToTs(ModuleSpecifier), - /// Ex. `./file` to `./file.ts` - NoExtension(ModuleSpecifier), - /// Ex. `./dir` to `./dir/index.ts` - Directory(ModuleSpecifier), -} - -impl SloppyImportsResolution { - pub fn as_specifier(&self) -> &ModuleSpecifier { - match self { - Self::JsToTs(specifier) => specifier, - Self::NoExtension(specifier) => specifier, - Self::Directory(specifier) => specifier, - } - } - - pub fn into_specifier(self) -> ModuleSpecifier { - match self { - Self::JsToTs(specifier) => specifier, - Self::NoExtension(specifier) => specifier, - Self::Directory(specifier) => specifier, - } - } - - pub fn as_suggestion_message(&self) -> String { - format!("Maybe {}", self.as_base_message()) - } - - pub fn as_quick_fix_message(&self) -> String { - let message = self.as_base_message(); - let mut chars = message.chars(); - format!( - "{}{}.", - chars.next().unwrap().to_uppercase(), - chars.as_str() - ) - } - - fn as_base_message(&self) -> String { - match self { - SloppyImportsResolution::JsToTs(specifier) => { - let media_type = MediaType::from_specifier(specifier); - format!("change the extension to '{}'", media_type.as_ts_extension()) - } - SloppyImportsResolution::NoExtension(specifier) => { - let media_type = MediaType::from_specifier(specifier); - format!("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()); - format!("specify path to '{}' file in directory instead", file_name) - } - } - } -} - #[derive(Debug)] -pub struct SloppyImportsResolver { - fs: Arc<dyn FileSystem>, - cache: Option<DashMap<PathBuf, Option<SloppyImportsFsEntry>>>, +pub struct SloppyImportsCachedFs { + fs: Arc<dyn deno_fs::FileSystem>, + cache: Option< + DashMap< + PathBuf, + Option<deno_resolver::sloppy_imports::SloppyImportsFsEntry>, + >, + >, } -impl SloppyImportsResolver { +impl SloppyImportsCachedFs { pub fn new(fs: Arc<dyn FileSystem>) -> Self { Self { fs, @@ -947,409 +881,34 @@ impl SloppyImportsResolver { pub fn new_without_stat_cache(fs: Arc<dyn FileSystem>) -> Self { Self { fs, cache: None } } +} - pub fn resolve( +impl deno_resolver::sloppy_imports::SloppyImportResolverFs + for SloppyImportsCachedFs +{ + fn stat_sync( &self, - specifier: &ModuleSpecifier, - mode: ResolutionMode, - ) -> Option<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, - original_media_type: MediaType, - probe_media_type_types: Vec<MediaType>, - reason: SloppyImportsResolutionReason, - ) -> Vec<(PathBuf, SloppyImportsResolutionReason)> { - probe_media_type_types - .into_iter() - .filter(|media_type| *media_type != original_media_type) - .map(|media_type| { - ( - PathBuf::from(format!( - "{}{}", - path_no_ext, - media_type.as_ts_extension() - )), - reason, - ) - }) - .collect::<Vec<_>>() - } - - if specifier.scheme() != "file" { - return None; - } - - let path = url_to_file_path(specifier).ok()?; - - #[derive(Clone, Copy)] - enum SloppyImportsResolutionReason { - JsToTs, - NoExtension, - Directory, - } - - let probe_paths: Vec<(PathBuf, SloppyImportsResolutionReason)> = - match self.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 None, - }; - let path_no_ext = path_without_ext(&path, media_type)?; - media_types_to_paths( - &path_no_ext, - media_type, - probe_media_type_types, - SloppyImportsResolutionReason::JsToTs, - ) - } else { - return None; - } - } - 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 None; - } - 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, - media_type, - 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 None; - } - probe_paths - } - }; - - for (probe_path, reason) in probe_paths { - if self.stat_sync(&probe_path) == Some(SloppyImportsFsEntry::File) { - if let Ok(specifier) = ModuleSpecifier::from_file_path(probe_path) { - match reason { - SloppyImportsResolutionReason::JsToTs => { - return Some(SloppyImportsResolution::JsToTs(specifier)); - } - SloppyImportsResolutionReason::NoExtension => { - return Some(SloppyImportsResolution::NoExtension(specifier)); - } - SloppyImportsResolutionReason::Directory => { - return Some(SloppyImportsResolution::Directory(specifier)); - } - } - } - } - } - - None - } - - fn stat_sync(&self, path: &Path) -> Option<SloppyImportsFsEntry> { + path: &Path, + ) -> Option<deno_resolver::sloppy_imports::SloppyImportsFsEntry> { if let Some(cache) = &self.cache { if let Some(entry) = cache.get(path) { return *entry; } } - let entry = self - .fs - .stat_sync(path) - .ok() - .and_then(|stat| SloppyImportsFsEntry::from_fs_stat(&stat)); + let entry = self.fs.stat_sync(path).ok().and_then(|stat| { + if stat.is_file { + Some(deno_resolver::sloppy_imports::SloppyImportsFsEntry::File) + } else if stat.is_directory { + Some(deno_resolver::sloppy_imports::SloppyImportsFsEntry::Dir) + } else { + None + } + }); + if let Some(cache) = &self.cache { cache.insert(path.to_owned(), entry); } entry } } - -#[cfg(test)] -mod test { - use test_util::TestContext; - - use super::*; - - #[test] - fn test_unstable_sloppy_imports() { - fn resolve(specifier: &ModuleSpecifier) -> Option<SloppyImportsResolution> { - resolve_with_mode(specifier, ResolutionMode::Execution) - } - - fn resolve_types( - specifier: &ModuleSpecifier, - ) -> Option<SloppyImportsResolution> { - resolve_with_mode(specifier, ResolutionMode::Types) - } - - fn resolve_with_mode( - specifier: &ModuleSpecifier, - mode: ResolutionMode, - ) -> Option<SloppyImportsResolution> { - SloppyImportsResolver::new(Arc::new(deno_fs::RealFs)) - .resolve(specifier, mode) - } - - let context = TestContext::default(); - let temp_dir = context.temp_dir().path(); - - // scenarios like resolving ./example.js to ./example.ts - for (ext_from, ext_to) in [("js", "ts"), ("js", "tsx"), ("mjs", "mts")] { - let ts_file = temp_dir.join(format!("file.{}", ext_to)); - ts_file.write(""); - assert_eq!(resolve(&ts_file.url_file()), None); - assert_eq!( - resolve( - &temp_dir - .url_dir() - .join(&format!("file.{}", ext_from)) - .unwrap() - ), - Some(SloppyImportsResolution::JsToTs(ts_file.url_file())), - ); - ts_file.remove_file(); - } - - // no extension scenarios - for ext in ["js", "ts", "js", "tsx", "jsx", "mjs", "mts"] { - let file = temp_dir.join(format!("file.{}", ext)); - file.write(""); - assert_eq!( - resolve( - &temp_dir - .url_dir() - .join("file") // no ext - .unwrap() - ), - Some(SloppyImportsResolution::NoExtension(file.url_file())) - ); - file.remove_file(); - } - - // .ts and .js exists, .js specified (goes to specified) - { - let ts_file = temp_dir.join("file.ts"); - ts_file.write(""); - let js_file = temp_dir.join("file.js"); - js_file.write(""); - assert_eq!(resolve(&js_file.url_file()), None); - } - - // only js exists, .js specified - { - let js_only_file = temp_dir.join("js_only.js"); - js_only_file.write(""); - assert_eq!(resolve(&js_only_file.url_file()), None); - assert_eq!(resolve_types(&js_only_file.url_file()), None); - } - - // resolving a directory to an index file - { - let routes_dir = temp_dir.join("routes"); - routes_dir.create_dir_all(); - let index_file = routes_dir.join("index.ts"); - index_file.write(""); - assert_eq!( - resolve(&routes_dir.url_file()), - Some(SloppyImportsResolution::Directory(index_file.url_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.url_file()), - Some(SloppyImportsResolution::NoExtension(api_file.url_file())), - ); - } - } - - #[test] - fn test_sloppy_import_resolution_suggestion_message() { - // directory - assert_eq!( - SloppyImportsResolution::Directory( - ModuleSpecifier::parse("file:///dir/index.js").unwrap() - ) - .as_suggestion_message(), - "Maybe specify path to 'index.js' file in directory instead" - ); - // no ext - assert_eq!( - SloppyImportsResolution::NoExtension( - ModuleSpecifier::parse("file:///dir/index.mjs").unwrap() - ) - .as_suggestion_message(), - "Maybe add a '.mjs' extension" - ); - // js to ts - assert_eq!( - SloppyImportsResolution::JsToTs( - ModuleSpecifier::parse("file:///dir/index.mts").unwrap() - ) - .as_suggestion_message(), - "Maybe change the extension to '.mts'" - ); - } -} diff --git a/cli/tools/lint/rules/mod.rs b/cli/tools/lint/rules/mod.rs index 2669ffda1..dd723ad15 100644 --- a/cli/tools/lint/rules/mod.rs +++ b/cli/tools/lint/rules/mod.rs @@ -14,7 +14,7 @@ use deno_graph::ModuleGraph; use deno_lint::diagnostic::LintDiagnostic; use deno_lint::rules::LintRule; -use crate::resolver::SloppyImportsResolver; +use crate::resolver::CliSloppyImportsResolver; mod no_sloppy_imports; mod no_slow_types; @@ -144,13 +144,13 @@ impl ConfiguredRules { } pub struct LintRuleProvider { - sloppy_imports_resolver: Option<Arc<SloppyImportsResolver>>, + sloppy_imports_resolver: Option<Arc<CliSloppyImportsResolver>>, workspace_resolver: Option<Arc<WorkspaceResolver>>, } impl LintRuleProvider { pub fn new( - sloppy_imports_resolver: Option<Arc<SloppyImportsResolver>>, + sloppy_imports_resolver: Option<Arc<CliSloppyImportsResolver>>, workspace_resolver: Option<Arc<WorkspaceResolver>>, ) -> Self { Self { diff --git a/cli/tools/lint/rules/no_sloppy_imports.rs b/cli/tools/lint/rules/no_sloppy_imports.rs index 4180be5be..2f6087588 100644 --- a/cli/tools/lint/rules/no_sloppy_imports.rs +++ b/cli/tools/lint/rules/no_sloppy_imports.rs @@ -16,24 +16,25 @@ use deno_lint::diagnostic::LintDiagnosticRange; use deno_lint::diagnostic::LintFix; use deno_lint::diagnostic::LintFixChange; use deno_lint::rules::LintRule; +use deno_resolver::sloppy_imports::SloppyImportsResolution; +use deno_resolver::sloppy_imports::SloppyImportsResolutionMode; use text_lines::LineAndColumnIndex; use crate::graph_util::CliJsrUrlProvider; -use crate::resolver::SloppyImportsResolution; -use crate::resolver::SloppyImportsResolver; +use crate::resolver::CliSloppyImportsResolver; use super::ExtendedLintRule; #[derive(Debug)] pub struct NoSloppyImportsRule { - sloppy_imports_resolver: Option<Arc<SloppyImportsResolver>>, + sloppy_imports_resolver: Option<Arc<CliSloppyImportsResolver>>, // None for making printing out the lint rules easy workspace_resolver: Option<Arc<WorkspaceResolver>>, } impl NoSloppyImportsRule { pub fn new( - sloppy_imports_resolver: Option<Arc<SloppyImportsResolver>>, + sloppy_imports_resolver: Option<Arc<CliSloppyImportsResolver>>, workspace_resolver: Option<Arc<WorkspaceResolver>>, ) -> Self { NoSloppyImportsRule { @@ -172,7 +173,7 @@ impl LintRule for NoSloppyImportsRule { #[derive(Debug)] struct SloppyImportCaptureResolver<'a> { workspace_resolver: &'a WorkspaceResolver, - sloppy_imports_resolver: &'a SloppyImportsResolver, + sloppy_imports_resolver: &'a CliSloppyImportsResolver, captures: RefCell<HashMap<Range, SloppyImportsResolution>>, } @@ -194,7 +195,13 @@ impl<'a> deno_graph::source::Resolver for SloppyImportCaptureResolver<'a> { } | deno_config::workspace::MappedResolution::ImportMap { specifier, .. - } => match self.sloppy_imports_resolver.resolve(&specifier, mode) { + } => match self.sloppy_imports_resolver.resolve( + &specifier, + match mode { + ResolutionMode::Execution => SloppyImportsResolutionMode::Execution, + ResolutionMode::Types => SloppyImportsResolutionMode::Types, + }, + ) { Some(res) => { self .captures diff --git a/cli/tools/registry/mod.rs b/cli/tools/registry/mod.rs index 941514b04..4098d62e3 100644 --- a/cli/tools/registry/mod.rs +++ b/cli/tools/registry/mod.rs @@ -43,7 +43,8 @@ use crate::cache::ParsedSourceCache; use crate::factory::CliFactory; use crate::graph_util::ModuleGraphCreator; use crate::http_util::HttpClient; -use crate::resolver::SloppyImportsResolver; +use crate::resolver::CliSloppyImportsResolver; +use crate::resolver::SloppyImportsCachedFs; use crate::tools::check::CheckOptions; use crate::tools::lint::collect_no_slow_type_diagnostics; use crate::tools::registry::diagnostics::PublishDiagnostic; @@ -108,7 +109,9 @@ pub async fn publish( } let specifier_unfurler = Arc::new(SpecifierUnfurler::new( if cli_options.unstable_sloppy_imports() { - Some(SloppyImportsResolver::new(cli_factory.fs().clone())) + Some(CliSloppyImportsResolver::new(SloppyImportsCachedFs::new( + cli_factory.fs().clone(), + ))) } else { None }, diff --git a/cli/tools/registry/unfurl.rs b/cli/tools/registry/unfurl.rs index 0f5b9fdd3..5ec726a64 100644 --- a/cli/tools/registry/unfurl.rs +++ b/cli/tools/registry/unfurl.rs @@ -12,9 +12,10 @@ use deno_graph::DynamicTemplatePart; use deno_graph::ParserModuleAnalyzer; use deno_graph::TypeScriptReference; use deno_package_json::PackageJsonDepValue; +use deno_resolver::sloppy_imports::SloppyImportsResolutionMode; use deno_runtime::deno_node::is_builtin_node_module; -use crate::resolver::SloppyImportsResolver; +use crate::resolver::CliSloppyImportsResolver; #[derive(Debug, Clone)] pub enum SpecifierUnfurlerDiagnostic { @@ -42,14 +43,14 @@ impl SpecifierUnfurlerDiagnostic { } pub struct SpecifierUnfurler { - sloppy_imports_resolver: Option<SloppyImportsResolver>, + sloppy_imports_resolver: Option<CliSloppyImportsResolver>, workspace_resolver: WorkspaceResolver, bare_node_builtins: bool, } impl SpecifierUnfurler { pub fn new( - sloppy_imports_resolver: Option<SloppyImportsResolver>, + sloppy_imports_resolver: Option<CliSloppyImportsResolver>, workspace_resolver: WorkspaceResolver, bare_node_builtins: bool, ) -> Self { @@ -179,7 +180,7 @@ impl SpecifierUnfurler { let resolved = if let Some(sloppy_imports_resolver) = &self.sloppy_imports_resolver { sloppy_imports_resolver - .resolve(&resolved, deno_graph::source::ResolutionMode::Execution) + .resolve(&resolved, SloppyImportsResolutionMode::Execution) .map(|res| res.into_specifier()) .unwrap_or(resolved) } else { @@ -388,6 +389,8 @@ fn to_range( mod tests { use std::sync::Arc; + use crate::resolver::SloppyImportsCachedFs; + use super::*; use deno_ast::MediaType; use deno_ast::ModuleSpecifier; @@ -455,7 +458,9 @@ mod tests { ); let fs = Arc::new(RealFs); let unfurler = SpecifierUnfurler::new( - Some(SloppyImportsResolver::new(fs)), + Some(CliSloppyImportsResolver::new(SloppyImportsCachedFs::new( + fs, + ))), workspace_resolver, true, ); |