fix(check): support sloppy imports with "compilerOptions.rootDirs" (#27973)

This commit is contained in:
Nayeem Rahman 2025-02-05 23:08:10 +00:00 committed by GitHub
parent 408d581fc1
commit bc8554878e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 3043 additions and 939 deletions

10
Cargo.lock generated
View file

@ -1579,9 +1579,9 @@ dependencies = [
[[package]]
name = "deno_config"
version = "0.47.1"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f7883c48549bab8e446a58c64ee3d106a13052d2ff5e864de765a60260cb02b"
checksum = "6c486df63f7fa0f2142c7eba286c7be87a3cd8c93f66f744fb5853a77cf4347b"
dependencies = [
"boxed_error",
"capacity_builder 0.5.0",
@ -2382,10 +2382,14 @@ dependencies = [
"deno_semver",
"deno_terminal 0.2.0",
"futures",
"import_map",
"indexmap 2.3.0",
"log",
"node_resolver",
"once_cell",
"parking_lot",
"serde",
"serde_json",
"sys_traits",
"test_server",
"thiserror 2.0.3",
@ -8033,6 +8037,8 @@ dependencies = [
"getrandom",
"libc",
"parking_lot",
"serde",
"serde_json",
"windows-sys 0.59.0",
]

View file

@ -54,7 +54,7 @@ deno_ast = { version = "=0.44.0", features = ["transpiling"] }
deno_core = { version = "0.333.0" }
deno_bench_util = { version = "0.183.0", path = "./bench_util" }
deno_config = { version = "=0.47.1", features = ["workspace"] }
deno_config = { version = "=0.48.0", features = ["workspace"] }
deno_lockfile = "=0.24.0"
deno_media_type = { version = "=0.2.5", features = ["module_specifier"] }
deno_npm = "=0.27.2"
@ -155,6 +155,7 @@ hyper = { version = "1.6.0", features = ["full"] }
hyper-rustls = { version = "0.27.2", default-features = false, features = ["http1", "http2", "tls12", "ring"] }
hyper-util = { version = "0.1.10", features = ["tokio", "client", "client-legacy", "server", "server-auto"] }
hyper_v014 = { package = "hyper", version = "0.14.26", features = ["runtime", "http1"] }
import_map = { version = "0.21.0", features = ["ext"] }
indexmap = { version = "2", features = ["serde"] }
ipnet = "2.3"
jsonc-parser = { version = "=0.26.2", features = ["serde"] }

View file

@ -9,7 +9,6 @@ use std::sync::Arc;
use deno_cache_dir::npm::NpmCacheDir;
use deno_config::workspace::Workspace;
use deno_config::workspace::WorkspaceDirectory;
use deno_config::workspace::WorkspaceResolver;
use deno_core::anyhow::Context;
use deno_core::error::AnyError;
use deno_core::futures::FutureExt;
@ -38,6 +37,7 @@ use deno_resolver::factory::ResolverFactoryOptions;
use deno_resolver::factory::SpecifiedImportMapProvider;
use deno_resolver::npm::managed::NpmResolutionCell;
use deno_resolver::npm::DenoInNpmPackageChecker;
use deno_resolver::workspace::WorkspaceResolver;
use deno_runtime::deno_fs;
use deno_runtime::deno_fs::RealFs;
use deno_runtime::deno_permissions::Permissions;
@ -97,7 +97,6 @@ use crate::resolver::CliDenoResolver;
use crate::resolver::CliNpmGraphResolver;
use crate::resolver::CliNpmReqResolver;
use crate::resolver::CliResolver;
use crate::resolver::CliSloppyImportsResolver;
use crate::resolver::FoundPackageJsonDepFlag;
use crate::standalone::binary::DenoCompileBinaryWriter;
use crate::sys::CliSys;
@ -160,7 +159,8 @@ struct CliSpecifiedImportMapProvider {
impl SpecifiedImportMapProvider for CliSpecifiedImportMapProvider {
async fn get(
&self,
) -> Result<Option<deno_config::workspace::SpecifiedImportMap>, AnyError> {
) -> Result<Option<deno_resolver::workspace::SpecifiedImportMap>, AnyError>
{
async fn resolve_import_map_value_from_specifier(
specifier: &Url,
file_fetcher: &CliFileFetcher,
@ -189,7 +189,7 @@ impl SpecifiedImportMapProvider for CliSpecifiedImportMapProvider {
.with_context(|| {
format!("Unable to load '{}' import map", specifier)
})?;
Ok(Some(deno_config::workspace::SpecifiedImportMap {
Ok(Some(deno_resolver::workspace::SpecifiedImportMap {
base_url: specifier,
value,
}))
@ -199,7 +199,7 @@ impl SpecifiedImportMapProvider for CliSpecifiedImportMapProvider {
self.workspace_external_import_map_loader.get_or_load()?
{
let path_url = deno_path_util::url_from_file_path(&import_map.path)?;
Ok(Some(deno_config::workspace::SpecifiedImportMap {
Ok(Some(deno_resolver::workspace::SpecifiedImportMap {
base_url: path_url,
value: import_map.value.clone(),
}))
@ -646,7 +646,6 @@ impl CliFactory {
ResolverFactoryOptions {
conditions_from_resolution_mode: Default::default(),
node_resolution_cache: Some(Arc::new(NodeResolutionThreadLocalCache)),
no_sloppy_imports_cache: false,
npm_system_info: self.flags.subcommand.npm_system_info(),
specified_import_map: Some(Box::new(CliSpecifiedImportMapProvider {
cli_options: self.cli_options()?.clone(),
@ -663,7 +662,7 @@ impl CliFactory {
DenoSubcommand::Publish(_) => {
// the node_modules directory is not published to jsr, so resolve
// dependencies via the package.json rather than using node resolution
Some(deno_config::workspace::PackageJsonDepResolution::Enabled)
Some(deno_resolver::workspace::PackageJsonDepResolution::Enabled)
}
_ => None,
},
@ -672,12 +671,6 @@ impl CliFactory {
})
}
pub fn sloppy_imports_resolver(
&self,
) -> Result<Option<&Arc<CliSloppyImportsResolver>>, AnyError> {
self.resolver_factory()?.sloppy_imports_resolver()
}
pub fn workspace(&self) -> Result<&Arc<Workspace>, AnyError> {
Ok(&self.workspace_directory()?.workspace)
}
@ -790,10 +783,9 @@ impl CliFactory {
}
pub async fn lint_rule_provider(&self) -> Result<LintRuleProvider, AnyError> {
Ok(LintRuleProvider::new(
self.sloppy_imports_resolver()?.cloned(),
Some(self.workspace_resolver().await?.clone()),
))
Ok(LintRuleProvider::new(Some(
self.workspace_resolver().await?.clone(),
)))
}
pub async fn node_resolver(&self) -> Result<&Arc<CliNodeResolver>, AnyError> {

View file

@ -34,8 +34,7 @@ use deno_graph::SpecifierError;
use deno_graph::WorkspaceFastCheckOption;
use deno_path_util::url_to_file_path;
use deno_resolver::npm::DenoInNpmPackageChecker;
use deno_resolver::sloppy_imports::SloppyImportsCachedFs;
use deno_resolver::sloppy_imports::SloppyImportsResolutionKind;
use deno_resolver::workspace::sloppy_imports_resolve;
use deno_runtime::deno_node;
use deno_runtime::deno_permissions::PermissionsContainer;
use deno_semver::jsr::JsrDepPackageReq;
@ -62,7 +61,6 @@ use crate::npm::CliNpmResolver;
use crate::resolver::CliCjsTracker;
use crate::resolver::CliNpmGraphResolver;
use crate::resolver::CliResolver;
use crate::resolver::CliSloppyImportsResolver;
use crate::sys::CliSys;
use crate::tools::check;
use crate::tools::check::CheckError;
@ -949,11 +947,14 @@ pub fn maybe_additional_sloppy_imports_message(
sys: &CliSys,
specifier: &ModuleSpecifier,
) -> Option<String> {
let (resolved, sloppy_reason) = sloppy_imports_resolve(
specifier,
deno_resolver::workspace::ResolutionKind::Execution,
sys.clone(),
)?;
Some(format!(
"{} {}",
CliSloppyImportsResolver::new(SloppyImportsCachedFs::new(sys.clone()))
.resolve(specifier, SloppyImportsResolutionKind::Execution)?
.as_suggestion_message(),
sloppy_reason.suggestion_message_for_specifier(&resolved),
RUN_WITH_SLOPPY_IMPORTS_MSG
))
}

View file

@ -3,8 +3,8 @@
use std::borrow::Cow;
use std::collections::BTreeMap;
use deno_config::workspace::PackageJsonDepResolution;
use deno_media_type::MediaType;
use deno_resolver::workspace::PackageJsonDepResolution;
use deno_runtime::deno_permissions::PermissionsOptions;
use deno_runtime::deno_telemetry::OtelConfig;
use deno_semver::Version;

View file

@ -9,7 +9,6 @@ use std::path::Path;
use deno_ast::SourceRange;
use deno_ast::SourceRangedForSpanned;
use deno_ast::SourceTextInfo;
use deno_config::workspace::MappedResolution;
use deno_core::error::AnyError;
use deno_core::serde::Deserialize;
use deno_core::serde::Serialize;
@ -20,6 +19,7 @@ use deno_error::JsErrorBox;
use deno_lint::diagnostic::LintDiagnosticRange;
use deno_path_util::url_to_file_path;
use deno_resolver::npm::managed::NpmResolutionCell;
use deno_resolver::workspace::MappedResolution;
use deno_runtime::deno_node::PathClean;
use deno_semver::jsr::JsrPackageNvReference;
use deno_semver::jsr::JsrPackageReqReference;
@ -1348,11 +1348,10 @@ impl CodeActionCollection {
let npm_ref = if let Ok(resolution) = workspace_resolver.resolve(
&dep_key,
document.specifier(),
deno_config::workspace::ResolutionKind::Execution,
deno_resolver::workspace::ResolutionKind::Execution,
) {
let specifier = match resolution {
MappedResolution::Normal { specifier, .. }
| MappedResolution::ImportMap { specifier, .. } => specifier,
MappedResolution::Normal { specifier, .. } => specifier,
_ => {
return None;
}

View file

@ -21,16 +21,12 @@ use deno_config::deno_json::TsConfig;
use deno_config::deno_json::TsConfigWithIgnoredOptions;
use deno_config::glob::FilePatterns;
use deno_config::glob::PathOrPatternSet;
use deno_config::workspace::CreateResolverOptions;
use deno_config::workspace::PackageJsonDepResolution;
use deno_config::workspace::SpecifiedImportMap;
use deno_config::workspace::VendorEnablement;
use deno_config::workspace::Workspace;
use deno_config::workspace::WorkspaceCache;
use deno_config::workspace::WorkspaceDirectory;
use deno_config::workspace::WorkspaceDirectoryEmptyOptions;
use deno_config::workspace::WorkspaceDiscoverOptions;
use deno_config::workspace::WorkspaceResolver;
use deno_core::anyhow::anyhow;
use deno_core::error::AnyError;
use deno_core::parking_lot::Mutex;
@ -49,7 +45,12 @@ use deno_npm::npm_rc::ResolvedNpmRc;
use deno_package_json::PackageJsonCache;
use deno_path_util::url_to_file_path;
use deno_resolver::npmrc::discover_npmrc_from_workspace;
use deno_resolver::sloppy_imports::SloppyImportsCachedFs;
use deno_resolver::workspace::CreateResolverOptions;
use deno_resolver::workspace::FsCacheOptions;
use deno_resolver::workspace::PackageJsonDepResolution;
use deno_resolver::workspace::SloppyImportsOptions;
use deno_resolver::workspace::SpecifiedImportMap;
use deno_resolver::workspace::WorkspaceResolver;
use deno_runtime::deno_node::PackageJson;
use indexmap::IndexSet;
use lsp_types::ClientCapabilities;
@ -65,7 +66,6 @@ use crate::args::LintFlags;
use crate::args::LintOptions;
use crate::file_fetcher::CliFileFetcher;
use crate::lsp::logging::lsp_warn;
use crate::resolver::CliSloppyImportsResolver;
use crate::sys::CliSys;
use crate::tools::lint::CliLinter;
use crate::tools::lint::CliLinterOptions;
@ -1206,7 +1206,6 @@ pub struct ConfigData {
pub lockfile: Option<Arc<CliLockfile>>,
pub npmrc: Option<Arc<ResolvedNpmRc>>,
pub resolver: Arc<WorkspaceResolver<CliSys>>,
pub sloppy_imports_resolver: Option<Arc<CliSloppyImportsResolver>>,
pub import_map_from_settings: Option<ModuleSpecifier>,
pub unstable: BTreeSet<String>,
watched_files: HashMap<ModuleSpecifier, ConfigWatchedFileType>,
@ -1569,35 +1568,52 @@ impl ConfigData {
None
}
};
let resolver = member_dir
let unstable = member_dir
.workspace
.create_resolver(
CliSys::default(),
CreateResolverOptions {
pkg_json_dep_resolution,
specified_import_map,
.unstable_features()
.iter()
.chain(settings.unstable.as_deref())
.cloned()
.collect::<BTreeSet<_>>();
let unstable_sloppy_imports = std::env::var("DENO_UNSTABLE_SLOPPY_IMPORTS")
.is_ok()
|| unstable.contains("sloppy-imports");
let resolver = WorkspaceResolver::from_workspace(
&member_dir.workspace,
CliSys::default(),
CreateResolverOptions {
pkg_json_dep_resolution,
specified_import_map,
sloppy_imports_options: if unstable_sloppy_imports {
SloppyImportsOptions::Enabled
} else {
SloppyImportsOptions::Disabled
},
fs_cache_options: FsCacheOptions::Disabled,
},
)
.inspect_err(|err| {
lsp_warn!(
" Failed to load resolver: {}",
err // will contain the specifier
);
})
.ok()
.unwrap_or_else(|| {
// create a dummy resolver
WorkspaceResolver::new_raw(
scope.clone(),
None,
member_dir.workspace.resolver_jsr_pkgs().collect(),
member_dir.workspace.package_jsons().cloned().collect(),
pkg_json_dep_resolution,
Default::default(),
Default::default(),
Default::default(),
Default::default(),
CliSys::default(),
)
.inspect_err(|err| {
lsp_warn!(
" Failed to load resolver: {}",
err // will contain the specifier
);
})
.ok()
.unwrap_or_else(|| {
// create a dummy resolver
WorkspaceResolver::new_raw(
scope.clone(),
None,
member_dir.workspace.resolver_jsr_pkgs().collect(),
member_dir.workspace.package_jsons().cloned().collect(),
pkg_json_dep_resolution,
Default::default(),
Default::default(),
CliSys::default(),
)
});
});
if !resolver.diagnostics().is_empty() {
lsp_warn!(
" Resolver diagnostics:\n{}",
@ -1609,26 +1625,8 @@ impl ConfigData {
.join("\n")
);
}
let unstable = member_dir
.workspace
.unstable_features()
.iter()
.chain(settings.unstable.as_deref())
.cloned()
.collect::<BTreeSet<_>>();
let unstable_sloppy_imports = std::env::var("DENO_UNSTABLE_SLOPPY_IMPORTS")
.is_ok()
|| unstable.contains("sloppy-imports");
let sloppy_imports_resolver = unstable_sloppy_imports.then(|| {
Arc::new(CliSloppyImportsResolver::new(
SloppyImportsCachedFs::new_without_stat_cache(CliSys::default()),
))
});
let resolver = Arc::new(resolver);
let lint_rule_provider = LintRuleProvider::new(
sloppy_imports_resolver.clone(),
Some(resolver.clone()),
);
let lint_rule_provider = LintRuleProvider::new(Some(resolver.clone()));
let lint_options = LintOptions::resolve(
member_dir.dir_path(),
@ -1676,7 +1674,6 @@ impl ConfigData {
canonicalized_scope,
member_dir,
resolver,
sloppy_imports_resolver,
fmt_config,
lint_config,
test_config,

View file

@ -27,9 +27,7 @@ use deno_graph::Resolution;
use deno_graph::ResolutionError;
use deno_graph::SpecifierError;
use deno_lint::linter::LintConfig as DenoLintConfig;
use deno_resolver::sloppy_imports::SloppyImportsCachedFs;
use deno_resolver::sloppy_imports::SloppyImportsResolution;
use deno_resolver::sloppy_imports::SloppyImportsResolutionKind;
use deno_resolver::workspace::sloppy_imports_resolve;
use deno_runtime::deno_node;
use deno_runtime::tokio_util::create_basic_runtime;
use deno_semver::jsr::JsrPackageReqReference;
@ -64,7 +62,6 @@ use crate::graph_util;
use crate::graph_util::enhanced_resolution_error_message;
use crate::lsp::logging::lsp_warn;
use crate::lsp::lsp_custom::DiagnosticBatchNotificationParams;
use crate::resolver::CliSloppyImportsResolver;
use crate::sys::CliSys;
use crate::tools::lint::CliLinter;
use crate::tools::lint::CliLinterOptions;
@ -1013,7 +1010,7 @@ fn generate_lint_diagnostics(
Arc::new(LintConfig::new_with_base(PathBuf::from("/"))),
Arc::new(CliLinter::new(CliLinterOptions {
configured_rules: {
let lint_rule_provider = LintRuleProvider::new(None, None);
let lint_rule_provider = LintRuleProvider::new(None);
lint_rule_provider.resolve_lint_rules(Default::default(), None)
},
fix: false,
@ -1443,14 +1440,14 @@ impl DenoDiagnostic {
pub fn to_lsp_diagnostic(&self, range: &lsp::Range) -> lsp::Diagnostic {
fn no_local_message(
specifier: &ModuleSpecifier,
maybe_sloppy_resolution: Option<&SloppyImportsResolution>,
suggestion_message: Option<String>,
) -> String {
let mut message = format!(
"Unable to load a local module: {}\n",
to_percent_decoded_str(specifier.as_ref())
);
if let Some(res) = maybe_sloppy_resolution {
message.push_str(&res.as_suggestion_message());
if let Some(suggestion_message) = suggestion_message {
message.push_str(&suggestion_message);
message.push('.');
} else {
message.push_str("Please check the file path.");
@ -1467,17 +1464,15 @@ 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 = CliSloppyImportsResolver::new(
SloppyImportsCachedFs::new(CliSys::default())
).resolve(specifier, SloppyImportsResolutionKind::Execution);
let data = maybe_sloppy_resolution.as_ref().map(|res| {
let sloppy_resolution = sloppy_imports_resolve(specifier, deno_resolver::workspace::ResolutionKind::Execution, CliSys::default());
let data = sloppy_resolution.as_ref().map(|(resolved, sloppy_reason)| {
json!({
"specifier": specifier,
"to": res.as_specifier(),
"message": res.as_quick_fix_message(),
"to": resolved,
"message": sloppy_reason.quick_fix_message_for_specifier(resolved),
})
});
(lsp::DiagnosticSeverity::ERROR, no_local_message(specifier, maybe_sloppy_resolution.as_ref()), data)
(lsp::DiagnosticSeverity::ERROR, no_local_message(specifier, sloppy_resolution.as_ref().map(|(resolved, sloppy_reason)| sloppy_reason.suggestion_message_for_specifier(resolved))), data)
},
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) => {

View file

@ -12,8 +12,6 @@ use deno_ast::MediaType;
use deno_cache_dir::npm::NpmCacheDir;
use deno_cache_dir::HttpCache;
use deno_config::deno_json::JsxImportSourceConfig;
use deno_config::workspace::PackageJsonDepResolution;
use deno_config::workspace::WorkspaceResolver;
use deno_core::parking_lot::Mutex;
use deno_core::url::Url;
use deno_graph::GraphImport;
@ -29,6 +27,8 @@ use deno_resolver::npm::CreateInNpmPkgCheckerOptions;
use deno_resolver::npm::DenoInNpmPackageChecker;
use deno_resolver::npm::NpmReqResolverOptions;
use deno_resolver::npmrc::create_default_npmrc;
use deno_resolver::workspace::PackageJsonDepResolution;
use deno_resolver::workspace::WorkspaceResolver;
use deno_resolver::DenoResolverOptions;
use deno_resolver::NodeAndNpmReqResolver;
use deno_semver::jsr::JsrPackageReqReference;
@ -844,9 +844,6 @@ impl<'a> ResolverFactory<'a> {
}
_ => None,
},
sloppy_imports_resolver: self
.config_data
.and_then(|d| d.sloppy_imports_resolver.clone()),
workspace_resolver: self
.config_data
.map(|d| d.resolver.clone())
@ -860,6 +857,8 @@ impl<'a> ResolverFactory<'a> {
PackageJsonDepResolution::Disabled,
Default::default(),
Default::default(),
Default::default(),
Default::default(),
self.sys.clone(),
))
}),

View file

@ -4,8 +4,6 @@ use std::sync::Arc;
use async_trait::async_trait;
use dashmap::DashSet;
use deno_config::workspace::MappedResolutionDiagnostic;
use deno_config::workspace::MappedResolutionError;
use deno_core::ModuleSpecifier;
use deno_error::JsErrorBox;
use deno_graph::source::ResolveError;
@ -14,8 +12,8 @@ use deno_graph::NpmLoadError;
use deno_graph::NpmResolvePkgReqsResult;
use deno_npm::resolution::NpmResolutionError;
use deno_resolver::npm::DenoInNpmPackageChecker;
use deno_resolver::sloppy_imports::SloppyImportsCachedFs;
use deno_resolver::sloppy_imports::SloppyImportsResolver;
use deno_resolver::workspace::MappedResolutionDiagnostic;
use deno_resolver::workspace::MappedResolutionError;
use deno_runtime::colors;
use deno_runtime::deno_node::is_builtin_node_module;
use deno_semver::package::PackageReq;
@ -35,14 +33,10 @@ pub type CliCjsTracker =
deno_resolver::cjs::CjsTracker<DenoInNpmPackageChecker, CliSys>;
pub type CliIsCjsResolver =
deno_resolver::cjs::IsCjsResolver<DenoInNpmPackageChecker, CliSys>;
pub type CliSloppyImportsCachedFs = SloppyImportsCachedFs<CliSys>;
pub type CliSloppyImportsResolver =
SloppyImportsResolver<CliSloppyImportsCachedFs>;
pub type CliDenoResolver = deno_resolver::DenoResolver<
DenoInNpmPackageChecker,
DenoIsBuiltInNodeModuleChecker,
CliNpmResolver,
CliSloppyImportsCachedFs,
CliSys,
>;
pub type CliNpmReqResolver = deno_resolver::npm::NpmReqResolver<

View file

@ -7,9 +7,7 @@ use std::sync::Arc;
use std::sync::OnceLock;
use deno_cache_dir::npm::NpmCacheDir;
use deno_config::workspace::MappedResolution;
use deno_config::workspace::ResolverWorkspaceJsrPackage;
use deno_config::workspace::WorkspaceResolver;
use deno_core::error::AnyError;
use deno_core::error::ModuleLoaderError;
use deno_core::futures::future::LocalBoxFuture;
@ -59,9 +57,9 @@ use deno_resolver::npm::NpmReqResolver;
use deno_resolver::npm::NpmReqResolverOptions;
use deno_resolver::npm::NpmResolver;
use deno_resolver::npm::NpmResolverCreateOptions;
use deno_resolver::sloppy_imports::SloppyImportsCachedFs;
use deno_resolver::sloppy_imports::SloppyImportsResolutionKind;
use deno_resolver::sloppy_imports::SloppyImportsResolver;
use deno_resolver::workspace::MappedResolution;
use deno_resolver::workspace::SloppyImportsOptions;
use deno_resolver::workspace::WorkspaceResolver;
use deno_runtime::code_cache::CodeCache;
use deno_runtime::deno_fs::FileSystem;
use deno_runtime::deno_node::create_host_defined_options;
@ -107,8 +105,6 @@ struct SharedModuleLoaderState {
npm_module_loader: Arc<DenoRtNpmModuleLoader>,
npm_registry_permission_checker: NpmRegistryReadPermissionChecker<DenoRtSys>,
npm_req_resolver: Arc<DenoRtNpmReqResolver>,
sloppy_imports_resolver:
Option<SloppyImportsResolver<SloppyImportsCachedFs<DenoRtSys>>>,
vfs: Arc<FileBackedVfs>,
workspace_resolver: WorkspaceResolver<DenoRtSys>,
}
@ -210,7 +206,7 @@ impl ModuleLoader for EmbeddedModuleLoader {
let mapped_resolution = self.shared.workspace_resolver.resolve(
raw_specifier,
&referrer,
deno_config::workspace::ResolutionKind::Execution,
deno_resolver::workspace::ResolutionKind::Execution,
);
match mapped_resolution {
@ -289,8 +285,7 @@ impl ModuleLoader for EmbeddedModuleLoader {
)
}
},
Ok(MappedResolution::Normal { specifier, .. })
| Ok(MappedResolution::ImportMap { specifier, .. }) => {
Ok(MappedResolution::Normal { specifier, .. }) => {
if let Ok(reference) =
NpmPackageReqReference::from_specifier(&specifier)
{
@ -322,18 +317,6 @@ impl ModuleLoader for EmbeddedModuleLoader {
}
}
// do sloppy imports resolution if enabled
let specifier = if let Some(sloppy_imports_resolver) =
&self.shared.sloppy_imports_resolver
{
sloppy_imports_resolver
.resolve(&specifier, SloppyImportsResolutionKind::Execution)
.map(|s| s.into_specifier())
.unwrap_or(specifier)
} else {
specifier
};
Ok(
self
.shared
@ -832,10 +815,6 @@ pub async fn run(
pkg_json_resolver.clone(),
sys.clone(),
));
let sloppy_imports_resolver =
metadata.unstable_config.sloppy_imports.then(|| {
SloppyImportsResolver::new(SloppyImportsCachedFs::new(sys.clone()))
});
let workspace_resolver = {
let import_map = match metadata.workspace_resolver.import_map {
Some(import_map) => Some(
@ -883,6 +862,12 @@ pub async fn run(
.collect(),
pkg_jsons,
metadata.workspace_resolver.pkg_json_resolution,
if metadata.unstable_config.sloppy_imports {
SloppyImportsOptions::Enabled
} else {
SloppyImportsOptions::Disabled
},
Default::default(),
Default::default(),
Default::default(),
sys.clone(),
@ -915,7 +900,6 @@ pub async fn run(
)),
npm_registry_permission_checker,
npm_req_resolver,
sloppy_imports_resolver,
vfs: vfs.clone(),
workspace_resolver,
}),

View file

@ -16,7 +16,6 @@ use capacity_builder::BytesAppendable;
use deno_ast::MediaType;
use deno_ast::ModuleKind;
use deno_ast::ModuleSpecifier;
use deno_config::workspace::WorkspaceResolver;
use deno_core::anyhow::bail;
use deno_core::anyhow::Context;
use deno_core::error::AnyError;
@ -49,6 +48,7 @@ use deno_npm::resolution::SerializedNpmResolutionSnapshot;
use deno_npm::NpmSystemInfo;
use deno_path_util::url_from_directory_path;
use deno_path_util::url_to_file_path;
use deno_resolver::workspace::WorkspaceResolver;
use indexmap::IndexMap;
use node_resolver::analyze::CjsAnalysis;
use node_resolver::analyze::CjsCodeAnalyzer;

View file

@ -59,21 +59,18 @@ pub async fn info(
let maybe_import_specifier = if let Ok(resolved) = resolver.resolve(
&specifier,
&cwd_url,
deno_config::workspace::ResolutionKind::Execution,
deno_resolver::workspace::ResolutionKind::Execution,
) {
match resolved {
deno_config::workspace::MappedResolution::Normal {
specifier, ..
}
| deno_config::workspace::MappedResolution::ImportMap {
deno_resolver::workspace::MappedResolution::Normal {
specifier,
..
}
| deno_config::workspace::MappedResolution::WorkspaceJsrPackage {
| deno_resolver::workspace::MappedResolution::WorkspaceJsrPackage {
specifier,
..
} => Some(specifier),
deno_config::workspace::MappedResolution::WorkspaceNpmPackage {
deno_resolver::workspace::MappedResolution::WorkspaceNpmPackage {
target_pkg_json,
sub_path,
..
@ -88,7 +85,7 @@ pub async fn info(
)?
.into_url()?,
),
deno_config::workspace::MappedResolution::PackageJson {
deno_resolver::workspace::MappedResolution::PackageJson {
alias,
sub_path,
dep_result,

View file

@ -499,7 +499,7 @@ fn collect_lint_files(
#[allow(clippy::print_stdout)]
pub fn print_rules_list(json: bool, maybe_rules_tags: Option<Vec<String>>) {
let rule_provider = LintRuleProvider::new(None, None);
let rule_provider = LintRuleProvider::new(None);
let mut all_rules = rule_provider.all_rules();
let configured_rules = rule_provider.resolve_lint_rules(
LintRulesConfig {
@ -686,7 +686,7 @@ mod tests {
}
fn get_all_rules() -> Vec<String> {
let rule_provider = LintRuleProvider::new(None, None);
let rule_provider = LintRuleProvider::new(None);
let configured_rules =
rule_provider.resolve_lint_rules(Default::default(), None);
let mut all_rules = configured_rules

View file

@ -7,15 +7,14 @@ use std::sync::Arc;
use deno_ast::ModuleSpecifier;
use deno_config::deno_json::ConfigFile;
use deno_config::deno_json::LintRulesConfig;
use deno_config::workspace::WorkspaceResolver;
use deno_core::anyhow::bail;
use deno_core::error::AnyError;
use deno_graph::ModuleGraph;
use deno_lint::diagnostic::LintDiagnostic;
use deno_lint::rules::LintRule;
use deno_lint::tags;
use deno_resolver::workspace::WorkspaceResolver;
use crate::resolver::CliSloppyImportsResolver;
use crate::sys::CliSys;
mod no_sloppy_imports;
@ -141,19 +140,14 @@ impl ConfiguredRules {
}
pub struct LintRuleProvider {
sloppy_imports_resolver: Option<Arc<CliSloppyImportsResolver>>,
workspace_resolver: Option<Arc<WorkspaceResolver<CliSys>>>,
}
impl LintRuleProvider {
pub fn new(
sloppy_imports_resolver: Option<Arc<CliSloppyImportsResolver>>,
workspace_resolver: Option<Arc<WorkspaceResolver<CliSys>>>,
) -> Self {
Self {
sloppy_imports_resolver,
workspace_resolver,
}
Self { workspace_resolver }
}
pub fn resolve_lint_rules_err_empty(
@ -172,7 +166,6 @@ impl LintRuleProvider {
let deno_lint_rules = deno_lint::rules::get_all_rules();
let cli_lint_rules = vec![CliLintRule(CliLintRuleKind::Extended(
Box::new(no_sloppy_imports::NoSloppyImportsRule::new(
self.sloppy_imports_resolver.clone(),
self.workspace_resolver.clone(),
)),
))];
@ -274,7 +267,7 @@ mod test {
include: None,
tags: None,
};
let rules_provider = LintRuleProvider::new(None, None);
let rules_provider = LintRuleProvider::new(None);
let rules = rules_provider.resolve_lint_rules(rules_config, None);
let mut rule_names = rules
.rules

View file

@ -6,7 +6,6 @@ use std::collections::HashMap;
use std::sync::Arc;
use deno_ast::SourceRange;
use deno_config::workspace::WorkspaceResolver;
use deno_error::JsErrorBox;
use deno_graph::source::ResolutionKind;
use deno_graph::source::ResolveError;
@ -17,31 +16,25 @@ use deno_lint::diagnostic::LintFix;
use deno_lint::diagnostic::LintFixChange;
use deno_lint::rules::LintRule;
use deno_lint::tags;
use deno_resolver::sloppy_imports::SloppyImportsResolution;
use deno_resolver::sloppy_imports::SloppyImportsResolutionKind;
use deno_resolver::workspace::SloppyImportsResolutionReason;
use deno_resolver::workspace::WorkspaceResolver;
use text_lines::LineAndColumnIndex;
use super::ExtendedLintRule;
use crate::graph_util::CliJsrUrlProvider;
use crate::resolver::CliSloppyImportsResolver;
use crate::sys::CliSys;
#[derive(Debug)]
pub struct NoSloppyImportsRule {
sloppy_imports_resolver: Option<Arc<CliSloppyImportsResolver>>,
// None for making printing out the lint rules easy
workspace_resolver: Option<Arc<WorkspaceResolver<CliSys>>>,
}
impl NoSloppyImportsRule {
pub fn new(
sloppy_imports_resolver: Option<Arc<CliSloppyImportsResolver>>,
workspace_resolver: Option<Arc<WorkspaceResolver<CliSys>>>,
) -> Self {
NoSloppyImportsRule {
sloppy_imports_resolver,
workspace_resolver,
}
NoSloppyImportsRule { workspace_resolver }
}
}
@ -54,7 +47,11 @@ impl ExtendedLintRule for NoSloppyImportsRule {
// do sloppy import resolution because sloppy import
// resolution requires knowing about the surrounding files
// in addition to the current one
self.sloppy_imports_resolver.is_none() || self.workspace_resolver.is_none()
let Some(workspace_resolver) = &self.workspace_resolver else {
return true;
};
!workspace_resolver.sloppy_imports_enabled()
&& !workspace_resolver.has_compiler_options_root_dirs()
}
fn help_docs_url(&self) -> Cow<'static, str> {
@ -75,16 +72,12 @@ impl LintRule for NoSloppyImportsRule {
let Some(workspace_resolver) = &self.workspace_resolver else {
return;
};
let Some(sloppy_imports_resolver) = &self.sloppy_imports_resolver else {
return;
};
if context.specifier().scheme() != "file" {
return;
}
let resolver = SloppyImportCaptureResolver {
workspace_resolver,
sloppy_imports_resolver,
captures: Default::default(),
};
@ -102,7 +95,9 @@ impl LintRule for NoSloppyImportsRule {
maybe_npm_resolver: None,
});
for (referrer, sloppy_import) in resolver.captures.borrow_mut().drain() {
for (referrer, (specifier, sloppy_reason)) in
resolver.captures.borrow_mut().drain()
{
let start_range =
context.text_info().loc_to_source_pos(LineAndColumnIndex {
line_index: referrer.range.start.line,
@ -126,10 +121,12 @@ impl LintRule for NoSloppyImportsRule {
custom_docs_url: Some(DOCS_URL.to_string()),
fixes: context
.specifier()
.make_relative(sloppy_import.as_specifier())
.make_relative(&specifier)
.map(|relative| {
vec![LintFix {
description: Cow::Owned(sloppy_import.as_quick_fix_message()),
description: Cow::Owned(
sloppy_reason.quick_fix_message_for_specifier(&specifier),
),
changes: vec![LintFixChange {
new_text: Cow::Owned({
let relative = if relative.starts_with("../") {
@ -176,8 +173,9 @@ impl LintRule for NoSloppyImportsRule {
#[derive(Debug)]
struct SloppyImportCaptureResolver<'a> {
workspace_resolver: &'a WorkspaceResolver<CliSys>,
sloppy_imports_resolver: &'a CliSloppyImportsResolver,
captures: RefCell<HashMap<Range, SloppyImportsResolution>>,
captures: RefCell<
HashMap<Range, (deno_ast::ModuleSpecifier, SloppyImportsResolutionReason)>,
>,
}
impl<'a> deno_graph::source::Resolver for SloppyImportCaptureResolver<'a> {
@ -194,45 +192,37 @@ impl<'a> deno_graph::source::Resolver for SloppyImportCaptureResolver<'a> {
&referrer_range.specifier,
match resolution_kind {
ResolutionKind::Execution => {
deno_config::workspace::ResolutionKind::Execution
deno_resolver::workspace::ResolutionKind::Execution
}
ResolutionKind::Types => {
deno_config::workspace::ResolutionKind::Types
deno_resolver::workspace::ResolutionKind::Types
}
},
)
.map_err(|err| ResolveError::Other(JsErrorBox::from_err(err)))?;
match resolution {
deno_config::workspace::MappedResolution::Normal {
specifier, ..
}
| deno_config::workspace::MappedResolution::ImportMap {
specifier, ..
} => match self.sloppy_imports_resolver.resolve(
&specifier,
match resolution_kind {
ResolutionKind::Execution => SloppyImportsResolutionKind::Execution,
ResolutionKind::Types => SloppyImportsResolutionKind::Types,
},
) {
Some(res) => {
deno_resolver::workspace::MappedResolution::Normal {
specifier,
sloppy_reason,
..
} => {
if let Some(sloppy_reason) = sloppy_reason {
self
.captures
.borrow_mut()
.entry(referrer_range.clone())
.or_insert_with(|| res.clone());
Ok(res.into_specifier())
.or_insert_with(|| (specifier.clone(), sloppy_reason));
}
None => Ok(specifier),
},
deno_config::workspace::MappedResolution::WorkspaceJsrPackage {
Ok(specifier)
}
deno_resolver::workspace::MappedResolution::WorkspaceJsrPackage {
..
}
| deno_config::workspace::MappedResolution::WorkspaceNpmPackage {
| deno_resolver::workspace::MappedResolution::WorkspaceNpmPackage {
..
}
| deno_config::workspace::MappedResolution::PackageJson { .. } => {
| deno_resolver::workspace::MappedResolution::PackageJson { .. } => {
// this error is ignored
Err(ResolveError::Other(JsErrorBox::generic("")))
}

View file

@ -120,7 +120,6 @@ pub async fn publish(
}
let specifier_unfurler = Arc::new(SpecifierUnfurler::new(
cli_factory.sloppy_imports_resolver()?.cloned(),
cli_factory.workspace_resolver().await?.clone(),
cli_options.unstable_bare_node_builtins(),
));

View file

@ -15,9 +15,6 @@ use deno_ast::ParsedSource;
use deno_ast::SourceRange;
use deno_ast::SourceTextInfo;
use deno_ast::SourceTextProvider;
use deno_config::workspace::MappedResolution;
use deno_config::workspace::PackageJsonDepResolution;
use deno_config::workspace::WorkspaceResolver;
use deno_core::anyhow;
use deno_core::ModuleSpecifier;
use deno_graph::DependencyDescriptor;
@ -27,12 +24,13 @@ use deno_graph::StaticDependencyKind;
use deno_graph::TypeScriptReference;
use deno_package_json::PackageJsonDepValue;
use deno_package_json::PackageJsonDepWorkspaceReq;
use deno_resolver::sloppy_imports::SloppyImportsResolutionKind;
use deno_resolver::workspace::MappedResolution;
use deno_resolver::workspace::PackageJsonDepResolution;
use deno_resolver::workspace::WorkspaceResolver;
use deno_runtime::deno_node::is_builtin_node_module;
use deno_semver::Version;
use deno_semver::VersionReq;
use crate::resolver::CliSloppyImportsResolver;
use crate::sys::CliSys;
#[derive(Debug, Clone)]
@ -190,14 +188,12 @@ enum UnfurlSpecifierError {
}
pub struct SpecifierUnfurler {
sloppy_imports_resolver: Option<Arc<CliSloppyImportsResolver>>,
workspace_resolver: Arc<WorkspaceResolver<CliSys>>,
bare_node_builtins: bool,
}
impl SpecifierUnfurler {
pub fn new(
sloppy_imports_resolver: Option<Arc<CliSloppyImportsResolver>>,
workspace_resolver: Arc<WorkspaceResolver<CliSys>>,
bare_node_builtins: bool,
) -> Self {
@ -206,7 +202,6 @@ impl SpecifierUnfurler {
PackageJsonDepResolution::Enabled
);
Self {
sloppy_imports_resolver,
workspace_resolver,
bare_node_builtins,
}
@ -216,7 +211,7 @@ impl SpecifierUnfurler {
&self,
referrer: &ModuleSpecifier,
specifier: &str,
resolution_kind: SloppyImportsResolutionKind,
resolution_kind: deno_resolver::workspace::ResolutionKind,
text_info: &SourceTextInfo,
range: &deno_graph::PositionRange,
diagnostic_reporter: &mut dyn FnMut(SpecifierUnfurlerDiagnostic),
@ -251,16 +246,15 @@ impl SpecifierUnfurler {
&self,
referrer: &ModuleSpecifier,
specifier: &str,
resolution_kind: SloppyImportsResolutionKind,
resolution_kind: deno_resolver::workspace::ResolutionKind,
) -> Result<Option<String>, UnfurlSpecifierError> {
let resolved = if let Ok(resolved) = self.workspace_resolver.resolve(
specifier,
referrer,
resolution_kind.into(),
) {
let resolved = if let Ok(resolved) =
self
.workspace_resolver
.resolve(specifier, referrer, resolution_kind)
{
match resolved {
MappedResolution::Normal { specifier, .. }
| MappedResolution::ImportMap { specifier, .. } => Some(specifier),
MappedResolution::Normal { specifier, .. } => Some(specifier),
MappedResolution::WorkspaceJsrPackage { pkg_req_ref, .. } => {
Some(ModuleSpecifier::parse(&pkg_req_ref.to_string()).unwrap())
}
@ -398,15 +392,6 @@ impl SpecifierUnfurler {
// } else {
// resolved
// };
let resolved =
if let Some(sloppy_imports_resolver) = &self.sloppy_imports_resolver {
sloppy_imports_resolver
.resolve(&resolved, resolution_kind)
.map(|res| res.into_specifier())
.unwrap_or(resolved)
} else {
resolved
};
let relative_resolved = relative_url(&resolved, referrer);
if relative_resolved == specifier {
Ok(None) // nothing to unfurl
@ -464,7 +449,7 @@ impl SpecifierUnfurler {
let maybe_unfurled = self.unfurl_specifier_reporting_diagnostic(
module_url,
specifier,
SloppyImportsResolutionKind::Execution, // dynamic imports are always execution
deno_resolver::workspace::ResolutionKind::Execution, // dynamic imports are always execution
text_info,
&dep.argument_range,
diagnostic_reporter,
@ -492,7 +477,7 @@ impl SpecifierUnfurler {
let unfurled = self.unfurl_specifier_reporting_diagnostic(
module_url,
specifier,
SloppyImportsResolutionKind::Execution, // dynamic imports are always execution
deno_resolver::workspace::ResolutionKind::Execution, // dynamic imports are always execution
text_info,
&dep.argument_range,
diagnostic_reporter,
@ -538,7 +523,7 @@ impl SpecifierUnfurler {
let analyze_specifier =
|specifier: &str,
range: &deno_graph::PositionRange,
resolution_kind: SloppyImportsResolutionKind,
resolution_kind: deno_resolver::workspace::ResolutionKind,
text_changes: &mut Vec<deno_ast::TextChange>,
diagnostic_reporter: &mut dyn FnMut(SpecifierUnfurlerDiagnostic)| {
if let Some(unfurled) = self.unfurl_specifier_reporting_diagnostic(
@ -559,18 +544,18 @@ impl SpecifierUnfurler {
match dep {
DependencyDescriptor::Static(dep) => {
let resolution_kind = if parsed_source.media_type().is_declaration() {
SloppyImportsResolutionKind::Types
deno_resolver::workspace::ResolutionKind::Types
} else {
match dep.kind {
StaticDependencyKind::Export
| StaticDependencyKind::Import
| StaticDependencyKind::ExportEquals
| StaticDependencyKind::ImportEquals => {
SloppyImportsResolutionKind::Execution
deno_resolver::workspace::ResolutionKind::Execution
}
StaticDependencyKind::ExportType
| StaticDependencyKind::ImportType => {
SloppyImportsResolutionKind::Types
deno_resolver::workspace::ResolutionKind::Types
}
}
};
@ -616,7 +601,7 @@ impl SpecifierUnfurler {
analyze_specifier(
&specifier_with_range.text,
&specifier_with_range.range,
SloppyImportsResolutionKind::Types,
deno_resolver::workspace::ResolutionKind::Types,
&mut text_changes,
diagnostic_reporter,
);
@ -625,7 +610,7 @@ impl SpecifierUnfurler {
analyze_specifier(
&jsdoc.specifier.text,
&jsdoc.specifier.range,
SloppyImportsResolutionKind::Types,
deno_resolver::workspace::ResolutionKind::Types,
&mut text_changes,
diagnostic_reporter,
);
@ -634,7 +619,7 @@ impl SpecifierUnfurler {
analyze_specifier(
&specifier_with_range.text,
&specifier_with_range.range,
SloppyImportsResolutionKind::Execution,
deno_resolver::workspace::ResolutionKind::Execution,
&mut text_changes,
diagnostic_reporter,
);
@ -643,7 +628,7 @@ impl SpecifierUnfurler {
analyze_specifier(
&specifier_with_range.text,
&specifier_with_range.range,
SloppyImportsResolutionKind::Types,
deno_resolver::workspace::ResolutionKind::Types,
&mut text_changes,
diagnostic_reporter,
);
@ -700,7 +685,7 @@ mod tests {
use deno_config::workspace::ResolverWorkspaceJsrPackage;
use deno_core::serde_json::json;
use deno_core::url::Url;
use deno_resolver::sloppy_imports::SloppyImportsCachedFs;
use deno_resolver::workspace::SloppyImportsOptions;
use deno_runtime::deno_node::PackageJson;
use deno_semver::Version;
use import_map::ImportMapWithDiagnostics;
@ -760,18 +745,14 @@ mod tests {
exports: IndexMap::from([(".".to_string(), "mod.ts".to_string())]),
}],
vec![Arc::new(package_json)],
deno_config::workspace::PackageJsonDepResolution::Enabled,
deno_resolver::workspace::PackageJsonDepResolution::Enabled,
SloppyImportsOptions::Enabled,
Default::default(),
Default::default(),
Default::default(),
CliSys::default(),
);
let unfurler = SpecifierUnfurler::new(
Some(Arc::new(CliSloppyImportsResolver::new(
SloppyImportsCachedFs::new(CliSys::default()),
))),
Arc::new(workspace_resolver),
true,
);
let unfurler = SpecifierUnfurler::new(Arc::new(workspace_resolver), true);
// Unfurling TS file should apply changes.
{
@ -926,18 +907,14 @@ export type * from "./c.d.ts";
Arc::new(pkg_json_subtract),
Arc::new(pkg_json_publishing),
],
deno_config::workspace::PackageJsonDepResolution::Enabled,
deno_resolver::workspace::PackageJsonDepResolution::Enabled,
Default::default(),
Default::default(),
Default::default(),
Default::default(),
sys.clone(),
);
let unfurler = SpecifierUnfurler::new(
Some(Arc::new(CliSloppyImportsResolver::new(
SloppyImportsCachedFs::new(sys),
))),
Arc::new(workspace_resolver),
true,
);
let unfurler = SpecifierUnfurler::new(Arc::new(workspace_resolver), true);
{
let source_code = r#"import add from "add";

View file

@ -33,13 +33,18 @@ deno_path_util.workspace = true
deno_semver.workspace = true
deno_terminal.workspace = true
futures.workspace = true
import_map.workspace = true
indexmap.workspace = true
log.workspace = true
node_resolver.workspace = true
once_cell.workspace = true
parking_lot.workspace = true
serde.workspace = true
serde_json.workspace = true
sys_traits.workspace = true
thiserror.workspace = true
url.workspace = true
[dev-dependencies]
sys_traits = { workspace = true, features = ["memory", "real", "serde_json"] }
test_util.workspace = true

View file

@ -12,7 +12,6 @@ use deno_cache_dir::HttpCacheRc;
use deno_cache_dir::LocalHttpCache;
use deno_config::deno_json::NodeModulesDirMode;
use deno_config::workspace::FolderConfigs;
use deno_config::workspace::PackageJsonDepResolution;
use deno_config::workspace::VendorEnablement;
use deno_config::workspace::WorkspaceDirectory;
use deno_config::workspace::WorkspaceDirectoryEmptyOptions;
@ -61,12 +60,13 @@ use crate::npm::NpmResolverCreateOptions;
use crate::npmrc::discover_npmrc_from_workspace;
use crate::npmrc::NpmRcDiscoverError;
use crate::npmrc::ResolvedNpmRcRc;
use crate::sloppy_imports::SloppyImportsCachedFs;
use crate::sloppy_imports::SloppyImportsResolver;
use crate::sloppy_imports::SloppyImportsResolverRc;
use crate::sync::new_rc;
use crate::sync::MaybeSend;
use crate::sync::MaybeSync;
use crate::workspace::FsCacheOptions;
use crate::workspace::PackageJsonDepResolution;
use crate::workspace::SloppyImportsOptions;
use crate::workspace::WorkspaceResolver;
use crate::DefaultDenoResolverRc;
use crate::DenoResolver;
use crate::DenoResolverOptions;
@ -133,7 +133,7 @@ pub trait SpecifiedImportMapProvider:
{
async fn get(
&self,
) -> Result<Option<deno_config::workspace::SpecifiedImportMap>, anyhow::Error>;
) -> Result<Option<crate::workspace::SpecifiedImportMap>, anyhow::Error>;
}
#[derive(Debug, Clone)]
@ -560,7 +560,6 @@ impl<TSys: WorkspaceFactorySys> WorkspaceFactory<TSys> {
#[derive(Debug, Default)]
pub struct ResolverFactoryOptions {
pub conditions_from_resolution_mode: ConditionsFromResolutionMode,
pub no_sloppy_imports_cache: bool,
pub npm_system_info: NpmSystemInfo,
pub node_resolution_cache: Option<node_resolver::NodeResolutionCacheRc>,
pub package_json_cache: Option<node_resolver::PackageJsonCacheRc>,
@ -593,8 +592,6 @@ pub struct ResolverFactory<TSys: WorkspaceFactorySys> {
npm_resolver: Deferred<NpmResolver<TSys>>,
npm_resolution: NpmResolutionCellRc,
pkg_json_resolver: Deferred<PackageJsonResolverRc<TSys>>,
sloppy_imports_resolver:
Deferred<Option<SloppyImportsResolverRc<SloppyImportsCachedFs<TSys>>>>,
workspace_factory: WorkspaceFactoryRc<TSys>,
workspace_resolver: async_once_cell::OnceCell<WorkspaceResolverRc<TSys>>,
}
@ -616,7 +613,6 @@ impl<TSys: WorkspaceFactorySys> ResolverFactory<TSys> {
npm_resolution: Default::default(),
npm_resolver: Default::default(),
pkg_json_resolver: Default::default(),
sloppy_imports_resolver: Default::default(),
workspace_factory,
workspace_resolver: Default::default(),
options,
@ -646,7 +642,6 @@ impl<TSys: WorkspaceFactorySys> ResolverFactory<TSys> {
.workspace_directory()?
.workspace
.vendor_dir_path(),
sloppy_imports_resolver: self.sloppy_imports_resolver()?.cloned(),
workspace_resolver: self.workspace_resolver().await?.clone(),
})))
}
@ -770,38 +765,6 @@ impl<TSys: WorkspaceFactorySys> ResolverFactory<TSys> {
})
}
pub fn sloppy_imports_resolver(
&self,
) -> Result<
Option<&SloppyImportsResolverRc<SloppyImportsCachedFs<TSys>>>,
anyhow::Error,
> {
self
.sloppy_imports_resolver
.get_or_try_init(|| {
let enabled = self.options.unstable_sloppy_imports
|| self
.workspace_factory
.workspace_directory()?
.workspace
.has_unstable("sloppy-imports");
if enabled {
Ok(Some(new_rc(SloppyImportsResolver::new(
if self.options.no_sloppy_imports_cache {
SloppyImportsCachedFs::new_without_stat_cache(
self.workspace_factory.sys.clone(),
)
} else {
SloppyImportsCachedFs::new(self.workspace_factory.sys.clone())
},
))))
} else {
Ok(None)
}
})
.map(|v| v.as_ref())
}
pub async fn workspace_resolver(
&self,
) -> Result<&WorkspaceResolverRc<TSys>, anyhow::Error> {
@ -815,7 +778,7 @@ impl<TSys: WorkspaceFactorySys> ResolverFactory<TSys> {
Some(import_map) => import_map.get().await?,
None => None,
};
let options = deno_config::workspace::CreateResolverOptions {
let options = crate::workspace::CreateResolverOptions {
pkg_json_dep_resolution: match self
.options
.package_json_dep_resolution
@ -834,9 +797,24 @@ impl<TSys: WorkspaceFactorySys> ResolverFactory<TSys> {
}
},
specified_import_map,
sloppy_imports_options: if self.options.unstable_sloppy_imports
|| self
.workspace_factory
.workspace_directory()?
.workspace
.has_unstable("sloppy-imports")
{
SloppyImportsOptions::Enabled
} else {
SloppyImportsOptions::Disabled
},
fs_cache_options: FsCacheOptions::Enabled,
};
let resolver = workspace
.create_resolver(self.workspace_factory.sys.clone(), options)?;
let resolver = WorkspaceResolver::from_workspace(
workspace,
self.workspace_factory.sys.clone(),
options,
)?;
if !resolver.diagnostics().is_empty() {
// todo(dsherret): do not log this in this crate... that should be
// a CLI responsibility

View file

@ -7,11 +7,6 @@ use std::path::PathBuf;
use boxed_error::Boxed;
use deno_cache_dir::npm::NpmCacheDir;
use deno_config::workspace::MappedResolution;
use deno_config::workspace::MappedResolutionDiagnostic;
use deno_config::workspace::MappedResolutionError;
use deno_config::workspace::WorkspaceResolvePkgJsonFolderError;
use deno_config::workspace::WorkspaceResolver;
use deno_error::JsError;
use deno_package_json::PackageJsonDepValue;
use deno_package_json::PackageJsonDepValueParseError;
@ -31,9 +26,6 @@ use npm::NpmReqResolverRc;
use npm::ResolveIfForNpmPackageErrorKind;
use npm::ResolvePkgFolderFromDenoReqError;
use npm::ResolveReqWithSubPathErrorKind;
use sloppy_imports::SloppyImportResolverFs;
use sloppy_imports::SloppyImportsResolutionKind;
use sloppy_imports::SloppyImportsResolverRc;
use sys_traits::FsCanonicalize;
use sys_traits::FsMetadata;
use sys_traits::FsRead;
@ -41,12 +33,18 @@ use sys_traits::FsReadDir;
use thiserror::Error;
use url::Url;
use crate::workspace::MappedResolution;
use crate::workspace::MappedResolutionDiagnostic;
use crate::workspace::MappedResolutionError;
use crate::workspace::WorkspaceResolvePkgJsonFolderError;
use crate::workspace::WorkspaceResolver;
pub mod cjs;
pub mod factory;
pub mod npm;
pub mod npmrc;
pub mod sloppy_imports;
mod sync;
pub mod workspace;
#[allow(clippy::disallowed_types)]
pub type WorkspaceResolverRc<TSys> =
@ -128,7 +126,6 @@ pub struct DenoResolverOptions<
TInNpmPackageChecker: InNpmPackageChecker,
TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker,
TNpmPackageFolderResolver: NpmPackageFolderResolver,
TSloppyImportResolverFs: SloppyImportResolverFs,
TSys: FsCanonicalize + FsMetadata + FsRead + FsReadDir,
> {
pub in_npm_pkg_checker: TInNpmPackageChecker,
@ -140,8 +137,6 @@ pub struct DenoResolverOptions<
TSys,
>,
>,
pub sloppy_imports_resolver:
Option<SloppyImportsResolverRc<TSloppyImportResolverFs>>,
pub workspace_resolver: WorkspaceResolverRc<TSys>,
/// Whether "bring your own node_modules" is enabled where Deno does not
/// setup the node_modules directories automatically, but instead uses
@ -155,14 +150,12 @@ pub type DenoResolverRc<
TInNpmPackageChecker,
TIsBuiltInNodeModuleChecker,
TNpmPackageFolderResolver,
TSloppyImportResolverFs,
TSys,
> = crate::sync::MaybeArc<
DenoResolver<
TInNpmPackageChecker,
TIsBuiltInNodeModuleChecker,
TNpmPackageFolderResolver,
TSloppyImportResolverFs,
TSys,
>,
>;
@ -173,7 +166,6 @@ pub type DefaultDenoResolverRc<TSys> = DenoResolverRc<
npm::DenoInNpmPackageChecker,
node_resolver::DenoIsBuiltInNodeModuleChecker,
npm::NpmResolver<TSys>,
sloppy_imports::SloppyImportsCachedFs<TSys>,
TSys,
>;
@ -184,7 +176,6 @@ pub struct DenoResolver<
TInNpmPackageChecker: InNpmPackageChecker,
TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker,
TNpmPackageFolderResolver: NpmPackageFolderResolver,
TSloppyImportResolverFs: SloppyImportResolverFs,
TSys: FsCanonicalize + FsMetadata + FsRead + FsReadDir,
> {
in_npm_pkg_checker: TInNpmPackageChecker,
@ -196,8 +187,6 @@ pub struct DenoResolver<
TSys,
>,
>,
sloppy_imports_resolver:
Option<SloppyImportsResolverRc<TSloppyImportResolverFs>>,
workspace_resolver: WorkspaceResolverRc<TSys>,
is_byonm: bool,
maybe_vendor_specifier: Option<Url>,
@ -207,14 +196,12 @@ impl<
TInNpmPackageChecker: InNpmPackageChecker,
TIsBuiltInNodeModuleChecker: IsBuiltInNodeModuleChecker,
TNpmPackageFolderResolver: NpmPackageFolderResolver,
TSloppyImportResolverFs: SloppyImportResolverFs,
TSys: FsCanonicalize + FsMetadata + FsRead + FsReadDir,
>
DenoResolver<
TInNpmPackageChecker,
TIsBuiltInNodeModuleChecker,
TNpmPackageFolderResolver,
TSloppyImportResolverFs,
TSys,
>
{
@ -223,14 +210,12 @@ impl<
TInNpmPackageChecker,
TIsBuiltInNodeModuleChecker,
TNpmPackageFolderResolver,
TSloppyImportResolverFs,
TSys,
>,
) -> Self {
Self {
in_npm_pkg_checker: options.in_npm_pkg_checker,
node_and_npm_resolver: options.node_and_req_resolver,
sloppy_imports_resolver: options.sloppy_imports_resolver,
workspace_resolver: options.workspace_resolver,
is_byonm: options.is_byonm,
maybe_vendor_specifier: options
@ -277,33 +262,10 @@ impl<
MappedResolution::Normal {
specifier,
maybe_diagnostic: current_diagnostic,
}
| MappedResolution::ImportMap {
specifier,
maybe_diagnostic: current_diagnostic,
..
} => {
maybe_diagnostic = current_diagnostic;
// do sloppy imports resolution if enabled
if let Some(sloppy_imports_resolver) = &self.sloppy_imports_resolver {
Ok(
sloppy_imports_resolver
.resolve(
&specifier,
match resolution_kind {
NodeResolutionKind::Execution => {
SloppyImportsResolutionKind::Execution
}
NodeResolutionKind::Types => {
SloppyImportsResolutionKind::Types
}
},
)
.map(|s| s.into_specifier())
.unwrap_or(specifier),
)
} else {
Ok(specifier)
}
Ok(specifier)
}
MappedResolution::WorkspaceJsrPackage { specifier, .. } => {
Ok(specifier)

View file

@ -1,582 +0,0 @@
// Copyright 2018-2025 the Deno authors. MIT license.
use std::borrow::Cow;
use std::path::Path;
use std::path::PathBuf;
use deno_media_type::MediaType;
use deno_path_util::url_from_file_path;
use deno_path_util::url_to_file_path;
use sys_traits::FsMetadata;
use sys_traits::FsMetadataValue;
use url::Url;
use crate::sync::MaybeDashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SloppyImportsFsEntry {
File,
Dir,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SloppyImportsResolution {
/// Ex. `./file.js` to `./file.ts`
JsToTs(Url),
/// Ex. `./file` to `./file.ts`
NoExtension(Url),
/// Ex. `./dir` to `./dir/index.ts`
Directory(Url),
}
impl SloppyImportsResolution {
pub fn as_specifier(&self) -> &Url {
match self {
Self::JsToTs(specifier) => specifier,
Self::NoExtension(specifier) => specifier,
Self::Directory(specifier) => specifier,
}
}
pub fn into_specifier(self) -> Url {
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)
}
}
}
}
/// The kind of resolution currently being done.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SloppyImportsResolutionKind {
/// Resolving for code that will be executed.
Execution,
/// Resolving for code that will be used for type information.
Types,
}
impl SloppyImportsResolutionKind {
pub fn is_types(&self) -> bool {
*self == SloppyImportsResolutionKind::Types
}
}
impl From<SloppyImportsResolutionKind>
for deno_config::workspace::ResolutionKind
{
fn from(value: SloppyImportsResolutionKind) -> Self {
match value {
SloppyImportsResolutionKind::Execution => Self::Execution,
SloppyImportsResolutionKind::Types => Self::Types,
}
}
}
pub trait SloppyImportResolverFs {
fn stat_sync(&self, path: &Path) -> Option<SloppyImportsFsEntry>;
fn is_file(&self, path: &Path) -> bool {
self.stat_sync(path) == Some(SloppyImportsFsEntry::File)
}
}
#[allow(clippy::disallowed_types)]
pub type SloppyImportsResolverRc<TSloppyImportResolverFs> =
crate::sync::MaybeArc<SloppyImportsResolver<TSloppyImportResolverFs>>;
#[derive(Debug)]
pub struct SloppyImportsResolver<Fs: SloppyImportResolverFs> {
fs: Fs,
}
impl<Fs: SloppyImportResolverFs> SloppyImportsResolver<Fs> {
pub fn new(fs: Fs) -> Self {
Self { fs }
}
pub fn resolve(
&self,
specifier: &Url,
resolution_kind: SloppyImportsResolutionKind,
) -> 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.fs.stat_sync(&path) {
Some(SloppyImportsFsEntry::File) => {
if resolution_kind.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 resolution_kind.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 resolution_kind.is_types() {
vec![MediaType::Mts, MediaType::Dmts, MediaType::Dts]
} else {
vec![MediaType::Mts]
},
SloppyImportsResolutionReason::JsToTs,
),
MediaType::Cjs => (
if resolution_kind.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::Css
| MediaType::SourceMap => {
return None;
}
MediaType::Unknown => (
if resolution_kind.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 resolution_kind.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.fs.is_file(&probe_path) {
if let Ok(specifier) = url_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
}
}
#[derive(Debug)]
pub struct SloppyImportsCachedFs<TSys: FsMetadata> {
sys: TSys,
cache: Option<MaybeDashMap<PathBuf, Option<SloppyImportsFsEntry>>>,
}
impl<TSys: FsMetadata> SloppyImportsCachedFs<TSys> {
pub fn new(sys: TSys) -> Self {
Self {
sys,
cache: Some(Default::default()),
}
}
pub fn new_without_stat_cache(sys: TSys) -> Self {
Self { sys, cache: None }
}
}
impl<TSys: FsMetadata> SloppyImportResolverFs for SloppyImportsCachedFs<TSys> {
fn stat_sync(&self, path: &Path) -> Option<SloppyImportsFsEntry> {
if let Some(cache) = &self.cache {
if let Some(entry) = cache.get(path) {
return *entry;
}
}
let entry = self.sys.fs_metadata(path).ok().and_then(|stat| {
if stat.file_type().is_file() {
Some(SloppyImportsFsEntry::File)
} else if stat.file_type().is_dir() {
Some(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: &Url) -> Option<SloppyImportsResolution> {
resolve_with_resolution_kind(
specifier,
SloppyImportsResolutionKind::Execution,
)
}
fn resolve_types(specifier: &Url) -> Option<SloppyImportsResolution> {
resolve_with_resolution_kind(
specifier,
SloppyImportsResolutionKind::Types,
)
}
fn resolve_with_resolution_kind(
specifier: &Url,
resolution_kind: SloppyImportsResolutionKind,
) -> Option<SloppyImportsResolution> {
struct RealSloppyImportsResolverFs;
impl SloppyImportResolverFs for RealSloppyImportsResolverFs {
fn stat_sync(&self, path: &Path) -> Option<SloppyImportsFsEntry> {
#[allow(clippy::disallowed_methods)]
let stat = std::fs::metadata(path).ok()?;
if stat.is_dir() {
Some(SloppyImportsFsEntry::Dir)
} else if stat.is_file() {
Some(SloppyImportsFsEntry::File)
} else {
None
}
}
}
SloppyImportsResolver::new(RealSloppyImportsResolverFs)
.resolve(specifier, resolution_kind)
}
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(
Url::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(
Url::parse("file:///dir/index.mjs").unwrap()
)
.as_suggestion_message(),
"Maybe add a '.mjs' extension"
);
// js to ts
assert_eq!(
SloppyImportsResolution::JsToTs(
Url::parse("file:///dir/index.mts").unwrap()
)
.as_suggestion_message(),
"Maybe change the extension to '.mts'"
);
}
}

2812
resolvers/deno/workspace.rs Normal file

File diff suppressed because it is too large Load diff

View file

@ -110,15 +110,6 @@ pub enum NodeResolutionKind {
Types,
}
impl From<NodeResolutionKind> for deno_config::workspace::ResolutionKind {
fn from(value: NodeResolutionKind) -> Self {
match value {
NodeResolutionKind::Execution => Self::Execution,
NodeResolutionKind::Types => Self::Types,
}
}
}
impl NodeResolutionKind {
pub fn is_types(&self) -> bool {
matches!(self, NodeResolutionKind::Types)

View file

@ -0,0 +1,4 @@
{
"args": "check --quiet subdir/mod.ts",
"output": ""
}

View file

@ -0,0 +1,6 @@
{
"compilerOptions": {
"rootDirs": ["subdir", "subdir_types"]
},
"unstable": ["sloppy-imports"]
}

View file

@ -0,0 +1,3 @@
import type { someType } from "./import";
const foo: someType = "";
console.log(foo);

View file

@ -0,0 +1 @@
export type someType = string;