mirror of
https://github.com/denoland/deno.git
synced 2025-02-21 12:53:05 -05:00

Extracted out of https://github.com/denoland/deno/pull/27838/files Reduces some allocations by accepting either a pathbuf or url for the referrer for resolution and returning either a pathbuf or url at the end, which the caller can then convert into to their preferred state. This is about 4% faster when still converting the final result to a url and 6% faster when keeping the result as a path in a benchmark I ran.
897 lines
26 KiB
Rust
897 lines
26 KiB
Rust
// Copyright 2018-2025 the Deno authors. MIT license.
|
|
|
|
use std::borrow::Cow;
|
|
use std::path::Path;
|
|
use std::path::PathBuf;
|
|
|
|
use boxed_error::Boxed;
|
|
use deno_cache_dir::npm::NpmCacheDir;
|
|
use deno_cache_dir::DenoDirResolutionError;
|
|
use deno_cache_dir::GlobalHttpCacheRc;
|
|
use deno_cache_dir::HttpCacheRc;
|
|
use deno_cache_dir::LocalHttpCache;
|
|
use deno_config::deno_json::NodeModulesDirMode;
|
|
use deno_config::workspace::FolderConfigs;
|
|
use deno_config::workspace::PackageJsonDepResolution;
|
|
use deno_config::workspace::VendorEnablement;
|
|
use deno_config::workspace::WorkspaceDirectory;
|
|
use deno_config::workspace::WorkspaceDirectoryEmptyOptions;
|
|
use deno_config::workspace::WorkspaceDiscoverError;
|
|
use deno_config::workspace::WorkspaceDiscoverOptions;
|
|
use deno_config::workspace::WorkspaceDiscoverStart;
|
|
use deno_npm::NpmSystemInfo;
|
|
use deno_path_util::fs::canonicalize_path_maybe_not_exists;
|
|
use deno_path_util::normalize_path;
|
|
use futures::future::FutureExt;
|
|
use node_resolver::ConditionsFromResolutionMode;
|
|
use node_resolver::DenoIsBuiltInNodeModuleChecker;
|
|
use node_resolver::NodeResolver;
|
|
use node_resolver::NodeResolverRc;
|
|
use node_resolver::PackageJsonResolver;
|
|
use node_resolver::PackageJsonResolverRc;
|
|
use sys_traits::EnvCacheDir;
|
|
use sys_traits::EnvCurrentDir;
|
|
use sys_traits::EnvHomeDir;
|
|
use sys_traits::EnvVar;
|
|
use sys_traits::FsCanonicalize;
|
|
use sys_traits::FsCreateDirAll;
|
|
use sys_traits::FsMetadata;
|
|
use sys_traits::FsOpen;
|
|
use sys_traits::FsRead;
|
|
use sys_traits::FsReadDir;
|
|
use sys_traits::FsRemoveFile;
|
|
use sys_traits::FsRename;
|
|
use sys_traits::SystemRandom;
|
|
use sys_traits::SystemTimeNow;
|
|
use sys_traits::ThreadSleep;
|
|
use thiserror::Error;
|
|
|
|
use crate::npm::managed::ManagedInNpmPkgCheckerCreateOptions;
|
|
use crate::npm::managed::ManagedNpmResolverCreateOptions;
|
|
use crate::npm::managed::NpmResolutionCellRc;
|
|
use crate::npm::ByonmNpmResolverCreateOptions;
|
|
use crate::npm::CreateInNpmPkgCheckerOptions;
|
|
use crate::npm::DenoInNpmPackageChecker;
|
|
use crate::npm::NpmReqResolver;
|
|
use crate::npm::NpmReqResolverOptions;
|
|
use crate::npm::NpmReqResolverRc;
|
|
use crate::npm::NpmResolver;
|
|
use crate::npm::NpmResolverCreateOptions;
|
|
use crate::npmrc::discover_npmrc_from_workspace;
|
|
use crate::npmrc::NpmRcDiscoverError;
|
|
use crate::npmrc::ResolvedNpmRcRc;
|
|
use crate::sloppy_imports::SloppyImportsCachedFs;
|
|
use crate::sloppy_imports::SloppyImportsResolver;
|
|
use crate::sloppy_imports::SloppyImportsResolverRc;
|
|
use crate::sync::new_rc;
|
|
use crate::sync::MaybeSend;
|
|
use crate::sync::MaybeSync;
|
|
use crate::DefaultDenoResolverRc;
|
|
use crate::DenoResolver;
|
|
use crate::DenoResolverOptions;
|
|
use crate::NodeAndNpmReqResolver;
|
|
use crate::NpmCacheDirRc;
|
|
use crate::WorkspaceResolverRc;
|
|
|
|
// todo(https://github.com/rust-lang/rust/issues/109737): remove once_cell after get_or_try_init is stabilized
|
|
#[cfg(feature = "sync")]
|
|
type Deferred<T> = once_cell::sync::OnceCell<T>;
|
|
#[cfg(not(feature = "sync"))]
|
|
type Deferred<T> = once_cell::unsync::OnceCell<T>;
|
|
|
|
#[allow(clippy::disallowed_types)]
|
|
type WorkspaceDirectoryRc = crate::sync::MaybeArc<WorkspaceDirectory>;
|
|
|
|
#[derive(Debug, Boxed)]
|
|
pub struct HttpCacheCreateError(pub Box<HttpCacheCreateErrorKind>);
|
|
|
|
#[derive(Debug, Error)]
|
|
pub enum HttpCacheCreateErrorKind {
|
|
#[error(transparent)]
|
|
DenoDirResolution(#[from] DenoDirResolutionError),
|
|
#[error(transparent)]
|
|
WorkspaceDiscover(#[from] WorkspaceDiscoverError),
|
|
}
|
|
|
|
#[derive(Debug, Boxed)]
|
|
pub struct NpmCacheDirCreateError(pub Box<NpmCacheDirCreateErrorKind>);
|
|
|
|
#[derive(Debug, Error)]
|
|
pub enum NpmCacheDirCreateErrorKind {
|
|
#[error(transparent)]
|
|
DenoDirResolution(#[from] DenoDirResolutionError),
|
|
#[error(transparent)]
|
|
NpmRcCreate(#[from] NpmRcCreateError),
|
|
}
|
|
|
|
#[derive(Debug, Boxed)]
|
|
pub struct NpmRcCreateError(pub Box<NpmRcCreateErrorKind>);
|
|
|
|
#[derive(Debug, Error)]
|
|
pub enum NpmRcCreateErrorKind {
|
|
#[error(transparent)]
|
|
WorkspaceDiscover(#[from] WorkspaceDiscoverError),
|
|
#[error(transparent)]
|
|
NpmRcDiscover(#[from] NpmRcDiscoverError),
|
|
}
|
|
|
|
#[derive(Debug, Default)]
|
|
pub enum ConfigDiscoveryOption {
|
|
#[default]
|
|
DiscoverCwd,
|
|
Discover {
|
|
start_paths: Vec<PathBuf>,
|
|
},
|
|
Path(PathBuf),
|
|
Disabled,
|
|
}
|
|
|
|
#[async_trait::async_trait(?Send)]
|
|
pub trait SpecifiedImportMapProvider:
|
|
std::fmt::Debug + MaybeSend + MaybeSync
|
|
{
|
|
async fn get(
|
|
&self,
|
|
) -> Result<Option<deno_config::workspace::SpecifiedImportMap>, anyhow::Error>;
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct DenoDirPathProviderOptions {
|
|
pub maybe_custom_root: Option<PathBuf>,
|
|
}
|
|
|
|
#[allow(clippy::disallowed_types)]
|
|
pub type DenoDirPathProviderRc<TSys> =
|
|
crate::sync::MaybeArc<DenoDirPathProvider<TSys>>;
|
|
|
|
/// Lazily creates the deno dir which might be useful in scenarios
|
|
/// where functionality wants to continue if the DENO_DIR can't be created.
|
|
#[derive(Debug)]
|
|
pub struct DenoDirPathProvider<
|
|
TSys: EnvCacheDir + EnvHomeDir + EnvVar + EnvCurrentDir,
|
|
> {
|
|
sys: TSys,
|
|
options: DenoDirPathProviderOptions,
|
|
deno_dir: Deferred<PathBuf>,
|
|
}
|
|
|
|
impl<TSys: EnvCacheDir + EnvHomeDir + EnvVar + EnvCurrentDir>
|
|
DenoDirPathProvider<TSys>
|
|
{
|
|
pub fn new(sys: TSys, options: DenoDirPathProviderOptions) -> Self {
|
|
Self {
|
|
sys,
|
|
options,
|
|
deno_dir: Default::default(),
|
|
}
|
|
}
|
|
|
|
pub fn get_or_create(&self) -> Result<&PathBuf, DenoDirResolutionError> {
|
|
self.deno_dir.get_or_try_init(|| {
|
|
deno_cache_dir::resolve_deno_dir(
|
|
&self.sys,
|
|
self.options.maybe_custom_root.clone(),
|
|
)
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct NpmProcessStateOptions {
|
|
pub node_modules_dir: Option<Cow<'static, str>>,
|
|
pub is_byonm: bool,
|
|
}
|
|
|
|
#[derive(Debug, Default)]
|
|
pub struct WorkspaceFactoryOptions<
|
|
TSys: EnvCacheDir + EnvHomeDir + EnvVar + EnvCurrentDir + FsCanonicalize,
|
|
> {
|
|
pub additional_config_file_names: &'static [&'static str],
|
|
pub config_discovery: ConfigDiscoveryOption,
|
|
pub deno_dir_path_provider: Option<DenoDirPathProviderRc<TSys>>,
|
|
pub is_package_manager_subcommand: bool,
|
|
pub node_modules_dir: Option<NodeModulesDirMode>,
|
|
pub no_npm: bool,
|
|
/// The process sate if using ext/node and the current process was "forked".
|
|
/// This value is found at `deno_lib::args::NPM_PROCESS_STATE`
|
|
/// but in most scenarios this can probably just be `None`.
|
|
pub npm_process_state: Option<NpmProcessStateOptions>,
|
|
pub vendor: Option<bool>,
|
|
}
|
|
|
|
#[allow(clippy::disallowed_types)]
|
|
pub type WorkspaceFactoryRc<TSys> =
|
|
crate::sync::MaybeArc<WorkspaceFactory<TSys>>;
|
|
|
|
pub struct WorkspaceFactory<
|
|
TSys: EnvCacheDir
|
|
+ EnvHomeDir
|
|
+ EnvVar
|
|
+ EnvCurrentDir
|
|
+ FsCanonicalize
|
|
+ FsCreateDirAll
|
|
+ FsMetadata
|
|
+ FsOpen
|
|
+ FsRead
|
|
+ FsReadDir
|
|
+ FsRemoveFile
|
|
+ FsRename
|
|
+ SystemRandom
|
|
+ SystemTimeNow
|
|
+ ThreadSleep
|
|
+ std::fmt::Debug
|
|
+ MaybeSend
|
|
+ MaybeSync
|
|
+ Clone
|
|
+ 'static,
|
|
> {
|
|
sys: TSys,
|
|
deno_dir_path: DenoDirPathProviderRc<TSys>,
|
|
global_http_cache: Deferred<GlobalHttpCacheRc<TSys>>,
|
|
http_cache: Deferred<HttpCacheRc>,
|
|
node_modules_dir_path: Deferred<Option<PathBuf>>,
|
|
npm_cache_dir: Deferred<NpmCacheDirRc>,
|
|
npmrc: Deferred<ResolvedNpmRcRc>,
|
|
node_modules_dir_mode: Deferred<NodeModulesDirMode>,
|
|
workspace_directory: Deferred<WorkspaceDirectoryRc>,
|
|
initial_cwd: PathBuf,
|
|
options: WorkspaceFactoryOptions<TSys>,
|
|
}
|
|
|
|
impl<
|
|
TSys: EnvCacheDir
|
|
+ EnvHomeDir
|
|
+ EnvVar
|
|
+ EnvCurrentDir
|
|
+ FsCanonicalize
|
|
+ FsCreateDirAll
|
|
+ FsMetadata
|
|
+ FsOpen
|
|
+ FsRead
|
|
+ FsReadDir
|
|
+ FsRemoveFile
|
|
+ FsRename
|
|
+ SystemRandom
|
|
+ SystemTimeNow
|
|
+ ThreadSleep
|
|
+ std::fmt::Debug
|
|
+ MaybeSend
|
|
+ MaybeSync
|
|
+ Clone
|
|
+ 'static,
|
|
> WorkspaceFactory<TSys>
|
|
{
|
|
pub fn new(
|
|
sys: TSys,
|
|
initial_cwd: PathBuf,
|
|
mut options: WorkspaceFactoryOptions<TSys>,
|
|
) -> Self {
|
|
Self {
|
|
deno_dir_path: options.deno_dir_path_provider.take().unwrap_or_else(
|
|
|| {
|
|
new_rc(DenoDirPathProvider::new(
|
|
sys.clone(),
|
|
DenoDirPathProviderOptions {
|
|
maybe_custom_root: None,
|
|
},
|
|
))
|
|
},
|
|
),
|
|
sys,
|
|
global_http_cache: Default::default(),
|
|
http_cache: Default::default(),
|
|
node_modules_dir_path: Default::default(),
|
|
npm_cache_dir: Default::default(),
|
|
npmrc: Default::default(),
|
|
node_modules_dir_mode: Default::default(),
|
|
workspace_directory: Default::default(),
|
|
initial_cwd,
|
|
options,
|
|
}
|
|
}
|
|
|
|
pub fn set_workspace_directory(
|
|
&mut self,
|
|
workspace_directory: WorkspaceDirectoryRc,
|
|
) {
|
|
self.workspace_directory = Deferred::from(workspace_directory);
|
|
}
|
|
|
|
pub fn initial_cwd(&self) -> &PathBuf {
|
|
&self.initial_cwd
|
|
}
|
|
|
|
pub fn no_npm(&self) -> bool {
|
|
self.options.no_npm
|
|
}
|
|
|
|
pub fn node_modules_dir_mode(
|
|
&self,
|
|
) -> Result<NodeModulesDirMode, anyhow::Error> {
|
|
self
|
|
.node_modules_dir_mode
|
|
.get_or_try_init(|| {
|
|
let raw_resolve = || -> Result<_, anyhow::Error> {
|
|
if let Some(process_state) = &self.options.npm_process_state {
|
|
if process_state.is_byonm {
|
|
return Ok(NodeModulesDirMode::Manual);
|
|
}
|
|
if process_state.node_modules_dir.is_some() {
|
|
return Ok(NodeModulesDirMode::Auto);
|
|
} else {
|
|
return Ok(NodeModulesDirMode::None);
|
|
}
|
|
}
|
|
if let Some(flag) = self.options.node_modules_dir {
|
|
return Ok(flag);
|
|
}
|
|
let workspace = &self.workspace_directory()?.workspace;
|
|
if let Some(mode) = workspace.node_modules_dir()? {
|
|
return Ok(mode);
|
|
}
|
|
|
|
let workspace = &self.workspace_directory()?.workspace;
|
|
|
|
if let Some(pkg_json) = workspace.root_pkg_json() {
|
|
if let Ok(deno_dir) = self.deno_dir_path() {
|
|
// `deno_dir` can be symlink in macOS or on the CI
|
|
if let Ok(deno_dir) =
|
|
canonicalize_path_maybe_not_exists(&self.sys, deno_dir)
|
|
{
|
|
if pkg_json.path.starts_with(deno_dir) {
|
|
// if the package.json is in deno_dir, then do not use node_modules
|
|
// next to it as local node_modules dir
|
|
return Ok(NodeModulesDirMode::None);
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(NodeModulesDirMode::Manual)
|
|
} else if workspace.vendor_dir_path().is_some() {
|
|
Ok(NodeModulesDirMode::Auto)
|
|
} else {
|
|
// use the global cache
|
|
Ok(NodeModulesDirMode::None)
|
|
}
|
|
};
|
|
|
|
let mode = raw_resolve()?;
|
|
if mode == NodeModulesDirMode::Manual
|
|
&& self.options.is_package_manager_subcommand
|
|
{
|
|
// force using the managed resolver for package management
|
|
// sub commands so that it sets up the node_modules directory
|
|
Ok(NodeModulesDirMode::Auto)
|
|
} else {
|
|
Ok(mode)
|
|
}
|
|
})
|
|
.copied()
|
|
}
|
|
|
|
/// Resolves the path to use for a local node_modules folder.
|
|
pub fn node_modules_dir_path(&self) -> Result<Option<&Path>, anyhow::Error> {
|
|
fn resolve_from_root(root_folder: &FolderConfigs, cwd: &Path) -> PathBuf {
|
|
root_folder
|
|
.deno_json
|
|
.as_ref()
|
|
.map(|c| Cow::Owned(c.dir_path()))
|
|
.or_else(|| {
|
|
root_folder
|
|
.pkg_json
|
|
.as_ref()
|
|
.map(|c| Cow::Borrowed(c.dir_path()))
|
|
})
|
|
.unwrap_or(Cow::Borrowed(cwd))
|
|
.join("node_modules")
|
|
}
|
|
|
|
self
|
|
.node_modules_dir_path
|
|
.get_or_try_init(|| {
|
|
if let Some(process_state) = &self.options.npm_process_state {
|
|
return Ok(
|
|
process_state
|
|
.node_modules_dir
|
|
.as_ref()
|
|
.map(|p| PathBuf::from(p.as_ref())),
|
|
);
|
|
}
|
|
|
|
let mode = self.node_modules_dir_mode()?;
|
|
let workspace = &self.workspace_directory()?.workspace;
|
|
let root_folder = workspace.root_folder_configs();
|
|
if !mode.uses_node_modules_dir() {
|
|
return Ok(None);
|
|
}
|
|
|
|
let node_modules_dir =
|
|
resolve_from_root(root_folder, &self.initial_cwd);
|
|
|
|
Ok(Some(canonicalize_path_maybe_not_exists(
|
|
&self.sys,
|
|
&node_modules_dir,
|
|
)?))
|
|
})
|
|
.map(|p| p.as_deref())
|
|
}
|
|
|
|
pub fn deno_dir_path(&self) -> Result<&PathBuf, DenoDirResolutionError> {
|
|
self.deno_dir_path.get_or_create()
|
|
}
|
|
|
|
pub fn global_http_cache(
|
|
&self,
|
|
) -> Result<&GlobalHttpCacheRc<TSys>, DenoDirResolutionError> {
|
|
self.global_http_cache.get_or_try_init(|| {
|
|
let global_cache_dir = self.deno_dir_path()?.join("remote");
|
|
let global_http_cache = new_rc(deno_cache_dir::GlobalHttpCache::new(
|
|
self.sys.clone(),
|
|
global_cache_dir,
|
|
));
|
|
Ok(global_http_cache)
|
|
})
|
|
}
|
|
|
|
pub fn http_cache(&self) -> Result<&HttpCacheRc, HttpCacheCreateError> {
|
|
self.http_cache.get_or_try_init(|| {
|
|
let global_cache = self.global_http_cache()?.clone();
|
|
match self.workspace_directory()?.workspace.vendor_dir_path() {
|
|
Some(local_path) => {
|
|
let local_cache = LocalHttpCache::new(
|
|
local_path.clone(),
|
|
global_cache,
|
|
deno_cache_dir::GlobalToLocalCopy::Allow,
|
|
);
|
|
Ok(new_rc(local_cache))
|
|
}
|
|
None => Ok(global_cache),
|
|
}
|
|
})
|
|
}
|
|
|
|
pub fn npm_cache_dir(
|
|
&self,
|
|
) -> Result<&NpmCacheDirRc, NpmCacheDirCreateError> {
|
|
self.npm_cache_dir.get_or_try_init(|| {
|
|
let npm_cache_dir = self.deno_dir_path()?.join("npm");
|
|
Ok(new_rc(NpmCacheDir::new(
|
|
&self.sys,
|
|
npm_cache_dir,
|
|
self.npmrc()?.get_all_known_registries_urls(),
|
|
)))
|
|
})
|
|
}
|
|
|
|
pub fn npmrc(&self) -> Result<&ResolvedNpmRcRc, NpmRcCreateError> {
|
|
self.npmrc.get_or_try_init(|| {
|
|
let (npmrc, _) = discover_npmrc_from_workspace(
|
|
&self.sys,
|
|
&self.workspace_directory()?.workspace,
|
|
)?;
|
|
Ok(new_rc(npmrc))
|
|
})
|
|
}
|
|
|
|
pub fn sys(&self) -> &TSys {
|
|
&self.sys
|
|
}
|
|
|
|
pub fn workspace_directory(
|
|
&self,
|
|
) -> Result<&WorkspaceDirectoryRc, WorkspaceDiscoverError> {
|
|
self.workspace_directory.get_or_try_init(|| {
|
|
let maybe_vendor_override = self.options.vendor.map(|v| match v {
|
|
true => VendorEnablement::Enable {
|
|
cwd: &self.initial_cwd,
|
|
},
|
|
false => VendorEnablement::Disable,
|
|
});
|
|
let resolve_workspace_discover_options = || {
|
|
let discover_pkg_json = !self.options.no_npm
|
|
&& !self.has_flag_env_var("DENO_NO_PACKAGE_JSON");
|
|
if !discover_pkg_json {
|
|
log::debug!("package.json auto-discovery is disabled");
|
|
}
|
|
WorkspaceDiscoverOptions {
|
|
deno_json_cache: None,
|
|
pkg_json_cache: Some(&node_resolver::PackageJsonThreadLocalCache),
|
|
workspace_cache: None,
|
|
additional_config_file_names: self
|
|
.options
|
|
.additional_config_file_names,
|
|
discover_pkg_json,
|
|
maybe_vendor_override,
|
|
}
|
|
};
|
|
let resolve_empty_options = || WorkspaceDirectoryEmptyOptions {
|
|
root_dir: new_rc(
|
|
deno_path_util::url_from_directory_path(&self.initial_cwd).unwrap(),
|
|
),
|
|
use_vendor_dir: maybe_vendor_override
|
|
.unwrap_or(VendorEnablement::Disable),
|
|
};
|
|
|
|
let dir = match &self.options.config_discovery {
|
|
ConfigDiscoveryOption::DiscoverCwd => WorkspaceDirectory::discover(
|
|
&self.sys,
|
|
WorkspaceDiscoverStart::Paths(&[self.initial_cwd.clone()]),
|
|
&resolve_workspace_discover_options(),
|
|
)?,
|
|
ConfigDiscoveryOption::Discover { start_paths } => {
|
|
WorkspaceDirectory::discover(
|
|
&self.sys,
|
|
WorkspaceDiscoverStart::Paths(start_paths),
|
|
&resolve_workspace_discover_options(),
|
|
)?
|
|
}
|
|
ConfigDiscoveryOption::Path(path) => {
|
|
let config_path = normalize_path(self.initial_cwd.join(path));
|
|
WorkspaceDirectory::discover(
|
|
&self.sys,
|
|
WorkspaceDiscoverStart::ConfigFile(&config_path),
|
|
&resolve_workspace_discover_options(),
|
|
)?
|
|
}
|
|
ConfigDiscoveryOption::Disabled => {
|
|
WorkspaceDirectory::empty(resolve_empty_options())
|
|
}
|
|
};
|
|
Ok(new_rc(dir))
|
|
})
|
|
}
|
|
|
|
fn has_flag_env_var(&self, name: &str) -> bool {
|
|
let value = self.sys.env_var_os(name);
|
|
match value {
|
|
Some(value) => value == "1",
|
|
None => false,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Default)]
|
|
pub struct ResolverFactoryOptions {
|
|
pub conditions_from_resolution_mode: ConditionsFromResolutionMode,
|
|
pub no_sloppy_imports_cache: bool,
|
|
pub npm_system_info: NpmSystemInfo,
|
|
pub package_json_cache: Option<node_resolver::PackageJsonCacheRc>,
|
|
pub package_json_dep_resolution: Option<PackageJsonDepResolution>,
|
|
pub specified_import_map: Option<Box<dyn SpecifiedImportMapProvider>>,
|
|
pub unstable_sloppy_imports: bool,
|
|
}
|
|
|
|
pub struct ResolverFactory<
|
|
TSys: EnvCacheDir
|
|
+ EnvCurrentDir
|
|
+ EnvHomeDir
|
|
+ EnvVar
|
|
+ FsCanonicalize
|
|
+ FsCreateDirAll
|
|
+ FsMetadata
|
|
+ FsOpen
|
|
+ FsRead
|
|
+ FsReadDir
|
|
+ FsRemoveFile
|
|
+ FsRename
|
|
+ ThreadSleep
|
|
+ SystemRandom
|
|
+ SystemTimeNow
|
|
+ std::fmt::Debug
|
|
+ MaybeSend
|
|
+ MaybeSync
|
|
+ Clone
|
|
+ 'static,
|
|
> {
|
|
options: ResolverFactoryOptions,
|
|
deno_resolver: async_once_cell::OnceCell<DefaultDenoResolverRc<TSys>>,
|
|
in_npm_package_checker: Deferred<DenoInNpmPackageChecker>,
|
|
node_resolver: Deferred<
|
|
NodeResolverRc<
|
|
DenoInNpmPackageChecker,
|
|
DenoIsBuiltInNodeModuleChecker,
|
|
NpmResolver<TSys>,
|
|
TSys,
|
|
>,
|
|
>,
|
|
npm_req_resolver: Deferred<
|
|
NpmReqResolverRc<
|
|
DenoInNpmPackageChecker,
|
|
DenoIsBuiltInNodeModuleChecker,
|
|
NpmResolver<TSys>,
|
|
TSys,
|
|
>,
|
|
>,
|
|
npm_resolver: Deferred<NpmResolver<TSys>>,
|
|
npm_resolution: NpmResolutionCellRc,
|
|
pkg_json_resolver: Deferred<PackageJsonResolverRc<TSys>>,
|
|
sloppy_imports_resolver:
|
|
Deferred<Option<SloppyImportsResolverRc<SloppyImportsCachedFs<TSys>>>>,
|
|
workspace_factory: WorkspaceFactoryRc<TSys>,
|
|
workspace_resolver: async_once_cell::OnceCell<WorkspaceResolverRc>,
|
|
}
|
|
|
|
impl<
|
|
TSys: EnvCacheDir
|
|
+ EnvCurrentDir
|
|
+ EnvHomeDir
|
|
+ EnvVar
|
|
+ FsCanonicalize
|
|
+ FsCreateDirAll
|
|
+ FsMetadata
|
|
+ FsOpen
|
|
+ FsRead
|
|
+ FsReadDir
|
|
+ FsRemoveFile
|
|
+ FsRename
|
|
+ ThreadSleep
|
|
+ SystemRandom
|
|
+ SystemTimeNow
|
|
+ std::fmt::Debug
|
|
+ MaybeSend
|
|
+ MaybeSync
|
|
+ Clone
|
|
+ 'static,
|
|
> ResolverFactory<TSys>
|
|
{
|
|
pub fn new(
|
|
workspace_factory: WorkspaceFactoryRc<TSys>,
|
|
options: ResolverFactoryOptions,
|
|
) -> Self {
|
|
Self {
|
|
deno_resolver: Default::default(),
|
|
in_npm_package_checker: Default::default(),
|
|
node_resolver: Default::default(),
|
|
npm_req_resolver: Default::default(),
|
|
npm_resolution: Default::default(),
|
|
npm_resolver: Default::default(),
|
|
pkg_json_resolver: Default::default(),
|
|
sloppy_imports_resolver: Default::default(),
|
|
workspace_factory,
|
|
workspace_resolver: Default::default(),
|
|
options,
|
|
}
|
|
}
|
|
|
|
pub async fn deno_resolver(
|
|
&self,
|
|
) -> Result<&DefaultDenoResolverRc<TSys>, anyhow::Error> {
|
|
self
|
|
.deno_resolver
|
|
.get_or_try_init(
|
|
async {
|
|
Ok(new_rc(DenoResolver::new(DenoResolverOptions {
|
|
in_npm_pkg_checker: self.in_npm_package_checker()?.clone(),
|
|
node_and_req_resolver: if self.workspace_factory.no_npm() {
|
|
None
|
|
} else {
|
|
Some(NodeAndNpmReqResolver {
|
|
node_resolver: self.node_resolver()?.clone(),
|
|
npm_req_resolver: self.npm_req_resolver()?.clone(),
|
|
})
|
|
},
|
|
is_byonm: self.use_byonm()?,
|
|
maybe_vendor_dir: self
|
|
.workspace_factory
|
|
.workspace_directory()?
|
|
.workspace
|
|
.vendor_dir_path(),
|
|
sloppy_imports_resolver: self.sloppy_imports_resolver()?.cloned(),
|
|
workspace_resolver: self.workspace_resolver().await?.clone(),
|
|
})))
|
|
}
|
|
// boxed to prevent the futures getting big and exploding the stack
|
|
.boxed_local(),
|
|
)
|
|
.await
|
|
}
|
|
|
|
pub fn in_npm_package_checker(
|
|
&self,
|
|
) -> Result<&DenoInNpmPackageChecker, anyhow::Error> {
|
|
self.in_npm_package_checker.get_or_try_init(|| {
|
|
let options = match self.use_byonm()? {
|
|
true => CreateInNpmPkgCheckerOptions::Byonm,
|
|
false => CreateInNpmPkgCheckerOptions::Managed(
|
|
ManagedInNpmPkgCheckerCreateOptions {
|
|
root_cache_dir_url: self
|
|
.workspace_factory
|
|
.npm_cache_dir()?
|
|
.root_dir_url(),
|
|
maybe_node_modules_path: self
|
|
.workspace_factory
|
|
.node_modules_dir_path()?,
|
|
},
|
|
),
|
|
};
|
|
Ok(DenoInNpmPackageChecker::new(options))
|
|
})
|
|
}
|
|
|
|
pub fn node_resolver(
|
|
&self,
|
|
) -> Result<
|
|
&NodeResolverRc<
|
|
DenoInNpmPackageChecker,
|
|
DenoIsBuiltInNodeModuleChecker,
|
|
NpmResolver<TSys>,
|
|
TSys,
|
|
>,
|
|
anyhow::Error,
|
|
> {
|
|
self.node_resolver.get_or_try_init(|| {
|
|
Ok(new_rc(NodeResolver::new(
|
|
self.in_npm_package_checker()?.clone(),
|
|
DenoIsBuiltInNodeModuleChecker,
|
|
self.npm_resolver()?.clone(),
|
|
self.pkg_json_resolver().clone(),
|
|
self.workspace_factory.sys.clone(),
|
|
self.options.conditions_from_resolution_mode.clone(),
|
|
)))
|
|
})
|
|
}
|
|
|
|
pub fn npm_resolution(&self) -> &NpmResolutionCellRc {
|
|
&self.npm_resolution
|
|
}
|
|
|
|
pub fn npm_req_resolver(
|
|
&self,
|
|
) -> Result<
|
|
&NpmReqResolverRc<
|
|
DenoInNpmPackageChecker,
|
|
DenoIsBuiltInNodeModuleChecker,
|
|
NpmResolver<TSys>,
|
|
TSys,
|
|
>,
|
|
anyhow::Error,
|
|
> {
|
|
self.npm_req_resolver.get_or_try_init(|| {
|
|
Ok(new_rc(NpmReqResolver::new(NpmReqResolverOptions {
|
|
in_npm_pkg_checker: self.in_npm_package_checker()?.clone(),
|
|
node_resolver: self.node_resolver()?.clone(),
|
|
npm_resolver: self.npm_resolver()?.clone(),
|
|
sys: self.workspace_factory.sys.clone(),
|
|
})))
|
|
})
|
|
}
|
|
|
|
pub fn npm_resolver(&self) -> Result<&NpmResolver<TSys>, anyhow::Error> {
|
|
self.npm_resolver.get_or_try_init(|| {
|
|
Ok(NpmResolver::<TSys>::new::<TSys>(if self.use_byonm()? {
|
|
NpmResolverCreateOptions::Byonm(ByonmNpmResolverCreateOptions {
|
|
sys: self.workspace_factory.sys.clone(),
|
|
pkg_json_resolver: self.pkg_json_resolver().clone(),
|
|
root_node_modules_dir: Some(
|
|
match self.workspace_factory.node_modules_dir_path()? {
|
|
Some(node_modules_path) => node_modules_path.to_path_buf(),
|
|
// path needs to be canonicalized for node resolution
|
|
// (node_modules_dir_path above is already canonicalized)
|
|
None => canonicalize_path_maybe_not_exists(
|
|
&self.workspace_factory.sys,
|
|
self.workspace_factory.initial_cwd(),
|
|
)?
|
|
.join("node_modules"),
|
|
},
|
|
),
|
|
})
|
|
} else {
|
|
NpmResolverCreateOptions::Managed(ManagedNpmResolverCreateOptions {
|
|
sys: self.workspace_factory.sys.clone(),
|
|
npm_resolution: self.npm_resolution().clone(),
|
|
npm_cache_dir: self.workspace_factory.npm_cache_dir()?.clone(),
|
|
maybe_node_modules_path: self
|
|
.workspace_factory
|
|
.node_modules_dir_path()?
|
|
.map(|p| p.to_path_buf()),
|
|
npm_system_info: self.options.npm_system_info.clone(),
|
|
npmrc: self.workspace_factory.npmrc()?.clone(),
|
|
})
|
|
}))
|
|
})
|
|
}
|
|
|
|
pub fn pkg_json_resolver(&self) -> &PackageJsonResolverRc<TSys> {
|
|
self.pkg_json_resolver.get_or_init(|| {
|
|
new_rc(PackageJsonResolver::new(
|
|
self.workspace_factory.sys.clone(),
|
|
self.options.package_json_cache.clone(),
|
|
))
|
|
})
|
|
}
|
|
|
|
pub fn sloppy_imports_resolver(
|
|
&self,
|
|
) -> Result<
|
|
Option<&SloppyImportsResolverRc<SloppyImportsCachedFs<TSys>>>,
|
|
anyhow::Error,
|
|
> {
|
|
self
|
|
.sloppy_imports_resolver
|
|
.get_or_try_init(|| {
|
|
let enabled = self.options.unstable_sloppy_imports
|
|
|| self
|
|
.workspace_factory
|
|
.workspace_directory()?
|
|
.workspace
|
|
.has_unstable("sloppy-imports");
|
|
if enabled {
|
|
Ok(Some(new_rc(SloppyImportsResolver::new(
|
|
if self.options.no_sloppy_imports_cache {
|
|
SloppyImportsCachedFs::new_without_stat_cache(
|
|
self.workspace_factory.sys.clone(),
|
|
)
|
|
} else {
|
|
SloppyImportsCachedFs::new(self.workspace_factory.sys.clone())
|
|
},
|
|
))))
|
|
} else {
|
|
Ok(None)
|
|
}
|
|
})
|
|
.map(|v| v.as_ref())
|
|
}
|
|
|
|
pub async fn workspace_resolver(
|
|
&self,
|
|
) -> Result<&WorkspaceResolverRc, anyhow::Error> {
|
|
self
|
|
.workspace_resolver
|
|
.get_or_try_init(
|
|
async {
|
|
let directory = self.workspace_factory.workspace_directory()?;
|
|
let workspace = &directory.workspace;
|
|
let specified_import_map = match &self.options.specified_import_map {
|
|
Some(import_map) => import_map.get().await?,
|
|
None => None,
|
|
};
|
|
let options = deno_config::workspace::CreateResolverOptions {
|
|
pkg_json_dep_resolution: match self
|
|
.options
|
|
.package_json_dep_resolution
|
|
{
|
|
Some(value) => value,
|
|
None => {
|
|
match self.workspace_factory.node_modules_dir_mode()? {
|
|
NodeModulesDirMode::Manual => {
|
|
PackageJsonDepResolution::Disabled
|
|
}
|
|
NodeModulesDirMode::Auto | NodeModulesDirMode::None => {
|
|
// todo(dsherret): should this be disabled for auto?
|
|
PackageJsonDepResolution::Enabled
|
|
}
|
|
}
|
|
}
|
|
},
|
|
specified_import_map,
|
|
};
|
|
let resolver =
|
|
workspace.create_resolver(&self.workspace_factory.sys, options)?;
|
|
if !resolver.diagnostics().is_empty() {
|
|
// todo(dsherret): do not log this in this crate... that should be
|
|
// a CLI responsibility
|
|
log::warn!(
|
|
"Import map diagnostics:\n{}",
|
|
resolver
|
|
.diagnostics()
|
|
.iter()
|
|
.map(|d| format!(" - {d}"))
|
|
.collect::<Vec<_>>()
|
|
.join("\n")
|
|
);
|
|
}
|
|
Ok(new_rc(resolver))
|
|
}
|
|
// boxed to prevent the futures getting big and exploding the stack
|
|
.boxed_local(),
|
|
)
|
|
.await
|
|
}
|
|
|
|
pub fn use_byonm(&self) -> Result<bool, anyhow::Error> {
|
|
Ok(
|
|
self.workspace_factory.node_modules_dir_mode()?
|
|
== NodeModulesDirMode::Manual,
|
|
)
|
|
}
|
|
}
|