mirror of
https://github.com/denoland/deno.git
synced 2024-11-21 05:42:25 -05:00
refactor(resolver): move more resolution code into deno_resolver (#26873)
Follow-up to cjs refactor. This moves most of the resolution code into the deno_resolver crate. Still pending is the npm resolution code.
This commit is contained in:
parent
de34c7ed29
commit
617350e79c
28 changed files with 1520 additions and 1048 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -2007,6 +2007,8 @@ version = "0.9.0"
|
|||
dependencies = [
|
||||
"anyhow",
|
||||
"base32",
|
||||
"dashmap",
|
||||
"deno_config",
|
||||
"deno_media_type",
|
||||
"deno_package_json",
|
||||
"deno_path_util",
|
||||
|
|
|
@ -49,6 +49,7 @@ deno_ast = { version = "=0.43.3", features = ["transpiling"] }
|
|||
deno_core = { version = "0.319.0" }
|
||||
|
||||
deno_bench_util = { version = "0.171.0", path = "./bench_util" }
|
||||
deno_config = { version = "=0.38.2", features = ["workspace", "sync"] }
|
||||
deno_lockfile = "=0.23.1"
|
||||
deno_media_type = { version = "0.2.0", features = ["module_specifier"] }
|
||||
deno_npm = "=0.25.4"
|
||||
|
|
|
@ -69,8 +69,8 @@ winres.workspace = true
|
|||
|
||||
[dependencies]
|
||||
deno_ast = { workspace = true, features = ["bundler", "cjs", "codegen", "proposal", "react", "sourcemap", "transforms", "typescript", "view", "visit"] }
|
||||
deno_cache_dir = { workspace = true }
|
||||
deno_config = { version = "=0.38.2", features = ["workspace", "sync"] }
|
||||
deno_cache_dir.workspace = true
|
||||
deno_config.workspace = true
|
||||
deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] }
|
||||
deno_doc = { version = "0.156.0", default-features = false, features = ["rust", "html", "syntect"] }
|
||||
deno_graph = { version = "=0.84.1" }
|
||||
|
|
|
@ -42,8 +42,9 @@ use crate::npm::CliNpmResolverCreateOptions;
|
|||
use crate::npm::CliNpmResolverManagedSnapshotOption;
|
||||
use crate::npm::CreateInNpmPkgCheckerOptions;
|
||||
use crate::resolver::CjsTracker;
|
||||
use crate::resolver::CliDenoResolver;
|
||||
use crate::resolver::CliDenoResolverFs;
|
||||
use crate::resolver::CliNodeResolver;
|
||||
use crate::resolver::CliNpmReqResolver;
|
||||
use crate::resolver::CliResolver;
|
||||
use crate::resolver::CliResolverOptions;
|
||||
use crate::resolver::CliSloppyImportsResolver;
|
||||
|
@ -71,6 +72,9 @@ use deno_core::error::AnyError;
|
|||
use deno_core::futures::FutureExt;
|
||||
use deno_core::FeatureChecker;
|
||||
|
||||
use deno_resolver::npm::NpmReqResolverOptions;
|
||||
use deno_resolver::DenoResolverOptions;
|
||||
use deno_resolver::NodeAndNpmReqResolver;
|
||||
use deno_runtime::deno_fs;
|
||||
use deno_runtime::deno_node::DenoFsNodeResolverEnv;
|
||||
use deno_runtime::deno_node::NodeResolver;
|
||||
|
@ -126,7 +130,7 @@ impl RootCertStoreProvider for CliRootCertStoreProvider {
|
|||
}
|
||||
}
|
||||
|
||||
struct Deferred<T>(once_cell::unsync::OnceCell<T>);
|
||||
pub struct Deferred<T>(once_cell::unsync::OnceCell<T>);
|
||||
|
||||
impl<T> Default for Deferred<T> {
|
||||
fn default() -> Self {
|
||||
|
@ -175,9 +179,9 @@ struct CliFactoryServices {
|
|||
blob_store: Deferred<Arc<BlobStore>>,
|
||||
caches: Deferred<Arc<Caches>>,
|
||||
cjs_tracker: Deferred<Arc<CjsTracker>>,
|
||||
cli_node_resolver: Deferred<Arc<CliNodeResolver>>,
|
||||
cli_options: Deferred<Arc<CliOptions>>,
|
||||
code_cache: Deferred<Arc<CodeCache>>,
|
||||
deno_resolver: Deferred<Arc<CliDenoResolver>>,
|
||||
emit_cache: Deferred<Arc<EmitCache>>,
|
||||
emitter: Deferred<Arc<Emitter>>,
|
||||
feature_checker: Deferred<Arc<FeatureChecker>>,
|
||||
|
@ -197,6 +201,7 @@ struct CliFactoryServices {
|
|||
node_code_translator: Deferred<Arc<CliNodeCodeTranslator>>,
|
||||
node_resolver: Deferred<Arc<NodeResolver>>,
|
||||
npm_cache_dir: Deferred<Arc<NpmCacheDir>>,
|
||||
npm_req_resolver: Deferred<Arc<CliNpmReqResolver>>,
|
||||
npm_resolver: Deferred<Arc<dyn CliNpmResolver>>,
|
||||
parsed_source_cache: Deferred<Arc<ParsedSourceCache>>,
|
||||
permission_desc_parser: Deferred<Arc<RuntimePermissionDescriptorParser>>,
|
||||
|
@ -523,6 +528,31 @@ impl CliFactory {
|
|||
.await
|
||||
}
|
||||
|
||||
pub async fn deno_resolver(&self) -> Result<&Arc<CliDenoResolver>, AnyError> {
|
||||
self
|
||||
.services
|
||||
.deno_resolver
|
||||
.get_or_try_init_async(async {
|
||||
let cli_options = self.cli_options()?;
|
||||
Ok(Arc::new(CliDenoResolver::new(DenoResolverOptions {
|
||||
in_npm_pkg_checker: self.in_npm_pkg_checker()?.clone(),
|
||||
node_and_req_resolver: if cli_options.no_npm() {
|
||||
None
|
||||
} else {
|
||||
Some(NodeAndNpmReqResolver {
|
||||
node_resolver: self.node_resolver().await?.clone(),
|
||||
npm_req_resolver: self.npm_req_resolver().await?.clone(),
|
||||
})
|
||||
},
|
||||
sloppy_imports_resolver: self.sloppy_imports_resolver()?.cloned(),
|
||||
workspace_resolver: self.workspace_resolver().await?.clone(),
|
||||
is_byonm: cli_options.use_byonm(),
|
||||
maybe_vendor_dir: cli_options.vendor_dir_path(),
|
||||
})))
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn resolver(&self) -> Result<&Arc<CliResolver>, AnyError> {
|
||||
self
|
||||
.services
|
||||
|
@ -531,17 +561,14 @@ impl CliFactory {
|
|||
async {
|
||||
let cli_options = self.cli_options()?;
|
||||
Ok(Arc::new(CliResolver::new(CliResolverOptions {
|
||||
sloppy_imports_resolver: self.sloppy_imports_resolver()?.cloned(),
|
||||
node_resolver: Some(self.cli_node_resolver().await?.clone()),
|
||||
npm_resolver: if cli_options.no_npm() {
|
||||
None
|
||||
} else {
|
||||
Some(self.npm_resolver().await?.clone())
|
||||
},
|
||||
workspace_resolver: self.workspace_resolver().await?.clone(),
|
||||
bare_node_builtins_enabled: cli_options
|
||||
.unstable_bare_node_builtins(),
|
||||
maybe_vendor_dir: cli_options.vendor_dir_path(),
|
||||
deno_resolver: self.deno_resolver().await?.clone(),
|
||||
})))
|
||||
}
|
||||
.boxed_local(),
|
||||
|
@ -624,7 +651,11 @@ impl CliFactory {
|
|||
Ok(Arc::new(NodeResolver::new(
|
||||
DenoFsNodeResolverEnv::new(self.fs().clone()),
|
||||
self.in_npm_pkg_checker()?.clone(),
|
||||
self.npm_resolver().await?.clone().into_npm_resolver(),
|
||||
self
|
||||
.npm_resolver()
|
||||
.await?
|
||||
.clone()
|
||||
.into_npm_pkg_folder_resolver(),
|
||||
self.pkg_json_resolver().clone(),
|
||||
)))
|
||||
}
|
||||
|
@ -656,13 +687,36 @@ impl CliFactory {
|
|||
DenoFsNodeResolverEnv::new(self.fs().clone()),
|
||||
self.in_npm_pkg_checker()?.clone(),
|
||||
node_resolver,
|
||||
self.npm_resolver().await?.clone().into_npm_resolver(),
|
||||
self
|
||||
.npm_resolver()
|
||||
.await?
|
||||
.clone()
|
||||
.into_npm_pkg_folder_resolver(),
|
||||
self.pkg_json_resolver().clone(),
|
||||
)))
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn npm_req_resolver(
|
||||
&self,
|
||||
) -> Result<&Arc<CliNpmReqResolver>, AnyError> {
|
||||
self
|
||||
.services
|
||||
.npm_req_resolver
|
||||
.get_or_try_init_async(async {
|
||||
let npm_resolver = self.npm_resolver().await?;
|
||||
Ok(Arc::new(CliNpmReqResolver::new(NpmReqResolverOptions {
|
||||
byonm_resolver: (npm_resolver.clone()).into_maybe_byonm(),
|
||||
fs: CliDenoResolverFs(self.fs().clone()),
|
||||
in_npm_pkg_checker: self.in_npm_pkg_checker()?.clone(),
|
||||
node_resolver: self.node_resolver().await?.clone(),
|
||||
npm_req_resolver: npm_resolver.clone().into_npm_req_resolver(),
|
||||
})))
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub fn pkg_json_resolver(&self) -> &Arc<PackageJsonResolver> {
|
||||
self.services.pkg_json_resolver.get_or_init(|| {
|
||||
Arc::new(PackageJsonResolver::new(DenoFsNodeResolverEnv::new(
|
||||
|
@ -799,23 +853,6 @@ impl CliFactory {
|
|||
})
|
||||
}
|
||||
|
||||
pub async fn cli_node_resolver(
|
||||
&self,
|
||||
) -> Result<&Arc<CliNodeResolver>, AnyError> {
|
||||
self
|
||||
.services
|
||||
.cli_node_resolver
|
||||
.get_or_try_init_async(async {
|
||||
Ok(Arc::new(CliNodeResolver::new(
|
||||
self.fs().clone(),
|
||||
self.in_npm_pkg_checker()?.clone(),
|
||||
self.node_resolver().await?.clone(),
|
||||
self.npm_resolver().await?.clone(),
|
||||
)))
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub fn permission_desc_parser(
|
||||
&self,
|
||||
) -> Result<&Arc<RuntimePermissionDescriptorParser>, AnyError> {
|
||||
|
@ -880,7 +917,6 @@ impl CliFactory {
|
|||
let fs = self.fs();
|
||||
let node_resolver = self.node_resolver().await?;
|
||||
let npm_resolver = self.npm_resolver().await?;
|
||||
let cli_node_resolver = self.cli_node_resolver().await?;
|
||||
let cli_npm_resolver = self.npm_resolver().await?.clone();
|
||||
let in_npm_pkg_checker = self.in_npm_pkg_checker()?;
|
||||
let maybe_file_watcher_communicator = if cli_options.has_hmr() {
|
||||
|
@ -891,6 +927,7 @@ impl CliFactory {
|
|||
let node_code_translator = self.node_code_translator().await?;
|
||||
let cjs_tracker = self.cjs_tracker()?.clone();
|
||||
let pkg_json_resolver = self.pkg_json_resolver().clone();
|
||||
let npm_req_resolver = self.npm_req_resolver().await?;
|
||||
|
||||
Ok(CliMainWorkerFactory::new(
|
||||
self.blob_store().clone(),
|
||||
|
@ -918,7 +955,8 @@ impl CliFactory {
|
|||
self.main_module_graph_container().await?.clone(),
|
||||
self.module_load_preparer().await?.clone(),
|
||||
node_code_translator.clone(),
|
||||
cli_node_resolver.clone(),
|
||||
node_resolver.clone(),
|
||||
npm_req_resolver.clone(),
|
||||
cli_npm_resolver.clone(),
|
||||
NpmModuleLoader::new(
|
||||
self.cjs_tracker()?.clone(),
|
||||
|
|
|
@ -344,9 +344,8 @@ impl<'a> TsResponseImportMapper<'a> {
|
|||
{
|
||||
let in_npm_pkg = self
|
||||
.resolver
|
||||
.maybe_node_resolver(Some(&self.file_referrer))
|
||||
.map(|n| n.in_npm_package(specifier))
|
||||
.unwrap_or(false);
|
||||
.in_npm_pkg_checker(Some(&self.file_referrer))
|
||||
.in_npm_package(specifier);
|
||||
if in_npm_pkg {
|
||||
if let Ok(Some(pkg_id)) =
|
||||
npm_resolver.resolve_pkg_id_from_specifier(specifier)
|
||||
|
|
|
@ -15,6 +15,9 @@ use deno_graph::Range;
|
|||
use deno_npm::NpmSystemInfo;
|
||||
use deno_path_util::url_from_directory_path;
|
||||
use deno_path_util::url_to_file_path;
|
||||
use deno_resolver::npm::NpmReqResolverOptions;
|
||||
use deno_resolver::DenoResolverOptions;
|
||||
use deno_resolver::NodeAndNpmReqResolver;
|
||||
use deno_runtime::deno_fs;
|
||||
use deno_runtime::deno_node::NodeResolver;
|
||||
use deno_runtime::deno_node::PackageJson;
|
||||
|
@ -43,6 +46,7 @@ use crate::args::CacheSetting;
|
|||
use crate::args::CliLockfile;
|
||||
use crate::args::NpmInstallDepsProvider;
|
||||
use crate::cache::DenoCacheEnvFsAdapter;
|
||||
use crate::factory::Deferred;
|
||||
use crate::graph_util::CliJsrUrlProvider;
|
||||
use crate::http_util::HttpClientProvider;
|
||||
use crate::lsp::config::Config;
|
||||
|
@ -57,8 +61,9 @@ use crate::npm::CliNpmResolverCreateOptions;
|
|||
use crate::npm::CliNpmResolverManagedSnapshotOption;
|
||||
use crate::npm::CreateInNpmPkgCheckerOptions;
|
||||
use crate::npm::ManagedCliNpmResolver;
|
||||
use crate::resolver::CliDenoResolver;
|
||||
use crate::resolver::CliDenoResolverFs;
|
||||
use crate::resolver::CliNodeResolver;
|
||||
use crate::resolver::CliNpmReqResolver;
|
||||
use crate::resolver::CliResolver;
|
||||
use crate::resolver::CliResolverOptions;
|
||||
use crate::resolver::IsCjsResolver;
|
||||
|
@ -71,10 +76,12 @@ use crate::util::progress_bar::ProgressBarStyle;
|
|||
#[derive(Debug, Clone)]
|
||||
struct LspScopeResolver {
|
||||
resolver: Arc<CliResolver>,
|
||||
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
|
||||
jsr_resolver: Option<Arc<JsrCacheResolver>>,
|
||||
npm_resolver: Option<Arc<dyn CliNpmResolver>>,
|
||||
node_resolver: Option<Arc<CliNodeResolver>>,
|
||||
pkg_json_resolver: Option<Arc<PackageJsonResolver>>,
|
||||
node_resolver: Option<Arc<NodeResolver>>,
|
||||
npm_pkg_req_resolver: Option<Arc<CliNpmReqResolver>>,
|
||||
pkg_json_resolver: Arc<PackageJsonResolver>,
|
||||
redirect_resolver: Option<Arc<RedirectResolver>>,
|
||||
graph_imports: Arc<IndexMap<ModuleSpecifier, GraphImport>>,
|
||||
package_json_deps_by_resolution: Arc<IndexMap<ModuleSpecifier, String>>,
|
||||
|
@ -83,12 +90,15 @@ struct LspScopeResolver {
|
|||
|
||||
impl Default for LspScopeResolver {
|
||||
fn default() -> Self {
|
||||
let factory = ResolverFactory::new(None);
|
||||
Self {
|
||||
resolver: create_cli_resolver(None, None, None),
|
||||
resolver: factory.cli_resolver().clone(),
|
||||
in_npm_pkg_checker: factory.in_npm_pkg_checker().clone(),
|
||||
jsr_resolver: None,
|
||||
npm_resolver: None,
|
||||
node_resolver: None,
|
||||
pkg_json_resolver: None,
|
||||
npm_pkg_req_resolver: None,
|
||||
pkg_json_resolver: factory.pkg_json_resolver().clone(),
|
||||
redirect_resolver: None,
|
||||
graph_imports: Default::default(),
|
||||
package_json_deps_by_resolution: Default::default(),
|
||||
|
@ -103,35 +113,16 @@ impl LspScopeResolver {
|
|||
cache: &LspCache,
|
||||
http_client_provider: Option<&Arc<HttpClientProvider>>,
|
||||
) -> Self {
|
||||
let mut npm_resolver = None;
|
||||
let mut node_resolver = None;
|
||||
let fs = Arc::new(deno_fs::RealFs);
|
||||
let pkg_json_resolver = Arc::new(PackageJsonResolver::new(
|
||||
deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()),
|
||||
));
|
||||
if let Some(http_client) = http_client_provider {
|
||||
npm_resolver = create_npm_resolver(
|
||||
config_data.map(|d| d.as_ref()),
|
||||
cache,
|
||||
http_client,
|
||||
&pkg_json_resolver,
|
||||
)
|
||||
.await;
|
||||
if let Some(npm_resolver) = &npm_resolver {
|
||||
let in_npm_pkg_checker = create_in_npm_pkg_checker(npm_resolver);
|
||||
node_resolver = Some(create_node_resolver(
|
||||
fs.clone(),
|
||||
in_npm_pkg_checker,
|
||||
npm_resolver,
|
||||
pkg_json_resolver.clone(),
|
||||
));
|
||||
}
|
||||
let mut factory = ResolverFactory::new(config_data);
|
||||
if let Some(http_client_provider) = http_client_provider {
|
||||
factory.init_npm_resolver(http_client_provider, cache).await;
|
||||
}
|
||||
let cli_resolver = create_cli_resolver(
|
||||
config_data.map(|d| d.as_ref()),
|
||||
npm_resolver.as_ref(),
|
||||
node_resolver.as_ref(),
|
||||
);
|
||||
let in_npm_pkg_checker = factory.in_npm_pkg_checker().clone();
|
||||
let npm_resolver = factory.npm_resolver().cloned();
|
||||
let node_resolver = factory.node_resolver().cloned();
|
||||
let npm_pkg_req_resolver = factory.npm_pkg_req_resolver().cloned();
|
||||
let cli_resolver = factory.cli_resolver().clone();
|
||||
let pkg_json_resolver = factory.pkg_json_resolver().clone();
|
||||
let jsr_resolver = Some(Arc::new(JsrCacheResolver::new(
|
||||
cache.for_specifier(config_data.map(|d| d.scope.as_ref())),
|
||||
config_data.map(|d| d.as_ref()),
|
||||
|
@ -171,7 +162,7 @@ impl LspScopeResolver {
|
|||
})
|
||||
.unwrap_or_default();
|
||||
let package_json_deps_by_resolution = (|| {
|
||||
let node_resolver = node_resolver.as_ref()?;
|
||||
let npm_pkg_req_resolver = npm_pkg_req_resolver.as_ref()?;
|
||||
let package_json = config_data?.maybe_pkg_json()?;
|
||||
let referrer = package_json.specifier();
|
||||
let dependencies = package_json.dependencies.as_ref()?;
|
||||
|
@ -181,7 +172,7 @@ impl LspScopeResolver {
|
|||
let req_ref =
|
||||
NpmPackageReqReference::from_str(&format!("npm:{name}")).ok()?;
|
||||
let specifier = into_specifier_and_media_type(Some(
|
||||
node_resolver
|
||||
npm_pkg_req_resolver
|
||||
.resolve_req_reference(
|
||||
&req_ref,
|
||||
&referrer,
|
||||
|
@ -201,10 +192,12 @@ impl LspScopeResolver {
|
|||
Arc::new(package_json_deps_by_resolution.unwrap_or_default());
|
||||
Self {
|
||||
resolver: cli_resolver,
|
||||
in_npm_pkg_checker,
|
||||
jsr_resolver,
|
||||
npm_pkg_req_resolver,
|
||||
npm_resolver,
|
||||
node_resolver,
|
||||
pkg_json_resolver: Some(pkg_json_resolver),
|
||||
pkg_json_resolver,
|
||||
redirect_resolver,
|
||||
graph_imports,
|
||||
package_json_deps_by_resolution,
|
||||
|
@ -213,34 +206,21 @@ impl LspScopeResolver {
|
|||
}
|
||||
|
||||
fn snapshot(&self) -> Arc<Self> {
|
||||
let mut factory = ResolverFactory::new(self.config_data.as_ref());
|
||||
let npm_resolver =
|
||||
self.npm_resolver.as_ref().map(|r| r.clone_snapshotted());
|
||||
let fs = Arc::new(deno_fs::RealFs);
|
||||
let pkg_json_resolver = Arc::new(PackageJsonResolver::new(
|
||||
deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()),
|
||||
));
|
||||
let mut node_resolver = None;
|
||||
if let Some(npm_resolver) = &npm_resolver {
|
||||
let in_npm_pkg_checker = create_in_npm_pkg_checker(npm_resolver);
|
||||
node_resolver = Some(create_node_resolver(
|
||||
fs,
|
||||
in_npm_pkg_checker,
|
||||
npm_resolver,
|
||||
pkg_json_resolver.clone(),
|
||||
));
|
||||
factory.set_npm_resolver(npm_resolver.clone());
|
||||
}
|
||||
let graph_resolver = create_cli_resolver(
|
||||
self.config_data.as_deref(),
|
||||
npm_resolver.as_ref(),
|
||||
node_resolver.as_ref(),
|
||||
);
|
||||
Arc::new(Self {
|
||||
resolver: graph_resolver,
|
||||
resolver: factory.cli_resolver().clone(),
|
||||
in_npm_pkg_checker: factory.in_npm_pkg_checker().clone(),
|
||||
jsr_resolver: self.jsr_resolver.clone(),
|
||||
npm_resolver,
|
||||
node_resolver,
|
||||
npm_pkg_req_resolver: factory.npm_pkg_req_resolver().cloned(),
|
||||
npm_resolver: factory.npm_resolver().cloned(),
|
||||
node_resolver: factory.node_resolver().cloned(),
|
||||
redirect_resolver: self.redirect_resolver.clone(),
|
||||
pkg_json_resolver: Some(pkg_json_resolver),
|
||||
pkg_json_resolver: factory.pkg_json_resolver().clone(),
|
||||
graph_imports: self.graph_imports.clone(),
|
||||
package_json_deps_by_resolution: self
|
||||
.package_json_deps_by_resolution
|
||||
|
@ -354,12 +334,12 @@ impl LspResolver {
|
|||
resolver.config_data.as_ref()
|
||||
}
|
||||
|
||||
pub fn maybe_node_resolver(
|
||||
pub fn in_npm_pkg_checker(
|
||||
&self,
|
||||
file_referrer: Option<&ModuleSpecifier>,
|
||||
) -> Option<&Arc<CliNodeResolver>> {
|
||||
) -> &Arc<dyn InNpmPackageChecker> {
|
||||
let resolver = self.get_scope_resolver(file_referrer);
|
||||
resolver.node_resolver.as_ref()
|
||||
&resolver.in_npm_pkg_checker
|
||||
}
|
||||
|
||||
pub fn maybe_managed_npm_resolver(
|
||||
|
@ -429,9 +409,9 @@ impl LspResolver {
|
|||
file_referrer: Option<&ModuleSpecifier>,
|
||||
) -> Option<(ModuleSpecifier, MediaType)> {
|
||||
let resolver = self.get_scope_resolver(file_referrer);
|
||||
let node_resolver = resolver.node_resolver.as_ref()?;
|
||||
let npm_pkg_req_resolver = resolver.npm_pkg_req_resolver.as_ref()?;
|
||||
Some(into_specifier_and_media_type(Some(
|
||||
node_resolver
|
||||
npm_pkg_req_resolver
|
||||
.resolve_req_reference(
|
||||
req_ref,
|
||||
referrer,
|
||||
|
@ -483,10 +463,11 @@ impl LspResolver {
|
|||
referrer_kind: NodeModuleKind,
|
||||
) -> bool {
|
||||
let resolver = self.get_scope_resolver(Some(referrer));
|
||||
let Some(node_resolver) = resolver.node_resolver.as_ref() else {
|
||||
let Some(npm_pkg_req_resolver) = resolver.npm_pkg_req_resolver.as_ref()
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
node_resolver
|
||||
npm_pkg_req_resolver
|
||||
.resolve_if_for_npm_pkg(
|
||||
specifier_text,
|
||||
referrer,
|
||||
|
@ -503,10 +484,9 @@ impl LspResolver {
|
|||
referrer: &ModuleSpecifier,
|
||||
) -> Result<Option<Arc<PackageJson>>, ClosestPkgJsonError> {
|
||||
let resolver = self.get_scope_resolver(Some(referrer));
|
||||
let Some(pkg_json_resolver) = resolver.pkg_json_resolver.as_ref() else {
|
||||
return Ok(None);
|
||||
};
|
||||
pkg_json_resolver.get_closest_package_json(referrer)
|
||||
resolver
|
||||
.pkg_json_resolver
|
||||
.get_closest_package_json(referrer)
|
||||
}
|
||||
|
||||
pub fn resolve_redirects(
|
||||
|
@ -558,131 +538,206 @@ impl LspResolver {
|
|||
}
|
||||
}
|
||||
|
||||
async fn create_npm_resolver(
|
||||
config_data: Option<&ConfigData>,
|
||||
cache: &LspCache,
|
||||
http_client_provider: &Arc<HttpClientProvider>,
|
||||
pkg_json_resolver: &Arc<PackageJsonResolver>,
|
||||
) -> Option<Arc<dyn CliNpmResolver>> {
|
||||
let enable_byonm = config_data.map(|d| d.byonm).unwrap_or(false);
|
||||
let options = if enable_byonm {
|
||||
CliNpmResolverCreateOptions::Byonm(CliByonmNpmResolverCreateOptions {
|
||||
fs: CliDenoResolverFs(Arc::new(deno_fs::RealFs)),
|
||||
pkg_json_resolver: pkg_json_resolver.clone(),
|
||||
root_node_modules_dir: config_data.and_then(|config_data| {
|
||||
config_data.node_modules_dir.clone().or_else(|| {
|
||||
url_to_file_path(&config_data.scope)
|
||||
.ok()
|
||||
.map(|p| p.join("node_modules/"))
|
||||
})
|
||||
}),
|
||||
})
|
||||
} else {
|
||||
let npmrc = config_data
|
||||
.and_then(|d| d.npmrc.clone())
|
||||
.unwrap_or_else(create_default_npmrc);
|
||||
let npm_cache_dir = Arc::new(NpmCacheDir::new(
|
||||
&DenoCacheEnvFsAdapter(&deno_fs::RealFs),
|
||||
cache.deno_dir().npm_folder_path(),
|
||||
npmrc.get_all_known_registries_urls(),
|
||||
));
|
||||
CliNpmResolverCreateOptions::Managed(CliManagedNpmResolverCreateOptions {
|
||||
http_client_provider: http_client_provider.clone(),
|
||||
snapshot: match config_data.and_then(|d| d.lockfile.as_ref()) {
|
||||
Some(lockfile) => {
|
||||
CliNpmResolverManagedSnapshotOption::ResolveFromLockfile(
|
||||
lockfile.clone(),
|
||||
)
|
||||
}
|
||||
None => CliNpmResolverManagedSnapshotOption::Specified(None),
|
||||
},
|
||||
// Don't provide the lockfile. We don't want these resolvers
|
||||
// updating it. Only the cache request should update the lockfile.
|
||||
maybe_lockfile: None,
|
||||
fs: Arc::new(deno_fs::RealFs),
|
||||
npm_cache_dir,
|
||||
// Use an "only" cache setting in order to make the
|
||||
// user do an explicit "cache" command and prevent
|
||||
// the cache from being filled with lots of packages while
|
||||
// the user is typing.
|
||||
cache_setting: CacheSetting::Only,
|
||||
text_only_progress_bar: ProgressBar::new(ProgressBarStyle::TextOnly),
|
||||
maybe_node_modules_path: config_data
|
||||
.and_then(|d| d.node_modules_dir.clone()),
|
||||
// only used for top level install, so we can ignore this
|
||||
npm_install_deps_provider: Arc::new(NpmInstallDepsProvider::empty()),
|
||||
npmrc,
|
||||
npm_system_info: NpmSystemInfo::default(),
|
||||
lifecycle_scripts: Default::default(),
|
||||
})
|
||||
};
|
||||
Some(create_cli_npm_resolver_for_lsp(options).await)
|
||||
#[derive(Default)]
|
||||
struct ResolverFactoryServices {
|
||||
cli_resolver: Deferred<Arc<CliResolver>>,
|
||||
in_npm_pkg_checker: Deferred<Arc<dyn InNpmPackageChecker>>,
|
||||
node_resolver: Deferred<Option<Arc<NodeResolver>>>,
|
||||
npm_pkg_req_resolver: Deferred<Option<Arc<CliNpmReqResolver>>>,
|
||||
npm_resolver: Option<Arc<dyn CliNpmResolver>>,
|
||||
}
|
||||
|
||||
fn create_in_npm_pkg_checker(
|
||||
npm_resolver: &Arc<dyn CliNpmResolver>,
|
||||
) -> Arc<dyn InNpmPackageChecker> {
|
||||
crate::npm::create_in_npm_pkg_checker(match npm_resolver.as_inner() {
|
||||
crate::npm::InnerCliNpmResolverRef::Byonm(_) => {
|
||||
CreateInNpmPkgCheckerOptions::Byonm
|
||||
struct ResolverFactory<'a> {
|
||||
config_data: Option<&'a Arc<ConfigData>>,
|
||||
fs: Arc<dyn deno_fs::FileSystem>,
|
||||
pkg_json_resolver: Arc<PackageJsonResolver>,
|
||||
services: ResolverFactoryServices,
|
||||
}
|
||||
|
||||
impl<'a> ResolverFactory<'a> {
|
||||
pub fn new(config_data: Option<&'a Arc<ConfigData>>) -> Self {
|
||||
let fs = Arc::new(deno_fs::RealFs);
|
||||
let pkg_json_resolver = Arc::new(PackageJsonResolver::new(
|
||||
deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()),
|
||||
));
|
||||
Self {
|
||||
config_data,
|
||||
fs,
|
||||
pkg_json_resolver,
|
||||
services: Default::default(),
|
||||
}
|
||||
crate::npm::InnerCliNpmResolverRef::Managed(m) => {
|
||||
CreateInNpmPkgCheckerOptions::Managed(
|
||||
CliManagedInNpmPkgCheckerCreateOptions {
|
||||
root_cache_dir_url: m.global_cache_root_url(),
|
||||
maybe_node_modules_path: m.maybe_node_modules_path(),
|
||||
}
|
||||
|
||||
async fn init_npm_resolver(
|
||||
&mut self,
|
||||
http_client_provider: &Arc<HttpClientProvider>,
|
||||
cache: &LspCache,
|
||||
) {
|
||||
let enable_byonm = self.config_data.map(|d| d.byonm).unwrap_or(false);
|
||||
let options = if enable_byonm {
|
||||
CliNpmResolverCreateOptions::Byonm(CliByonmNpmResolverCreateOptions {
|
||||
fs: CliDenoResolverFs(Arc::new(deno_fs::RealFs)),
|
||||
pkg_json_resolver: self.pkg_json_resolver.clone(),
|
||||
root_node_modules_dir: self.config_data.and_then(|config_data| {
|
||||
config_data.node_modules_dir.clone().or_else(|| {
|
||||
url_to_file_path(&config_data.scope)
|
||||
.ok()
|
||||
.map(|p| p.join("node_modules/"))
|
||||
})
|
||||
}),
|
||||
})
|
||||
} else {
|
||||
let npmrc = self
|
||||
.config_data
|
||||
.and_then(|d| d.npmrc.clone())
|
||||
.unwrap_or_else(create_default_npmrc);
|
||||
let npm_cache_dir = Arc::new(NpmCacheDir::new(
|
||||
&DenoCacheEnvFsAdapter(self.fs.as_ref()),
|
||||
cache.deno_dir().npm_folder_path(),
|
||||
npmrc.get_all_known_registries_urls(),
|
||||
));
|
||||
CliNpmResolverCreateOptions::Managed(CliManagedNpmResolverCreateOptions {
|
||||
http_client_provider: http_client_provider.clone(),
|
||||
snapshot: match self.config_data.and_then(|d| d.lockfile.as_ref()) {
|
||||
Some(lockfile) => {
|
||||
CliNpmResolverManagedSnapshotOption::ResolveFromLockfile(
|
||||
lockfile.clone(),
|
||||
)
|
||||
}
|
||||
None => CliNpmResolverManagedSnapshotOption::Specified(None),
|
||||
},
|
||||
// Don't provide the lockfile. We don't want these resolvers
|
||||
// updating it. Only the cache request should update the lockfile.
|
||||
maybe_lockfile: None,
|
||||
fs: Arc::new(deno_fs::RealFs),
|
||||
npm_cache_dir,
|
||||
// Use an "only" cache setting in order to make the
|
||||
// user do an explicit "cache" command and prevent
|
||||
// the cache from being filled with lots of packages while
|
||||
// the user is typing.
|
||||
cache_setting: CacheSetting::Only,
|
||||
text_only_progress_bar: ProgressBar::new(ProgressBarStyle::TextOnly),
|
||||
maybe_node_modules_path: self
|
||||
.config_data
|
||||
.and_then(|d| d.node_modules_dir.clone()),
|
||||
// only used for top level install, so we can ignore this
|
||||
npm_install_deps_provider: Arc::new(NpmInstallDepsProvider::empty()),
|
||||
npmrc,
|
||||
npm_system_info: NpmSystemInfo::default(),
|
||||
lifecycle_scripts: Default::default(),
|
||||
})
|
||||
};
|
||||
self.set_npm_resolver(create_cli_npm_resolver_for_lsp(options).await);
|
||||
}
|
||||
|
||||
pub fn set_npm_resolver(&mut self, npm_resolver: Arc<dyn CliNpmResolver>) {
|
||||
self.services.npm_resolver = Some(npm_resolver);
|
||||
}
|
||||
|
||||
pub fn npm_resolver(&self) -> Option<&Arc<dyn CliNpmResolver>> {
|
||||
self.services.npm_resolver.as_ref()
|
||||
}
|
||||
|
||||
pub fn cli_resolver(&self) -> &Arc<CliResolver> {
|
||||
self.services.cli_resolver.get_or_init(|| {
|
||||
let npm_req_resolver = self.npm_pkg_req_resolver().cloned();
|
||||
let deno_resolver = Arc::new(CliDenoResolver::new(DenoResolverOptions {
|
||||
in_npm_pkg_checker: self.in_npm_pkg_checker().clone(),
|
||||
node_and_req_resolver: match (self.node_resolver(), npm_req_resolver) {
|
||||
(Some(node_resolver), Some(npm_req_resolver)) => {
|
||||
Some(NodeAndNpmReqResolver {
|
||||
node_resolver: node_resolver.clone(),
|
||||
npm_req_resolver,
|
||||
})
|
||||
}
|
||||
_ => 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())
|
||||
.unwrap_or_else(|| {
|
||||
Arc::new(WorkspaceResolver::new_raw(
|
||||
// this is fine because this is only used before initialization
|
||||
Arc::new(ModuleSpecifier::parse("file:///").unwrap()),
|
||||
None,
|
||||
Vec::new(),
|
||||
Vec::new(),
|
||||
PackageJsonDepResolution::Disabled,
|
||||
))
|
||||
}),
|
||||
is_byonm: self.config_data.map(|d| d.byonm).unwrap_or(false),
|
||||
maybe_vendor_dir: self.config_data.and_then(|d| d.vendor_dir.as_ref()),
|
||||
}));
|
||||
Arc::new(CliResolver::new(CliResolverOptions {
|
||||
deno_resolver,
|
||||
npm_resolver: self.npm_resolver().cloned(),
|
||||
bare_node_builtins_enabled: self
|
||||
.config_data
|
||||
.is_some_and(|d| d.unstable.contains("bare-node-builtins")),
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn pkg_json_resolver(&self) -> &Arc<PackageJsonResolver> {
|
||||
&self.pkg_json_resolver
|
||||
}
|
||||
|
||||
pub fn in_npm_pkg_checker(&self) -> &Arc<dyn InNpmPackageChecker> {
|
||||
self.services.in_npm_pkg_checker.get_or_init(|| {
|
||||
crate::npm::create_in_npm_pkg_checker(
|
||||
match self.services.npm_resolver.as_ref().map(|r| r.as_inner()) {
|
||||
Some(crate::npm::InnerCliNpmResolverRef::Byonm(_)) | None => {
|
||||
CreateInNpmPkgCheckerOptions::Byonm
|
||||
}
|
||||
Some(crate::npm::InnerCliNpmResolverRef::Managed(m)) => {
|
||||
CreateInNpmPkgCheckerOptions::Managed(
|
||||
CliManagedInNpmPkgCheckerCreateOptions {
|
||||
root_cache_dir_url: m.global_cache_root_url(),
|
||||
maybe_node_modules_path: m.maybe_node_modules_path(),
|
||||
},
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn create_node_resolver(
|
||||
fs: Arc<dyn deno_fs::FileSystem>,
|
||||
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
|
||||
npm_resolver: &Arc<dyn CliNpmResolver>,
|
||||
pkg_json_resolver: Arc<PackageJsonResolver>,
|
||||
) -> Arc<CliNodeResolver> {
|
||||
let node_resolver_inner = Arc::new(NodeResolver::new(
|
||||
deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()),
|
||||
in_npm_pkg_checker.clone(),
|
||||
npm_resolver.clone().into_npm_resolver(),
|
||||
pkg_json_resolver.clone(),
|
||||
));
|
||||
Arc::new(CliNodeResolver::new(
|
||||
fs,
|
||||
in_npm_pkg_checker,
|
||||
node_resolver_inner,
|
||||
npm_resolver.clone(),
|
||||
))
|
||||
}
|
||||
pub fn node_resolver(&self) -> Option<&Arc<NodeResolver>> {
|
||||
self
|
||||
.services
|
||||
.node_resolver
|
||||
.get_or_init(|| {
|
||||
let npm_resolver = self.services.npm_resolver.as_ref()?;
|
||||
Some(Arc::new(NodeResolver::new(
|
||||
deno_runtime::deno_node::DenoFsNodeResolverEnv::new(self.fs.clone()),
|
||||
self.in_npm_pkg_checker().clone(),
|
||||
npm_resolver.clone().into_npm_pkg_folder_resolver(),
|
||||
self.pkg_json_resolver.clone(),
|
||||
)))
|
||||
})
|
||||
.as_ref()
|
||||
}
|
||||
|
||||
fn create_cli_resolver(
|
||||
config_data: Option<&ConfigData>,
|
||||
npm_resolver: Option<&Arc<dyn CliNpmResolver>>,
|
||||
node_resolver: Option<&Arc<CliNodeResolver>>,
|
||||
) -> Arc<CliResolver> {
|
||||
Arc::new(CliResolver::new(CliResolverOptions {
|
||||
node_resolver: node_resolver.cloned(),
|
||||
npm_resolver: npm_resolver.cloned(),
|
||||
workspace_resolver: config_data.map(|d| d.resolver.clone()).unwrap_or_else(
|
||||
|| {
|
||||
Arc::new(WorkspaceResolver::new_raw(
|
||||
// this is fine because this is only used before initialization
|
||||
Arc::new(ModuleSpecifier::parse("file:///").unwrap()),
|
||||
None,
|
||||
Vec::new(),
|
||||
Vec::new(),
|
||||
PackageJsonDepResolution::Disabled,
|
||||
))
|
||||
},
|
||||
),
|
||||
maybe_vendor_dir: config_data.and_then(|d| d.vendor_dir.as_ref()),
|
||||
bare_node_builtins_enabled: config_data
|
||||
.is_some_and(|d| d.unstable.contains("bare-node-builtins")),
|
||||
sloppy_imports_resolver: config_data
|
||||
.and_then(|d| d.sloppy_imports_resolver.clone()),
|
||||
}))
|
||||
pub fn npm_pkg_req_resolver(&self) -> Option<&Arc<CliNpmReqResolver>> {
|
||||
self
|
||||
.services
|
||||
.npm_pkg_req_resolver
|
||||
.get_or_init(|| {
|
||||
let node_resolver = self.node_resolver()?;
|
||||
let npm_resolver = self.npm_resolver()?;
|
||||
Some(Arc::new(CliNpmReqResolver::new(NpmReqResolverOptions {
|
||||
byonm_resolver: (npm_resolver.clone()).into_maybe_byonm(),
|
||||
fs: CliDenoResolverFs(self.fs.clone()),
|
||||
in_npm_pkg_checker: self.in_npm_pkg_checker().clone(),
|
||||
node_resolver: node_resolver.clone(),
|
||||
npm_req_resolver: npm_resolver.clone().into_npm_req_resolver(),
|
||||
})))
|
||||
})
|
||||
.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
|
|
|
@ -37,6 +37,7 @@ use crate::util::v8::init_v8_flags;
|
|||
|
||||
use args::TaskFlags;
|
||||
use deno_resolver::npm::ByonmResolvePkgFolderFromDenoReqError;
|
||||
use deno_resolver::npm::ResolvePkgFolderFromDenoReqError;
|
||||
use deno_runtime::WorkerExecutionMode;
|
||||
pub use deno_runtime::UNSTABLE_GRANULAR_FLAGS;
|
||||
|
||||
|
@ -50,7 +51,6 @@ use deno_runtime::fmt_errors::format_js_error;
|
|||
use deno_runtime::tokio_util::create_and_run_current_thread_with_maybe_metrics;
|
||||
use deno_terminal::colors;
|
||||
use factory::CliFactory;
|
||||
use npm::ResolvePkgFolderFromDenoReqError;
|
||||
use standalone::MODULE_NOT_FOUND;
|
||||
use standalone::UNSUPPORTED_SCHEME;
|
||||
use std::env;
|
||||
|
|
|
@ -27,7 +27,7 @@ use crate::node;
|
|||
use crate::node::CliNodeCodeTranslator;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::resolver::CjsTracker;
|
||||
use crate::resolver::CliNodeResolver;
|
||||
use crate::resolver::CliNpmReqResolver;
|
||||
use crate::resolver::CliResolver;
|
||||
use crate::resolver::ModuleCodeStringSource;
|
||||
use crate::resolver::NotSupportedKindInNpmError;
|
||||
|
@ -70,6 +70,7 @@ use deno_runtime::code_cache;
|
|||
use deno_runtime::deno_fs::FileSystem;
|
||||
use deno_runtime::deno_node::create_host_defined_options;
|
||||
use deno_runtime::deno_node::NodeRequireLoader;
|
||||
use deno_runtime::deno_node::NodeResolver;
|
||||
use deno_runtime::deno_permissions::PermissionsContainer;
|
||||
use deno_semver::npm::NpmPackageReqReference;
|
||||
use node_resolver::errors::ClosestPkgJsonError;
|
||||
|
@ -215,7 +216,8 @@ struct SharedCliModuleLoaderState {
|
|||
main_module_graph_container: Arc<MainModuleGraphContainer>,
|
||||
module_load_preparer: Arc<ModuleLoadPreparer>,
|
||||
node_code_translator: Arc<CliNodeCodeTranslator>,
|
||||
node_resolver: Arc<CliNodeResolver>,
|
||||
node_resolver: Arc<NodeResolver>,
|
||||
npm_req_resolver: Arc<CliNpmReqResolver>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
npm_module_loader: NpmModuleLoader,
|
||||
parsed_source_cache: Arc<ParsedSourceCache>,
|
||||
|
@ -238,7 +240,8 @@ impl CliModuleLoaderFactory {
|
|||
main_module_graph_container: Arc<MainModuleGraphContainer>,
|
||||
module_load_preparer: Arc<ModuleLoadPreparer>,
|
||||
node_code_translator: Arc<CliNodeCodeTranslator>,
|
||||
node_resolver: Arc<CliNodeResolver>,
|
||||
node_resolver: Arc<NodeResolver>,
|
||||
npm_req_resolver: Arc<CliNpmReqResolver>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
npm_module_loader: NpmModuleLoader,
|
||||
parsed_source_cache: Arc<ParsedSourceCache>,
|
||||
|
@ -264,6 +267,7 @@ impl CliModuleLoaderFactory {
|
|||
module_load_preparer,
|
||||
node_code_translator,
|
||||
node_resolver,
|
||||
npm_req_resolver,
|
||||
npm_resolver,
|
||||
npm_module_loader,
|
||||
parsed_source_cache,
|
||||
|
@ -425,7 +429,7 @@ impl<TGraphContainer: ModuleGraphContainer>
|
|||
if let Some(code_source) = self.load_prepared_module(specifier).await? {
|
||||
return Ok(code_source);
|
||||
}
|
||||
if self.shared.node_resolver.in_npm_package(specifier) {
|
||||
if self.shared.in_npm_pkg_checker.in_npm_package(specifier) {
|
||||
return self
|
||||
.shared
|
||||
.npm_module_loader
|
||||
|
@ -470,7 +474,7 @@ impl<TGraphContainer: ModuleGraphContainer>
|
|||
raw_specifier: &str,
|
||||
referrer: &ModuleSpecifier,
|
||||
) -> Result<ModuleSpecifier, AnyError> {
|
||||
if self.shared.node_resolver.in_npm_package(referrer) {
|
||||
if self.shared.in_npm_pkg_checker.in_npm_package(referrer) {
|
||||
return Ok(
|
||||
self
|
||||
.shared
|
||||
|
@ -518,12 +522,16 @@ impl<TGraphContainer: ModuleGraphContainer>
|
|||
if self.shared.is_repl {
|
||||
if let Ok(reference) = NpmPackageReqReference::from_specifier(&specifier)
|
||||
{
|
||||
return self.shared.node_resolver.resolve_req_reference(
|
||||
&reference,
|
||||
referrer,
|
||||
self.shared.cjs_tracker.get_referrer_kind(referrer),
|
||||
NodeResolutionMode::Execution,
|
||||
);
|
||||
return self
|
||||
.shared
|
||||
.npm_req_resolver
|
||||
.resolve_req_reference(
|
||||
&reference,
|
||||
referrer,
|
||||
self.shared.cjs_tracker.get_referrer_kind(referrer),
|
||||
NodeResolutionMode::Execution,
|
||||
)
|
||||
.map_err(AnyError::from);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -538,7 +546,7 @@ impl<TGraphContainer: ModuleGraphContainer>
|
|||
self
|
||||
.shared
|
||||
.node_resolver
|
||||
.resolve_package_sub_path_from_deno_module(
|
||||
.resolve_package_subpath_from_deno_module(
|
||||
&package_folder,
|
||||
module.nv_reference.sub_path(),
|
||||
Some(referrer),
|
||||
|
@ -828,7 +836,7 @@ impl<TGraphContainer: ModuleGraphContainer> ModuleLoader
|
|||
name: &str,
|
||||
) -> Option<deno_core::v8::Local<'s, deno_core::v8::Data>> {
|
||||
let name = deno_core::ModuleSpecifier::parse(name).ok()?;
|
||||
if self.0.shared.node_resolver.in_npm_package(&name) {
|
||||
if self.0.shared.in_npm_pkg_checker.in_npm_package(&name) {
|
||||
Some(create_host_defined_options(scope))
|
||||
} else {
|
||||
None
|
||||
|
@ -865,7 +873,7 @@ impl<TGraphContainer: ModuleGraphContainer> ModuleLoader
|
|||
_maybe_referrer: Option<String>,
|
||||
is_dynamic: bool,
|
||||
) -> Pin<Box<dyn Future<Output = Result<(), AnyError>>>> {
|
||||
if self.0.shared.node_resolver.in_npm_package(specifier) {
|
||||
if self.0.shared.in_npm_pkg_checker.in_npm_package(specifier) {
|
||||
return Box::pin(deno_core::futures::future::ready(Ok(())));
|
||||
}
|
||||
|
||||
|
|
13
cli/node.rs
13
cli/node.rs
|
@ -7,8 +7,6 @@ use deno_ast::MediaType;
|
|||
use deno_ast::ModuleSpecifier;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_graph::ParsedSourceStore;
|
||||
use deno_path_util::url_from_file_path;
|
||||
use deno_path_util::url_to_file_path;
|
||||
use deno_runtime::deno_fs;
|
||||
use deno_runtime::deno_node::DenoFsNodeResolverEnv;
|
||||
use node_resolver::analyze::CjsAnalysis as ExtNodeCjsAnalysis;
|
||||
|
@ -22,7 +20,6 @@ use crate::cache::CacheDBHash;
|
|||
use crate::cache::NodeAnalysisCache;
|
||||
use crate::cache::ParsedSourceCache;
|
||||
use crate::resolver::CjsTracker;
|
||||
use crate::util::fs::canonicalize_path_maybe_not_exists_with_fs;
|
||||
|
||||
pub type CliNodeCodeTranslator =
|
||||
NodeCodeTranslator<CliCjsCodeAnalyzer, DenoFsNodeResolverEnv>;
|
||||
|
@ -37,13 +34,9 @@ pub fn resolve_specifier_into_node_modules(
|
|||
specifier: &ModuleSpecifier,
|
||||
fs: &dyn deno_fs::FileSystem,
|
||||
) -> ModuleSpecifier {
|
||||
url_to_file_path(specifier)
|
||||
.ok()
|
||||
// this path might not exist at the time the graph is being created
|
||||
// because the node_modules folder might not yet exist
|
||||
.and_then(|path| canonicalize_path_maybe_not_exists_with_fs(&path, fs).ok())
|
||||
.and_then(|path| url_from_file_path(&path).ok())
|
||||
.unwrap_or_else(|| specifier.clone())
|
||||
node_resolver::resolve_specifier_into_node_modules(specifier, &|path| {
|
||||
fs.realpath_sync(path).map_err(|err| err.into_io_error())
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
|
|
|
@ -2,19 +2,17 @@
|
|||
|
||||
use std::borrow::Cow;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::serde_json;
|
||||
use deno_core::url::Url;
|
||||
use deno_resolver::npm::ByonmNpmResolver;
|
||||
use deno_resolver::npm::ByonmNpmResolverCreateOptions;
|
||||
use deno_resolver::npm::CliNpmReqResolver;
|
||||
use deno_runtime::deno_node::DenoFsNodeResolverEnv;
|
||||
use deno_runtime::deno_node::NodePermissions;
|
||||
use deno_runtime::ops::process::NpmProcessStateProvider;
|
||||
use deno_semver::package::PackageReq;
|
||||
use node_resolver::NpmResolver;
|
||||
use node_resolver::NpmPackageFolderResolver;
|
||||
|
||||
use crate::args::NpmProcessState;
|
||||
use crate::args::NpmProcessStateKind;
|
||||
|
@ -22,7 +20,6 @@ use crate::resolver::CliDenoResolverFs;
|
|||
|
||||
use super::CliNpmResolver;
|
||||
use super::InnerCliNpmResolverRef;
|
||||
use super::ResolvePkgFolderFromDenoReqError;
|
||||
|
||||
pub type CliByonmNpmResolverCreateOptions =
|
||||
ByonmNpmResolverCreateOptions<CliDenoResolverFs, DenoFsNodeResolverEnv>;
|
||||
|
@ -47,7 +44,13 @@ impl NpmProcessStateProvider for CliByonmWrapper {
|
|||
}
|
||||
|
||||
impl CliNpmResolver for CliByonmNpmResolver {
|
||||
fn into_npm_resolver(self: Arc<Self>) -> Arc<dyn NpmResolver> {
|
||||
fn into_npm_pkg_folder_resolver(
|
||||
self: Arc<Self>,
|
||||
) -> Arc<dyn NpmPackageFolderResolver> {
|
||||
self
|
||||
}
|
||||
|
||||
fn into_npm_req_resolver(self: Arc<Self>) -> Arc<dyn CliNpmReqResolver> {
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -57,6 +60,10 @@ impl CliNpmResolver for CliByonmNpmResolver {
|
|||
Arc::new(CliByonmWrapper(self))
|
||||
}
|
||||
|
||||
fn into_maybe_byonm(self: Arc<Self>) -> Option<Arc<CliByonmNpmResolver>> {
|
||||
Some(self)
|
||||
}
|
||||
|
||||
fn clone_snapshotted(&self) -> Arc<dyn CliNpmResolver> {
|
||||
Arc::new(self.clone())
|
||||
}
|
||||
|
@ -69,17 +76,6 @@ impl CliNpmResolver for CliByonmNpmResolver {
|
|||
self.root_node_modules_dir()
|
||||
}
|
||||
|
||||
fn resolve_pkg_folder_from_deno_module_req(
|
||||
&self,
|
||||
req: &PackageReq,
|
||||
referrer: &Url,
|
||||
) -> Result<PathBuf, ResolvePkgFolderFromDenoReqError> {
|
||||
ByonmNpmResolver::resolve_pkg_folder_from_deno_module_req(
|
||||
self, req, referrer,
|
||||
)
|
||||
.map_err(ResolvePkgFolderFromDenoReqError::Byonm)
|
||||
}
|
||||
|
||||
fn ensure_read_permission<'a>(
|
||||
&self,
|
||||
permissions: &mut dyn NodePermissions,
|
||||
|
|
|
@ -22,6 +22,7 @@ use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot;
|
|||
use deno_npm::NpmPackageId;
|
||||
use deno_npm::NpmResolutionPackage;
|
||||
use deno_npm::NpmSystemInfo;
|
||||
use deno_resolver::npm::CliNpmReqResolver;
|
||||
use deno_runtime::colors;
|
||||
use deno_runtime::deno_fs::FileSystem;
|
||||
use deno_runtime::deno_node::NodePermissions;
|
||||
|
@ -31,7 +32,7 @@ use deno_semver::package::PackageReq;
|
|||
use node_resolver::errors::PackageFolderResolveError;
|
||||
use node_resolver::errors::PackageFolderResolveIoError;
|
||||
use node_resolver::InNpmPackageChecker;
|
||||
use node_resolver::NpmResolver;
|
||||
use node_resolver::NpmPackageFolderResolver;
|
||||
use resolution::AddPkgReqsResult;
|
||||
|
||||
use crate::args::CliLockfile;
|
||||
|
@ -605,7 +606,7 @@ fn npm_process_state(
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
impl NpmResolver for ManagedCliNpmResolver {
|
||||
impl NpmPackageFolderResolver for ManagedCliNpmResolver {
|
||||
fn resolve_package_folder_from_package(
|
||||
&self,
|
||||
name: &str,
|
||||
|
@ -635,8 +636,29 @@ impl NpmProcessStateProvider for ManagedCliNpmResolver {
|
|||
}
|
||||
}
|
||||
|
||||
impl CliNpmReqResolver for ManagedCliNpmResolver {
|
||||
fn resolve_pkg_folder_from_deno_module_req(
|
||||
&self,
|
||||
req: &PackageReq,
|
||||
_referrer: &ModuleSpecifier,
|
||||
) -> Result<PathBuf, ResolvePkgFolderFromDenoReqError> {
|
||||
let pkg_id = self
|
||||
.resolve_pkg_id_from_pkg_req(req)
|
||||
.map_err(|err| ResolvePkgFolderFromDenoReqError::Managed(err.into()))?;
|
||||
self
|
||||
.resolve_pkg_folder_from_pkg_id(&pkg_id)
|
||||
.map_err(ResolvePkgFolderFromDenoReqError::Managed)
|
||||
}
|
||||
}
|
||||
|
||||
impl CliNpmResolver for ManagedCliNpmResolver {
|
||||
fn into_npm_resolver(self: Arc<Self>) -> Arc<dyn NpmResolver> {
|
||||
fn into_npm_pkg_folder_resolver(
|
||||
self: Arc<Self>,
|
||||
) -> Arc<dyn NpmPackageFolderResolver> {
|
||||
self
|
||||
}
|
||||
|
||||
fn into_npm_req_resolver(self: Arc<Self>) -> Arc<dyn CliNpmReqResolver> {
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -687,19 +709,6 @@ impl CliNpmResolver for ManagedCliNpmResolver {
|
|||
self.fs_resolver.node_modules_path()
|
||||
}
|
||||
|
||||
fn resolve_pkg_folder_from_deno_module_req(
|
||||
&self,
|
||||
req: &PackageReq,
|
||||
_referrer: &ModuleSpecifier,
|
||||
) -> Result<PathBuf, ResolvePkgFolderFromDenoReqError> {
|
||||
let pkg_id = self
|
||||
.resolve_pkg_id_from_pkg_req(req)
|
||||
.map_err(|err| ResolvePkgFolderFromDenoReqError::Managed(err.into()))?;
|
||||
self
|
||||
.resolve_pkg_folder_from_pkg_id(&pkg_id)
|
||||
.map_err(ResolvePkgFolderFromDenoReqError::Managed)
|
||||
}
|
||||
|
||||
fn ensure_read_permission<'a>(
|
||||
&self,
|
||||
permissions: &mut dyn NodePermissions,
|
||||
|
|
|
@ -6,19 +6,18 @@ mod managed;
|
|||
|
||||
use std::borrow::Cow;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use common::maybe_auth_header_for_npm_registry;
|
||||
use dashmap::DashMap;
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::serde_json;
|
||||
use deno_npm::npm_rc::ResolvedNpmRc;
|
||||
use deno_npm::registry::NpmPackageInfo;
|
||||
use deno_resolver::npm::ByonmInNpmPackageChecker;
|
||||
use deno_resolver::npm::ByonmNpmResolver;
|
||||
use deno_resolver::npm::ByonmResolvePkgFolderFromDenoReqError;
|
||||
use deno_resolver::npm::CliNpmReqResolver;
|
||||
use deno_resolver::npm::ResolvePkgFolderFromDenoReqError;
|
||||
use deno_runtime::deno_node::NodePermissions;
|
||||
use deno_runtime::ops::process::NpmProcessStateProvider;
|
||||
use deno_semver::package::PackageNv;
|
||||
|
@ -26,8 +25,7 @@ use deno_semver::package::PackageReq;
|
|||
use managed::cache::registry_info::get_package_url;
|
||||
use managed::create_managed_in_npm_pkg_checker;
|
||||
use node_resolver::InNpmPackageChecker;
|
||||
use node_resolver::NpmResolver;
|
||||
use thiserror::Error;
|
||||
use node_resolver::NpmPackageFolderResolver;
|
||||
|
||||
use crate::file_fetcher::FileFetcher;
|
||||
|
||||
|
@ -38,14 +36,6 @@ pub use self::managed::CliManagedNpmResolverCreateOptions;
|
|||
pub use self::managed::CliNpmResolverManagedSnapshotOption;
|
||||
pub use self::managed::ManagedCliNpmResolver;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ResolvePkgFolderFromDenoReqError {
|
||||
#[error(transparent)]
|
||||
Managed(deno_core::error::AnyError),
|
||||
#[error(transparent)]
|
||||
Byonm(#[from] ByonmResolvePkgFolderFromDenoReqError),
|
||||
}
|
||||
|
||||
pub enum CliNpmResolverCreateOptions {
|
||||
Managed(CliManagedNpmResolverCreateOptions),
|
||||
Byonm(CliByonmNpmResolverCreateOptions),
|
||||
|
@ -95,11 +85,17 @@ pub enum InnerCliNpmResolverRef<'a> {
|
|||
Byonm(&'a CliByonmNpmResolver),
|
||||
}
|
||||
|
||||
pub trait CliNpmResolver: NpmResolver {
|
||||
fn into_npm_resolver(self: Arc<Self>) -> Arc<dyn NpmResolver>;
|
||||
pub trait CliNpmResolver: NpmPackageFolderResolver + CliNpmReqResolver {
|
||||
fn into_npm_pkg_folder_resolver(
|
||||
self: Arc<Self>,
|
||||
) -> Arc<dyn NpmPackageFolderResolver>;
|
||||
fn into_npm_req_resolver(self: Arc<Self>) -> Arc<dyn CliNpmReqResolver>;
|
||||
fn into_process_state_provider(
|
||||
self: Arc<Self>,
|
||||
) -> Arc<dyn NpmProcessStateProvider>;
|
||||
fn into_maybe_byonm(self: Arc<Self>) -> Option<Arc<CliByonmNpmResolver>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn clone_snapshotted(&self) -> Arc<dyn CliNpmResolver>;
|
||||
|
||||
|
@ -121,12 +117,6 @@ pub trait CliNpmResolver: NpmResolver {
|
|||
|
||||
fn root_node_modules_path(&self) -> Option<&Path>;
|
||||
|
||||
fn resolve_pkg_folder_from_deno_module_req(
|
||||
&self,
|
||||
req: &PackageReq,
|
||||
referrer: &ModuleSpecifier,
|
||||
) -> Result<PathBuf, ResolvePkgFolderFromDenoReqError>;
|
||||
|
||||
fn ensure_read_permission<'a>(
|
||||
&self,
|
||||
permissions: &mut dyn NodePermissions,
|
||||
|
|
769
cli/resolver.rs
769
cli/resolver.rs
|
@ -4,10 +4,8 @@ use async_trait::async_trait;
|
|||
use dashmap::DashMap;
|
||||
use dashmap::DashSet;
|
||||
use deno_ast::MediaType;
|
||||
use deno_config::workspace::MappedResolution;
|
||||
use deno_config::workspace::MappedResolutionDiagnostic;
|
||||
use deno_config::workspace::MappedResolutionError;
|
||||
use deno_config::workspace::WorkspaceResolver;
|
||||
use deno_core::anyhow::anyhow;
|
||||
use deno_core::anyhow::Context;
|
||||
use deno_core::error::AnyError;
|
||||
|
@ -20,28 +18,14 @@ use deno_graph::source::UnknownBuiltInNodeModuleError;
|
|||
use deno_graph::NpmLoadError;
|
||||
use deno_graph::NpmResolvePkgReqsResult;
|
||||
use deno_npm::resolution::NpmResolutionError;
|
||||
use deno_package_json::PackageJsonDepValue;
|
||||
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;
|
||||
use deno_runtime::deno_node::is_builtin_node_module;
|
||||
use deno_runtime::deno_node::NodeResolver;
|
||||
use deno_runtime::deno_node::PackageJsonResolver;
|
||||
use deno_semver::npm::NpmPackageReqReference;
|
||||
use deno_runtime::deno_node::DenoFsNodeResolverEnv;
|
||||
use deno_semver::package::PackageReq;
|
||||
use node_resolver::errors::ClosestPkgJsonError;
|
||||
use node_resolver::errors::NodeResolveError;
|
||||
use node_resolver::errors::NodeResolveErrorKind;
|
||||
use node_resolver::errors::PackageFolderResolveErrorKind;
|
||||
use node_resolver::errors::PackageFolderResolveIoError;
|
||||
use node_resolver::errors::PackageNotFoundError;
|
||||
use node_resolver::errors::PackageResolveErrorKind;
|
||||
use node_resolver::errors::PackageSubpathResolveError;
|
||||
use node_resolver::InNpmPackageChecker;
|
||||
use node_resolver::NodeModuleKind;
|
||||
use node_resolver::NodeResolution;
|
||||
use node_resolver::NodeResolutionMode;
|
||||
use std::borrow::Cow;
|
||||
use std::path::Path;
|
||||
|
@ -56,6 +40,20 @@ use crate::npm::InnerCliNpmResolverRef;
|
|||
use crate::util::sync::AtomicFlag;
|
||||
use crate::util::text_encoding::from_utf8_lossy_owned;
|
||||
|
||||
pub type CjsTracker = deno_resolver::cjs::CjsTracker<DenoFsNodeResolverEnv>;
|
||||
pub type IsCjsResolver =
|
||||
deno_resolver::cjs::IsCjsResolver<DenoFsNodeResolverEnv>;
|
||||
pub type IsCjsResolverOptions = deno_resolver::cjs::IsCjsResolverOptions;
|
||||
pub type CliSloppyImportsResolver =
|
||||
SloppyImportsResolver<SloppyImportsCachedFs>;
|
||||
pub type CliDenoResolver = deno_resolver::DenoResolver<
|
||||
CliDenoResolverFs,
|
||||
DenoFsNodeResolverEnv,
|
||||
SloppyImportsCachedFs,
|
||||
>;
|
||||
pub type CliNpmReqResolver =
|
||||
deno_resolver::npm::NpmReqResolver<CliDenoResolverFs, DenoFsNodeResolverEnv>;
|
||||
|
||||
pub struct ModuleCodeStringSource {
|
||||
pub code: ModuleSourceCode,
|
||||
pub found_url: ModuleSpecifier,
|
||||
|
@ -77,6 +75,10 @@ impl deno_resolver::fs::DenoResolverFs for CliDenoResolverFs {
|
|||
self.0.realpath_sync(path).map_err(|e| e.into_io_error())
|
||||
}
|
||||
|
||||
fn exists_sync(&self, path: &Path) -> bool {
|
||||
self.0.exists_sync(path)
|
||||
}
|
||||
|
||||
fn is_dir_sync(&self, path: &Path) -> bool {
|
||||
self.0.is_dir_sync(path)
|
||||
}
|
||||
|
@ -102,211 +104,6 @@ impl deno_resolver::fs::DenoResolverFs for CliDenoResolverFs {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CliNodeResolver {
|
||||
fs: Arc<dyn deno_fs::FileSystem>,
|
||||
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
|
||||
node_resolver: Arc<NodeResolver>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
}
|
||||
|
||||
impl CliNodeResolver {
|
||||
pub fn new(
|
||||
fs: Arc<dyn deno_fs::FileSystem>,
|
||||
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
|
||||
node_resolver: Arc<NodeResolver>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
) -> Self {
|
||||
Self {
|
||||
fs,
|
||||
in_npm_pkg_checker,
|
||||
node_resolver,
|
||||
npm_resolver,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn in_npm_package(&self, specifier: &ModuleSpecifier) -> bool {
|
||||
self.in_npm_pkg_checker.in_npm_package(specifier)
|
||||
}
|
||||
|
||||
pub fn resolve_if_for_npm_pkg(
|
||||
&self,
|
||||
specifier: &str,
|
||||
referrer: &ModuleSpecifier,
|
||||
referrer_kind: NodeModuleKind,
|
||||
mode: NodeResolutionMode,
|
||||
) -> Result<Option<NodeResolution>, AnyError> {
|
||||
let resolution_result =
|
||||
self.resolve(specifier, referrer, referrer_kind, mode);
|
||||
match resolution_result {
|
||||
Ok(res) => Ok(Some(res)),
|
||||
Err(err) => {
|
||||
let err = err.into_kind();
|
||||
match err {
|
||||
NodeResolveErrorKind::RelativeJoin(_)
|
||||
| NodeResolveErrorKind::PackageImportsResolve(_)
|
||||
| NodeResolveErrorKind::UnsupportedEsmUrlScheme(_)
|
||||
| NodeResolveErrorKind::DataUrlReferrer(_)
|
||||
| NodeResolveErrorKind::TypesNotFound(_)
|
||||
| NodeResolveErrorKind::FinalizeResolution(_) => Err(err.into()),
|
||||
NodeResolveErrorKind::PackageResolve(err) => {
|
||||
let err = err.into_kind();
|
||||
match err {
|
||||
PackageResolveErrorKind::ClosestPkgJson(_)
|
||||
| PackageResolveErrorKind::InvalidModuleSpecifier(_)
|
||||
| PackageResolveErrorKind::ExportsResolve(_)
|
||||
| PackageResolveErrorKind::SubpathResolve(_) => Err(err.into()),
|
||||
PackageResolveErrorKind::PackageFolderResolve(err) => {
|
||||
match err.as_kind() {
|
||||
PackageFolderResolveErrorKind::Io(
|
||||
PackageFolderResolveIoError { package_name, .. },
|
||||
)
|
||||
| PackageFolderResolveErrorKind::PackageNotFound(
|
||||
PackageNotFoundError { package_name, .. },
|
||||
) => {
|
||||
if self.in_npm_package(referrer) {
|
||||
return Err(err.into());
|
||||
}
|
||||
if let Some(byonm_npm_resolver) =
|
||||
self.npm_resolver.as_byonm()
|
||||
{
|
||||
if byonm_npm_resolver
|
||||
.find_ancestor_package_json_with_dep(
|
||||
package_name,
|
||||
referrer,
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
return Err(anyhow!(
|
||||
concat!(
|
||||
"Could not resolve \"{}\", but found it in a package.json. ",
|
||||
"Deno expects the node_modules/ directory to be up to date. ",
|
||||
"Did you forget to run `deno install`?"
|
||||
),
|
||||
specifier
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
PackageFolderResolveErrorKind::ReferrerNotFound(_) => {
|
||||
if self.in_npm_package(referrer) {
|
||||
return Err(err.into());
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve(
|
||||
&self,
|
||||
specifier: &str,
|
||||
referrer: &ModuleSpecifier,
|
||||
referrer_kind: NodeModuleKind,
|
||||
mode: NodeResolutionMode,
|
||||
) -> Result<NodeResolution, NodeResolveError> {
|
||||
self
|
||||
.node_resolver
|
||||
.resolve(specifier, referrer, referrer_kind, mode)
|
||||
}
|
||||
|
||||
pub fn resolve_req_reference(
|
||||
&self,
|
||||
req_ref: &NpmPackageReqReference,
|
||||
referrer: &ModuleSpecifier,
|
||||
referrer_kind: NodeModuleKind,
|
||||
mode: NodeResolutionMode,
|
||||
) -> Result<ModuleSpecifier, AnyError> {
|
||||
self.resolve_req_with_sub_path(
|
||||
req_ref.req(),
|
||||
req_ref.sub_path(),
|
||||
referrer,
|
||||
referrer_kind,
|
||||
mode,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn resolve_req_with_sub_path(
|
||||
&self,
|
||||
req: &PackageReq,
|
||||
sub_path: Option<&str>,
|
||||
referrer: &ModuleSpecifier,
|
||||
referrer_kind: NodeModuleKind,
|
||||
mode: NodeResolutionMode,
|
||||
) -> Result<ModuleSpecifier, AnyError> {
|
||||
let package_folder = self
|
||||
.npm_resolver
|
||||
.resolve_pkg_folder_from_deno_module_req(req, referrer)?;
|
||||
let resolution_result = self.resolve_package_sub_path_from_deno_module(
|
||||
&package_folder,
|
||||
sub_path,
|
||||
Some(referrer),
|
||||
referrer_kind,
|
||||
mode,
|
||||
);
|
||||
match resolution_result {
|
||||
Ok(url) => Ok(url),
|
||||
Err(err) => {
|
||||
if self.npm_resolver.as_byonm().is_some() {
|
||||
let package_json_path = package_folder.join("package.json");
|
||||
if !self.fs.exists_sync(&package_json_path) {
|
||||
return Err(anyhow!(
|
||||
"Could not find '{}'. Deno expects the node_modules/ directory to be up to date. Did you forget to run `deno install`?",
|
||||
package_json_path.display(),
|
||||
));
|
||||
}
|
||||
}
|
||||
Err(err.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_package_sub_path_from_deno_module(
|
||||
&self,
|
||||
package_folder: &Path,
|
||||
sub_path: Option<&str>,
|
||||
maybe_referrer: Option<&ModuleSpecifier>,
|
||||
referrer_kind: NodeModuleKind,
|
||||
mode: NodeResolutionMode,
|
||||
) -> Result<ModuleSpecifier, PackageSubpathResolveError> {
|
||||
self.node_resolver.resolve_package_subpath_from_deno_module(
|
||||
package_folder,
|
||||
sub_path,
|
||||
maybe_referrer,
|
||||
referrer_kind,
|
||||
mode,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn handle_if_in_node_modules(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
) -> Result<Option<ModuleSpecifier>, AnyError> {
|
||||
// skip canonicalizing if we definitely know it's unnecessary
|
||||
if specifier.scheme() == "file"
|
||||
&& specifier.path().contains("/node_modules/")
|
||||
{
|
||||
// Specifiers in the node_modules directory are canonicalized
|
||||
// so canoncalize then check if it's in the node_modules directory.
|
||||
// If so, check if we need to store this specifier as being a CJS
|
||||
// resolution.
|
||||
let specifier = crate::node::resolve_specifier_into_node_modules(
|
||||
specifier,
|
||||
self.fs.as_ref(),
|
||||
);
|
||||
return Ok(Some(specifier));
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
#[error("{media_type} files are not supported in npm packages: {specifier}")]
|
||||
pub struct NotSupportedKindInNpmError {
|
||||
|
@ -409,305 +206,36 @@ impl NpmModuleLoader {
|
|||
}
|
||||
}
|
||||
|
||||
/// Keeps track of what module specifiers were resolved as CJS.
|
||||
///
|
||||
/// Modules that are `.js` or `.ts` are only known to be CJS or
|
||||
/// ESM after they're loaded based on their contents. So these files
|
||||
/// will be "maybe CJS" until they're loaded.
|
||||
#[derive(Debug)]
|
||||
pub struct CjsTracker {
|
||||
is_cjs_resolver: IsCjsResolver,
|
||||
known: DashMap<ModuleSpecifier, NodeModuleKind>,
|
||||
pub struct CliResolverOptions {
|
||||
pub deno_resolver: Arc<CliDenoResolver>,
|
||||
pub npm_resolver: Option<Arc<dyn CliNpmResolver>>,
|
||||
pub bare_node_builtins_enabled: bool,
|
||||
}
|
||||
|
||||
impl CjsTracker {
|
||||
pub fn new(
|
||||
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
|
||||
pkg_json_resolver: Arc<PackageJsonResolver>,
|
||||
options: IsCjsResolverOptions,
|
||||
) -> Self {
|
||||
Self {
|
||||
is_cjs_resolver: IsCjsResolver::new(
|
||||
in_npm_pkg_checker,
|
||||
pkg_json_resolver,
|
||||
options,
|
||||
),
|
||||
known: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether the file might be treated as CJS, but it's not for sure
|
||||
/// yet because the source hasn't been loaded to see whether it contains
|
||||
/// imports or exports.
|
||||
pub fn is_maybe_cjs(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
media_type: MediaType,
|
||||
) -> Result<bool, ClosestPkgJsonError> {
|
||||
self.treat_as_cjs_with_is_script(specifier, media_type, None)
|
||||
}
|
||||
|
||||
/// Gets whether the file is CJS. If true, this is for sure
|
||||
/// cjs because `is_script` is provided.
|
||||
///
|
||||
/// `is_script` should be `true` when the contents of the file at the
|
||||
/// provided specifier are known to be a script and not an ES module.
|
||||
pub fn is_cjs_with_known_is_script(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
media_type: MediaType,
|
||||
is_script: bool,
|
||||
) -> Result<bool, ClosestPkgJsonError> {
|
||||
self.treat_as_cjs_with_is_script(specifier, media_type, Some(is_script))
|
||||
}
|
||||
|
||||
fn treat_as_cjs_with_is_script(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
media_type: MediaType,
|
||||
is_script: Option<bool>,
|
||||
) -> Result<bool, ClosestPkgJsonError> {
|
||||
let kind = match self
|
||||
.get_known_kind_with_is_script(specifier, media_type, is_script)
|
||||
{
|
||||
Some(kind) => kind,
|
||||
None => self.is_cjs_resolver.check_based_on_pkg_json(specifier)?,
|
||||
};
|
||||
Ok(kind == NodeModuleKind::Cjs)
|
||||
}
|
||||
|
||||
pub fn get_known_kind(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
media_type: MediaType,
|
||||
) -> Option<NodeModuleKind> {
|
||||
self.get_known_kind_with_is_script(specifier, media_type, None)
|
||||
}
|
||||
|
||||
pub fn get_referrer_kind(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
) -> NodeModuleKind {
|
||||
if specifier.scheme() != "file" {
|
||||
return NodeModuleKind::Esm;
|
||||
}
|
||||
self
|
||||
.get_known_kind(specifier, MediaType::from_specifier(specifier))
|
||||
.unwrap_or(NodeModuleKind::Esm)
|
||||
}
|
||||
|
||||
fn get_known_kind_with_is_script(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
media_type: MediaType,
|
||||
is_script: Option<bool>,
|
||||
) -> Option<NodeModuleKind> {
|
||||
self.is_cjs_resolver.get_known_kind_with_is_script(
|
||||
specifier,
|
||||
media_type,
|
||||
is_script,
|
||||
&self.known,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IsCjsResolverOptions {
|
||||
pub detect_cjs: bool,
|
||||
pub is_node_main: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IsCjsResolver {
|
||||
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
|
||||
pkg_json_resolver: Arc<PackageJsonResolver>,
|
||||
options: IsCjsResolverOptions,
|
||||
}
|
||||
|
||||
impl IsCjsResolver {
|
||||
pub fn new(
|
||||
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
|
||||
pkg_json_resolver: Arc<PackageJsonResolver>,
|
||||
options: IsCjsResolverOptions,
|
||||
) -> Self {
|
||||
Self {
|
||||
in_npm_pkg_checker,
|
||||
pkg_json_resolver,
|
||||
options,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_lsp_referrer_kind(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
is_script: Option<bool>,
|
||||
) -> NodeModuleKind {
|
||||
if specifier.scheme() != "file" {
|
||||
return NodeModuleKind::Esm;
|
||||
}
|
||||
match MediaType::from_specifier(specifier) {
|
||||
MediaType::Mts | MediaType::Mjs | MediaType::Dmts => NodeModuleKind::Esm,
|
||||
MediaType::Cjs | MediaType::Cts | MediaType::Dcts => NodeModuleKind::Cjs,
|
||||
MediaType::Dts => {
|
||||
// dts files are always determined based on the package.json because
|
||||
// they contain imports/exports even when considered CJS
|
||||
self.check_based_on_pkg_json(specifier).unwrap_or(NodeModuleKind::Esm)
|
||||
}
|
||||
MediaType::Wasm |
|
||||
MediaType::Json => NodeModuleKind::Esm,
|
||||
MediaType::JavaScript
|
||||
| MediaType::Jsx
|
||||
| MediaType::TypeScript
|
||||
| MediaType::Tsx
|
||||
// treat these as unknown
|
||||
| MediaType::Css
|
||||
| MediaType::SourceMap
|
||||
| MediaType::Unknown => {
|
||||
match is_script {
|
||||
Some(true) => self.check_based_on_pkg_json(specifier).unwrap_or(NodeModuleKind::Esm),
|
||||
Some(false) | None => NodeModuleKind::Esm,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_known_kind_with_is_script(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
media_type: MediaType,
|
||||
is_script: Option<bool>,
|
||||
known_cache: &DashMap<ModuleSpecifier, NodeModuleKind>,
|
||||
) -> Option<NodeModuleKind> {
|
||||
if specifier.scheme() != "file" {
|
||||
return Some(NodeModuleKind::Esm);
|
||||
}
|
||||
|
||||
match media_type {
|
||||
MediaType::Mts | MediaType::Mjs | MediaType::Dmts => Some(NodeModuleKind::Esm),
|
||||
MediaType::Cjs | MediaType::Cts | MediaType::Dcts => Some(NodeModuleKind::Cjs),
|
||||
MediaType::Dts => {
|
||||
// dts files are always determined based on the package.json because
|
||||
// they contain imports/exports even when considered CJS
|
||||
if let Some(value) = known_cache.get(specifier).map(|v| *v) {
|
||||
Some(value)
|
||||
} else {
|
||||
let value = self.check_based_on_pkg_json(specifier).ok();
|
||||
if let Some(value) = value {
|
||||
known_cache.insert(specifier.clone(), value);
|
||||
}
|
||||
Some(value.unwrap_or(NodeModuleKind::Esm))
|
||||
}
|
||||
}
|
||||
MediaType::Wasm |
|
||||
MediaType::Json => Some(NodeModuleKind::Esm),
|
||||
MediaType::JavaScript
|
||||
| MediaType::Jsx
|
||||
| MediaType::TypeScript
|
||||
| MediaType::Tsx
|
||||
// treat these as unknown
|
||||
| MediaType::Css
|
||||
| MediaType::SourceMap
|
||||
| MediaType::Unknown => {
|
||||
if let Some(value) = known_cache.get(specifier).map(|v| *v) {
|
||||
if value == NodeModuleKind::Cjs && is_script == Some(false) {
|
||||
// we now know this is actually esm
|
||||
known_cache.insert(specifier.clone(), NodeModuleKind::Esm);
|
||||
Some(NodeModuleKind::Esm)
|
||||
} else {
|
||||
Some(value)
|
||||
}
|
||||
} else if is_script == Some(false) {
|
||||
// we know this is esm
|
||||
known_cache.insert(specifier.clone(), NodeModuleKind::Esm);
|
||||
Some(NodeModuleKind::Esm)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_based_on_pkg_json(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
) -> Result<NodeModuleKind, ClosestPkgJsonError> {
|
||||
if self.in_npm_pkg_checker.in_npm_package(specifier) {
|
||||
if let Some(pkg_json) =
|
||||
self.pkg_json_resolver.get_closest_package_json(specifier)?
|
||||
{
|
||||
let is_file_location_cjs = pkg_json.typ != "module";
|
||||
Ok(if is_file_location_cjs {
|
||||
NodeModuleKind::Cjs
|
||||
} else {
|
||||
NodeModuleKind::Esm
|
||||
})
|
||||
} else {
|
||||
Ok(NodeModuleKind::Cjs)
|
||||
}
|
||||
} else if self.options.detect_cjs || self.options.is_node_main {
|
||||
if let Some(pkg_json) =
|
||||
self.pkg_json_resolver.get_closest_package_json(specifier)?
|
||||
{
|
||||
let is_cjs_type = pkg_json.typ == "commonjs"
|
||||
|| self.options.is_node_main && pkg_json.typ == "none";
|
||||
Ok(if is_cjs_type {
|
||||
NodeModuleKind::Cjs
|
||||
} else {
|
||||
NodeModuleKind::Esm
|
||||
})
|
||||
} else if self.options.is_node_main {
|
||||
Ok(NodeModuleKind::Cjs)
|
||||
} else {
|
||||
Ok(NodeModuleKind::Esm)
|
||||
}
|
||||
} else {
|
||||
Ok(NodeModuleKind::Esm)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type CliSloppyImportsResolver =
|
||||
SloppyImportsResolver<SloppyImportsCachedFs>;
|
||||
|
||||
/// A resolver that takes care of resolution, taking into account loaded
|
||||
/// import map, JSX settings.
|
||||
#[derive(Debug)]
|
||||
pub struct CliResolver {
|
||||
node_resolver: Option<Arc<CliNodeResolver>>,
|
||||
deno_resolver: Arc<CliDenoResolver>,
|
||||
npm_resolver: Option<Arc<dyn CliNpmResolver>>,
|
||||
sloppy_imports_resolver: Option<Arc<CliSloppyImportsResolver>>,
|
||||
workspace_resolver: Arc<WorkspaceResolver>,
|
||||
maybe_vendor_specifier: Option<ModuleSpecifier>,
|
||||
found_package_json_dep_flag: AtomicFlag,
|
||||
bare_node_builtins_enabled: bool,
|
||||
warned_pkgs: DashSet<PackageReq>,
|
||||
}
|
||||
|
||||
pub struct CliResolverOptions<'a> {
|
||||
pub node_resolver: Option<Arc<CliNodeResolver>>,
|
||||
pub npm_resolver: Option<Arc<dyn CliNpmResolver>>,
|
||||
pub sloppy_imports_resolver: Option<Arc<CliSloppyImportsResolver>>,
|
||||
pub workspace_resolver: Arc<WorkspaceResolver>,
|
||||
pub bare_node_builtins_enabled: bool,
|
||||
pub maybe_vendor_dir: Option<&'a PathBuf>,
|
||||
}
|
||||
|
||||
impl CliResolver {
|
||||
pub fn new(options: CliResolverOptions) -> Self {
|
||||
Self {
|
||||
node_resolver: options.node_resolver,
|
||||
deno_resolver: options.deno_resolver,
|
||||
npm_resolver: options.npm_resolver,
|
||||
sloppy_imports_resolver: options.sloppy_imports_resolver,
|
||||
workspace_resolver: options.workspace_resolver,
|
||||
maybe_vendor_specifier: options
|
||||
.maybe_vendor_dir
|
||||
.and_then(|v| ModuleSpecifier::from_directory_path(v).ok()),
|
||||
found_package_json_dep_flag: Default::default(),
|
||||
bare_node_builtins_enabled: options.bare_node_builtins_enabled,
|
||||
warned_pkgs: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
// todo(dsherret): move this off CliResolver as CliResolver is acting
|
||||
// like a factory by doing this (it's beyond its responsibility)
|
||||
pub fn create_graph_npm_resolver(&self) -> WorkerCliNpmGraphResolver {
|
||||
WorkerCliNpmGraphResolver {
|
||||
npm_resolver: self.npm_resolver.as_ref(),
|
||||
|
@ -730,223 +258,50 @@ impl CliResolver {
|
|||
}
|
||||
}
|
||||
|
||||
let referrer = &referrer_range.specifier;
|
||||
let resolution = self
|
||||
.deno_resolver
|
||||
.resolve(
|
||||
raw_specifier,
|
||||
&referrer_range.specifier,
|
||||
referrer_kind,
|
||||
to_node_mode(mode),
|
||||
)
|
||||
.map_err(|err| match err.into_kind() {
|
||||
deno_resolver::DenoResolveErrorKind::MappedResolution(
|
||||
mapped_resolution_error,
|
||||
) => match mapped_resolution_error {
|
||||
MappedResolutionError::Specifier(e) => ResolveError::Specifier(e),
|
||||
// deno_graph checks specifically for an ImportMapError
|
||||
MappedResolutionError::ImportMap(e) => ResolveError::Other(e.into()),
|
||||
err => ResolveError::Other(err.into()),
|
||||
},
|
||||
err => ResolveError::Other(err.into()),
|
||||
})?;
|
||||
|
||||
// Use node resolution if we're in an npm package
|
||||
if let Some(node_resolver) = self.node_resolver.as_ref() {
|
||||
if referrer.scheme() == "file" && node_resolver.in_npm_package(referrer) {
|
||||
return node_resolver
|
||||
.resolve(raw_specifier, referrer, referrer_kind, to_node_mode(mode))
|
||||
.map(|res| res.into_url())
|
||||
.map_err(|e| ResolveError::Other(e.into()));
|
||||
}
|
||||
if resolution.found_package_json_dep {
|
||||
// mark that we need to do an "npm install" later
|
||||
self.found_package_json_dep_flag.raise();
|
||||
}
|
||||
|
||||
// Attempt to resolve with the workspace resolver
|
||||
let result: Result<_, ResolveError> = self
|
||||
.workspace_resolver
|
||||
.resolve(raw_specifier, referrer)
|
||||
.map_err(|err| match err {
|
||||
MappedResolutionError::Specifier(err) => ResolveError::Specifier(err),
|
||||
MappedResolutionError::ImportMap(err) => {
|
||||
ResolveError::Other(err.into())
|
||||
}
|
||||
MappedResolutionError::Workspace(err) => {
|
||||
ResolveError::Other(err.into())
|
||||
}
|
||||
});
|
||||
let result = match result {
|
||||
Ok(resolution) => match resolution {
|
||||
MappedResolution::Normal {
|
||||
specifier,
|
||||
maybe_diagnostic,
|
||||
}
|
||||
| MappedResolution::ImportMap {
|
||||
specifier,
|
||||
maybe_diagnostic,
|
||||
} => {
|
||||
if let Some(diagnostic) = maybe_diagnostic {
|
||||
match &*diagnostic {
|
||||
MappedResolutionDiagnostic::ConstraintNotMatchedLocalVersion { reference, .. } => {
|
||||
if self.warned_pkgs.insert(reference.req().clone()) {
|
||||
log::warn!("{} {}\n at {}", colors::yellow("Warning"), diagnostic, referrer_range);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// do sloppy imports resolution if enabled
|
||||
if let Some(sloppy_imports_resolver) = &self.sloppy_imports_resolver {
|
||||
Ok(
|
||||
sloppy_imports_resolver
|
||||
.resolve(
|
||||
&specifier,
|
||||
match mode {
|
||||
ResolutionMode::Execution => {
|
||||
SloppyImportsResolutionMode::Execution
|
||||
}
|
||||
ResolutionMode::Types => SloppyImportsResolutionMode::Types,
|
||||
},
|
||||
)
|
||||
.map(|s| s.into_specifier())
|
||||
.unwrap_or(specifier),
|
||||
)
|
||||
} else {
|
||||
Ok(specifier)
|
||||
}
|
||||
}
|
||||
MappedResolution::WorkspaceJsrPackage { specifier, .. } => {
|
||||
Ok(specifier)
|
||||
}
|
||||
MappedResolution::WorkspaceNpmPackage {
|
||||
target_pkg_json: pkg_json,
|
||||
sub_path,
|
||||
..
|
||||
} => self
|
||||
.node_resolver
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.resolve_package_sub_path_from_deno_module(
|
||||
pkg_json.dir_path(),
|
||||
sub_path.as_deref(),
|
||||
Some(referrer),
|
||||
referrer_kind,
|
||||
to_node_mode(mode),
|
||||
)
|
||||
.map_err(|e| ResolveError::Other(e.into())),
|
||||
MappedResolution::PackageJson {
|
||||
dep_result,
|
||||
alias,
|
||||
sub_path,
|
||||
if let Some(diagnostic) = resolution.maybe_diagnostic {
|
||||
match &*diagnostic {
|
||||
MappedResolutionDiagnostic::ConstraintNotMatchedLocalVersion {
|
||||
reference,
|
||||
..
|
||||
} => {
|
||||
// found a specifier in the package.json, so mark that
|
||||
// we need to do an "npm install" later
|
||||
self.found_package_json_dep_flag.raise();
|
||||
|
||||
dep_result
|
||||
.as_ref()
|
||||
.map_err(|e| ResolveError::Other(e.clone().into()))
|
||||
.and_then(|dep| match dep {
|
||||
PackageJsonDepValue::Req(req) => {
|
||||
ModuleSpecifier::parse(&format!(
|
||||
"npm:{}{}",
|
||||
req,
|
||||
sub_path.map(|s| format!("/{}", s)).unwrap_or_default()
|
||||
))
|
||||
.map_err(|e| ResolveError::Other(e.into()))
|
||||
}
|
||||
PackageJsonDepValue::Workspace(version_req) => self
|
||||
.workspace_resolver
|
||||
.resolve_workspace_pkg_json_folder_for_pkg_json_dep(
|
||||
alias,
|
||||
version_req,
|
||||
)
|
||||
.map_err(|e| ResolveError::Other(e.into()))
|
||||
.and_then(|pkg_folder| {
|
||||
self
|
||||
.node_resolver
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.resolve_package_sub_path_from_deno_module(
|
||||
pkg_folder,
|
||||
sub_path.as_deref(),
|
||||
Some(referrer),
|
||||
referrer_kind,
|
||||
to_node_mode(mode),
|
||||
)
|
||||
.map_err(|e| ResolveError::Other(e.into()))
|
||||
}),
|
||||
})
|
||||
}
|
||||
},
|
||||
Err(err) => Err(err),
|
||||
};
|
||||
|
||||
// When the user is vendoring, don't allow them to import directly from the vendor/ directory
|
||||
// as it might cause them confusion or duplicate dependencies. Additionally, this folder has
|
||||
// special treatment in the language server so it will definitely cause issues/confusion there
|
||||
// if they do this.
|
||||
if let Some(vendor_specifier) = &self.maybe_vendor_specifier {
|
||||
if let Ok(specifier) = &result {
|
||||
if specifier.as_str().starts_with(vendor_specifier.as_str()) {
|
||||
return Err(ResolveError::Other(anyhow!("Importing from the vendor directory is not permitted. Use a remote specifier instead or disable vendoring.")));
|
||||
if self.warned_pkgs.insert(reference.req().clone()) {
|
||||
log::warn!(
|
||||
"{} {}\n at {}",
|
||||
colors::yellow("Warning"),
|
||||
diagnostic,
|
||||
referrer_range
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let Some(node_resolver) = &self.node_resolver else {
|
||||
return result;
|
||||
};
|
||||
|
||||
let is_byonm = self
|
||||
.npm_resolver
|
||||
.as_ref()
|
||||
.is_some_and(|r| r.as_byonm().is_some());
|
||||
match result {
|
||||
Ok(specifier) => {
|
||||
if let Ok(npm_req_ref) =
|
||||
NpmPackageReqReference::from_specifier(&specifier)
|
||||
{
|
||||
// check if the npm specifier resolves to a workspace member
|
||||
if let Some(pkg_folder) = self
|
||||
.workspace_resolver
|
||||
.resolve_workspace_pkg_json_folder_for_npm_specifier(
|
||||
npm_req_ref.req(),
|
||||
)
|
||||
{
|
||||
return node_resolver
|
||||
.resolve_package_sub_path_from_deno_module(
|
||||
pkg_folder,
|
||||
npm_req_ref.sub_path(),
|
||||
Some(referrer),
|
||||
referrer_kind,
|
||||
to_node_mode(mode),
|
||||
)
|
||||
.map_err(|e| ResolveError::Other(e.into()));
|
||||
}
|
||||
|
||||
// do npm resolution for byonm
|
||||
if is_byonm {
|
||||
return node_resolver
|
||||
.resolve_req_reference(
|
||||
&npm_req_ref,
|
||||
referrer,
|
||||
referrer_kind,
|
||||
to_node_mode(mode),
|
||||
)
|
||||
.map_err(|err| err.into());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(match node_resolver.handle_if_in_node_modules(&specifier)? {
|
||||
Some(specifier) => specifier,
|
||||
None => specifier,
|
||||
})
|
||||
}
|
||||
Err(err) => {
|
||||
// If byonm, check if the bare specifier resolves to an npm package
|
||||
if is_byonm && referrer.scheme() == "file" {
|
||||
let maybe_resolution = node_resolver
|
||||
.resolve_if_for_npm_pkg(
|
||||
raw_specifier,
|
||||
referrer,
|
||||
referrer_kind,
|
||||
to_node_mode(mode),
|
||||
)
|
||||
.map_err(ResolveError::Other)?;
|
||||
if let Some(res) = maybe_resolution {
|
||||
match res {
|
||||
NodeResolution::Module(url) => return Ok(url),
|
||||
NodeResolution::BuiltIn(_) => {
|
||||
// don't resolve bare specifiers for built-in modules via node resolution
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
Ok(resolution.url)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ use deno_core::RequestedModuleType;
|
|||
use deno_core::ResolutionKind;
|
||||
use deno_npm::npm_rc::ResolvedNpmRc;
|
||||
use deno_package_json::PackageJsonDepValue;
|
||||
use deno_resolver::npm::NpmReqResolverOptions;
|
||||
use deno_runtime::deno_fs;
|
||||
use deno_runtime::deno_node::create_host_defined_options;
|
||||
use deno_runtime::deno_node::NodeRequireLoader;
|
||||
|
@ -79,7 +80,7 @@ use crate::npm::CliNpmResolverManagedSnapshotOption;
|
|||
use crate::npm::CreateInNpmPkgCheckerOptions;
|
||||
use crate::resolver::CjsTracker;
|
||||
use crate::resolver::CliDenoResolverFs;
|
||||
use crate::resolver::CliNodeResolver;
|
||||
use crate::resolver::CliNpmReqResolver;
|
||||
use crate::resolver::IsCjsResolverOptions;
|
||||
use crate::resolver::NpmModuleLoader;
|
||||
use crate::util::progress_bar::ProgressBar;
|
||||
|
@ -107,8 +108,9 @@ struct SharedModuleLoaderState {
|
|||
fs: Arc<dyn deno_fs::FileSystem>,
|
||||
modules: StandaloneModules,
|
||||
node_code_translator: Arc<CliNodeCodeTranslator>,
|
||||
node_resolver: Arc<CliNodeResolver>,
|
||||
node_resolver: Arc<NodeResolver>,
|
||||
npm_module_loader: Arc<NpmModuleLoader>,
|
||||
npm_req_resolver: Arc<CliNpmReqResolver>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
workspace_resolver: WorkspaceResolver,
|
||||
}
|
||||
|
@ -190,7 +192,7 @@ impl ModuleLoader for EmbeddedModuleLoader {
|
|||
self
|
||||
.shared
|
||||
.node_resolver
|
||||
.resolve_package_sub_path_from_deno_module(
|
||||
.resolve_package_subpath_from_deno_module(
|
||||
pkg_json.dir_path(),
|
||||
sub_path.as_deref(),
|
||||
Some(&referrer),
|
||||
|
@ -204,15 +206,17 @@ impl ModuleLoader for EmbeddedModuleLoader {
|
|||
alias,
|
||||
..
|
||||
}) => match dep_result.as_ref().map_err(|e| AnyError::from(e.clone()))? {
|
||||
PackageJsonDepValue::Req(req) => {
|
||||
self.shared.node_resolver.resolve_req_with_sub_path(
|
||||
PackageJsonDepValue::Req(req) => self
|
||||
.shared
|
||||
.npm_req_resolver
|
||||
.resolve_req_with_sub_path(
|
||||
req,
|
||||
sub_path.as_deref(),
|
||||
&referrer,
|
||||
referrer_kind,
|
||||
NodeResolutionMode::Execution,
|
||||
)
|
||||
}
|
||||
.map_err(AnyError::from),
|
||||
PackageJsonDepValue::Workspace(version_req) => {
|
||||
let pkg_folder = self
|
||||
.shared
|
||||
|
@ -225,7 +229,7 @@ impl ModuleLoader for EmbeddedModuleLoader {
|
|||
self
|
||||
.shared
|
||||
.node_resolver
|
||||
.resolve_package_sub_path_from_deno_module(
|
||||
.resolve_package_subpath_from_deno_module(
|
||||
pkg_folder,
|
||||
sub_path.as_deref(),
|
||||
Some(&referrer),
|
||||
|
@ -240,12 +244,12 @@ impl ModuleLoader for EmbeddedModuleLoader {
|
|||
if let Ok(reference) =
|
||||
NpmPackageReqReference::from_specifier(&specifier)
|
||||
{
|
||||
return self.shared.node_resolver.resolve_req_reference(
|
||||
return Ok(self.shared.npm_req_resolver.resolve_req_reference(
|
||||
&reference,
|
||||
&referrer,
|
||||
referrer_kind,
|
||||
NodeResolutionMode::Execution,
|
||||
);
|
||||
)?);
|
||||
}
|
||||
|
||||
if specifier.scheme() == "jsr" {
|
||||
|
@ -260,14 +264,14 @@ impl ModuleLoader for EmbeddedModuleLoader {
|
|||
self
|
||||
.shared
|
||||
.node_resolver
|
||||
.handle_if_in_node_modules(&specifier)?
|
||||
.handle_if_in_node_modules(&specifier)
|
||||
.unwrap_or(specifier),
|
||||
)
|
||||
}
|
||||
Err(err)
|
||||
if err.is_unmapped_bare_specifier() && referrer.scheme() == "file" =>
|
||||
{
|
||||
let maybe_res = self.shared.node_resolver.resolve_if_for_npm_pkg(
|
||||
let maybe_res = self.shared.npm_req_resolver.resolve_if_for_npm_pkg(
|
||||
raw_specifier,
|
||||
&referrer,
|
||||
referrer_kind,
|
||||
|
@ -651,7 +655,7 @@ pub async fn run(data: StandaloneData) -> Result<i32, AnyError> {
|
|||
let node_resolver = Arc::new(NodeResolver::new(
|
||||
deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()),
|
||||
in_npm_pkg_checker.clone(),
|
||||
npm_resolver.clone().into_npm_resolver(),
|
||||
npm_resolver.clone().into_npm_pkg_folder_resolver(),
|
||||
pkg_json_resolver.clone(),
|
||||
));
|
||||
let cjs_tracker = Arc::new(CjsTracker::new(
|
||||
|
@ -664,12 +668,14 @@ pub async fn run(data: StandaloneData) -> Result<i32, AnyError> {
|
|||
));
|
||||
let cache_db = Caches::new(deno_dir_provider.clone());
|
||||
let node_analysis_cache = NodeAnalysisCache::new(cache_db.node_analysis_db());
|
||||
let cli_node_resolver = Arc::new(CliNodeResolver::new(
|
||||
fs.clone(),
|
||||
in_npm_pkg_checker.clone(),
|
||||
node_resolver.clone(),
|
||||
npm_resolver.clone(),
|
||||
));
|
||||
let npm_req_resolver =
|
||||
Arc::new(CliNpmReqResolver::new(NpmReqResolverOptions {
|
||||
byonm_resolver: (npm_resolver.clone()).into_maybe_byonm(),
|
||||
fs: CliDenoResolverFs(fs.clone()),
|
||||
in_npm_pkg_checker: in_npm_pkg_checker.clone(),
|
||||
node_resolver: node_resolver.clone(),
|
||||
npm_req_resolver: npm_resolver.clone().into_npm_req_resolver(),
|
||||
}));
|
||||
let cjs_esm_code_analyzer = CliCjsCodeAnalyzer::new(
|
||||
node_analysis_cache,
|
||||
cjs_tracker.clone(),
|
||||
|
@ -681,7 +687,7 @@ pub async fn run(data: StandaloneData) -> Result<i32, AnyError> {
|
|||
deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()),
|
||||
in_npm_pkg_checker,
|
||||
node_resolver.clone(),
|
||||
npm_resolver.clone().into_npm_resolver(),
|
||||
npm_resolver.clone().into_npm_pkg_folder_resolver(),
|
||||
pkg_json_resolver.clone(),
|
||||
));
|
||||
let workspace_resolver = {
|
||||
|
@ -739,7 +745,7 @@ pub async fn run(data: StandaloneData) -> Result<i32, AnyError> {
|
|||
fs: fs.clone(),
|
||||
modules,
|
||||
node_code_translator: node_code_translator.clone(),
|
||||
node_resolver: cli_node_resolver.clone(),
|
||||
node_resolver: node_resolver.clone(),
|
||||
npm_module_loader: Arc::new(NpmModuleLoader::new(
|
||||
cjs_tracker.clone(),
|
||||
fs.clone(),
|
||||
|
@ -747,6 +753,7 @@ pub async fn run(data: StandaloneData) -> Result<i32, AnyError> {
|
|||
)),
|
||||
npm_resolver: npm_resolver.clone(),
|
||||
workspace_resolver,
|
||||
npm_req_resolver,
|
||||
}),
|
||||
};
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ use crate::cache::FastInsecureHasher;
|
|||
use crate::cache::ModuleInfoCache;
|
||||
use crate::node;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::npm::ResolvePkgFolderFromDenoReqError;
|
||||
use crate::resolver::CjsTracker;
|
||||
use crate::util::checksum;
|
||||
use crate::util::path::mapped_specifier_for_tsc;
|
||||
|
@ -34,6 +33,7 @@ use deno_graph::GraphKind;
|
|||
use deno_graph::Module;
|
||||
use deno_graph::ModuleGraph;
|
||||
use deno_graph::ResolutionResolved;
|
||||
use deno_resolver::npm::ResolvePkgFolderFromDenoReqError;
|
||||
use deno_runtime::deno_fs;
|
||||
use deno_runtime::deno_node::NodeResolver;
|
||||
use deno_semver::npm::NpmPackageReqReference;
|
||||
|
|
|
@ -155,7 +155,7 @@ impl SharedWorkerState {
|
|||
NodeExtInitServices {
|
||||
node_require_loader,
|
||||
node_resolver: self.node_resolver.clone(),
|
||||
npm_resolver: self.npm_resolver.clone().into_npm_resolver(),
|
||||
npm_resolver: self.npm_resolver.clone().into_npm_pkg_folder_resolver(),
|
||||
pkg_json_resolver: self.pkg_json_resolver.clone(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ use deno_core::url::Url;
|
|||
use deno_core::v8;
|
||||
use deno_core::v8::ExternalReference;
|
||||
use node_resolver::errors::ClosestPkgJsonError;
|
||||
use node_resolver::NpmResolverRc;
|
||||
use node_resolver::NpmPackageFolderResolverRc;
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
extern crate libz_sys as zlib;
|
||||
|
@ -183,7 +183,7 @@ fn op_node_build_os() -> String {
|
|||
pub struct NodeExtInitServices {
|
||||
pub node_require_loader: NodeRequireLoaderRc,
|
||||
pub node_resolver: NodeResolverRc,
|
||||
pub npm_resolver: NpmResolverRc,
|
||||
pub npm_resolver: NpmPackageFolderResolverRc,
|
||||
pub pkg_json_resolver: PackageJsonResolverRc,
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ use std::rc::Rc;
|
|||
use crate::NodePermissions;
|
||||
use crate::NodeRequireLoaderRc;
|
||||
use crate::NodeResolverRc;
|
||||
use crate::NpmResolverRc;
|
||||
use crate::NpmPackageFolderResolverRc;
|
||||
use crate::PackageJsonResolverRc;
|
||||
|
||||
#[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
|
||||
|
@ -220,7 +220,7 @@ pub fn op_require_resolve_deno_dir(
|
|||
#[string] request: String,
|
||||
#[string] parent_filename: String,
|
||||
) -> Result<Option<String>, AnyError> {
|
||||
let resolver = state.borrow::<NpmResolverRc>();
|
||||
let resolver = state.borrow::<NpmPackageFolderResolverRc>();
|
||||
Ok(
|
||||
resolver
|
||||
.resolve_package_folder_from_package(
|
||||
|
|
|
@ -16,6 +16,8 @@ path = "lib.rs"
|
|||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
base32.workspace = true
|
||||
dashmap.workspace = true
|
||||
deno_config.workspace = true
|
||||
deno_media_type.workspace = true
|
||||
deno_package_json.workspace = true
|
||||
deno_package_json.features = ["sync"]
|
||||
|
|
272
resolvers/deno/cjs.rs
Normal file
272
resolvers/deno/cjs.rs
Normal file
|
@ -0,0 +1,272 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use dashmap::DashMap;
|
||||
use deno_media_type::MediaType;
|
||||
use node_resolver::env::NodeResolverEnv;
|
||||
use node_resolver::errors::ClosestPkgJsonError;
|
||||
use node_resolver::InNpmPackageChecker;
|
||||
use node_resolver::NodeModuleKind;
|
||||
use node_resolver::PackageJsonResolver;
|
||||
use url::Url;
|
||||
|
||||
/// Keeps track of what module specifiers were resolved as CJS.
|
||||
///
|
||||
/// Modules that are `.js`, `.ts`, `.jsx`, and `tsx` are only known to
|
||||
/// be CJS or ESM after they're loaded based on their contents. So these
|
||||
/// files will be "maybe CJS" until they're loaded.
|
||||
#[derive(Debug)]
|
||||
pub struct CjsTracker<TEnv: NodeResolverEnv> {
|
||||
is_cjs_resolver: IsCjsResolver<TEnv>,
|
||||
known: DashMap<Url, NodeModuleKind>,
|
||||
}
|
||||
|
||||
impl<TEnv: NodeResolverEnv> CjsTracker<TEnv> {
|
||||
pub fn new(
|
||||
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
|
||||
pkg_json_resolver: Arc<PackageJsonResolver<TEnv>>,
|
||||
options: IsCjsResolverOptions,
|
||||
) -> Self {
|
||||
Self {
|
||||
is_cjs_resolver: IsCjsResolver::new(
|
||||
in_npm_pkg_checker,
|
||||
pkg_json_resolver,
|
||||
options,
|
||||
),
|
||||
known: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether the file might be treated as CJS, but it's not for sure
|
||||
/// yet because the source hasn't been loaded to see whether it contains
|
||||
/// imports or exports.
|
||||
pub fn is_maybe_cjs(
|
||||
&self,
|
||||
specifier: &Url,
|
||||
media_type: MediaType,
|
||||
) -> Result<bool, ClosestPkgJsonError> {
|
||||
self.treat_as_cjs_with_is_script(specifier, media_type, None)
|
||||
}
|
||||
|
||||
/// Gets whether the file is CJS. If true, this is for sure
|
||||
/// cjs because `is_script` is provided.
|
||||
///
|
||||
/// `is_script` should be `true` when the contents of the file at the
|
||||
/// provided specifier are known to be a script and not an ES module.
|
||||
pub fn is_cjs_with_known_is_script(
|
||||
&self,
|
||||
specifier: &Url,
|
||||
media_type: MediaType,
|
||||
is_script: bool,
|
||||
) -> Result<bool, ClosestPkgJsonError> {
|
||||
self.treat_as_cjs_with_is_script(specifier, media_type, Some(is_script))
|
||||
}
|
||||
|
||||
fn treat_as_cjs_with_is_script(
|
||||
&self,
|
||||
specifier: &Url,
|
||||
media_type: MediaType,
|
||||
is_script: Option<bool>,
|
||||
) -> Result<bool, ClosestPkgJsonError> {
|
||||
let kind = match self
|
||||
.get_known_kind_with_is_script(specifier, media_type, is_script)
|
||||
{
|
||||
Some(kind) => kind,
|
||||
None => self.is_cjs_resolver.check_based_on_pkg_json(specifier)?,
|
||||
};
|
||||
Ok(kind == NodeModuleKind::Cjs)
|
||||
}
|
||||
|
||||
/// Gets the referrer for the specified module specifier.
|
||||
///
|
||||
/// Generally the referrer should already be tracked by calling
|
||||
/// `is_cjs_with_known_is_script` before calling this method.
|
||||
pub fn get_referrer_kind(&self, specifier: &Url) -> NodeModuleKind {
|
||||
if specifier.scheme() != "file" {
|
||||
return NodeModuleKind::Esm;
|
||||
}
|
||||
self
|
||||
.get_known_kind(specifier, MediaType::from_specifier(specifier))
|
||||
.unwrap_or(NodeModuleKind::Esm)
|
||||
}
|
||||
|
||||
fn get_known_kind(
|
||||
&self,
|
||||
specifier: &Url,
|
||||
media_type: MediaType,
|
||||
) -> Option<NodeModuleKind> {
|
||||
self.get_known_kind_with_is_script(specifier, media_type, None)
|
||||
}
|
||||
|
||||
fn get_known_kind_with_is_script(
|
||||
&self,
|
||||
specifier: &Url,
|
||||
media_type: MediaType,
|
||||
is_script: Option<bool>,
|
||||
) -> Option<NodeModuleKind> {
|
||||
self.is_cjs_resolver.get_known_kind_with_is_script(
|
||||
specifier,
|
||||
media_type,
|
||||
is_script,
|
||||
&self.known,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IsCjsResolverOptions {
|
||||
pub detect_cjs: bool,
|
||||
pub is_node_main: bool,
|
||||
}
|
||||
|
||||
/// Resolves whether a module is CJS or ESM.
|
||||
#[derive(Debug)]
|
||||
pub struct IsCjsResolver<TEnv: NodeResolverEnv> {
|
||||
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
|
||||
pkg_json_resolver: Arc<PackageJsonResolver<TEnv>>,
|
||||
options: IsCjsResolverOptions,
|
||||
}
|
||||
|
||||
impl<TEnv: NodeResolverEnv> IsCjsResolver<TEnv> {
|
||||
pub fn new(
|
||||
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
|
||||
pkg_json_resolver: Arc<PackageJsonResolver<TEnv>>,
|
||||
options: IsCjsResolverOptions,
|
||||
) -> Self {
|
||||
Self {
|
||||
in_npm_pkg_checker,
|
||||
pkg_json_resolver,
|
||||
options,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the referrer kind for a script in the LSP.
|
||||
pub fn get_lsp_referrer_kind(
|
||||
&self,
|
||||
specifier: &Url,
|
||||
is_script: Option<bool>,
|
||||
) -> NodeModuleKind {
|
||||
if specifier.scheme() != "file" {
|
||||
return NodeModuleKind::Esm;
|
||||
}
|
||||
match MediaType::from_specifier(specifier) {
|
||||
MediaType::Mts | MediaType::Mjs | MediaType::Dmts => NodeModuleKind::Esm,
|
||||
MediaType::Cjs | MediaType::Cts | MediaType::Dcts => NodeModuleKind::Cjs,
|
||||
MediaType::Dts => {
|
||||
// dts files are always determined based on the package.json because
|
||||
// they contain imports/exports even when considered CJS
|
||||
self.check_based_on_pkg_json(specifier).unwrap_or(NodeModuleKind::Esm)
|
||||
}
|
||||
MediaType::Wasm |
|
||||
MediaType::Json => NodeModuleKind::Esm,
|
||||
MediaType::JavaScript
|
||||
| MediaType::Jsx
|
||||
| MediaType::TypeScript
|
||||
| MediaType::Tsx
|
||||
// treat these as unknown
|
||||
| MediaType::Css
|
||||
| MediaType::SourceMap
|
||||
| MediaType::Unknown => {
|
||||
match is_script {
|
||||
Some(true) => self.check_based_on_pkg_json(specifier).unwrap_or(NodeModuleKind::Esm),
|
||||
Some(false) | None => NodeModuleKind::Esm,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_known_kind_with_is_script(
|
||||
&self,
|
||||
specifier: &Url,
|
||||
media_type: MediaType,
|
||||
is_script: Option<bool>,
|
||||
known_cache: &DashMap<Url, NodeModuleKind>,
|
||||
) -> Option<NodeModuleKind> {
|
||||
if specifier.scheme() != "file" {
|
||||
return Some(NodeModuleKind::Esm);
|
||||
}
|
||||
|
||||
match media_type {
|
||||
MediaType::Mts | MediaType::Mjs | MediaType::Dmts => Some(NodeModuleKind::Esm),
|
||||
MediaType::Cjs | MediaType::Cts | MediaType::Dcts => Some(NodeModuleKind::Cjs),
|
||||
MediaType::Dts => {
|
||||
// dts files are always determined based on the package.json because
|
||||
// they contain imports/exports even when considered CJS
|
||||
if let Some(value) = known_cache.get(specifier).map(|v| *v) {
|
||||
Some(value)
|
||||
} else {
|
||||
let value = self.check_based_on_pkg_json(specifier).ok();
|
||||
if let Some(value) = value {
|
||||
known_cache.insert(specifier.clone(), value);
|
||||
}
|
||||
Some(value.unwrap_or(NodeModuleKind::Esm))
|
||||
}
|
||||
}
|
||||
MediaType::Wasm |
|
||||
MediaType::Json => Some(NodeModuleKind::Esm),
|
||||
MediaType::JavaScript
|
||||
| MediaType::Jsx
|
||||
| MediaType::TypeScript
|
||||
| MediaType::Tsx
|
||||
// treat these as unknown
|
||||
| MediaType::Css
|
||||
| MediaType::SourceMap
|
||||
| MediaType::Unknown => {
|
||||
if let Some(value) = known_cache.get(specifier).map(|v| *v) {
|
||||
if value == NodeModuleKind::Cjs && is_script == Some(false) {
|
||||
// we now know this is actually esm
|
||||
known_cache.insert(specifier.clone(), NodeModuleKind::Esm);
|
||||
Some(NodeModuleKind::Esm)
|
||||
} else {
|
||||
Some(value)
|
||||
}
|
||||
} else if is_script == Some(false) {
|
||||
// we know this is esm
|
||||
known_cache.insert(specifier.clone(), NodeModuleKind::Esm);
|
||||
Some(NodeModuleKind::Esm)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_based_on_pkg_json(
|
||||
&self,
|
||||
specifier: &Url,
|
||||
) -> Result<NodeModuleKind, ClosestPkgJsonError> {
|
||||
if self.in_npm_pkg_checker.in_npm_package(specifier) {
|
||||
if let Some(pkg_json) =
|
||||
self.pkg_json_resolver.get_closest_package_json(specifier)?
|
||||
{
|
||||
let is_file_location_cjs = pkg_json.typ != "module";
|
||||
Ok(if is_file_location_cjs {
|
||||
NodeModuleKind::Cjs
|
||||
} else {
|
||||
NodeModuleKind::Esm
|
||||
})
|
||||
} else {
|
||||
Ok(NodeModuleKind::Cjs)
|
||||
}
|
||||
} else if self.options.detect_cjs || self.options.is_node_main {
|
||||
if let Some(pkg_json) =
|
||||
self.pkg_json_resolver.get_closest_package_json(specifier)?
|
||||
{
|
||||
let is_cjs_type = pkg_json.typ == "commonjs"
|
||||
|| self.options.is_node_main && pkg_json.typ == "none";
|
||||
Ok(if is_cjs_type {
|
||||
NodeModuleKind::Cjs
|
||||
} else {
|
||||
NodeModuleKind::Esm
|
||||
})
|
||||
} else if self.options.is_node_main {
|
||||
Ok(NodeModuleKind::Cjs)
|
||||
} else {
|
||||
Ok(NodeModuleKind::Esm)
|
||||
}
|
||||
} else {
|
||||
Ok(NodeModuleKind::Esm)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,6 +12,7 @@ pub struct DirEntry {
|
|||
pub trait DenoResolverFs {
|
||||
fn read_to_string_lossy(&self, path: &Path) -> std::io::Result<String>;
|
||||
fn realpath_sync(&self, path: &Path) -> std::io::Result<PathBuf>;
|
||||
fn exists_sync(&self, path: &Path) -> bool;
|
||||
fn is_dir_sync(&self, path: &Path) -> bool;
|
||||
fn read_dir_sync(&self, dir_path: &Path) -> std::io::Result<Vec<DirEntry>>;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,439 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
#![deny(clippy::print_stderr)]
|
||||
#![deny(clippy::print_stdout)]
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
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_package_json::PackageJsonDepValue;
|
||||
use deno_package_json::PackageJsonDepValueParseError;
|
||||
use deno_semver::npm::NpmPackageReqReference;
|
||||
use fs::DenoResolverFs;
|
||||
use node_resolver::env::NodeResolverEnv;
|
||||
use node_resolver::errors::NodeResolveError;
|
||||
use node_resolver::errors::PackageSubpathResolveError;
|
||||
use node_resolver::InNpmPackageChecker;
|
||||
use node_resolver::NodeModuleKind;
|
||||
use node_resolver::NodeResolution;
|
||||
use node_resolver::NodeResolutionMode;
|
||||
use node_resolver::NodeResolver;
|
||||
use npm::MissingPackageNodeModulesFolderError;
|
||||
use npm::NodeModulesOutOfDateError;
|
||||
use npm::NpmReqResolver;
|
||||
use npm::ResolveIfForNpmPackageError;
|
||||
use npm::ResolvePkgFolderFromDenoReqError;
|
||||
use npm::ResolveReqWithSubPathError;
|
||||
use sloppy_imports::SloppyImportResolverFs;
|
||||
use sloppy_imports::SloppyImportsResolutionMode;
|
||||
use sloppy_imports::SloppyImportsResolver;
|
||||
use thiserror::Error;
|
||||
use url::Url;
|
||||
|
||||
pub mod cjs;
|
||||
pub mod fs;
|
||||
pub mod npm;
|
||||
pub mod sloppy_imports;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DenoResolution {
|
||||
pub url: Url,
|
||||
pub maybe_diagnostic: Option<Box<MappedResolutionDiagnostic>>,
|
||||
pub found_package_json_dep: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum DenoResolveErrorKind {
|
||||
#[error("Importing from the vendor directory is not permitted. Use a remote specifier instead or disable vendoring.")]
|
||||
InvalidVendorFolderImport,
|
||||
#[error(transparent)]
|
||||
MappedResolution(#[from] MappedResolutionError),
|
||||
#[error(transparent)]
|
||||
MissingPackageNodeModulesFolder(#[from] MissingPackageNodeModulesFolderError),
|
||||
#[error(transparent)]
|
||||
Node(#[from] NodeResolveError),
|
||||
#[error(transparent)]
|
||||
NodeModulesOutOfDate(#[from] NodeModulesOutOfDateError),
|
||||
#[error(transparent)]
|
||||
PackageJsonDepValueParse(#[from] PackageJsonDepValueParseError),
|
||||
#[error(transparent)]
|
||||
PackageJsonDepValueUrlParse(url::ParseError),
|
||||
#[error(transparent)]
|
||||
PackageSubpathResolve(#[from] PackageSubpathResolveError),
|
||||
#[error(transparent)]
|
||||
ResolvePkgFolderFromDenoReq(#[from] ResolvePkgFolderFromDenoReqError),
|
||||
#[error(transparent)]
|
||||
WorkspaceResolvePkgJsonFolder(#[from] WorkspaceResolvePkgJsonFolderError),
|
||||
}
|
||||
|
||||
impl DenoResolveErrorKind {
|
||||
pub fn into_box(self) -> DenoResolveError {
|
||||
DenoResolveError(Box::new(self))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error(transparent)]
|
||||
pub struct DenoResolveError(pub Box<DenoResolveErrorKind>);
|
||||
|
||||
impl DenoResolveError {
|
||||
pub fn as_kind(&self) -> &DenoResolveErrorKind {
|
||||
&self.0
|
||||
}
|
||||
|
||||
pub fn into_kind(self) -> DenoResolveErrorKind {
|
||||
*self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> From<E> for DenoResolveError
|
||||
where
|
||||
DenoResolveErrorKind: From<E>,
|
||||
{
|
||||
fn from(err: E) -> Self {
|
||||
DenoResolveError(Box::new(DenoResolveErrorKind::from(err)))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NodeAndNpmReqResolver<
|
||||
Fs: DenoResolverFs,
|
||||
TNodeResolverEnv: NodeResolverEnv,
|
||||
> {
|
||||
pub node_resolver: Arc<NodeResolver<TNodeResolverEnv>>,
|
||||
pub npm_req_resolver: Arc<NpmReqResolver<Fs, TNodeResolverEnv>>,
|
||||
}
|
||||
|
||||
pub struct DenoResolverOptions<
|
||||
'a,
|
||||
Fs: DenoResolverFs,
|
||||
TNodeResolverEnv: NodeResolverEnv,
|
||||
TSloppyImportResolverFs: SloppyImportResolverFs,
|
||||
> {
|
||||
pub in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
|
||||
pub node_and_req_resolver:
|
||||
Option<NodeAndNpmReqResolver<Fs, TNodeResolverEnv>>,
|
||||
pub sloppy_imports_resolver:
|
||||
Option<Arc<SloppyImportsResolver<TSloppyImportResolverFs>>>,
|
||||
pub workspace_resolver: Arc<WorkspaceResolver>,
|
||||
/// Whether "bring your own node_modules" is enabled where Deno does not
|
||||
/// setup the node_modules directories automatically, but instead uses
|
||||
/// what already exists on the file system.
|
||||
pub is_byonm: bool,
|
||||
pub maybe_vendor_dir: Option<&'a PathBuf>,
|
||||
}
|
||||
|
||||
/// A resolver that takes care of resolution, taking into account loaded
|
||||
/// import map, JSX settings.
|
||||
#[derive(Debug)]
|
||||
pub struct DenoResolver<
|
||||
Fs: DenoResolverFs,
|
||||
TNodeResolverEnv: NodeResolverEnv,
|
||||
TSloppyImportResolverFs: SloppyImportResolverFs,
|
||||
> {
|
||||
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
|
||||
node_and_npm_resolver: Option<NodeAndNpmReqResolver<Fs, TNodeResolverEnv>>,
|
||||
sloppy_imports_resolver:
|
||||
Option<Arc<SloppyImportsResolver<TSloppyImportResolverFs>>>,
|
||||
workspace_resolver: Arc<WorkspaceResolver>,
|
||||
is_byonm: bool,
|
||||
maybe_vendor_specifier: Option<Url>,
|
||||
}
|
||||
|
||||
impl<
|
||||
Fs: DenoResolverFs,
|
||||
TNodeResolverEnv: NodeResolverEnv,
|
||||
TSloppyImportResolverFs: SloppyImportResolverFs,
|
||||
> DenoResolver<Fs, TNodeResolverEnv, TSloppyImportResolverFs>
|
||||
{
|
||||
pub fn new(
|
||||
options: DenoResolverOptions<Fs, TNodeResolverEnv, TSloppyImportResolverFs>,
|
||||
) -> 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
|
||||
.maybe_vendor_dir
|
||||
.and_then(|v| deno_path_util::url_from_directory_path(v).ok()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve(
|
||||
&self,
|
||||
raw_specifier: &str,
|
||||
referrer: &Url,
|
||||
referrer_kind: NodeModuleKind,
|
||||
mode: NodeResolutionMode,
|
||||
) -> Result<DenoResolution, DenoResolveError> {
|
||||
let mut found_package_json_dep = false;
|
||||
let mut maybe_diagnostic = None;
|
||||
// Use node resolution if we're in an npm package
|
||||
if let Some(node_and_npm_resolver) = self.node_and_npm_resolver.as_ref() {
|
||||
let node_resolver = &node_and_npm_resolver.node_resolver;
|
||||
if referrer.scheme() == "file"
|
||||
&& self.in_npm_pkg_checker.in_npm_package(referrer)
|
||||
{
|
||||
return node_resolver
|
||||
.resolve(raw_specifier, referrer, referrer_kind, mode)
|
||||
.map(|res| DenoResolution {
|
||||
url: res.into_url(),
|
||||
found_package_json_dep,
|
||||
maybe_diagnostic,
|
||||
})
|
||||
.map_err(|e| e.into());
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to resolve with the workspace resolver
|
||||
let result: Result<_, DenoResolveError> = self
|
||||
.workspace_resolver
|
||||
.resolve(raw_specifier, referrer)
|
||||
.map_err(|err| err.into());
|
||||
let result = match result {
|
||||
Ok(resolution) => match resolution {
|
||||
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 mode {
|
||||
NodeResolutionMode::Execution => {
|
||||
SloppyImportsResolutionMode::Execution
|
||||
}
|
||||
NodeResolutionMode::Types => {
|
||||
SloppyImportsResolutionMode::Types
|
||||
}
|
||||
},
|
||||
)
|
||||
.map(|s| s.into_specifier())
|
||||
.unwrap_or(specifier),
|
||||
)
|
||||
} else {
|
||||
Ok(specifier)
|
||||
}
|
||||
}
|
||||
MappedResolution::WorkspaceJsrPackage { specifier, .. } => {
|
||||
Ok(specifier)
|
||||
}
|
||||
MappedResolution::WorkspaceNpmPackage {
|
||||
target_pkg_json: pkg_json,
|
||||
sub_path,
|
||||
..
|
||||
} => self
|
||||
.node_and_npm_resolver
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.node_resolver
|
||||
.resolve_package_subpath_from_deno_module(
|
||||
pkg_json.dir_path(),
|
||||
sub_path.as_deref(),
|
||||
Some(referrer),
|
||||
referrer_kind,
|
||||
mode,
|
||||
)
|
||||
.map_err(|e| e.into()),
|
||||
MappedResolution::PackageJson {
|
||||
dep_result,
|
||||
alias,
|
||||
sub_path,
|
||||
..
|
||||
} => {
|
||||
// found a specifier in the package.json, so mark that
|
||||
// we need to do an "npm install" later
|
||||
found_package_json_dep = true;
|
||||
|
||||
dep_result
|
||||
.as_ref()
|
||||
.map_err(|e| {
|
||||
DenoResolveErrorKind::PackageJsonDepValueParse(e.clone())
|
||||
.into_box()
|
||||
})
|
||||
.and_then(|dep| match dep {
|
||||
// todo(dsherret): it seems bad that we're converting this
|
||||
// to a url because the req might not be a valid url.
|
||||
PackageJsonDepValue::Req(req) => Url::parse(&format!(
|
||||
"npm:{}{}",
|
||||
req,
|
||||
sub_path.map(|s| format!("/{}", s)).unwrap_or_default()
|
||||
))
|
||||
.map_err(|e| {
|
||||
DenoResolveErrorKind::PackageJsonDepValueUrlParse(e).into_box()
|
||||
}),
|
||||
PackageJsonDepValue::Workspace(version_req) => self
|
||||
.workspace_resolver
|
||||
.resolve_workspace_pkg_json_folder_for_pkg_json_dep(
|
||||
alias,
|
||||
version_req,
|
||||
)
|
||||
.map_err(|e| {
|
||||
DenoResolveErrorKind::WorkspaceResolvePkgJsonFolder(e)
|
||||
.into_box()
|
||||
})
|
||||
.and_then(|pkg_folder| {
|
||||
self
|
||||
.node_and_npm_resolver
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.node_resolver
|
||||
.resolve_package_subpath_from_deno_module(
|
||||
pkg_folder,
|
||||
sub_path.as_deref(),
|
||||
Some(referrer),
|
||||
referrer_kind,
|
||||
mode,
|
||||
)
|
||||
.map_err(|e| {
|
||||
DenoResolveErrorKind::PackageSubpathResolve(e).into_box()
|
||||
})
|
||||
}),
|
||||
})
|
||||
}
|
||||
},
|
||||
Err(err) => Err(err),
|
||||
};
|
||||
|
||||
// When the user is vendoring, don't allow them to import directly from the vendor/ directory
|
||||
// as it might cause them confusion or duplicate dependencies. Additionally, this folder has
|
||||
// special treatment in the language server so it will definitely cause issues/confusion there
|
||||
// if they do this.
|
||||
if let Some(vendor_specifier) = &self.maybe_vendor_specifier {
|
||||
if let Ok(specifier) = &result {
|
||||
if specifier.as_str().starts_with(vendor_specifier.as_str()) {
|
||||
return Err(
|
||||
DenoResolveErrorKind::InvalidVendorFolderImport.into_box(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let Some(NodeAndNpmReqResolver {
|
||||
node_resolver,
|
||||
npm_req_resolver,
|
||||
}) = &self.node_and_npm_resolver
|
||||
else {
|
||||
return Ok(DenoResolution {
|
||||
url: result?,
|
||||
maybe_diagnostic,
|
||||
found_package_json_dep,
|
||||
});
|
||||
};
|
||||
|
||||
match result {
|
||||
Ok(specifier) => {
|
||||
if let Ok(npm_req_ref) =
|
||||
NpmPackageReqReference::from_specifier(&specifier)
|
||||
{
|
||||
// check if the npm specifier resolves to a workspace member
|
||||
if let Some(pkg_folder) = self
|
||||
.workspace_resolver
|
||||
.resolve_workspace_pkg_json_folder_for_npm_specifier(
|
||||
npm_req_ref.req(),
|
||||
)
|
||||
{
|
||||
return node_resolver
|
||||
.resolve_package_subpath_from_deno_module(
|
||||
pkg_folder,
|
||||
npm_req_ref.sub_path(),
|
||||
Some(referrer),
|
||||
referrer_kind,
|
||||
mode,
|
||||
)
|
||||
.map(|url| DenoResolution {
|
||||
url,
|
||||
maybe_diagnostic,
|
||||
found_package_json_dep,
|
||||
})
|
||||
.map_err(|e| e.into());
|
||||
}
|
||||
|
||||
// do npm resolution for byonm
|
||||
if self.is_byonm {
|
||||
return npm_req_resolver
|
||||
.resolve_req_reference(
|
||||
&npm_req_ref,
|
||||
referrer,
|
||||
referrer_kind,
|
||||
mode,
|
||||
)
|
||||
.map(|url| DenoResolution {
|
||||
url,
|
||||
maybe_diagnostic,
|
||||
found_package_json_dep,
|
||||
})
|
||||
.map_err(|err| match err {
|
||||
ResolveReqWithSubPathError::MissingPackageNodeModulesFolder(
|
||||
err,
|
||||
) => err.into(),
|
||||
ResolveReqWithSubPathError::ResolvePkgFolderFromDenoReq(
|
||||
err,
|
||||
) => err.into(),
|
||||
ResolveReqWithSubPathError::PackageSubpathResolve(err) => {
|
||||
err.into()
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(DenoResolution {
|
||||
url: node_resolver
|
||||
.handle_if_in_node_modules(&specifier)
|
||||
.unwrap_or(specifier),
|
||||
maybe_diagnostic,
|
||||
found_package_json_dep,
|
||||
})
|
||||
}
|
||||
Err(err) => {
|
||||
// If byonm, check if the bare specifier resolves to an npm package
|
||||
if self.is_byonm && referrer.scheme() == "file" {
|
||||
let maybe_resolution = npm_req_resolver
|
||||
.resolve_if_for_npm_pkg(
|
||||
raw_specifier,
|
||||
referrer,
|
||||
referrer_kind,
|
||||
mode,
|
||||
)
|
||||
.map_err(|e| match e {
|
||||
ResolveIfForNpmPackageError::NodeResolve(e) => {
|
||||
DenoResolveErrorKind::Node(e).into_box()
|
||||
}
|
||||
ResolveIfForNpmPackageError::NodeModulesOutOfDate(e) => e.into(),
|
||||
})?;
|
||||
if let Some(res) = maybe_resolution {
|
||||
match res {
|
||||
NodeResolution::Module(url) => {
|
||||
return Ok(DenoResolution {
|
||||
url,
|
||||
maybe_diagnostic,
|
||||
found_package_json_dep,
|
||||
})
|
||||
}
|
||||
NodeResolution::BuiltIn(_) => {
|
||||
// don't resolve bare specifiers for built-in modules via node resolution
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ use node_resolver::errors::PackageFolderResolveIoError;
|
|||
use node_resolver::errors::PackageJsonLoadError;
|
||||
use node_resolver::errors::PackageNotFoundError;
|
||||
use node_resolver::InNpmPackageChecker;
|
||||
use node_resolver::NpmResolver;
|
||||
use node_resolver::NpmPackageFolderResolver;
|
||||
use node_resolver::PackageJsonResolverRc;
|
||||
use thiserror::Error;
|
||||
use url::Url;
|
||||
|
@ -24,6 +24,8 @@ use url::Url;
|
|||
use crate::fs::DenoResolverFs;
|
||||
|
||||
use super::local::normalize_pkg_name_for_node_modules_deno_folder;
|
||||
use super::CliNpmReqResolver;
|
||||
use super::ResolvePkgFolderFromDenoReqError;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ByonmResolvePkgFolderFromDenoReqError {
|
||||
|
@ -303,7 +305,24 @@ impl<Fs: DenoResolverFs, TEnv: NodeResolverEnv> ByonmNpmResolver<Fs, TEnv> {
|
|||
impl<
|
||||
Fs: DenoResolverFs + Send + Sync + std::fmt::Debug,
|
||||
TEnv: NodeResolverEnv,
|
||||
> NpmResolver for ByonmNpmResolver<Fs, TEnv>
|
||||
> CliNpmReqResolver for ByonmNpmResolver<Fs, TEnv>
|
||||
{
|
||||
fn resolve_pkg_folder_from_deno_module_req(
|
||||
&self,
|
||||
req: &PackageReq,
|
||||
referrer: &Url,
|
||||
) -> Result<PathBuf, ResolvePkgFolderFromDenoReqError> {
|
||||
ByonmNpmResolver::resolve_pkg_folder_from_deno_module_req(
|
||||
self, req, referrer,
|
||||
)
|
||||
.map_err(ResolvePkgFolderFromDenoReqError::Byonm)
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
Fs: DenoResolverFs + Send + Sync + std::fmt::Debug,
|
||||
TEnv: NodeResolverEnv,
|
||||
> NpmPackageFolderResolver for ByonmNpmResolver<Fs, TEnv>
|
||||
{
|
||||
fn resolve_package_folder_from_package(
|
||||
&self,
|
||||
|
|
|
@ -1,10 +1,256 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
mod byonm;
|
||||
mod local;
|
||||
use std::fmt::Debug;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use deno_semver::npm::NpmPackageReqReference;
|
||||
use deno_semver::package::PackageReq;
|
||||
use node_resolver::env::NodeResolverEnv;
|
||||
use node_resolver::errors::NodeResolveError;
|
||||
use node_resolver::errors::NodeResolveErrorKind;
|
||||
use node_resolver::errors::PackageFolderResolveErrorKind;
|
||||
use node_resolver::errors::PackageFolderResolveIoError;
|
||||
use node_resolver::errors::PackageNotFoundError;
|
||||
use node_resolver::errors::PackageResolveErrorKind;
|
||||
use node_resolver::errors::PackageSubpathResolveError;
|
||||
use node_resolver::InNpmPackageChecker;
|
||||
use node_resolver::NodeModuleKind;
|
||||
use node_resolver::NodeResolution;
|
||||
use node_resolver::NodeResolutionMode;
|
||||
use node_resolver::NodeResolver;
|
||||
use thiserror::Error;
|
||||
use url::Url;
|
||||
|
||||
use crate::fs::DenoResolverFs;
|
||||
|
||||
pub use byonm::ByonmInNpmPackageChecker;
|
||||
pub use byonm::ByonmNpmResolver;
|
||||
pub use byonm::ByonmNpmResolverCreateOptions;
|
||||
pub use byonm::ByonmResolvePkgFolderFromDenoReqError;
|
||||
pub use local::normalize_pkg_name_for_node_modules_deno_folder;
|
||||
|
||||
mod byonm;
|
||||
mod local;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
#[error("Could not resolve \"{}\", but found it in a package.json. Deno expects the node_modules/ directory to be up to date. Did you forget to run `deno install`?", specifier)]
|
||||
pub struct NodeModulesOutOfDateError {
|
||||
pub specifier: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
#[error("Could not find '{}'. Deno expects the node_modules/ directory to be up to date. Did you forget to run `deno install`?", package_json_path.display())]
|
||||
pub struct MissingPackageNodeModulesFolderError {
|
||||
pub package_json_path: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ResolveIfForNpmPackageError {
|
||||
#[error(transparent)]
|
||||
NodeResolve(#[from] NodeResolveError),
|
||||
#[error(transparent)]
|
||||
NodeModulesOutOfDate(#[from] NodeModulesOutOfDateError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ResolveReqWithSubPathError {
|
||||
#[error(transparent)]
|
||||
MissingPackageNodeModulesFolder(#[from] MissingPackageNodeModulesFolderError),
|
||||
#[error(transparent)]
|
||||
ResolvePkgFolderFromDenoReq(#[from] ResolvePkgFolderFromDenoReqError),
|
||||
#[error(transparent)]
|
||||
PackageSubpathResolve(#[from] PackageSubpathResolveError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ResolvePkgFolderFromDenoReqError {
|
||||
// todo(dsherret): don't use anyhow here
|
||||
#[error(transparent)]
|
||||
Managed(anyhow::Error),
|
||||
#[error(transparent)]
|
||||
Byonm(#[from] ByonmResolvePkgFolderFromDenoReqError),
|
||||
}
|
||||
|
||||
// todo(dsherret): a temporary trait until we extract
|
||||
// out the CLI npm resolver into here
|
||||
pub trait CliNpmReqResolver: Debug + Send + Sync {
|
||||
fn resolve_pkg_folder_from_deno_module_req(
|
||||
&self,
|
||||
req: &PackageReq,
|
||||
referrer: &Url,
|
||||
) -> Result<PathBuf, ResolvePkgFolderFromDenoReqError>;
|
||||
}
|
||||
|
||||
pub struct NpmReqResolverOptions<
|
||||
Fs: DenoResolverFs,
|
||||
TNodeResolverEnv: NodeResolverEnv,
|
||||
> {
|
||||
/// The resolver when "bring your own node_modules" is enabled where Deno
|
||||
/// does not setup the node_modules directories automatically, but instead
|
||||
/// uses what already exists on the file system.
|
||||
pub byonm_resolver: Option<Arc<ByonmNpmResolver<Fs, TNodeResolverEnv>>>,
|
||||
pub fs: Fs,
|
||||
pub in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
|
||||
pub node_resolver: Arc<NodeResolver<TNodeResolverEnv>>,
|
||||
pub npm_req_resolver: Arc<dyn CliNpmReqResolver>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NpmReqResolver<Fs: DenoResolverFs, TNodeResolverEnv: NodeResolverEnv>
|
||||
{
|
||||
byonm_resolver: Option<Arc<ByonmNpmResolver<Fs, TNodeResolverEnv>>>,
|
||||
fs: Fs,
|
||||
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
|
||||
node_resolver: Arc<NodeResolver<TNodeResolverEnv>>,
|
||||
npm_resolver: Arc<dyn CliNpmReqResolver>,
|
||||
}
|
||||
|
||||
impl<Fs: DenoResolverFs, TNodeResolverEnv: NodeResolverEnv>
|
||||
NpmReqResolver<Fs, TNodeResolverEnv>
|
||||
{
|
||||
pub fn new(options: NpmReqResolverOptions<Fs, TNodeResolverEnv>) -> Self {
|
||||
Self {
|
||||
byonm_resolver: options.byonm_resolver,
|
||||
fs: options.fs,
|
||||
in_npm_pkg_checker: options.in_npm_pkg_checker,
|
||||
node_resolver: options.node_resolver,
|
||||
npm_resolver: options.npm_req_resolver,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_req_reference(
|
||||
&self,
|
||||
req_ref: &NpmPackageReqReference,
|
||||
referrer: &Url,
|
||||
referrer_kind: NodeModuleKind,
|
||||
mode: NodeResolutionMode,
|
||||
) -> Result<Url, ResolveReqWithSubPathError> {
|
||||
self.resolve_req_with_sub_path(
|
||||
req_ref.req(),
|
||||
req_ref.sub_path(),
|
||||
referrer,
|
||||
referrer_kind,
|
||||
mode,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn resolve_req_with_sub_path(
|
||||
&self,
|
||||
req: &PackageReq,
|
||||
sub_path: Option<&str>,
|
||||
referrer: &Url,
|
||||
referrer_kind: NodeModuleKind,
|
||||
mode: NodeResolutionMode,
|
||||
) -> Result<Url, ResolveReqWithSubPathError> {
|
||||
let package_folder = self
|
||||
.npm_resolver
|
||||
.resolve_pkg_folder_from_deno_module_req(req, referrer)?;
|
||||
let resolution_result =
|
||||
self.node_resolver.resolve_package_subpath_from_deno_module(
|
||||
&package_folder,
|
||||
sub_path,
|
||||
Some(referrer),
|
||||
referrer_kind,
|
||||
mode,
|
||||
);
|
||||
match resolution_result {
|
||||
Ok(url) => Ok(url),
|
||||
Err(err) => {
|
||||
if self.byonm_resolver.is_some() {
|
||||
let package_json_path = package_folder.join("package.json");
|
||||
if !self.fs.exists_sync(&package_json_path) {
|
||||
return Err(
|
||||
MissingPackageNodeModulesFolderError { package_json_path }.into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
Err(err.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_if_for_npm_pkg(
|
||||
&self,
|
||||
specifier: &str,
|
||||
referrer: &Url,
|
||||
referrer_kind: NodeModuleKind,
|
||||
mode: NodeResolutionMode,
|
||||
) -> Result<Option<NodeResolution>, ResolveIfForNpmPackageError> {
|
||||
let resolution_result =
|
||||
self
|
||||
.node_resolver
|
||||
.resolve(specifier, referrer, referrer_kind, mode);
|
||||
match resolution_result {
|
||||
Ok(res) => Ok(Some(res)),
|
||||
Err(err) => {
|
||||
let err = err.into_kind();
|
||||
match err {
|
||||
NodeResolveErrorKind::RelativeJoin(_)
|
||||
| NodeResolveErrorKind::PackageImportsResolve(_)
|
||||
| NodeResolveErrorKind::UnsupportedEsmUrlScheme(_)
|
||||
| NodeResolveErrorKind::DataUrlReferrer(_)
|
||||
| NodeResolveErrorKind::TypesNotFound(_)
|
||||
| NodeResolveErrorKind::FinalizeResolution(_) => {
|
||||
Err(ResolveIfForNpmPackageError::NodeResolve(err.into()))
|
||||
}
|
||||
NodeResolveErrorKind::PackageResolve(err) => {
|
||||
let err = err.into_kind();
|
||||
match err {
|
||||
PackageResolveErrorKind::ClosestPkgJson(_)
|
||||
| PackageResolveErrorKind::InvalidModuleSpecifier(_)
|
||||
| PackageResolveErrorKind::ExportsResolve(_)
|
||||
| PackageResolveErrorKind::SubpathResolve(_) => {
|
||||
Err(ResolveIfForNpmPackageError::NodeResolve(
|
||||
NodeResolveErrorKind::PackageResolve(err.into()).into(),
|
||||
))
|
||||
}
|
||||
PackageResolveErrorKind::PackageFolderResolve(err) => {
|
||||
match err.as_kind() {
|
||||
PackageFolderResolveErrorKind::Io(
|
||||
PackageFolderResolveIoError { package_name, .. },
|
||||
)
|
||||
| PackageFolderResolveErrorKind::PackageNotFound(
|
||||
PackageNotFoundError { package_name, .. },
|
||||
) => {
|
||||
if self.in_npm_pkg_checker.in_npm_package(referrer) {
|
||||
return Err(ResolveIfForNpmPackageError::NodeResolve(
|
||||
NodeResolveErrorKind::PackageResolve(err.into()).into(),
|
||||
));
|
||||
}
|
||||
if let Some(byonm_npm_resolver) = &self.byonm_resolver {
|
||||
if byonm_npm_resolver
|
||||
.find_ancestor_package_json_with_dep(
|
||||
package_name,
|
||||
referrer,
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
return Err(
|
||||
ResolveIfForNpmPackageError::NodeModulesOutOfDate(
|
||||
NodeModulesOutOfDateError {
|
||||
specifier: specifier.to_string(),
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
PackageFolderResolveErrorKind::ReferrerNotFound(_) => {
|
||||
if self.in_npm_pkg_checker.in_npm_package(referrer) {
|
||||
return Err(ResolveIfForNpmPackageError::NodeResolve(
|
||||
NodeResolveErrorKind::PackageResolve(err.into()).into(),
|
||||
));
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ use crate::npm::InNpmPackageCheckerRc;
|
|||
use crate::resolution::NodeResolverRc;
|
||||
use crate::NodeModuleKind;
|
||||
use crate::NodeResolutionMode;
|
||||
use crate::NpmResolverRc;
|
||||
use crate::NpmPackageFolderResolverRc;
|
||||
use crate::PackageJsonResolverRc;
|
||||
use crate::PathClean;
|
||||
|
||||
|
@ -66,7 +66,7 @@ pub struct NodeCodeTranslator<
|
|||
env: TNodeResolverEnv,
|
||||
in_npm_pkg_checker: InNpmPackageCheckerRc,
|
||||
node_resolver: NodeResolverRc<TNodeResolverEnv>,
|
||||
npm_resolver: NpmResolverRc,
|
||||
npm_resolver: NpmPackageFolderResolverRc,
|
||||
pkg_json_resolver: PackageJsonResolverRc<TNodeResolverEnv>,
|
||||
}
|
||||
|
||||
|
@ -78,7 +78,7 @@ impl<TCjsCodeAnalyzer: CjsCodeAnalyzer, TNodeResolverEnv: NodeResolverEnv>
|
|||
env: TNodeResolverEnv,
|
||||
in_npm_pkg_checker: InNpmPackageCheckerRc,
|
||||
node_resolver: NodeResolverRc<TNodeResolverEnv>,
|
||||
npm_resolver: NpmResolverRc,
|
||||
npm_resolver: NpmPackageFolderResolverRc,
|
||||
pkg_json_resolver: PackageJsonResolverRc<TNodeResolverEnv>,
|
||||
) -> Self {
|
||||
Self {
|
||||
|
|
|
@ -15,13 +15,14 @@ mod sync;
|
|||
pub use deno_package_json::PackageJson;
|
||||
pub use npm::InNpmPackageChecker;
|
||||
pub use npm::InNpmPackageCheckerRc;
|
||||
pub use npm::NpmResolver;
|
||||
pub use npm::NpmResolverRc;
|
||||
pub use npm::NpmPackageFolderResolver;
|
||||
pub use npm::NpmPackageFolderResolverRc;
|
||||
pub use package_json::PackageJsonResolver;
|
||||
pub use package_json::PackageJsonResolverRc;
|
||||
pub use package_json::PackageJsonThreadLocalCache;
|
||||
pub use path::PathClean;
|
||||
pub use resolution::parse_npm_pkg_name;
|
||||
pub use resolution::resolve_specifier_into_node_modules;
|
||||
pub use resolution::NodeModuleKind;
|
||||
pub use resolution::NodeResolution;
|
||||
pub use resolution::NodeResolutionMode;
|
||||
|
|
|
@ -13,10 +13,13 @@ use crate::sync::MaybeSend;
|
|||
use crate::sync::MaybeSync;
|
||||
|
||||
#[allow(clippy::disallowed_types)]
|
||||
pub type NpmResolverRc = crate::sync::MaybeArc<dyn NpmResolver>;
|
||||
pub type NpmPackageFolderResolverRc =
|
||||
crate::sync::MaybeArc<dyn NpmPackageFolderResolver>;
|
||||
|
||||
pub trait NpmResolver: std::fmt::Debug + MaybeSend + MaybeSync {
|
||||
/// Resolves an npm package folder path from an npm package referrer.
|
||||
pub trait NpmPackageFolderResolver:
|
||||
std::fmt::Debug + MaybeSend + MaybeSync
|
||||
{
|
||||
/// Resolves an npm package folder path from the specified referrer.
|
||||
fn resolve_package_folder_from_package(
|
||||
&self,
|
||||
specifier: &str,
|
||||
|
|
|
@ -41,7 +41,7 @@ use crate::errors::TypesNotFoundErrorData;
|
|||
use crate::errors::UnsupportedDirImportError;
|
||||
use crate::errors::UnsupportedEsmUrlSchemeError;
|
||||
use crate::npm::InNpmPackageCheckerRc;
|
||||
use crate::NpmResolverRc;
|
||||
use crate::NpmPackageFolderResolverRc;
|
||||
use crate::PackageJsonResolverRc;
|
||||
use crate::PathClean;
|
||||
use deno_package_json::PackageJson;
|
||||
|
@ -101,7 +101,7 @@ pub type NodeResolverRc<TEnv> = crate::sync::MaybeArc<NodeResolver<TEnv>>;
|
|||
pub struct NodeResolver<TEnv: NodeResolverEnv> {
|
||||
env: TEnv,
|
||||
in_npm_pkg_checker: InNpmPackageCheckerRc,
|
||||
npm_resolver: NpmResolverRc,
|
||||
npm_pkg_folder_resolver: NpmPackageFolderResolverRc,
|
||||
pkg_json_resolver: PackageJsonResolverRc<TEnv>,
|
||||
}
|
||||
|
||||
|
@ -109,13 +109,13 @@ impl<TEnv: NodeResolverEnv> NodeResolver<TEnv> {
|
|||
pub fn new(
|
||||
env: TEnv,
|
||||
in_npm_pkg_checker: InNpmPackageCheckerRc,
|
||||
npm_resolver: NpmResolverRc,
|
||||
npm_pkg_folder_resolver: NpmPackageFolderResolverRc,
|
||||
pkg_json_resolver: PackageJsonResolverRc<TEnv>,
|
||||
) -> Self {
|
||||
Self {
|
||||
env,
|
||||
in_npm_pkg_checker,
|
||||
npm_resolver,
|
||||
npm_pkg_folder_resolver,
|
||||
pkg_json_resolver,
|
||||
}
|
||||
}
|
||||
|
@ -1126,7 +1126,7 @@ impl<TEnv: NodeResolverEnv> NodeResolver<TEnv> {
|
|||
mode: NodeResolutionMode,
|
||||
) -> Result<Url, PackageResolveError> {
|
||||
let package_dir_path = self
|
||||
.npm_resolver
|
||||
.npm_pkg_folder_resolver
|
||||
.resolve_package_folder_from_package(package_name, referrer)?;
|
||||
|
||||
// todo: error with this instead when can't find package
|
||||
|
@ -1412,6 +1412,25 @@ impl<TEnv: NodeResolverEnv> NodeResolver<TEnv> {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolves a specifier that is pointing into a node_modules folder by canonicalizing it.
|
||||
///
|
||||
/// Returns `None` when the specifier is not in a node_modules folder.
|
||||
pub fn handle_if_in_node_modules(&self, specifier: &Url) -> Option<Url> {
|
||||
// skip canonicalizing if we definitely know it's unnecessary
|
||||
if specifier.scheme() == "file"
|
||||
&& specifier.path().contains("/node_modules/")
|
||||
{
|
||||
// Specifiers in the node_modules directory are canonicalized
|
||||
// so canoncalize then check if it's in the node_modules directory.
|
||||
let specifier = resolve_specifier_into_node_modules(specifier, &|path| {
|
||||
self.env.realpath_sync(path)
|
||||
});
|
||||
return Some(specifier);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_bin_entry_value<'a>(
|
||||
|
@ -1660,6 +1679,28 @@ pub fn parse_npm_pkg_name(
|
|||
Ok((package_name, package_subpath, is_scoped))
|
||||
}
|
||||
|
||||
/// Resolves a specifier that is pointing into a node_modules folder.
|
||||
///
|
||||
/// Note: This should be called whenever getting the specifier from
|
||||
/// a Module::External(module) reference because that module might
|
||||
/// not be fully resolved at the time deno_graph is analyzing it
|
||||
/// because the node_modules folder might not exist at that time.
|
||||
pub fn resolve_specifier_into_node_modules(
|
||||
specifier: &Url,
|
||||
canonicalize: &impl Fn(&Path) -> std::io::Result<PathBuf>,
|
||||
) -> Url {
|
||||
deno_path_util::url_to_file_path(specifier)
|
||||
.ok()
|
||||
// this path might not exist at the time the graph is being created
|
||||
// because the node_modules folder might not yet exist
|
||||
.and_then(|path| {
|
||||
deno_path_util::canonicalize_path_maybe_not_exists(&path, canonicalize)
|
||||
.ok()
|
||||
})
|
||||
.and_then(|path| deno_path_util::url_from_file_path(&path).ok())
|
||||
.unwrap_or_else(|| specifier.clone())
|
||||
}
|
||||
|
||||
fn pattern_key_compare(a: &str, b: &str) -> i32 {
|
||||
let a_pattern_index = a.find('*');
|
||||
let b_pattern_index = b.find('*');
|
||||
|
|
Loading…
Reference in a new issue