0
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-02-07 23:06:50 -05:00

refactor: add WorkspaceFactory and ResolverFactory (#27766)

Allows easily constructing a `DenoResolver` using the exact same logic
that we use in the CLI (useful for dnt and for external bundlers). This
code is then used in the CLI to ensure the logic is always up-to-date.

```rs
use std::rc::Rc;

use deno_resolver:🏭:ResolverFactory;
use deno_resolver:🏭:WorkspaceFactory;
use sys_traits::impls::RealSys;

let sys = RealSys;
let cwd = sys.env_current_dir()?;
let workspace_factory = Rc::new(WorkspaceFactory::new(sys, cwd, Default::default()));
let resolver_factory = ResolverFactory::new(workspace_factory.clone(), Default::default());
let deno_resolver = resolver_factory.deno_resolver().await?;
```
This commit is contained in:
David Sherret 2025-01-23 18:52:55 -05:00 committed by Bartek Iwańczuk
parent f81debe9f6
commit a63f614ba1
No known key found for this signature in database
GPG key ID: 0C6BCDDC3B3AD750
47 changed files with 1902 additions and 1166 deletions

18
Cargo.lock generated
View file

@ -284,6 +284,12 @@ dependencies = [
"tokio",
]
[[package]]
name = "async-once-cell"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4288f83726785267c6f2ef073a3d83dc3f9b81464e9f99898240cced85fce35a"
[[package]]
name = "async-recursion"
version = "1.1.1"
@ -1460,9 +1466,9 @@ dependencies = [
[[package]]
name = "deno_cache_dir"
version = "0.16.0"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e73ed17f285731a23df9779ca1e0e721de866db6776ed919ebd9235e0a107c4c"
checksum = "27429da4d0e601baaa41415a43468d49a586645d13497f12e8a9346f9f6b1347"
dependencies = [
"async-trait",
"base32",
@ -2219,9 +2225,9 @@ dependencies = [
[[package]]
name = "deno_path_util"
version = "0.3.0"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "420e8211aaba7fde83ccaa9a5dad855c3b940ed988d70c95159acd600a70dc87"
checksum = "c87b8996966ae1b13ee9c20219b1d10fc53905b9570faae6adfa34614fd15224"
dependencies = [
"deno_error",
"percent-encoding",
@ -2282,6 +2288,7 @@ name = "deno_resolver"
version = "0.18.0"
dependencies = [
"anyhow",
"async-once-cell",
"async-trait",
"base32",
"boxed_error",
@ -2294,8 +2301,11 @@ dependencies = [
"deno_package_json",
"deno_path_util",
"deno_semver",
"deno_terminal 0.2.0",
"futures",
"log",
"node_resolver",
"once_cell",
"parking_lot",
"sys_traits",
"test_server",

View file

@ -54,11 +54,11 @@ deno_ast = { version = "=0.44.0", features = ["transpiling"] }
deno_core = { version = "0.331.0" }
deno_bench_util = { version = "0.181.0", path = "./bench_util" }
deno_config = { version = "=0.45.0", features = ["workspace", "sync"] }
deno_config = { version = "=0.45.0", features = ["workspace"] }
deno_lockfile = "=0.24.0"
deno_media_type = { version = "=0.2.5", features = ["module_specifier"] }
deno_npm = "=0.27.2"
deno_path_util = "=0.3.0"
deno_path_util = "=0.3.1"
deno_permissions = { version = "0.46.0", path = "./runtime/permissions" }
deno_runtime = { version = "0.195.0", path = "./runtime" }
deno_semver = "=0.7.1"
@ -107,6 +107,7 @@ node_resolver = { version = "0.25.0", path = "./resolvers/node" }
aes = "=0.8.3"
anyhow = "1.0.57"
async-once-cell = "0.5.4"
async-trait = "0.1.73"
base32 = "=0.5.1"
base64 = "0.21.7"
@ -125,7 +126,7 @@ console_static_text = "=0.8.1"
dashmap = "5.5.3"
data-encoding = "2.3.3"
data-url = "=0.3.1"
deno_cache_dir = "=0.16.0"
deno_cache_dir = "=0.17.0"
deno_error = "=0.5.5"
deno_package_json = { version = "0.4.0", default-features = false }
deno_unsync = "0.4.2"

View file

@ -67,8 +67,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.workspace = true
deno_cache_dir = { workspace = true, features = ["sync"] }
deno_config = { workspace = true, features = ["sync", "workspace"] }
deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] }
deno_doc = { version = "=0.164.0", features = ["rust", "comrak"] }
deno_error.workspace = true
@ -79,7 +79,7 @@ deno_lockfile.workspace = true
deno_media_type = { workspace = true, features = ["data_url", "decoding", "module_specifier"] }
deno_npm.workspace = true
deno_npm_cache.workspace = true
deno_package_json.workspace = true
deno_package_json = { workspace = true, features = ["sync"] }
deno_path_util.workspace = true
deno_resolver = { workspace = true, features = ["sync"] }
deno_runtime = { workspace = true, features = ["include_js_files_for_snapshotting"] }

View file

@ -34,6 +34,7 @@ use deno_graph::GraphKind;
use deno_lib::args::CaData;
use deno_lib::args::UnstableConfig;
use deno_lib::version::DENO_VERSION_INFO;
use deno_npm::NpmSystemInfo;
use deno_path_util::normalize_path;
use deno_path_util::url_to_file_path;
use deno_runtime::deno_permissions::SysDescriptor;
@ -500,6 +501,52 @@ impl DenoSubcommand {
| Self::Lsp
)
}
pub fn npm_system_info(&self) -> NpmSystemInfo {
match self {
DenoSubcommand::Compile(CompileFlags {
target: Some(target),
..
}) => {
// the values of NpmSystemInfo align with the possible values for the
// `arch` and `platform` fields of Node.js' `process` global:
// https://nodejs.org/api/process.html
match target.as_str() {
"aarch64-apple-darwin" => NpmSystemInfo {
os: "darwin".into(),
cpu: "arm64".into(),
},
"aarch64-unknown-linux-gnu" => NpmSystemInfo {
os: "linux".into(),
cpu: "arm64".into(),
},
"x86_64-apple-darwin" => NpmSystemInfo {
os: "darwin".into(),
cpu: "x64".into(),
},
"x86_64-unknown-linux-gnu" => NpmSystemInfo {
os: "linux".into(),
cpu: "x64".into(),
},
"x86_64-pc-windows-msvc" => NpmSystemInfo {
os: "win32".into(),
cpu: "x64".into(),
},
value => {
log::warn!(
concat!(
"Not implemented npm system info for target '{}'. Using current ",
"system default. This may impact architecture specific dependencies."
),
value,
);
NpmSystemInfo::default()
}
}
}
_ => NpmSystemInfo::default(),
}
}
}
impl Default for DenoSubcommand {

View file

@ -1,24 +0,0 @@
// Copyright 2018-2025 the Deno authors. MIT license.
use deno_core::error::AnyError;
use deno_core::serde_json;
use deno_core::url::Url;
use crate::file_fetcher::CliFileFetcher;
use crate::file_fetcher::TextDecodedFile;
pub async fn resolve_import_map_value_from_specifier(
specifier: &Url,
file_fetcher: &CliFileFetcher,
) -> Result<serde_json::Value, AnyError> {
if specifier.scheme() == "data" {
let data_url_text =
deno_media_type::data_url::RawDataUrl::parse(specifier)?.decode()?;
Ok(serde_json::from_str(&data_url_text)?)
} else {
let file = TextDecodedFile::decode(
file_fetcher.fetch_bypass_permissions(specifier).await?,
)?;
Ok(serde_json::from_str(&file.source)?)
}
}

View file

@ -3,7 +3,6 @@
pub mod deno_json;
mod flags;
mod flags_net;
mod import_map;
mod lockfile;
mod package_json;
@ -35,17 +34,9 @@ pub use deno_config::deno_json::TsConfigForEmit;
pub use deno_config::deno_json::TsConfigType;
pub use deno_config::deno_json::TsTypeLib;
pub use deno_config::glob::FilePatterns;
use deno_config::workspace::CreateResolverOptions;
use deno_config::workspace::FolderConfigs;
use deno_config::workspace::PackageJsonDepResolution;
use deno_config::workspace::VendorEnablement;
use deno_config::workspace::Workspace;
use deno_config::workspace::WorkspaceDirectory;
use deno_config::workspace::WorkspaceDirectoryEmptyOptions;
use deno_config::workspace::WorkspaceDiscoverOptions;
use deno_config::workspace::WorkspaceDiscoverStart;
use deno_config::workspace::WorkspaceLintConfig;
use deno_config::workspace::WorkspaceResolver;
use deno_core::anyhow::bail;
use deno_core::anyhow::Context;
use deno_core::error::AnyError;
@ -57,16 +48,11 @@ pub use deno_json::check_warn_tsconfig;
use deno_lib::args::has_flag_env_var;
use deno_lib::args::npm_pkg_req_ref_to_binary_command;
use deno_lib::args::CaData;
use deno_lib::args::NpmProcessStateKind;
use deno_lib::args::NPM_PROCESS_STATE;
use deno_lib::version::DENO_VERSION_INFO;
use deno_lib::worker::StorageKeyResolver;
use deno_lint::linter::LintConfig as DenoLintConfig;
use deno_npm::npm_rc::NpmRc;
use deno_npm::npm_rc::ResolvedNpmRc;
use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot;
use deno_npm::NpmSystemInfo;
use deno_path_util::normalize_path;
use deno_runtime::deno_permissions::PermissionsOptions;
use deno_runtime::inspector_server::InspectorServer;
use deno_semver::npm::NpmPackageReqReference;
@ -75,46 +61,16 @@ use deno_telemetry::OtelConfig;
use deno_terminal::colors;
use dotenvy::from_filename;
pub use flags::*;
use import_map::resolve_import_map_value_from_specifier;
pub use lockfile::AtomicWriteFileWithRetriesError;
pub use lockfile::CliLockfile;
pub use lockfile::CliLockfileReadFromPathOptions;
use once_cell::sync::Lazy;
pub use package_json::NpmInstallDepsProvider;
pub use package_json::PackageJsonDepValueParseWithLocationError;
use sys_traits::EnvHomeDir;
use sys_traits::FsRead;
use thiserror::Error;
use crate::cache::DenoDirProvider;
use crate::file_fetcher::CliFileFetcher;
use crate::sys::CliSys;
use crate::util::fs::canonicalize_path_maybe_not_exists;
pub fn npm_registry_url() -> &'static Url {
static NPM_REGISTRY_DEFAULT_URL: Lazy<Url> = Lazy::new(|| {
let env_var_name = "NPM_CONFIG_REGISTRY";
if let Ok(registry_url) = std::env::var(env_var_name) {
// ensure there is a trailing slash for the directory
let registry_url = format!("{}/", registry_url.trim_end_matches('/'));
match Url::parse(&registry_url) {
Ok(url) => {
return url;
}
Err(err) => {
log::debug!(
"Invalid {} environment variable: {:#}",
env_var_name,
err,
);
}
}
}
Url::parse("https://registry.npmjs.org").unwrap()
});
&NPM_REGISTRY_DEFAULT_URL
}
pub static DENO_DISABLE_PEDANTIC_NODE_WARNINGS: Lazy<bool> = Lazy::new(|| {
std::env::var("DENO_DISABLE_PEDANTIC_NODE_WARNINGS")
@ -214,6 +170,53 @@ pub fn ts_config_to_transpile_and_emit_options(
))
}
#[derive(Debug, Clone)]
pub struct ExternalImportMap {
pub path: PathBuf,
pub value: serde_json::Value,
}
#[derive(Debug)]
pub struct WorkspaceExternalImportMapLoader {
sys: CliSys,
workspace: Arc<Workspace>,
maybe_external_import_map:
once_cell::sync::OnceCell<Option<ExternalImportMap>>,
}
impl WorkspaceExternalImportMapLoader {
pub fn new(sys: CliSys, workspace: Arc<Workspace>) -> Self {
Self {
sys,
workspace,
maybe_external_import_map: Default::default(),
}
}
pub fn get_or_load(&self) -> Result<Option<&ExternalImportMap>, AnyError> {
self
.maybe_external_import_map
.get_or_try_init(|| {
let Some(deno_json) = self.workspace.root_deno_json() else {
return Ok(None);
};
if deno_json.is_an_import_map() {
return Ok(None);
}
let Some(path) = deno_json.to_import_map_path()? else {
return Ok(None);
};
let contents =
self.sys.fs_read_to_string(&path).with_context(|| {
format!("Unable to read import map at '{}'", path.display())
})?;
let value = serde_json::from_str(&contents)?;
Ok(Some(ExternalImportMap { path, value }))
})
.map(|v| v.as_ref())
}
}
pub struct WorkspaceBenchOptions {
pub filter: Option<String>,
pub json: bool,
@ -480,162 +483,26 @@ fn resolve_lint_rules_options(
}
}
pub fn discover_npmrc_from_workspace(
workspace: &Workspace,
) -> Result<(Arc<ResolvedNpmRc>, Option<PathBuf>), AnyError> {
let root_folder = workspace.root_folder_configs();
discover_npmrc(
root_folder.pkg_json.as_ref().map(|p| p.path.clone()),
root_folder.deno_json.as_ref().and_then(|cf| {
if cf.specifier.scheme() == "file" {
Some(cf.specifier.to_file_path().unwrap())
} else {
None
}
}),
)
}
/// Discover `.npmrc` file - currently we only support it next to `package.json`
/// or next to `deno.json`.
///
/// In the future we will need to support it in user directory or global directory
/// as per https://docs.npmjs.com/cli/v10/configuring-npm/npmrc#files.
fn discover_npmrc(
maybe_package_json_path: Option<PathBuf>,
maybe_deno_json_path: Option<PathBuf>,
) -> Result<(Arc<ResolvedNpmRc>, Option<PathBuf>), AnyError> {
const NPMRC_NAME: &str = ".npmrc";
fn get_env_var(var_name: &str) -> Option<String> {
std::env::var(var_name).ok()
}
#[derive(Debug, Error)]
#[error("Error loading .npmrc at {}.", path.display())]
struct NpmRcLoadError {
path: PathBuf,
#[source]
source: std::io::Error,
}
fn try_to_read_npmrc(
dir: &Path,
) -> Result<Option<(String, PathBuf)>, NpmRcLoadError> {
let path = dir.join(NPMRC_NAME);
let maybe_source = match std::fs::read_to_string(&path) {
Ok(source) => Some(source),
Err(err) if err.kind() == std::io::ErrorKind::NotFound => None,
Err(err) => return Err(NpmRcLoadError { path, source: err }),
};
Ok(maybe_source.map(|source| (source, path)))
}
fn try_to_parse_npmrc(
source: String,
path: &Path,
) -> Result<Arc<ResolvedNpmRc>, AnyError> {
let npmrc = NpmRc::parse(&source, &get_env_var).with_context(|| {
format!("Failed to parse .npmrc at {}", path.display())
})?;
let resolved = npmrc
.as_resolved(npm_registry_url())
.context("Failed to resolve .npmrc options")?;
log::debug!(".npmrc found at: '{}'", path.display());
Ok(Arc::new(resolved))
}
// 1. Try `.npmrc` next to `package.json`
if let Some(package_json_path) = maybe_package_json_path {
if let Some(package_json_dir) = package_json_path.parent() {
if let Some((source, path)) = try_to_read_npmrc(package_json_dir)? {
return try_to_parse_npmrc(source, &path).map(|r| (r, Some(path)));
}
}
}
// 2. Try `.npmrc` next to `deno.json(c)`
if let Some(deno_json_path) = maybe_deno_json_path {
if let Some(deno_json_dir) = deno_json_path.parent() {
if let Some((source, path)) = try_to_read_npmrc(deno_json_dir)? {
return try_to_parse_npmrc(source, &path).map(|r| (r, Some(path)));
}
}
}
// TODO(bartlomieju): update to read both files - one in the project root and one and
// home dir and then merge them.
// 3. Try `.npmrc` in the user's home directory
if let Some(home_dir) = crate::sys::CliSys::default().env_home_dir() {
match try_to_read_npmrc(&home_dir) {
Ok(Some((source, path))) => {
return try_to_parse_npmrc(source, &path).map(|r| (r, Some(path)));
}
Ok(None) => {}
Err(err) if err.source.kind() == std::io::ErrorKind::PermissionDenied => {
log::debug!(
"Skipping .npmrc in home directory due to permission denied error. {:#}",
err
);
}
Err(err) => {
return Err(err.into());
}
}
}
log::debug!("No .npmrc file found");
Ok((create_default_npmrc(), None))
}
pub fn create_default_npmrc() -> Arc<ResolvedNpmRc> {
Arc::new(ResolvedNpmRc {
default_config: deno_npm::npm_rc::RegistryConfigWithUrl {
registry_url: npm_registry_url().clone(),
config: Default::default(),
},
scopes: Default::default(),
registry_configs: Default::default(),
})
}
/// Overrides for the options below that when set will
/// use these values over the values derived from the
/// CLI flags or config file.
#[derive(Default, Clone)]
struct CliOptionOverrides {
import_map_specifier: Option<Option<ModuleSpecifier>>,
}
/// Holds the resolved options of many sources used by subcommands
/// and provides some helper function for creating common objects.
#[derive(Debug)]
pub struct CliOptions {
// the source of the options is a detail the rest of the
// application need not concern itself with, so keep these private
flags: Arc<Flags>,
initial_cwd: PathBuf,
main_module_cell: std::sync::OnceLock<Result<ModuleSpecifier, AnyError>>,
maybe_node_modules_folder: Option<PathBuf>,
npmrc: Arc<ResolvedNpmRc>,
maybe_lockfile: Option<Arc<CliLockfile>>,
maybe_external_import_map: Option<(PathBuf, serde_json::Value)>,
overrides: CliOptionOverrides,
pub start_dir: Arc<WorkspaceDirectory>,
pub deno_dir_provider: Arc<DenoDirProvider>,
}
impl CliOptions {
#[allow(clippy::too_many_arguments)]
pub fn new(
sys: &CliSys,
flags: Arc<Flags>,
initial_cwd: PathBuf,
maybe_lockfile: Option<Arc<CliLockfile>>,
npmrc: Arc<ResolvedNpmRc>,
start_dir: Arc<WorkspaceDirectory>,
force_global_cache: bool,
maybe_external_import_map: Option<(PathBuf, serde_json::Value)>,
) -> Result<Self, AnyError> {
if let Some(insecure_allowlist) =
flags.unsafely_ignore_certificate_errors.as_ref()
@ -652,154 +519,38 @@ impl CliOptions {
}
}
let maybe_lockfile = maybe_lockfile.filter(|_| !force_global_cache);
let deno_dir_provider = Arc::new(DenoDirProvider::new(
sys.clone(),
flags.internal.cache_path.clone(),
));
let maybe_node_modules_folder = resolve_node_modules_folder(
&initial_cwd,
&flags,
&start_dir.workspace,
&deno_dir_provider,
)
.with_context(|| "Resolving node_modules folder.")?;
load_env_variables_from_env_file(flags.env_file.as_ref());
Ok(Self {
flags,
initial_cwd,
maybe_lockfile,
npmrc,
maybe_node_modules_folder,
overrides: Default::default(),
main_module_cell: std::sync::OnceLock::new(),
maybe_external_import_map,
start_dir,
deno_dir_provider,
})
}
pub fn from_flags(sys: &CliSys, flags: Arc<Flags>) -> Result<Self, AnyError> {
let initial_cwd =
std::env::current_dir().with_context(|| "Failed getting cwd.")?;
let maybe_vendor_override = flags.vendor.map(|v| match v {
true => VendorEnablement::Enable { cwd: &initial_cwd },
false => VendorEnablement::Disable,
});
let resolve_workspace_discover_options = || {
let additional_config_file_names: &'static [&'static str] =
if matches!(flags.subcommand, DenoSubcommand::Publish(..)) {
&["jsr.json", "jsr.jsonc"]
} else {
&[]
};
let discover_pkg_json = flags.config_flag != ConfigFlag::Disabled
&& !flags.no_npm
&& !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,
discover_pkg_json,
maybe_vendor_override,
}
};
let resolve_empty_options = || WorkspaceDirectoryEmptyOptions {
root_dir: Arc::new(
ModuleSpecifier::from_directory_path(&initial_cwd).unwrap(),
),
use_vendor_dir: maybe_vendor_override
.unwrap_or(VendorEnablement::Disable),
};
let start_dir = match &flags.config_flag {
ConfigFlag::Discover => {
if let Some(start_paths) = flags.config_path_args(&initial_cwd) {
WorkspaceDirectory::discover(
sys,
WorkspaceDiscoverStart::Paths(&start_paths),
&resolve_workspace_discover_options(),
)?
} else {
WorkspaceDirectory::empty(resolve_empty_options())
}
}
ConfigFlag::Path(path) => {
let config_path = normalize_path(initial_cwd.join(path));
WorkspaceDirectory::discover(
sys,
WorkspaceDiscoverStart::ConfigFile(&config_path),
&resolve_workspace_discover_options(),
)?
}
ConfigFlag::Disabled => {
WorkspaceDirectory::empty(resolve_empty_options())
}
};
pub fn from_flags(
sys: &CliSys,
flags: Arc<Flags>,
initial_cwd: PathBuf,
maybe_external_import_map: Option<&ExternalImportMap>,
start_dir: Arc<WorkspaceDirectory>,
) -> Result<Self, AnyError> {
for diagnostic in start_dir.workspace.diagnostics() {
log::warn!("{} {}", colors::yellow("Warning"), diagnostic);
}
let (npmrc, _) = discover_npmrc_from_workspace(&start_dir.workspace)?;
fn load_external_import_map(
deno_json: &ConfigFile,
) -> Result<Option<(PathBuf, serde_json::Value)>, AnyError> {
if !deno_json.is_an_import_map() {
if let Some(path) = deno_json.to_import_map_path()? {
let contents = std::fs::read_to_string(&path).with_context(|| {
format!("Unable to read import map at '{}'", path.display())
})?;
let map = serde_json::from_str(&contents)?;
return Ok(Some((path, map)));
}
}
Ok(None)
}
let external_import_map =
if let Some(deno_json) = start_dir.workspace.root_deno_json() {
load_external_import_map(deno_json)?
} else {
None
};
let maybe_lock_file = CliLockfile::discover(
sys,
&flags,
&start_dir.workspace,
external_import_map.as_ref().map(|(_, v)| v),
maybe_external_import_map.as_ref().map(|v| &v.value),
)?;
log::debug!("Finished config loading.");
Self::new(
sys,
flags,
initial_cwd,
maybe_lock_file.map(Arc::new),
npmrc,
Arc::new(start_dir),
false,
external_import_map,
)
}
/// This method is purposefully verbose to disourage its use. Do not use it
/// except in the factory structs. Instead, prefer specific methods on `CliOptions`
/// that can take all sources of information into account (ex. config files or env vars).
pub fn into_self_and_flags(
self: Arc<CliOptions>,
) -> (Arc<CliOptions>, Arc<Flags>) {
let flags = self.flags.clone();
(self, flags)
Self::new(flags, initial_cwd, maybe_lock_file.map(Arc::new), start_dir)
}
#[inline(always)]
@ -842,49 +593,7 @@ impl CliOptions {
}
pub fn npm_system_info(&self) -> NpmSystemInfo {
match self.sub_command() {
DenoSubcommand::Compile(CompileFlags {
target: Some(target),
..
}) => {
// the values of NpmSystemInfo align with the possible values for the
// `arch` and `platform` fields of Node.js' `process` global:
// https://nodejs.org/api/process.html
match target.as_str() {
"aarch64-apple-darwin" => NpmSystemInfo {
os: "darwin".into(),
cpu: "arm64".into(),
},
"aarch64-unknown-linux-gnu" => NpmSystemInfo {
os: "linux".into(),
cpu: "arm64".into(),
},
"x86_64-apple-darwin" => NpmSystemInfo {
os: "darwin".into(),
cpu: "x64".into(),
},
"x86_64-unknown-linux-gnu" => NpmSystemInfo {
os: "linux".into(),
cpu: "x64".into(),
},
"x86_64-pc-windows-msvc" => NpmSystemInfo {
os: "win32".into(),
cpu: "x64".into(),
},
value => {
log::warn!(
concat!(
"Not implemented npm system info for target '{}'. Using current ",
"system default. This may impact architecture specific dependencies."
),
value,
);
NpmSystemInfo::default()
}
}
}
_ => NpmSystemInfo::default(),
}
self.sub_command().npm_system_info()
}
/// Resolve the specifier for a specified import map.
@ -893,72 +602,12 @@ impl CliOptions {
/// happens to be an import map.
pub fn resolve_specified_import_map_specifier(
&self,
) -> Result<Option<ModuleSpecifier>, AnyError> {
match self.overrides.import_map_specifier.clone() {
Some(maybe_url) => Ok(maybe_url),
None => resolve_import_map_specifier(
self.flags.import_map_path.as_deref(),
self.workspace().root_deno_json().map(|c| c.as_ref()),
&self.initial_cwd,
),
}
}
pub async fn create_workspace_resolver(
&self,
file_fetcher: &CliFileFetcher,
pkg_json_dep_resolution: PackageJsonDepResolution,
) -> Result<WorkspaceResolver, AnyError> {
let overrode_no_import_map: bool = self
.overrides
.import_map_specifier
.as_ref()
.map(|s| s.is_none())
== Some(true);
let cli_arg_specified_import_map = if overrode_no_import_map {
// use a fake empty import map
Some(deno_config::workspace::SpecifiedImportMap {
base_url: self.workspace().root_dir().join("import_map.json").unwrap(),
value: serde_json::Value::Object(Default::default()),
})
} else {
let maybe_import_map_specifier =
self.resolve_specified_import_map_specifier()?;
match maybe_import_map_specifier {
Some(specifier) => {
let value =
resolve_import_map_value_from_specifier(&specifier, file_fetcher)
.await
.with_context(|| {
format!("Unable to load '{}' import map", specifier)
})?;
Some(deno_config::workspace::SpecifiedImportMap {
base_url: specifier,
value,
})
}
None => {
if let Some((path, import_map)) =
self.maybe_external_import_map.as_ref()
{
let path_url = deno_path_util::url_from_file_path(path)?;
Some(deno_config::workspace::SpecifiedImportMap {
base_url: path_url,
value: import_map.clone(),
})
} else {
None
}
}
}
};
Ok(self.workspace().create_resolver(
&CliSys::default(),
CreateResolverOptions {
pkg_json_dep_resolution,
specified_import_map: cli_arg_specified_import_map,
},
)?)
) -> Result<Option<ModuleSpecifier>, ImportMapSpecifierResolveError> {
resolve_import_map_specifier(
self.flags.import_map_path.as_deref(),
self.workspace().root_deno_json().map(|c| c.as_ref()),
&self.initial_cwd,
)
}
pub fn node_ipc_fd(&self) -> Option<i64> {
@ -1068,19 +717,6 @@ impl CliOptions {
}
}
pub fn resolve_npm_resolution_snapshot(
&self,
) -> Result<Option<ValidSerializedNpmResolutionSnapshot>, AnyError> {
if let Some(NpmProcessStateKind::Snapshot(snapshot)) =
NPM_PROCESS_STATE.as_ref().map(|s| &s.kind)
{
// TODO(bartlomieju): remove this clone
Ok(Some(snapshot.clone().into_valid()?))
} else {
Ok(None)
}
}
pub fn resolve_storage_key_resolver(&self) -> StorageKeyResolver {
if let Some(location) = &self.flags.location {
StorageKeyResolver::from_flag(location)
@ -1099,14 +735,6 @@ impl CliOptions {
NPM_PROCESS_STATE.is_some()
}
pub fn has_node_modules_dir(&self) -> bool {
self.maybe_node_modules_folder.is_some()
}
pub fn node_modules_dir_path(&self) -> Option<&PathBuf> {
self.maybe_node_modules_folder.as_ref()
}
pub fn node_modules_dir(
&self,
) -> Result<
@ -1170,10 +798,6 @@ impl CliOptions {
})
}
pub fn npmrc(&self) -> &Arc<ResolvedNpmRc> {
&self.npmrc
}
pub fn resolve_fmt_options_for_members(
&self,
fmt_flags: &FmtFlags,
@ -1571,41 +1195,6 @@ impl CliOptions {
self.workspace().package_jsons().next().is_some() || self.is_node_main()
}
fn byonm_enabled(&self) -> bool {
// check if enabled via unstable
self.node_modules_dir().ok().flatten() == Some(NodeModulesDirMode::Manual)
|| NPM_PROCESS_STATE
.as_ref()
.map(|s| matches!(s.kind, NpmProcessStateKind::Byonm))
.unwrap_or(false)
}
pub fn use_byonm(&self) -> bool {
if matches!(
self.sub_command(),
DenoSubcommand::Install(_)
| DenoSubcommand::Add(_)
| DenoSubcommand::Remove(_)
| DenoSubcommand::Init(_)
| DenoSubcommand::Outdated(_)
) {
// For `deno install/add/remove/init` we want to force the managed resolver so it can set up `node_modules/` directory.
return false;
}
if self.node_modules_dir().ok().flatten().is_none()
&& self.maybe_node_modules_folder.is_some()
&& self
.workspace()
.config_folders()
.values()
.any(|f| f.pkg_json.is_some())
{
return true;
}
self.byonm_enabled()
}
pub fn unstable_sloppy_imports(&self) -> bool {
self.flags.unstable_config.sloppy_imports
|| self.workspace().has_unstable("sloppy-imports")
@ -1730,63 +1319,6 @@ impl CliOptions {
}
}
/// Resolves the path to use for a local node_modules folder.
fn resolve_node_modules_folder(
cwd: &Path,
flags: &Flags,
workspace: &Workspace,
deno_dir_provider: &Arc<DenoDirProvider>,
) -> Result<Option<PathBuf>, AnyError> {
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")
}
let root_folder = workspace.root_folder_configs();
let use_node_modules_dir = if let Some(mode) = flags.node_modules_dir {
Some(mode.uses_node_modules_dir())
} else {
workspace
.node_modules_dir()?
.map(|m| m.uses_node_modules_dir())
.or(flags.vendor)
.or_else(|| root_folder.deno_json.as_ref().and_then(|c| c.json.vendor))
};
let path = if use_node_modules_dir == Some(false) {
return Ok(None);
} else if let Some(state) = &*NPM_PROCESS_STATE {
return Ok(state.local_node_modules_path.as_ref().map(PathBuf::from));
} else if root_folder.pkg_json.is_some() {
let node_modules_dir = resolve_from_root(root_folder, cwd);
if let Ok(deno_dir) = deno_dir_provider.get_or_create() {
// `deno_dir.root` can be symlink in macOS
if let Ok(root) = canonicalize_path_maybe_not_exists(&deno_dir.root) {
if node_modules_dir.starts_with(root) {
// if the package.json is in deno_dir, then do not use node_modules
// next to it as local node_modules dir
return Ok(None);
}
}
}
node_modules_dir
} else if use_node_modules_dir.is_none() {
return Ok(None);
} else {
resolve_from_root(root_folder, cwd)
};
Ok(Some(canonicalize_path_maybe_not_exists(&path)?))
}
fn try_resolve_node_binary_main_entrypoint(
specifier: &str,
initial_cwd: &Path,
@ -1817,22 +1349,31 @@ fn try_resolve_node_binary_main_entrypoint(
}
}
#[derive(Debug, Error)]
#[error("Bad URL for import map.")]
pub struct ImportMapSpecifierResolveError {
#[source]
source: deno_path_util::ResolveUrlOrPathError,
}
fn resolve_import_map_specifier(
maybe_import_map_path: Option<&str>,
maybe_config_file: Option<&ConfigFile>,
current_dir: &Path,
) -> Result<Option<ModuleSpecifier>, AnyError> {
) -> Result<Option<Url>, ImportMapSpecifierResolveError> {
if let Some(import_map_path) = maybe_import_map_path {
if let Some(config_file) = &maybe_config_file {
if config_file.json.import_map.is_some() {
log::warn!("{} the configuration file \"{}\" contains an entry for \"importMap\" that is being ignored.", colors::yellow("Warning"), config_file.specifier);
log::warn!(
"{} the configuration file \"{}\" contains an entry for \"importMap\" that is being ignored.",
colors::yellow("Warning"),
config_file.specifier,
);
}
}
let specifier =
deno_core::resolve_url_or_path(import_map_path, current_dir)
.with_context(|| {
format!("Bad URL (\"{import_map_path}\") for import map.")
})?;
deno_path_util::resolve_url_or_path(import_map_path, current_dir)
.map_err(|source| ImportMapSpecifierResolveError { source })?;
Ok(Some(specifier))
} else {
Ok(None)
@ -1873,9 +1414,9 @@ fn load_env_variables_from_env_file(filename: Option<&Vec<String>>) {
Ok(_) => (),
Err(error) => {
match error {
dotenvy::Error::LineParse(line, index)=> log::info!("{} Parsing failed within the specified environment file: {} at index: {} of the value: {}",colors::yellow("Warning"), env_file_name, index, line),
dotenvy::Error::Io(_)=> log::info!("{} The `--env-file` flag was used, but the environment file specified '{}' was not found.",colors::yellow("Warning"),env_file_name),
dotenvy::Error::EnvVar(_)=> log::info!("{} One or more of the environment variables isn't present or not unicode within the specified environment file: {}",colors::yellow("Warning"),env_file_name),
dotenvy::Error::LineParse(line, index)=> log::info!("{} Parsing failed within the specified environment file: {} at index: {} of the value: {}", colors::yellow("Warning"), env_file_name, index, line),
dotenvy::Error::Io(_)=> log::info!("{} The `--env-file` flag was used, but the environment file specified '{}' was not found.", colors::yellow("Warning"), env_file_name),
dotenvy::Error::EnvVar(_)=> log::info!("{} One or more of the environment variables isn't present or not unicode within the specified environment file: {}", colors::yellow("Warning"), env_file_name),
_ => log::info!("{} Unknown failure occurred with the specified environment file: {}", colors::yellow("Warning"), env_file_name),
}
}
@ -1916,8 +1457,7 @@ mod test {
"importMap": "import_map.json"
}"#;
let cwd = &std::env::current_dir().unwrap();
let config_specifier =
ModuleSpecifier::parse("file:///deno/deno.jsonc").unwrap();
let config_specifier = Url::parse("file:///deno/deno.jsonc").unwrap();
let config_file = ConfigFile::new(config_text, config_specifier).unwrap();
let actual = resolve_import_map_specifier(
Some("import-map.json"),
@ -1926,7 +1466,7 @@ mod test {
);
let import_map_path = cwd.join("import-map.json");
let expected_specifier =
ModuleSpecifier::from_file_path(import_map_path).unwrap();
deno_path_util::url_from_file_path(&import_map_path).unwrap();
assert!(actual.is_ok());
let actual = actual.unwrap();
assert_eq!(actual, Some(expected_specifier));
@ -1935,8 +1475,7 @@ mod test {
#[test]
fn resolve_import_map_none() {
let config_text = r#"{}"#;
let config_specifier =
ModuleSpecifier::parse("file:///deno/deno.jsonc").unwrap();
let config_specifier = Url::parse("file:///deno/deno.jsonc").unwrap();
let config_file = ConfigFile::new(config_text, config_specifier).unwrap();
let actual = resolve_import_map_specifier(
None,

50
cli/cache/deno_dir.rs vendored
View file

@ -2,46 +2,39 @@
use std::env;
use std::path::PathBuf;
use std::sync::Arc;
use deno_cache_dir::DenoDirResolutionError;
use super::DiskCache;
use crate::factory::CliDenoDirPathProvider;
use crate::sys::CliSys;
/// Lazily creates the deno dir which might be useful in scenarios
/// where functionality wants to continue if the DENO_DIR can't be created.
pub struct DenoDirProvider {
deno_dir_path_provider: Arc<CliDenoDirPathProvider>,
sys: CliSys,
maybe_custom_root: Option<PathBuf>,
deno_dir: std::sync::OnceLock<Result<DenoDir, DenoDirResolutionError>>,
deno_dir: once_cell::sync::OnceCell<DenoDir>,
}
impl DenoDirProvider {
pub fn new(sys: CliSys, maybe_custom_root: Option<PathBuf>) -> Self {
pub fn new(
sys: CliSys,
deno_dir_path_provider: Arc<CliDenoDirPathProvider>,
) -> Self {
Self {
sys,
maybe_custom_root,
deno_dir_path_provider,
deno_dir: Default::default(),
}
}
pub fn get_or_create(&self) -> Result<&DenoDir, DenoDirResolutionError> {
self
.deno_dir
.get_or_init(|| {
DenoDir::new(self.sys.clone(), self.maybe_custom_root.clone())
})
.as_ref()
.map_err(|err| match err {
DenoDirResolutionError::NoCacheOrHomeDir => {
DenoDirResolutionError::NoCacheOrHomeDir
}
DenoDirResolutionError::FailedCwd { source } => {
DenoDirResolutionError::FailedCwd {
source: std::io::Error::new(source.kind(), source.to_string()),
}
}
})
self.deno_dir.get_or_try_init(|| {
let path = self.deno_dir_path_provider.get_or_create()?;
Ok(DenoDir::new(self.sys.clone(), path.clone()))
})
}
}
@ -56,23 +49,14 @@ pub struct DenoDir {
}
impl DenoDir {
pub fn new(
sys: CliSys,
maybe_custom_root: Option<PathBuf>,
) -> Result<Self, deno_cache_dir::DenoDirResolutionError> {
let root = deno_cache_dir::resolve_deno_dir(
&sys_traits::impls::RealSys,
maybe_custom_root,
)?;
pub fn new(sys: CliSys, root: PathBuf) -> Self {
assert!(root.is_absolute());
let gen_path = root.join("gen");
let deno_dir = Self {
Self {
root,
gen_cache: DiskCache::new(sys, &gen_path),
};
Ok(deno_dir)
gen_cache: DiskCache::new(sys, gen_path),
}
}
/// The root directory of the DENO_DIR for display purposes only.

View file

@ -1,7 +1,6 @@
// Copyright 2018-2025 the Deno authors. MIT license.
use std::ffi::OsStr;
use std::fs;
use std::path::Component;
use std::path::Path;
use std::path::PathBuf;
@ -13,6 +12,7 @@ use deno_cache_dir::CACHE_PERM;
use deno_core::url::Host;
use deno_core::url::Url;
use deno_path_util::fs::atomic_write_file_with_retries;
use sys_traits::FsRead;
use crate::sys::CliSys;
@ -24,12 +24,9 @@ pub struct DiskCache {
impl DiskCache {
/// `location` must be an absolute path.
pub fn new(sys: CliSys, location: &Path) -> Self {
pub fn new(sys: CliSys, location: PathBuf) -> Self {
assert!(location.is_absolute());
Self {
sys,
location: location.to_owned(),
}
Self { sys, location }
}
fn get_cache_filename(&self, url: &Url) -> Option<PathBuf> {
@ -119,7 +116,7 @@ impl DiskCache {
pub fn get(&self, filename: &Path) -> std::io::Result<Vec<u8>> {
let path = self.location.join(filename);
fs::read(path)
Ok(self.sys.fs_read(path)?.into_owned())
}
pub fn set(&self, filename: &Path, data: &[u8]) -> std::io::Result<()> {
@ -141,7 +138,7 @@ mod tests {
fn test_set_get_cache_file() {
let temp_dir = TempDir::new();
let sub_dir = temp_dir.path().join("sub_dir");
let cache = DiskCache::new(RealSys, &sub_dir.to_path_buf());
let cache = DiskCache::new(RealSys, sub_dir.to_path_buf());
let path = PathBuf::from("foo/bar.txt");
cache.set(&path, b"hello").unwrap();
assert_eq!(cache.get(&path).unwrap(), b"hello");
@ -155,7 +152,7 @@ mod tests {
PathBuf::from("/deno_dir/")
};
let cache = DiskCache::new(RealSys, &cache_location);
let cache = DiskCache::new(RealSys, cache_location);
let mut test_cases = vec![
(
@ -211,7 +208,7 @@ mod tests {
} else {
"/foo"
};
let cache = DiskCache::new(RealSys, &PathBuf::from(p));
let cache = DiskCache::new(RealSys, PathBuf::from(p));
let mut test_cases = vec![
(
@ -259,7 +256,7 @@ mod tests {
PathBuf::from("/deno_dir/")
};
let cache = DiskCache::new(RealSys, &cache_location);
let cache = DiskCache::new(RealSys, cache_location);
let mut test_cases = vec!["unknown://localhost/test.ts"];

2
cli/cache/emit.rs vendored
View file

@ -167,7 +167,7 @@ mod test {
pub fn emit_cache_general_use() {
let temp_dir = TempDir::new();
let disk_cache =
DiskCache::new(CliSys::default(), temp_dir.path().as_path());
DiskCache::new(CliSys::default(), temp_dir.path().to_path_buf());
let cache = EmitCache {
disk_cache: disk_cache.clone(),
file_serializer: EmitFileSerializer {

7
cli/cache/mod.rs vendored
View file

@ -57,7 +57,6 @@ pub use parsed_source::LazyGraphSourceParser;
pub use parsed_source::ParsedSourceCache;
pub type GlobalHttpCache = deno_cache_dir::GlobalHttpCache<CliSys>;
pub type LocalHttpCache = deno_cache_dir::LocalHttpCache<CliSys>;
pub type LocalLspHttpCache = deno_cache_dir::LocalLspHttpCache<CliSys>;
pub use deno_cache_dir::HttpCache;
use deno_error::JsErrorBox;
@ -120,11 +119,7 @@ impl FetchCacher {
} else if specifier.scheme() == "file" {
specifier.to_file_path().ok()
} else {
#[allow(deprecated)]
self
.global_http_cache
.get_global_cache_filepath(specifier)
.ok()
self.global_http_cache.local_path_for_url(specifier).ok()
}
}
}

View file

@ -1,37 +1,44 @@
// Copyright 2018-2025 the Deno authors. MIT license.
use std::borrow::Cow;
use std::future::Future;
use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc;
use deno_cache_dir::npm::NpmCacheDir;
use deno_config::workspace::PackageJsonDepResolution;
use deno_config::workspace::WorkspaceDirectory;
use deno_config::workspace::WorkspaceResolver;
use deno_core::anyhow::Context;
use deno_core::error::AnyError;
use deno_core::futures::FutureExt;
use deno_core::serde_json;
use deno_core::url::Url;
use deno_core::FeatureChecker;
use deno_error::JsErrorBox;
use deno_lib::args::get_root_cert_store;
use deno_lib::args::resolve_npm_resolution_snapshot;
use deno_lib::args::CaData;
use deno_lib::args::NpmProcessStateKind;
use deno_lib::args::NPM_PROCESS_STATE;
use deno_lib::loader::NpmModuleLoader;
use deno_lib::npm::create_npm_process_state_provider;
use deno_lib::npm::NpmRegistryReadPermissionChecker;
use deno_lib::npm::NpmRegistryReadPermissionCheckerMode;
use deno_lib::worker::LibMainWorkerFactory;
use deno_lib::worker::LibMainWorkerOptions;
use deno_npm::npm_rc::ResolvedNpmRc;
use deno_npm_cache::NpmCacheSetting;
use deno_resolver::cjs::IsCjsResolutionMode;
use deno_resolver::npm::managed::ManagedInNpmPkgCheckerCreateOptions;
use deno_resolver::factory::ConfigDiscoveryOption;
use deno_resolver::factory::DenoDirPathProviderOptions;
use deno_resolver::factory::NpmProcessStateOptions;
use deno_resolver::factory::ResolverFactoryOptions;
use deno_resolver::factory::SpecifiedImportMapProvider;
use deno_resolver::npm::managed::NpmResolutionCell;
use deno_resolver::npm::CreateInNpmPkgCheckerOptions;
use deno_resolver::npm::DenoInNpmPackageChecker;
use deno_resolver::npm::NpmReqResolverOptions;
use deno_resolver::sloppy_imports::SloppyImportsCachedFs;
use deno_resolver::DenoResolverOptions;
use deno_resolver::NodeAndNpmReqResolver;
use deno_runtime::deno_fs;
use deno_runtime::deno_fs::RealFs;
use deno_runtime::deno_node::RealIsBuiltInNodeModuleChecker;
use deno_runtime::deno_permissions::Permissions;
use deno_runtime::deno_permissions::PermissionsContainer;
use deno_runtime::deno_tls::rustls::RootCertStore;
@ -39,16 +46,18 @@ use deno_runtime::deno_tls::RootCertStoreProvider;
use deno_runtime::deno_web::BlobStore;
use deno_runtime::inspector_server::InspectorServer;
use deno_runtime::permissions::RuntimePermissionDescriptorParser;
use log::warn;
use node_resolver::analyze::NodeCodeTranslator;
use once_cell::sync::OnceCell;
use sys_traits::EnvCurrentDir;
use crate::args::check_warn_tsconfig;
use crate::args::CliOptions;
use crate::args::ConfigFlag;
use crate::args::DenoSubcommand;
use crate::args::Flags;
use crate::args::NpmInstallDepsProvider;
use crate::args::TsConfigType;
use crate::args::WorkspaceExternalImportMapLoader;
use crate::cache::Caches;
use crate::cache::CodeCache;
use crate::cache::DenoDir;
@ -56,12 +65,12 @@ use crate::cache::DenoDirProvider;
use crate::cache::EmitCache;
use crate::cache::GlobalHttpCache;
use crate::cache::HttpCache;
use crate::cache::LocalHttpCache;
use crate::cache::ModuleInfoCache;
use crate::cache::NodeAnalysisCache;
use crate::cache::ParsedSourceCache;
use crate::emit::Emitter;
use crate::file_fetcher::CliFileFetcher;
use crate::file_fetcher::TextDecodedFile;
use crate::graph_container::MainModuleGraphContainer;
use crate::graph_util::FileWatcherReporter;
use crate::graph_util::ModuleGraphBuilder;
@ -75,13 +84,10 @@ use crate::node::CliNodeResolver;
use crate::node::CliPackageJsonResolver;
use crate::npm::installer::NpmInstaller;
use crate::npm::installer::NpmResolutionInstaller;
use crate::npm::CliByonmNpmResolverCreateOptions;
use crate::npm::CliManagedNpmResolverCreateOptions;
use crate::npm::CliNpmCache;
use crate::npm::CliNpmCacheHttpClient;
use crate::npm::CliNpmRegistryInfoProvider;
use crate::npm::CliNpmResolver;
use crate::npm::CliNpmResolverCreateOptions;
use crate::npm::CliNpmResolverManagedSnapshotOption;
use crate::npm::CliNpmTarballCache;
use crate::npm::NpmResolutionInitializer;
@ -100,7 +106,6 @@ use crate::tools::lint::LintRuleProvider;
use crate::tools::run::hmr::HmrRunner;
use crate::tsc::TypeCheckingCjsTracker;
use crate::util::file_watcher::WatcherCommunicator;
use crate::util::fs::canonicalize_path_maybe_not_exists;
use crate::util::progress_bar::ProgressBar;
use crate::util::progress_bar::ProgressBarStyle;
use crate::worker::CliMainWorkerFactory;
@ -143,6 +148,74 @@ impl RootCertStoreProvider for CliRootCertStoreProvider {
}
}
#[derive(Debug)]
struct CliSpecifiedImportMapProvider {
cli_options: Arc<CliOptions>,
file_fetcher: Arc<CliFileFetcher>,
workspace_external_import_map_loader: Arc<WorkspaceExternalImportMapLoader>,
}
#[async_trait::async_trait(?Send)]
impl SpecifiedImportMapProvider for CliSpecifiedImportMapProvider {
async fn get(
&self,
) -> Result<Option<deno_config::workspace::SpecifiedImportMap>, AnyError> {
async fn resolve_import_map_value_from_specifier(
specifier: &Url,
file_fetcher: &CliFileFetcher,
) -> Result<serde_json::Value, AnyError> {
if specifier.scheme() == "data" {
let data_url_text =
deno_media_type::data_url::RawDataUrl::parse(specifier)?.decode()?;
Ok(serde_json::from_str(&data_url_text)?)
} else {
let file = TextDecodedFile::decode(
file_fetcher.fetch_bypass_permissions(specifier).await?,
)?;
Ok(serde_json::from_str(&file.source)?)
}
}
let maybe_import_map_specifier =
self.cli_options.resolve_specified_import_map_specifier()?;
match maybe_import_map_specifier {
Some(specifier) => {
let value = resolve_import_map_value_from_specifier(
&specifier,
&self.file_fetcher,
)
.await
.with_context(|| {
format!("Unable to load '{}' import map", specifier)
})?;
Ok(Some(deno_config::workspace::SpecifiedImportMap {
base_url: specifier,
value,
}))
}
None => {
if let Some(import_map) =
self.workspace_external_import_map_loader.get_or_load()?
{
let path_url = deno_path_util::url_from_file_path(&import_map.path)?;
Ok(Some(deno_config::workspace::SpecifiedImportMap {
base_url: path_url,
value: import_map.value.clone(),
}))
} else {
Ok(None)
}
}
}
}
}
pub type CliWorkspaceFactory = deno_resolver::factory::WorkspaceFactory<CliSys>;
pub type CliDenoDirPathProvider =
deno_resolver::factory::DenoDirPathProvider<CliSys>;
pub type CliResolverFactory = deno_resolver::factory::ResolverFactory<CliSys>;
pub struct Deferred<T>(once_cell::unsync::OnceCell<T>);
impl<T> Default for Deferred<T> {
@ -152,10 +225,6 @@ impl<T> Default for Deferred<T> {
}
impl<T> Deferred<T> {
pub fn from_value(value: T) -> Self {
Self(once_cell::unsync::OnceCell::from(value))
}
#[inline(always)]
pub fn get_or_try_init(
&self,
@ -194,17 +263,15 @@ struct CliFactoryServices {
cjs_tracker: Deferred<Arc<CliCjsTracker>>,
cli_options: Deferred<Arc<CliOptions>>,
code_cache: Deferred<Arc<CodeCache>>,
deno_resolver: Deferred<Arc<CliDenoResolver>>,
deno_dir_path_provider: Deferred<Arc<CliDenoDirPathProvider>>,
deno_dir_provider: Deferred<Arc<DenoDirProvider>>,
emit_cache: Deferred<Arc<EmitCache>>,
emitter: Deferred<Arc<Emitter>>,
feature_checker: Deferred<Arc<FeatureChecker>>,
file_fetcher: Deferred<Arc<CliFileFetcher>>,
found_pkg_json_dep_flag: Arc<FoundPackageJsonDepFlag>,
fs: Deferred<Arc<dyn deno_fs::FileSystem>>,
global_http_cache: Deferred<Arc<GlobalHttpCache>>,
http_cache: Deferred<Arc<dyn HttpCache>>,
http_client_provider: Deferred<Arc<HttpClientProvider>>,
in_npm_pkg_checker: Deferred<DenoInNpmPackageChecker>,
main_graph_container: Deferred<Arc<MainModuleGraphContainer>>,
maybe_file_watcher_reporter: Deferred<Option<FileWatcherReporter>>,
maybe_inspector_server: Deferred<Option<Arc<InspectorServer>>>,
@ -213,36 +280,39 @@ struct CliFactoryServices {
module_info_cache: Deferred<Arc<ModuleInfoCache>>,
module_load_preparer: Deferred<Arc<ModuleLoadPreparer>>,
node_code_translator: Deferred<Arc<CliNodeCodeTranslator>>,
node_resolver: Deferred<Arc<CliNodeResolver>>,
npm_cache: Deferred<Arc<CliNpmCache>>,
npm_cache_dir: Deferred<Arc<NpmCacheDir>>,
npm_cache_http_client: Deferred<Arc<CliNpmCacheHttpClient>>,
npm_graph_resolver: Deferred<Arc<CliNpmGraphResolver>>,
npm_installer: Deferred<Arc<NpmInstaller>>,
npm_registry_info_provider: Deferred<Arc<CliNpmRegistryInfoProvider>>,
npm_req_resolver: Deferred<Arc<CliNpmReqResolver>>,
npm_resolution: Arc<NpmResolutionCell>,
npm_resolution_initializer: Deferred<Arc<NpmResolutionInitializer>>,
npm_resolution_installer: Deferred<Arc<NpmResolutionInstaller>>,
npm_resolver: Deferred<CliNpmResolver>,
npm_tarball_cache: Deferred<Arc<CliNpmTarballCache>>,
parsed_source_cache: Deferred<Arc<ParsedSourceCache>>,
permission_desc_parser:
Deferred<Arc<RuntimePermissionDescriptorParser<CliSys>>>,
pkg_json_resolver: Deferred<Arc<CliPackageJsonResolver>>,
resolver: Deferred<Arc<CliResolver>>,
resolver_factory: Deferred<Arc<CliResolverFactory>>,
root_cert_store_provider: Deferred<Arc<dyn RootCertStoreProvider>>,
root_permissions_container: Deferred<PermissionsContainer>,
sloppy_imports_resolver: Deferred<Option<Arc<CliSloppyImportsResolver>>>,
text_only_progress_bar: Deferred<ProgressBar>,
type_checker: Deferred<Arc<TypeChecker>>,
workspace_resolver: Deferred<Arc<WorkspaceResolver>>,
workspace_factory: Deferred<Arc<CliWorkspaceFactory>>,
workspace_external_import_map_loader:
Deferred<Arc<WorkspaceExternalImportMapLoader>>,
}
#[derive(Debug, Default)]
struct CliFactoryOverrides {
initial_cwd: Option<PathBuf>,
workspace_directory: Option<Arc<WorkspaceDirectory>>,
}
pub struct CliFactory {
watcher_communicator: Option<Arc<WatcherCommunicator>>,
flags: Arc<Flags>,
services: CliFactoryServices,
overrides: CliFactoryOverrides,
}
impl CliFactory {
@ -251,18 +321,7 @@ impl CliFactory {
flags,
watcher_communicator: None,
services: Default::default(),
}
}
pub fn from_cli_options(cli_options: Arc<CliOptions>) -> Self {
let (cli_options, flags) = cli_options.into_self_and_flags();
CliFactory {
watcher_communicator: None,
flags,
services: CliFactoryServices {
cli_options: Deferred::from_value(cli_options),
..Default::default()
},
overrides: Default::default(),
}
}
@ -274,27 +333,63 @@ impl CliFactory {
watcher_communicator: Some(watcher_communicator),
flags,
services: Default::default(),
overrides: Default::default(),
}
}
pub fn set_initial_cwd(&mut self, initial_cwd: PathBuf) {
self.overrides.initial_cwd = Some(initial_cwd);
}
pub fn set_workspace_dir(&mut self, dir: Arc<WorkspaceDirectory>) {
self.overrides.workspace_directory = Some(dir);
}
pub fn cli_options(&self) -> Result<&Arc<CliOptions>, AnyError> {
self.services.cli_options.get_or_try_init(|| {
CliOptions::from_flags(&self.sys(), self.flags.clone()).map(Arc::new)
let workspace_factory = self.workspace_factory()?;
let workspace_directory = workspace_factory.workspace_directory()?;
let maybe_external_import_map =
self.workspace_external_import_map_loader()?.get_or_load()?;
CliOptions::from_flags(
&self.sys(),
self.flags.clone(),
workspace_factory.initial_cwd().clone(),
maybe_external_import_map,
workspace_directory.clone(),
)
.map(Arc::new)
})
}
pub fn deno_dir_provider(&self) -> Result<&Arc<DenoDirProvider>, AnyError> {
Ok(&self.cli_options()?.deno_dir_provider)
pub fn deno_dir_path_provider(&self) -> &Arc<CliDenoDirPathProvider> {
self.services.deno_dir_path_provider.get_or_init(|| {
Arc::new(CliDenoDirPathProvider::new(
self.sys(),
DenoDirPathProviderOptions {
maybe_custom_root: self.flags.internal.cache_path.clone(),
},
))
})
}
pub fn deno_dir_provider(&self) -> &Arc<DenoDirProvider> {
self.services.deno_dir_provider.get_or_init(|| {
Arc::new(DenoDirProvider::new(
self.sys(),
self.deno_dir_path_provider().clone(),
))
})
}
pub fn deno_dir(&self) -> Result<&DenoDir, AnyError> {
Ok(self.deno_dir_provider()?.get_or_create()?)
Ok(self.deno_dir_provider().get_or_create()?)
}
pub fn caches(&self) -> Result<&Arc<Caches>, AnyError> {
self.services.caches.get_or_try_init(|| {
let cli_options = self.cli_options()?;
let caches = Arc::new(Caches::new(self.deno_dir_provider()?.clone()));
let caches = Arc::new(Caches::new(self.deno_dir_provider().clone()));
// Warm up the caches we know we'll likely need based on the CLI mode
match cli_options.sub_command() {
DenoSubcommand::Run(_)
@ -340,29 +435,11 @@ impl CliFactory {
}
pub fn global_http_cache(&self) -> Result<&Arc<GlobalHttpCache>, AnyError> {
self.services.global_http_cache.get_or_try_init(|| {
Ok(Arc::new(GlobalHttpCache::new(
self.sys(),
self.deno_dir()?.remote_folder_path(),
)))
})
Ok(self.workspace_factory()?.global_http_cache()?)
}
pub fn http_cache(&self) -> Result<&Arc<dyn HttpCache>, AnyError> {
self.services.http_cache.get_or_try_init(|| {
let global_cache = self.global_http_cache()?.clone();
match self.cli_options()?.vendor_dir_path() {
Some(local_path) => {
let local_cache = LocalHttpCache::new(
local_path.clone(),
global_cache,
deno_cache_dir::GlobalToLocalCopy::Allow,
);
Ok(Arc::new(local_cache))
}
None => Ok(global_cache),
}
})
Ok(self.workspace_factory()?.http_cache()?)
}
pub fn http_client_provider(&self) -> &Arc<HttpClientProvider> {
@ -401,22 +478,7 @@ impl CliFactory {
pub fn in_npm_pkg_checker(
&self,
) -> Result<&DenoInNpmPackageChecker, AnyError> {
self.services.in_npm_pkg_checker.get_or_try_init(|| {
let cli_options = self.cli_options()?;
let options = if cli_options.use_byonm() {
CreateInNpmPkgCheckerOptions::Byonm
} else {
CreateInNpmPkgCheckerOptions::Managed(
ManagedInNpmPkgCheckerCreateOptions {
root_cache_dir_url: self.npm_cache_dir()?.root_dir_url(),
maybe_node_modules_path: cli_options
.node_modules_dir_path()
.map(|p| p.as_path()),
},
)
};
Ok(DenoInNpmPackageChecker::new(options))
})
self.resolver_factory()?.in_npm_package_checker()
}
pub fn npm_cache(&self) -> Result<&Arc<CliNpmCache>, AnyError> {
@ -426,21 +488,13 @@ impl CliFactory {
self.npm_cache_dir()?.clone(),
self.sys(),
NpmCacheSetting::from_cache_setting(&cli_options.cache_setting()),
cli_options.npmrc().clone(),
self.npmrc()?.clone(),
)))
})
}
pub fn npm_cache_dir(&self) -> Result<&Arc<NpmCacheDir>, AnyError> {
self.services.npm_cache_dir.get_or_try_init(|| {
let global_path = self.deno_dir()?.npm_folder_path();
let cli_options = self.cli_options()?;
Ok(Arc::new(NpmCacheDir::new(
&self.sys(),
global_path,
cli_options.npmrc().get_all_known_registries_urls(),
)))
})
Ok(self.workspace_factory()?.npm_cache_dir()?)
}
pub fn npm_cache_http_client(&self) -> &Arc<CliNpmCacheHttpClient> {
@ -469,8 +523,7 @@ impl CliFactory {
pub fn npm_installer_if_managed(
&self,
) -> Result<Option<&Arc<NpmInstaller>>, AnyError> {
let options = self.cli_options()?;
if options.use_byonm() || options.no_npm() {
if self.resolver_factory()?.use_byonm()? || self.cli_options()?.no_npm() {
Ok(None)
} else {
Ok(Some(self.npm_installer()?))
@ -480,19 +533,22 @@ impl CliFactory {
pub fn npm_installer(&self) -> Result<&Arc<NpmInstaller>, AnyError> {
self.services.npm_installer.get_or_try_init(|| {
let cli_options = self.cli_options()?;
let workspace_factory = self.workspace_factory()?;
Ok(Arc::new(NpmInstaller::new(
self.npm_cache()?.clone(),
Arc::new(NpmInstallDepsProvider::from_workspace(
cli_options.workspace(),
)),
self.npm_resolution().clone(),
self.npm_resolution()?.clone(),
self.npm_resolution_initializer()?.clone(),
self.npm_resolution_installer()?.clone(),
self.text_only_progress_bar(),
self.sys(),
self.npm_tarball_cache()?.clone(),
cli_options.maybe_lockfile().cloned(),
cli_options.node_modules_dir_path().cloned(),
workspace_factory
.node_modules_dir_path()?
.map(|p| p.to_path_buf()),
cli_options.lifecycle_scripts_config(),
cli_options.npm_system_info(),
)))
@ -506,17 +562,16 @@ impl CliFactory {
.services
.npm_registry_info_provider
.get_or_try_init(|| {
let cli_options = self.cli_options()?;
Ok(Arc::new(CliNpmRegistryInfoProvider::new(
self.npm_cache()?.clone(),
self.npm_cache_http_client().clone(),
cli_options.npmrc().clone(),
self.npmrc()?.clone(),
)))
})
}
pub fn npm_resolution(&self) -> &Arc<NpmResolutionCell> {
&self.services.npm_resolution
pub fn npm_resolution(&self) -> Result<&Arc<NpmResolutionCell>, AnyError> {
Ok(self.resolver_factory()?.npm_resolution())
}
pub fn npm_resolution_initializer(
@ -529,8 +584,8 @@ impl CliFactory {
let cli_options = self.cli_options()?;
Ok(Arc::new(NpmResolutionInitializer::new(
self.npm_registry_info_provider()?.clone(),
self.npm_resolution().clone(),
match cli_options.resolve_npm_resolution_snapshot()? {
self.npm_resolution()?.clone(),
match resolve_npm_resolution_snapshot()? {
Some(snapshot) => {
CliNpmResolverManagedSnapshotOption::Specified(Some(snapshot))
}
@ -554,71 +609,59 @@ impl CliFactory {
let cli_options = self.cli_options()?;
Ok(Arc::new(NpmResolutionInstaller::new(
self.npm_registry_info_provider()?.clone(),
self.npm_resolution().clone(),
self.npm_resolution()?.clone(),
cli_options.maybe_lockfile().cloned(),
)))
})
}
pub async fn npm_resolver(&self) -> Result<&CliNpmResolver, AnyError> {
self
.services
.npm_resolver
.get_or_try_init_async(
async {
let cli_options = self.cli_options()?;
Ok(CliNpmResolver::new(if cli_options.use_byonm() {
CliNpmResolverCreateOptions::Byonm(
CliByonmNpmResolverCreateOptions {
sys: self.sys(),
pkg_json_resolver: self.pkg_json_resolver().clone(),
root_node_modules_dir: Some(
match cli_options.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(
cli_options.initial_cwd(),
)?
.join("node_modules"),
},
),
},
)
} else {
self
.npm_resolution_initializer()?
.ensure_initialized()
.await?;
CliNpmResolverCreateOptions::Managed(
CliManagedNpmResolverCreateOptions {
sys: self.sys(),
npm_resolution: self.npm_resolution().clone(),
npm_cache_dir: self.npm_cache_dir()?.clone(),
maybe_node_modules_path: cli_options
.node_modules_dir_path()
.cloned(),
npm_system_info: cli_options.npm_system_info(),
npmrc: cli_options.npmrc().clone(),
},
)
}))
}
.boxed_local(),
)
.await
self.initialize_npm_resolution_if_managed().await?;
self.resolver_factory()?.npm_resolver()
}
pub fn npm_tarball_cache(
&self,
) -> Result<&Arc<CliNpmTarballCache>, AnyError> {
self.services.npm_tarball_cache.get_or_try_init(|| {
let cli_options = self.cli_options()?;
Ok(Arc::new(CliNpmTarballCache::new(
self.npm_cache()?.clone(),
self.npm_cache_http_client().clone(),
self.sys(),
cli_options.npmrc().clone(),
self.npmrc()?.clone(),
)))
})
}
pub fn npmrc(&self) -> Result<&Arc<ResolvedNpmRc>, AnyError> {
Ok(self.workspace_factory()?.npmrc()?)
}
pub fn resolver_factory(&self) -> Result<&Arc<CliResolverFactory>, AnyError> {
self.services.resolver_factory.get_or_try_init(|| {
Ok(Arc::new(CliResolverFactory::new(
self.workspace_factory()?.clone(),
ResolverFactoryOptions {
conditions_from_resolution_mode: Default::default(),
no_sloppy_imports_cache: false,
npm_system_info: self.flags.subcommand.npm_system_info(),
specified_import_map: Some(Box::new(CliSpecifiedImportMapProvider {
cli_options: self.cli_options()?.clone(),
file_fetcher: self.file_fetcher()?.clone(),
workspace_external_import_map_loader: self
.workspace_external_import_map_loader()?
.clone(),
})),
unstable_sloppy_imports: self.flags.unstable_config.sloppy_imports,
package_json_dep_resolution: match &self.flags.subcommand {
DenoSubcommand::Publish(_) => {
// the node_modules directory is not published to jsr, so resolve
// dependencies via the package.json rather than using node resolution
Some(deno_config::workspace::PackageJsonDepResolution::Enabled)
}
_ => None,
},
},
)))
})
}
@ -626,82 +669,48 @@ impl CliFactory {
pub fn sloppy_imports_resolver(
&self,
) -> Result<Option<&Arc<CliSloppyImportsResolver>>, AnyError> {
self
.services
.sloppy_imports_resolver
.get_or_try_init(|| {
Ok(self.cli_options()?.unstable_sloppy_imports().then(|| {
Arc::new(CliSloppyImportsResolver::new(SloppyImportsCachedFs::new(
self.sys(),
)))
}))
})
.map(|maybe| maybe.as_ref())
self.resolver_factory()?.sloppy_imports_resolver()
}
pub fn workspace_directory(
&self,
) -> Result<&Arc<WorkspaceDirectory>, AnyError> {
Ok(self.workspace_factory()?.workspace_directory()?)
}
fn workspace_factory(&self) -> Result<&Arc<CliWorkspaceFactory>, AnyError> {
self.services.workspace_factory.get_or_try_init(|| {
let initial_cwd = match self.overrides.initial_cwd.clone() {
Some(v) => v,
None => self
.sys()
.env_current_dir()
.with_context(|| "Failed getting cwd.")?,
};
let options = new_workspace_factory_options(
&initial_cwd,
&self.flags,
self.deno_dir_path_provider().clone(),
);
let mut factory =
CliWorkspaceFactory::new(self.sys(), initial_cwd, options);
if let Some(workspace_dir) = &self.overrides.workspace_directory {
factory.set_workspace_directory(workspace_dir.clone());
}
Ok(Arc::new(factory))
})
}
pub async fn workspace_resolver(
&self,
) -> Result<&Arc<WorkspaceResolver>, AnyError> {
self
.services
.workspace_resolver
.get_or_try_init_async(async {
let cli_options = self.cli_options()?;
let resolver = cli_options
.create_workspace_resolver(
self.file_fetcher()?,
if cli_options.use_byonm()
&& !matches!(
cli_options.sub_command(),
DenoSubcommand::Publish(_)
)
{
PackageJsonDepResolution::Disabled
} else {
// todo(dsherret): this should be false for nodeModulesDir: true
PackageJsonDepResolution::Enabled
},
)
.await?;
if !resolver.diagnostics().is_empty() {
warn!(
"Import map diagnostics:\n{}",
resolver
.diagnostics()
.iter()
.map(|d| format!(" - {d}"))
.collect::<Vec<_>>()
.join("\n")
);
}
Ok(Arc::new(resolver))
})
.await
self.initialize_npm_resolution_if_managed().await?;
self.resolver_factory()?.workspace_resolver().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
self.initialize_npm_resolution_if_managed().await?;
self.resolver_factory()?.deno_resolver().await
}
pub async fn resolver(&self) -> Result<&Arc<CliResolver>, AnyError> {
@ -787,23 +796,19 @@ impl CliFactory {
}
pub async fn node_resolver(&self) -> Result<&Arc<CliNodeResolver>, AnyError> {
self
.services
.node_resolver
.get_or_try_init_async(
async {
Ok(Arc::new(CliNodeResolver::new(
self.in_npm_pkg_checker()?.clone(),
RealIsBuiltInNodeModuleChecker,
self.npm_resolver().await?.clone(),
self.pkg_json_resolver().clone(),
self.sys(),
node_resolver::ConditionsFromResolutionMode::default(),
)))
}
.boxed_local(),
)
.await
self.initialize_npm_resolution_if_managed().await?;
self.resolver_factory()?.node_resolver()
}
async fn initialize_npm_resolution_if_managed(&self) -> Result<(), AnyError> {
let npm_resolver = self.resolver_factory()?.npm_resolver()?;
if npm_resolver.is_managed() {
self
.npm_resolution_initializer()?
.ensure_initialized()
.await?;
}
Ok(())
}
pub async fn node_code_translator(
@ -821,7 +826,7 @@ impl CliFactory {
self.in_npm_pkg_checker()?.clone(),
node_resolver,
self.npm_resolver().await?.clone(),
self.pkg_json_resolver().clone(),
self.pkg_json_resolver()?.clone(),
self.sys(),
)))
})
@ -839,29 +844,14 @@ impl CliFactory {
))
}
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 {
sys: self.sys(),
in_npm_pkg_checker: self.in_npm_pkg_checker()?.clone(),
node_resolver: self.node_resolver().await?.clone(),
npm_resolver: npm_resolver.clone(),
})))
})
.await
pub fn npm_req_resolver(&self) -> Result<&Arc<CliNpmReqResolver>, AnyError> {
self.resolver_factory()?.npm_req_resolver()
}
pub fn pkg_json_resolver(&self) -> &Arc<CliPackageJsonResolver> {
self
.services
.pkg_json_resolver
.get_or_init(|| Arc::new(CliPackageJsonResolver::new(self.sys())))
pub fn pkg_json_resolver(
&self,
) -> Result<&Arc<CliPackageJsonResolver>, AnyError> {
Ok(self.resolver_factory()?.pkg_json_resolver())
}
pub async fn type_checker(&self) -> Result<&Arc<TypeChecker>, AnyError> {
@ -987,7 +977,7 @@ impl CliFactory {
let options = self.cli_options()?;
Ok(Arc::new(CliCjsTracker::new(
self.in_npm_pkg_checker()?.clone(),
self.pkg_json_resolver().clone(),
self.pkg_json_resolver()?.clone(),
if options.is_node_main() || options.unstable_detect_cjs() {
IsCjsResolutionMode::ImplicitTypeCommonJs
} else if options.detect_cjs() {
@ -1056,6 +1046,20 @@ impl CliFactory {
})
}
fn workspace_external_import_map_loader(
&self,
) -> Result<&Arc<WorkspaceExternalImportMapLoader>, AnyError> {
self
.services
.workspace_external_import_map_loader
.get_or_try_init(|| {
Ok(Arc::new(WorkspaceExternalImportMapLoader::new(
self.sys(),
self.workspace_directory()?.workspace.clone(),
)))
})
}
pub async fn create_cli_main_worker_factory(
&self,
) -> Result<CliMainWorkerFactory, AnyError> {
@ -1072,14 +1076,18 @@ 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?;
let pkg_json_resolver = self.pkg_json_resolver()?.clone();
let npm_req_resolver = self.npm_req_resolver()?;
let workspace_factory = self.workspace_factory()?;
let npm_registry_permission_checker = {
let mode = if cli_options.use_byonm() {
let mode = if self.resolver_factory()?.use_byonm()? {
NpmRegistryReadPermissionCheckerMode::Byonm
} else if let Some(node_modules_dir) = cli_options.node_modules_dir_path()
} else if let Some(node_modules_dir) =
workspace_factory.node_modules_dir_path()?
{
NpmRegistryReadPermissionCheckerMode::Local(node_modules_dir.clone())
NpmRegistryReadPermissionCheckerMode::Local(
node_modules_dir.to_path_buf(),
)
} else {
NpmRegistryReadPermissionCheckerMode::Global(
self.npm_cache_dir()?.root_dir().to_path_buf(),
@ -1151,6 +1159,7 @@ impl CliFactory {
&self,
) -> Result<LibMainWorkerOptions, AnyError> {
let cli_options = self.cli_options()?;
let workspace_factory = self.workspace_factory()?;
Ok(LibMainWorkerOptions {
argv: cli_options.argv().clone(),
// This optimization is only available for "run" subcommand
@ -1160,7 +1169,9 @@ impl CliFactory {
log_level: cli_options.log_level().unwrap_or(log::Level::Info).into(),
enable_op_summary_metrics: cli_options.enable_op_summary_metrics(),
enable_testing_features: cli_options.enable_testing_features(),
has_node_modules_dir: cli_options.has_node_modules_dir(),
has_node_modules_dir: workspace_factory
.node_modules_dir_path()?
.is_some(),
inspect_brk: cli_options.inspect_brk().is_some(),
inspect_wait: cli_options.inspect_wait().is_some(),
strace_ops: cli_options.strace_ops().clone(),
@ -1223,3 +1234,57 @@ impl CliFactory {
})
}
}
fn new_workspace_factory_options(
initial_cwd: &Path,
flags: &Flags,
deno_dir_path_provider: Arc<CliDenoDirPathProvider>,
) -> deno_resolver::factory::WorkspaceFactoryOptions<CliSys> {
deno_resolver::factory::WorkspaceFactoryOptions {
additional_config_file_names: if matches!(
flags.subcommand,
DenoSubcommand::Publish(..)
) {
&["jsr.json", "jsr.jsonc"]
} else {
&[]
},
config_discovery: match &flags.config_flag {
ConfigFlag::Discover => {
if let Some(start_paths) = flags.config_path_args(initial_cwd) {
ConfigDiscoveryOption::Discover { start_paths }
} else {
ConfigDiscoveryOption::Disabled
}
}
ConfigFlag::Path(path) => {
ConfigDiscoveryOption::Path(PathBuf::from(path))
}
ConfigFlag::Disabled => ConfigDiscoveryOption::Disabled,
},
deno_dir_path_provider: Some(deno_dir_path_provider),
// For `deno install/add/remove/init` we want to force the managed
// resolver so it can set up the `node_modules/` directory.
is_package_manager_subcommand: matches!(
flags.subcommand,
DenoSubcommand::Install(_)
| DenoSubcommand::Add(_)
| DenoSubcommand::Remove(_)
| DenoSubcommand::Init(_)
| DenoSubcommand::Outdated(_)
),
no_npm: flags.no_npm,
node_modules_dir: flags.node_modules_dir,
npm_process_state: NPM_PROCESS_STATE.as_ref().map(|s| {
NpmProcessStateOptions {
node_modules_dir: s
.local_node_modules_path
.as_ref()
.map(|s| Cow::Borrowed(s.as_str())),
is_byonm: matches!(s.kind, NpmProcessStateKind::Byonm),
}
}),
vendor: flags.vendor,
}
}

View file

@ -15,7 +15,7 @@ path = "lib.rs"
[dependencies]
capacity_builder.workspace = true
deno_config.workspace = true
deno_config = { workspace = true, features = ["sync", "workspace"] }
deno_error.workspace = true
deno_fs = { workspace = true, features = ["sync_fs"] }
deno_media_type.workspace = true

View file

@ -7,6 +7,8 @@ use std::io::Seek;
use std::path::PathBuf;
use std::sync::LazyLock;
use deno_npm::resolution::PackageIdNotFoundError;
use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot;
use deno_runtime::colors;
use deno_runtime::deno_tls::deno_native_certs::load_native_certs;
use deno_runtime::deno_tls::rustls;
@ -32,8 +34,10 @@ pub fn has_trace_permissions_enabled() -> bool {
}
pub fn has_flag_env_var(name: &str) -> bool {
let value = std::env::var(name);
matches!(value.as_ref().map(|s| s.as_str()), Ok("1"))
match std::env::var_os(name) {
Some(value) => value == "1",
None => false,
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
@ -187,6 +191,19 @@ pub static NPM_PROCESS_STATE: LazyLock<Option<NpmProcessState>> =
Some(state)
});
pub fn resolve_npm_resolution_snapshot(
) -> Result<Option<ValidSerializedNpmResolutionSnapshot>, PackageIdNotFoundError>
{
if let Some(NpmProcessStateKind::Snapshot(snapshot)) =
NPM_PROCESS_STATE.as_ref().map(|s| &s.kind)
{
// TODO(bartlomieju): remove this clone
Ok(Some(snapshot.clone().into_valid()?))
} else {
Ok(None)
}
}
#[derive(Clone, Default, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct UnstableConfig {
// TODO(bartlomieju): remove in Deno 2.5

View file

@ -94,8 +94,10 @@ impl LspCache {
.ok()
});
let sys = CliSys::default();
let deno_dir = DenoDir::new(sys.clone(), global_cache_path)
.expect("should be infallible with absolute custom root");
let deno_dir_root =
deno_cache_dir::resolve_deno_dir(&sys, global_cache_path)
.expect("should be infallible with absolute custom root");
let deno_dir = DenoDir::new(sys.clone(), deno_dir_root);
let global =
Arc::new(GlobalHttpCache::new(sys, deno_dir.remote_folder_path()));
Self {

View file

@ -47,6 +47,7 @@ use deno_lint::linter::LintConfig as DenoLintConfig;
use deno_npm::npm_rc::ResolvedNpmRc;
use deno_package_json::PackageJsonCache;
use deno_path_util::url_to_file_path;
use deno_resolver::npmrc::discover_npmrc_from_workspace;
use deno_resolver::sloppy_imports::SloppyImportsCachedFs;
use deno_runtime::deno_node::PackageJson;
use indexmap::IndexSet;
@ -56,7 +57,6 @@ use tower_lsp::lsp_types as lsp;
use super::logging::lsp_log;
use super::lsp_custom;
use super::urls::url_to_uri;
use crate::args::discover_npmrc_from_workspace;
use crate::args::CliLockfile;
use crate::args::CliLockfileReadFromPathOptions;
use crate::args::ConfigFile;
@ -1362,21 +1362,22 @@ impl ConfigData {
}
// todo(dsherret): cache this so we don't load this so many times
let npmrc = discover_npmrc_from_workspace(&member_dir.workspace)
.inspect(|(_, path)| {
if let Some(path) = path {
lsp_log!(" Resolved .npmrc: \"{}\"", path.display());
let npmrc =
discover_npmrc_from_workspace(&CliSys::default(), &member_dir.workspace)
.inspect(|(_, path)| {
if let Some(path) = path {
lsp_log!(" Resolved .npmrc: \"{}\"", path.display());
if let Ok(specifier) = ModuleSpecifier::from_file_path(path) {
add_watched_file(specifier, ConfigWatchedFileType::NpmRc);
if let Ok(specifier) = ModuleSpecifier::from_file_path(path) {
add_watched_file(specifier, ConfigWatchedFileType::NpmRc);
}
}
}
})
.inspect_err(|err| {
lsp_warn!(" Couldn't read .npmrc for \"{scope}\": {err}");
})
.map(|(r, _)| r)
.ok();
})
.inspect_err(|err| {
lsp_warn!(" Couldn't read .npmrc for \"{scope}\": {err}");
})
.map(|(r, _)| Arc::new(r))
.ok();
let default_file_pattern_base =
scope.to_file_path().unwrap_or_else(|_| PathBuf::from("/"));
let fmt_config = Arc::new(

View file

@ -13,8 +13,6 @@ use std::sync::Arc;
use deno_ast::MediaType;
use deno_cache_dir::file_fetcher::CacheSetting;
use deno_config::workspace::WorkspaceDirectory;
use deno_config::workspace::WorkspaceDiscoverOptions;
use deno_core::anyhow::anyhow;
use deno_core::error::AnyError;
use deno_core::resolve_url;
@ -28,7 +26,6 @@ use deno_core::ModuleSpecifier;
use deno_graph::GraphKind;
use deno_graph::Resolution;
use deno_lib::args::get_root_cert_store;
use deno_lib::args::has_flag_env_var;
use deno_lib::args::CaData;
use deno_lib::version::DENO_VERSION_INFO;
use deno_path_util::url_to_file_path;
@ -97,8 +94,6 @@ use super::tsc::TsServer;
use super::urls;
use super::urls::uri_to_url;
use super::urls::url_to_uri;
use crate::args::create_default_npmrc;
use crate::args::CliOptions;
use crate::args::Flags;
use crate::args::InternalFlags;
use crate::args::UnstableFmtOptions;
@ -255,7 +250,7 @@ impl LanguageServer {
force_global_cache: bool,
) -> LspResult<Option<Value>> {
async fn create_graph_for_caching(
cli_options: CliOptions,
factory: CliFactory,
roots: Vec<ModuleSpecifier>,
open_docs: Vec<Arc<Document>>,
) -> Result<(), AnyError> {
@ -263,8 +258,6 @@ impl LanguageServer {
.into_iter()
.map(|d| (d.specifier().clone(), d))
.collect::<HashMap<_, _>>();
let cli_options = Arc::new(cli_options);
let factory = CliFactory::from_cli_options(cli_options.clone());
let module_graph_builder = factory.module_graph_builder().await?;
let module_graph_creator = factory.module_graph_creator().await?;
let mut inner_loader = module_graph_builder.create_graph_loader();
@ -293,9 +286,11 @@ impl LanguageServer {
// Update the lockfile on the file system with anything new
// found after caching
if let Some(lockfile) = cli_options.maybe_lockfile() {
if let Err(err) = &lockfile.write_if_changed() {
lsp_warn!("{:#}", err);
if let Ok(cli_options) = factory.cli_options() {
if let Some(lockfile) = cli_options.maybe_lockfile() {
if let Err(err) = &lockfile.write_if_changed() {
lsp_warn!("{:#}", err);
}
}
}
@ -319,11 +314,11 @@ impl LanguageServer {
match prepare_cache_result {
Ok(result) => {
// cache outside the lock
let cli_options = result.cli_options;
let cli_factory = result.cli_factory;
let roots = result.roots;
let open_docs = result.open_docs;
let handle = spawn(async move {
create_graph_for_caching(cli_options, roots, open_docs).await
create_graph_for_caching(cli_factory, roots, open_docs).await
});
if let Err(err) = handle.await.unwrap() {
@ -3492,7 +3487,7 @@ impl tower_lsp::LanguageServer for LanguageServer {
}
struct PrepareCacheResult {
cli_options: CliOptions,
cli_factory: CliFactory,
roots: Vec<ModuleSpecifier>,
open_docs: Vec<Arc<Document>>,
}
@ -3626,66 +3621,44 @@ impl Inner {
let initial_cwd = config_data
.and_then(|d| d.scope.to_file_path().ok())
.unwrap_or_else(|| self.initial_cwd.clone());
let workspace = match config_data {
Some(d) => d.member_dir.clone(),
None => Arc::new(WorkspaceDirectory::discover(
&CliSys::default(),
deno_config::workspace::WorkspaceDiscoverStart::Paths(&[
initial_cwd.clone()
]),
&WorkspaceDiscoverOptions {
deno_json_cache: None,
pkg_json_cache: None,
workspace_cache: None,
additional_config_file_names: &[],
discover_pkg_json: !has_flag_env_var("DENO_NO_PACKAGE_JSON"),
maybe_vendor_override: if force_global_cache {
Some(deno_config::workspace::VendorEnablement::Disable)
} else {
None
},
},
)?),
};
let cli_options = CliOptions::new(
&CliSys::default(),
Arc::new(Flags {
internal: InternalFlags {
cache_path: Some(self.cache.deno_dir().root.clone()),
..Default::default()
},
ca_stores: workspace_settings.certificate_stores.clone(),
ca_data: workspace_settings.tls_certificate.clone().map(CaData::File),
unsafely_ignore_certificate_errors: workspace_settings
.unsafely_ignore_certificate_errors
.clone(),
import_map_path: config_data.and_then(|d| {
d.import_map_from_settings
.as_ref()
.map(|url| url.to_string())
}),
// bit of a hack to force the lsp to cache the @types/node package
type_check_mode: crate::args::TypeCheckMode::Local,
permissions: crate::args::PermissionFlags {
// allow remote import permissions in the lsp for now
allow_import: Some(vec![]),
..Default::default()
},
let mut cli_factory = CliFactory::from_flags(Arc::new(Flags {
internal: InternalFlags {
cache_path: Some(self.cache.deno_dir().root.clone()),
..Default::default()
},
ca_stores: workspace_settings.certificate_stores.clone(),
ca_data: workspace_settings.tls_certificate.clone().map(CaData::File),
unsafely_ignore_certificate_errors: workspace_settings
.unsafely_ignore_certificate_errors
.clone(),
import_map_path: config_data.and_then(|d| {
d.import_map_from_settings
.as_ref()
.map(|url| url.to_string())
}),
initial_cwd,
config_data.and_then(|d| d.lockfile.clone()),
config_data
.and_then(|d| d.npmrc.clone())
.unwrap_or_else(create_default_npmrc),
workspace,
force_global_cache,
None,
)?;
// bit of a hack to force the lsp to cache the @types/node package
type_check_mode: crate::args::TypeCheckMode::Local,
permissions: crate::args::PermissionFlags {
// allow remote import permissions in the lsp for now
allow_import: Some(vec![]),
..Default::default()
},
vendor: if force_global_cache {
Some(false)
} else {
None
},
no_lock: force_global_cache,
..Default::default()
}));
cli_factory.set_initial_cwd(initial_cwd);
if let Some(d) = &config_data {
cli_factory.set_workspace_dir(d.member_dir.clone());
};
let open_docs = self.documents.documents(DocumentsFilter::OpenDiagnosable);
Ok(PrepareCacheResult {
cli_options,
cli_factory,
open_docs,
roots,
})

View file

@ -6,16 +6,18 @@ use dashmap::DashMap;
use deno_core::anyhow::anyhow;
use deno_core::error::AnyError;
use deno_core::serde_json;
use deno_core::url::Url;
use deno_npm::npm_rc::NpmRc;
use deno_semver::package::PackageNv;
use deno_semver::Version;
use once_cell::sync::Lazy;
use serde::Deserialize;
use super::search::PackageSearchApi;
use crate::args::npm_registry_url;
use crate::file_fetcher::CliFileFetcher;
use crate::file_fetcher::TextDecodedFile;
use crate::npm::NpmFetchResolver;
use crate::sys::CliSys;
#[derive(Debug)]
pub struct CliNpmSearchApi {
@ -111,6 +113,14 @@ fn parse_npm_search_response(source: &str) -> Result<Vec<String>, AnyError> {
Ok(objects.into_iter().map(|o| o.package.name).collect())
}
// this is buried here because generally you want to use the ResolvedNpmRc instead of this.
fn npm_registry_url() -> &'static Url {
static NPM_REGISTRY_DEFAULT_URL: Lazy<Url> =
Lazy::new(|| deno_resolver::npmrc::npm_registry_url(&CliSys::default()));
&NPM_REGISTRY_DEFAULT_URL
}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -28,20 +28,20 @@ use deno_resolver::npm::managed::NpmResolutionCell;
use deno_resolver::npm::CreateInNpmPkgCheckerOptions;
use deno_resolver::npm::DenoInNpmPackageChecker;
use deno_resolver::npm::NpmReqResolverOptions;
use deno_resolver::npmrc::create_default_npmrc;
use deno_resolver::DenoResolverOptions;
use deno_resolver::NodeAndNpmReqResolver;
use deno_runtime::deno_node::RealIsBuiltInNodeModuleChecker;
use deno_semver::jsr::JsrPackageReqReference;
use deno_semver::npm::NpmPackageReqReference;
use deno_semver::package::PackageNv;
use deno_semver::package::PackageReq;
use indexmap::IndexMap;
use node_resolver::DenoIsBuiltInNodeModuleChecker;
use node_resolver::NodeResolutionKind;
use node_resolver::ResolutionMode;
use super::cache::LspCache;
use super::jsr::JsrCacheResolver;
use crate::args::create_default_npmrc;
use crate::args::CliLockfile;
use crate::args::LifecycleScriptsConfig;
use crate::args::NpmCachingStrategy;
@ -263,18 +263,19 @@ impl LspScopeResolver {
}
CliNpmResolver::Managed(managed_npm_resolver) => {
CliNpmResolverCreateOptions::Managed({
let sys = CliSys::default();
let npmrc = self
.config_data
.as_ref()
.and_then(|d| d.npmrc.clone())
.unwrap_or_else(create_default_npmrc);
.unwrap_or_else(|| Arc::new(create_default_npmrc(&sys)));
let npm_cache_dir = Arc::new(NpmCacheDir::new(
&CliSys::default(),
&sys,
managed_npm_resolver.global_cache_root_path().to_path_buf(),
npmrc.get_all_known_registries_urls(),
));
CliManagedNpmResolverCreateOptions {
sys: CliSys::default(),
sys,
npm_cache_dir,
maybe_node_modules_path: managed_npm_resolver
.root_node_modules_path()
@ -710,7 +711,7 @@ impl<'a> ResolverFactory<'a> {
let npmrc = self
.config_data
.and_then(|d| d.npmrc.clone())
.unwrap_or_else(create_default_npmrc);
.unwrap_or_else(|| Arc::new(create_default_npmrc(&sys)));
let npm_cache_dir = Arc::new(NpmCacheDir::new(
&sys,
cache.deno_dir().npm_folder_path(),
@ -917,7 +918,7 @@ impl<'a> ResolverFactory<'a> {
let npm_resolver = self.services.npm_resolver.as_ref()?;
Some(Arc::new(CliNodeResolver::new(
self.in_npm_pkg_checker().clone(),
RealIsBuiltInNodeModuleChecker,
DenoIsBuiltInNodeModuleChecker,
npm_resolver.clone(),
self.pkg_json_resolver.clone(),
self.sys.clone(),

View file

@ -134,7 +134,7 @@ async fn run_subcommand(flags: Arc<Flags>) -> Result<i32, AnyError> {
tools::check::check(flags, check_flags).await
}),
DenoSubcommand::Clean => spawn_subcommand(async move {
tools::clean::clean()
tools::clean::clean(flags)
}),
DenoSubcommand::Compile(compile_flags) => spawn_subcommand(async {
tools::compile::compile(flags, compile_flags).await

View file

@ -49,10 +49,10 @@ use deno_runtime::code_cache;
use deno_runtime::deno_node::create_host_defined_options;
use deno_runtime::deno_node::ops::require::UnableToGetCwdError;
use deno_runtime::deno_node::NodeRequireLoader;
use deno_runtime::deno_node::RealIsBuiltInNodeModuleChecker;
use deno_runtime::deno_permissions::PermissionsContainer;
use deno_semver::npm::NpmPackageReqReference;
use node_resolver::errors::ClosestPkgJsonError;
use node_resolver::DenoIsBuiltInNodeModuleChecker;
use node_resolver::InNpmPackageChecker;
use node_resolver::NodeResolutionKind;
use node_resolver::ResolutionMode;
@ -91,7 +91,7 @@ use crate::util::text_encoding::source_map_from_code;
pub type CliNpmModuleLoader = deno_lib::loader::NpmModuleLoader<
CliCjsCodeAnalyzer,
DenoInNpmPackageChecker,
RealIsBuiltInNodeModuleChecker,
DenoIsBuiltInNodeModuleChecker,
CliNpmResolver,
CliSys,
>;

View file

@ -9,11 +9,11 @@ use deno_error::JsErrorBox;
use deno_graph::ParsedSourceStore;
use deno_resolver::npm::DenoInNpmPackageChecker;
use deno_runtime::deno_fs;
use deno_runtime::deno_node::RealIsBuiltInNodeModuleChecker;
use node_resolver::analyze::CjsAnalysis as ExtNodeCjsAnalysis;
use node_resolver::analyze::CjsAnalysisExports;
use node_resolver::analyze::CjsCodeAnalyzer;
use node_resolver::analyze::NodeCodeTranslator;
use node_resolver::DenoIsBuiltInNodeModuleChecker;
use serde::Deserialize;
use serde::Serialize;
@ -27,7 +27,7 @@ use crate::sys::CliSys;
pub type CliNodeCodeTranslator = NodeCodeTranslator<
CliCjsCodeAnalyzer,
DenoInNpmPackageChecker,
RealIsBuiltInNodeModuleChecker,
DenoIsBuiltInNodeModuleChecker,
CliNpmResolver,
CliSys,
>;

View file

@ -18,8 +18,8 @@ use deno_resolver::sloppy_imports::SloppyImportsCachedFs;
use deno_resolver::sloppy_imports::SloppyImportsResolver;
use deno_runtime::colors;
use deno_runtime::deno_node::is_builtin_node_module;
use deno_runtime::deno_node::RealIsBuiltInNodeModuleChecker;
use deno_semver::package::PackageReq;
use node_resolver::DenoIsBuiltInNodeModuleChecker;
use node_resolver::NodeResolutionKind;
use node_resolver::ResolutionMode;
@ -40,14 +40,14 @@ pub type CliSloppyImportsResolver =
SloppyImportsResolver<CliSloppyImportsCachedFs>;
pub type CliDenoResolver = deno_resolver::DenoResolver<
DenoInNpmPackageChecker,
RealIsBuiltInNodeModuleChecker,
DenoIsBuiltInNodeModuleChecker,
CliNpmResolver,
CliSloppyImportsCachedFs,
CliSys,
>;
pub type CliNpmReqResolver = deno_resolver::npm::NpmReqResolver<
DenoInNpmPackageChecker,
RealIsBuiltInNodeModuleChecker,
DenoIsBuiltInNodeModuleChecker,
CliNpmResolver,
CliSys,
>;

View file

@ -26,14 +26,14 @@ deno_runtime = { workspace = true, features = ["include_js_files_for_snapshottin
deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] }
[dependencies]
deno_cache_dir.workspace = true
deno_config.workspace = true
deno_cache_dir = { workspace = true, features = ["sync"] }
deno_config = { workspace = true, features = ["sync", "workspace"] }
deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] }
deno_error.workspace = true
deno_lib.workspace = true
deno_media_type = { workspace = true, features = ["data_url", "decoding"] }
deno_npm.workspace = true
deno_package_json.workspace = true
deno_package_json = { workspace = true, features = ["sync"] }
deno_path_util.workspace = true
deno_resolver = { workspace = true, features = ["sync"] }
deno_runtime = { workspace = true, features = ["include_js_files_for_snapshotting"] }

View file

@ -11,10 +11,10 @@ use deno_media_type::MediaType;
use deno_resolver::npm::DenoInNpmPackageChecker;
use deno_resolver::npm::NpmReqResolver;
use deno_runtime::deno_fs::FileSystem;
use deno_runtime::deno_node::RealIsBuiltInNodeModuleChecker;
use node_resolver::analyze::CjsAnalysis;
use node_resolver::analyze::CjsAnalysisExports;
use node_resolver::analyze::NodeCodeTranslator;
use node_resolver::DenoIsBuiltInNodeModuleChecker;
use crate::binary::StandaloneModules;
use crate::file_system::DenoRtSys;
@ -25,14 +25,14 @@ pub type DenoRtNpmResolver = deno_resolver::npm::NpmResolver<DenoRtSys>;
pub type DenoRtNpmModuleLoader = NpmModuleLoader<
CjsCodeAnalyzer,
DenoInNpmPackageChecker,
RealIsBuiltInNodeModuleChecker,
DenoIsBuiltInNodeModuleChecker,
DenoRtNpmResolver,
DenoRtSys,
>;
pub type DenoRtNodeCodeTranslator = NodeCodeTranslator<
CjsCodeAnalyzer,
DenoInNpmPackageChecker,
RealIsBuiltInNodeModuleChecker,
DenoIsBuiltInNodeModuleChecker,
DenoRtNpmResolver,
DenoRtSys,
>;
@ -43,7 +43,7 @@ pub type DenoRtNodeResolver = deno_runtime::deno_node::NodeResolver<
>;
pub type DenoRtNpmReqResolver = NpmReqResolver<
DenoInNpmPackageChecker,
RealIsBuiltInNodeModuleChecker,
DenoIsBuiltInNodeModuleChecker,
DenoRtNpmResolver,
DenoRtSys,
>;

View file

@ -63,7 +63,6 @@ use deno_runtime::code_cache::CodeCache;
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::RealIsBuiltInNodeModuleChecker;
use deno_runtime::deno_permissions::Permissions;
use deno_runtime::deno_permissions::PermissionsContainer;
use deno_runtime::deno_tls::rustls::RootCertStore;
@ -75,6 +74,7 @@ use deno_runtime::WorkerLogLevel;
use deno_semver::npm::NpmPackageReqReference;
use node_resolver::analyze::NodeCodeTranslator;
use node_resolver::errors::ClosestPkgJsonError;
use node_resolver::DenoIsBuiltInNodeModuleChecker;
use node_resolver::NodeResolutionKind;
use node_resolver::NodeResolver;
use node_resolver::PackageJsonResolver;
@ -760,7 +760,7 @@ pub async fn run(
let has_node_modules_dir = npm_resolver.root_node_modules_path().is_some();
let node_resolver = Arc::new(NodeResolver::new(
in_npm_pkg_checker.clone(),
RealIsBuiltInNodeModuleChecker,
DenoIsBuiltInNodeModuleChecker,
npm_resolver.clone(),
pkg_json_resolver.clone(),
sys.clone(),

View file

@ -1,14 +1,15 @@
// Copyright 2018-2025 the Deno authors. MIT license.
use std::path::Path;
use std::sync::Arc;
use deno_core::anyhow::Context;
use deno_core::error::AnyError;
use crate::cache::DenoDir;
use crate::args::Flags;
use crate::colors;
use crate::display;
use crate::sys::CliSys;
use crate::factory::CliFactory;
use crate::util::progress_bar::ProgressBar;
use crate::util::progress_bar::ProgressBarStyle;
use crate::util::progress_bar::ProgressMessagePrompt;
@ -29,8 +30,9 @@ impl CleanState {
}
}
pub fn clean() -> Result<(), AnyError> {
let deno_dir = DenoDir::new(CliSys::default(), None)?;
pub fn clean(flags: Arc<Flags>) -> Result<(), AnyError> {
let factory = CliFactory::from_flags(flags);
let deno_dir = factory.deno_dir()?;
if deno_dir.root.exists() {
let no_of_files = walkdir::WalkDir::new(&deno_dir.root).into_iter().count();
let progress_bar = ProgressBar::new(ProgressBarStyle::ProgressBars);

View file

@ -59,7 +59,8 @@ pub async fn format(
fmt_flags: FmtFlags,
) -> Result<(), AnyError> {
if fmt_flags.is_stdin() {
let cli_options = CliOptions::from_flags(&CliSys::default(), flags)?;
let factory = CliFactory::from_flags(flags);
let cli_options = factory.cli_options()?;
let start_dir = &cli_options.start_dir;
let fmt_config = start_dir
.to_fmt_config(FilePatterns::new_with_base(start_dir.dir_path()))?;

View file

@ -50,7 +50,7 @@ pub async fn info(
let npm_resolver = factory.npm_resolver().await?;
let maybe_lockfile = cli_options.maybe_lockfile();
let resolver = factory.workspace_resolver().await?.clone();
let npmrc = cli_options.npmrc();
let npmrc = factory.npmrc()?;
let node_resolver = factory.node_resolver().await?;
let cwd_url =
@ -185,7 +185,7 @@ fn print_cache_info(
) -> Result<(), AnyError> {
let dir = factory.deno_dir()?;
#[allow(deprecated)]
let modules_cache = factory.global_http_cache()?.get_global_cache_location();
let modules_cache = factory.global_http_cache()?.dir_path();
let npm_cache = factory.deno_dir()?.npm_folder_path();
let typescript_cache = &dir.gen_cache.location;
let registry_cache = dir.registries_folder_path();

View file

@ -306,9 +306,7 @@ async fn install_local(
InstallFlagsLocal::TopLevel => {
let factory = CliFactory::from_flags(flags);
// surface any errors in the package.json
if let Some(npm_installer) = factory.npm_installer_if_managed()? {
npm_installer.ensure_no_pkg_json_dep_errors()?;
}
factory.npm_installer()?.ensure_no_pkg_json_dep_errors()?;
crate::tools::registry::cache_top_level_deps(&factory, None).await?;
if let Some(lockfile) = factory.cli_options()?.maybe_lockfile() {
@ -375,7 +373,7 @@ async fn install_global(
log::Level::Trace,
);
let npmrc = factory.cli_options().unwrap().npmrc();
let npmrc = factory.npmrc()?;
let deps_file_fetcher = Arc::new(deps_file_fetcher);
let jsr_resolver = Arc::new(JsrFetchResolver::new(deps_file_fetcher.clone()));

View file

@ -423,7 +423,7 @@ pub async fn add(
log::Level::Trace,
);
let npmrc = cli_factory.cli_options().unwrap().npmrc();
let npmrc = cli_factory.npmrc()?;
let deps_file_fetcher = Arc::new(deps_file_fetcher);
let jsr_resolver = Arc::new(JsrFetchResolver::new(deps_file_fetcher.clone()));

View file

@ -19,15 +19,13 @@ pub async fn cache_top_level_deps(
factory: &CliFactory,
jsr_resolver: Option<Arc<crate::jsr::JsrFetchResolver>>,
) -> Result<(), AnyError> {
let npm_installer = factory.npm_installer_if_managed()?;
let npm_installer = factory.npm_installer()?;
let cli_options = factory.cli_options()?;
if let Some(npm_installer) = &npm_installer {
npm_installer
.ensure_top_level_package_json_install()
.await?;
if let Some(lockfile) = cli_options.maybe_lockfile() {
lockfile.error_if_changed()?;
}
npm_installer
.ensure_top_level_package_json_install()
.await?;
if let Some(lockfile) = cli_options.maybe_lockfile() {
lockfile.error_if_changed()?;
}
// cache as many entries in the import map as we can
let resolver = factory.workspace_resolver().await?;
@ -141,9 +139,7 @@ pub async fn cache_top_level_deps(
maybe_graph_error = graph_builder.graph_roots_valid(graph, &roots);
}
if let Some(npm_installer) = &npm_installer {
npm_installer.cache_packages(PackageCaching::All).await?;
}
npm_installer.cache_packages(PackageCaching::All).await?;
maybe_graph_error?;

View file

@ -194,7 +194,7 @@ pub async fn outdated(
let file_fetcher = Arc::new(file_fetcher);
let npm_fetch_resolver = Arc::new(NpmFetchResolver::new(
file_fetcher.clone(),
cli_options.npmrc().clone(),
factory.npmrc()?.clone(),
));
let jsr_fetch_resolver =
Arc::new(JsrFetchResolver::new(file_fetcher.clone()));

View file

@ -15,6 +15,7 @@ use deno_core::v8;
use deno_core::v8::ExternalReference;
use deno_error::JsErrorBox;
use node_resolver::errors::ClosestPkgJsonError;
use node_resolver::DenoIsBuiltInNodeModuleChecker;
use node_resolver::InNpmPackageChecker;
use node_resolver::IsBuiltInNodeModuleChecker;
use node_resolver::NpmPackageFolderResolver;
@ -25,24 +26,25 @@ extern crate libz_sys as zlib;
mod global;
pub mod ops;
mod polyfill;
pub use deno_package_json::PackageJson;
use deno_permissions::PermissionCheckError;
pub use node_resolver::PathClean;
pub use node_resolver::DENO_SUPPORTED_BUILTIN_NODE_MODULES as SUPPORTED_BUILTIN_NODE_MODULES;
pub use ops::ipc::ChildPipeFd;
use ops::vm;
pub use ops::vm::create_v8_context;
pub use ops::vm::init_global_template;
pub use ops::vm::ContextInitMode;
pub use ops::vm::VM_CONTEXT_INDEX;
pub use polyfill::is_builtin_node_module;
pub use polyfill::SUPPORTED_BUILTIN_NODE_MODULES;
pub use polyfill::SUPPORTED_BUILTIN_NODE_MODULES_WITH_PREFIX;
use crate::global::global_object_middleware;
use crate::global::global_template_middleware;
pub fn is_builtin_node_module(module_name: &str) -> bool {
DenoIsBuiltInNodeModuleChecker.is_builtin_node_module(module_name)
}
pub trait NodePermissions {
fn check_net_url(
&mut self,
@ -813,16 +815,6 @@ deno_core::extension!(deno_node,
},
);
#[derive(Debug)]
pub struct RealIsBuiltInNodeModuleChecker;
impl IsBuiltInNodeModuleChecker for RealIsBuiltInNodeModuleChecker {
#[inline]
fn is_builtin_node_module(&self, specifier: &str) -> bool {
is_builtin_node_module(specifier)
}
}
pub trait ExtNodeSys:
sys_traits::BaseFsCanonicalize
+ sys_traits::BaseFsMetadata
@ -837,7 +829,7 @@ impl ExtNodeSys for sys_traits::impls::RealSys {}
pub type NodeResolver<TInNpmPackageChecker, TNpmPackageFolderResolver, TSys> =
node_resolver::NodeResolver<
TInNpmPackageChecker,
RealIsBuiltInNodeModuleChecker,
DenoIsBuiltInNodeModuleChecker,
TNpmPackageFolderResolver,
TSys,
>;

View file

@ -1,100 +0,0 @@
// Copyright 2018-2025 the Deno authors. MIT license.
/// e.g. `is_builtin_node_module("assert")`
pub fn is_builtin_node_module(module_name: &str) -> bool {
SUPPORTED_BUILTIN_NODE_MODULES
.iter()
.any(|m| *m == module_name)
}
macro_rules! generate_builtin_node_module_lists {
($( $module_name:literal ,)+) => {
pub static SUPPORTED_BUILTIN_NODE_MODULES: &[&str] = &[
$(
$module_name,
)+
];
pub static SUPPORTED_BUILTIN_NODE_MODULES_WITH_PREFIX: &[&str] = &[
$(
concat!("node:", $module_name),
)+
];
};
}
// NOTE(bartlomieju): keep this list in sync with `ext/node/polyfills/01_require.js`
generate_builtin_node_module_lists! {
"_http_agent",
"_http_common",
"_http_outgoing",
"_http_server",
"_stream_duplex",
"_stream_passthrough",
"_stream_readable",
"_stream_transform",
"_stream_writable",
"_tls_common",
"_tls_wrap",
"assert",
"assert/strict",
"async_hooks",
"buffer",
"child_process",
"cluster",
"console",
"constants",
"crypto",
"dgram",
"diagnostics_channel",
"dns",
"dns/promises",
"domain",
"events",
"fs",
"fs/promises",
"http",
"http2",
"https",
"inspector",
"inspector/promises",
"module",
"net",
"os",
"path",
"path/posix",
"path/win32",
"perf_hooks",
"process",
"punycode",
"querystring",
"readline",
"readline/promises",
"repl",
"stream",
"stream/consumers",
"stream/promises",
"stream/web",
"string_decoder",
"sys",
"test",
"timers",
"timers/promises",
"tls",
"tty",
"url",
"util",
"util/types",
"v8",
"vm",
"wasi",
"worker_threads",
"zlib",
}
#[test]
fn test_builtins_are_sorted() {
let mut builtins_list = SUPPORTED_BUILTIN_NODE_MODULES.to_vec();
builtins_list.sort();
assert_eq!(SUPPORTED_BUILTIN_NODE_MODULES, builtins_list);
}

View file

@ -14,10 +14,11 @@ description = "Deno resolution algorithm"
path = "lib.rs"
[features]
sync = ["dashmap", "deno_package_json/sync", "node_resolver/sync"]
sync = ["dashmap", "deno_package_json/sync", "node_resolver/sync", "deno_config/sync", "deno_cache_dir/sync"]
[dependencies]
anyhow.workspace = true
async-once-cell.workspace = true
async-trait.workspace = true
base32.workspace = true
boxed_error.workspace = true
@ -30,8 +31,11 @@ deno_npm.workspace = true
deno_package_json.workspace = true
deno_path_util.workspace = true
deno_semver.workspace = true
deno_terminal.workspace = true
futures.workspace = true
log.workspace = true
node_resolver.workspace = true
once_cell.workspace = true
parking_lot.workspace = true
sys_traits.workspace = true
thiserror.workspace = true

893
resolvers/deno/factory.rs Normal file
View file

@ -0,0 +1,893 @@
// 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_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 {
options,
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(),
}
}
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()))
})
}
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,
)
}
}

View file

@ -13,7 +13,6 @@ use deno_config::workspace::MappedResolutionError;
use deno_config::workspace::WorkspaceResolvePkgJsonFolderError;
use deno_config::workspace::WorkspaceResolver;
use deno_error::JsError;
use deno_npm::npm_rc::ResolvedNpmRc;
use deno_package_json::PackageJsonDepValue;
use deno_package_json::PackageJsonDepValueParseError;
use deno_semver::npm::NpmPackageReqReference;
@ -43,16 +42,15 @@ use thiserror::Error;
use url::Url;
pub mod cjs;
pub mod factory;
pub mod npm;
pub mod npmrc;
pub mod sloppy_imports;
mod sync;
#[allow(clippy::disallowed_types)]
pub type WorkspaceResolverRc = crate::sync::MaybeArc<WorkspaceResolver>;
#[allow(clippy::disallowed_types)]
pub(crate) type ResolvedNpmRcRc = crate::sync::MaybeArc<ResolvedNpmRc>;
#[allow(clippy::disallowed_types)]
pub(crate) type NpmCacheDirRc = crate::sync::MaybeArc<NpmCacheDir>;
@ -148,6 +146,33 @@ pub struct DenoResolverOptions<
pub maybe_vendor_dir: Option<&'a PathBuf>,
}
#[allow(clippy::disallowed_types)]
pub type DenoResolverRc<
TInNpmPackageChecker,
TIsBuiltInNodeModuleChecker,
TNpmPackageFolderResolver,
TSloppyImportResolverFs,
TSys,
> = crate::sync::MaybeArc<
DenoResolver<
TInNpmPackageChecker,
TIsBuiltInNodeModuleChecker,
TNpmPackageFolderResolver,
TSloppyImportResolverFs,
TSys,
>,
>;
/// Helper type for a DenoResolverRc that has the implementations
/// used by the Deno CLI.
pub type DefaultDenoResolverRc<TSys> = DenoResolverRc<
npm::DenoInNpmPackageChecker,
node_resolver::DenoIsBuiltInNodeModuleChecker,
npm::NpmResolver<TSys>,
sloppy_imports::SloppyImportsCachedFs<TSys>,
TSys,
>;
/// A resolver that takes care of resolution, taking into account loaded
/// import map, JSX settings.
#[derive(Debug)]

View file

@ -17,7 +17,7 @@ use url::Url;
use super::resolution::NpmResolutionCellRc;
use super::NpmCacheDirRc;
use crate::ResolvedNpmRcRc;
use crate::npmrc::ResolvedNpmRcRc;
/// Resolves packages from the global npm cache.
#[derive(Debug)]

View file

@ -28,8 +28,8 @@ use self::global::GlobalNpmPackageResolver;
use self::local::LocalNpmPackageResolver;
pub use self::resolution::NpmResolutionCell;
pub use self::resolution::NpmResolutionCellRc;
use crate::npmrc::ResolvedNpmRcRc;
use crate::NpmCacheDirRc;
use crate::ResolvedNpmRcRc;
#[derive(Debug, thiserror::Error, deno_error::JsError)]
pub enum ResolvePkgFolderFromDenoModuleError {
@ -268,6 +268,7 @@ impl InNpmPackageChecker for ManagedInNpmPackageChecker {
}
}
#[derive(Debug)]
pub struct ManagedInNpmPkgCheckerCreateOptions<'a> {
pub root_cache_dir_url: &'a Url,
pub maybe_node_modules_path: Option<&'a Path>,

View file

@ -50,6 +50,7 @@ mod byonm;
mod local;
pub mod managed;
#[derive(Debug)]
pub enum CreateInNpmPkgCheckerOptions<'a> {
Managed(ManagedInNpmPkgCheckerCreateOptions<'a>),
Byonm,

200
resolvers/deno/npmrc.rs Normal file
View file

@ -0,0 +1,200 @@
// 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_config::workspace::Workspace;
use deno_npm::npm_rc::NpmRc;
use deno_npm::npm_rc::ResolvedNpmRc;
use sys_traits::EnvHomeDir;
use sys_traits::EnvVar;
use sys_traits::FsRead;
use thiserror::Error;
use url::Url;
#[allow(clippy::disallowed_types)]
pub type ResolvedNpmRcRc = crate::sync::MaybeArc<ResolvedNpmRc>;
#[derive(Debug, Boxed)]
pub struct NpmRcDiscoverError(pub Box<NpmRcDiscoverErrorKind>);
#[derive(Debug, Error)]
pub enum NpmRcDiscoverErrorKind {
#[error(transparent)]
Load(#[from] NpmRcLoadError),
#[error(transparent)]
Parse(#[from] NpmRcParseError),
#[error(transparent)]
Resolve(#[from] NpmRcOptionsResolveError),
#[error(transparent)]
UrlToFilePath(#[from] deno_path_util::UrlToFilePathError),
}
#[derive(Debug, Error)]
#[error("Error loading .npmrc at {}.", path.display())]
pub struct NpmRcLoadError {
path: PathBuf,
#[source]
source: std::io::Error,
}
#[derive(Debug, Error)]
#[error("Failed to parse .npmrc at {}.", path.display())]
pub struct NpmRcParseError {
path: PathBuf,
#[source]
source: std::io::Error,
}
#[derive(Debug, Error)]
#[error("Failed to resolve .npmrc options at {}.", path.display())]
pub struct NpmRcOptionsResolveError {
path: PathBuf,
#[source]
source: deno_npm::npm_rc::ResolveError,
}
/// Discover `.npmrc` file - currently we only support it next to `package.json`,
/// next to `deno.json`, or in the user's home directory.
///
/// In the future we will need to support it in the global directory
/// as per https://docs.npmjs.com/cli/v10/configuring-npm/npmrc#files.
pub fn discover_npmrc_from_workspace<TSys: EnvVar + EnvHomeDir + FsRead>(
sys: &TSys,
workspace: &Workspace,
) -> Result<(ResolvedNpmRc, Option<PathBuf>), NpmRcDiscoverError> {
let root_folder = workspace.root_folder_configs();
discover_npmrc(
sys,
root_folder.pkg_json.as_ref().map(|p| p.path.clone()),
match &root_folder.deno_json {
Some(cf) if cf.specifier.scheme() == "file" => {
Some(deno_path_util::url_to_file_path(&cf.specifier)?)
}
_ => None,
},
)
}
fn discover_npmrc<TSys: EnvVar + EnvHomeDir + FsRead>(
sys: &TSys,
maybe_package_json_path: Option<PathBuf>,
maybe_deno_json_path: Option<PathBuf>,
) -> Result<(ResolvedNpmRc, Option<PathBuf>), NpmRcDiscoverError> {
const NPMRC_NAME: &str = ".npmrc";
fn try_to_read_npmrc(
sys: &impl FsRead,
dir: &Path,
) -> Result<Option<(Cow<'static, str>, PathBuf)>, NpmRcLoadError> {
let path = dir.join(NPMRC_NAME);
let maybe_source = match sys.fs_read_to_string(&path) {
Ok(source) => Some(source),
Err(err) if err.kind() == std::io::ErrorKind::NotFound => None,
Err(err) => return Err(NpmRcLoadError { path, source: err }),
};
Ok(maybe_source.map(|source| (source, path)))
}
fn try_to_parse_npmrc(
sys: &impl EnvVar,
source: &str,
path: &Path,
) -> Result<ResolvedNpmRc, NpmRcDiscoverError> {
let npmrc = NpmRc::parse(source, &|name| sys.env_var(name).ok()).map_err(
|source| {
NpmRcParseError {
path: path.to_path_buf(),
// todo(dsherret): use source directly here once it's no longer an internal type
source: std::io::Error::new(std::io::ErrorKind::InvalidData, source),
}
},
)?;
let resolved =
npmrc
.as_resolved(&npm_registry_url(sys))
.map_err(|source| NpmRcOptionsResolveError {
path: path.to_path_buf(),
source,
})?;
log::debug!(".npmrc found at: '{}'", path.display());
Ok(resolved)
}
// 1. Try `.npmrc` next to `package.json`
if let Some(package_json_path) = maybe_package_json_path {
if let Some(package_json_dir) = package_json_path.parent() {
if let Some((source, path)) = try_to_read_npmrc(sys, package_json_dir)? {
return try_to_parse_npmrc(sys, &source, &path)
.map(|r| (r, Some(path)));
}
}
}
// 2. Try `.npmrc` next to `deno.json(c)`
if let Some(deno_json_path) = maybe_deno_json_path {
if let Some(deno_json_dir) = deno_json_path.parent() {
if let Some((source, path)) = try_to_read_npmrc(sys, deno_json_dir)? {
return try_to_parse_npmrc(sys, &source, &path)
.map(|r| (r, Some(path)));
}
}
}
// TODO(bartlomieju): update to read both files - one in the project root and one and
// home dir and then merge them.
// 3. Try `.npmrc` in the user's home directory
if let Some(home_dir) = sys.env_home_dir() {
match try_to_read_npmrc(sys, &home_dir) {
Ok(Some((source, path))) => {
return try_to_parse_npmrc(sys, &source, &path)
.map(|r| (r, Some(path)));
}
Ok(None) => {}
Err(err) if err.source.kind() == std::io::ErrorKind::PermissionDenied => {
log::debug!(
"Skipping .npmrc in home directory due to permission denied error. {:#}",
err
);
}
Err(err) => {
return Err(err.into());
}
}
}
log::debug!("No .npmrc file found");
Ok((create_default_npmrc(sys), None))
}
pub fn create_default_npmrc(sys: &impl EnvVar) -> ResolvedNpmRc {
ResolvedNpmRc {
default_config: deno_npm::npm_rc::RegistryConfigWithUrl {
registry_url: npm_registry_url(sys).clone(),
config: Default::default(),
},
scopes: Default::default(),
registry_configs: Default::default(),
}
}
pub fn npm_registry_url(sys: &impl EnvVar) -> Url {
let env_var_name = "NPM_CONFIG_REGISTRY";
if let Ok(registry_url) = sys.env_var(env_var_name) {
// ensure there is a trailing slash for the directory
let registry_url = format!("{}/", registry_url.trim_end_matches('/'));
match Url::parse(&registry_url) {
Ok(url) => {
return url;
}
Err(err) => {
log::debug!("Invalid {} environment variable: {:#}", env_var_name, err,);
}
}
}
Url::parse("https://registry.npmjs.org").unwrap()
}

View file

@ -0,0 +1,103 @@
// Copyright 2018-2025 the Deno authors. MIT license.
pub trait IsBuiltInNodeModuleChecker: std::fmt::Debug {
/// e.g. `is_builtin_node_module("assert")`
fn is_builtin_node_module(&self, module_name: &str) -> bool;
}
/// An implementation of IsBuiltInNodeModuleChecker that uses
/// the list of built-in node_modules that are supported by Deno
/// in the `deno_node` crate (ext/node).
#[derive(Debug)]
pub struct DenoIsBuiltInNodeModuleChecker;
impl IsBuiltInNodeModuleChecker for DenoIsBuiltInNodeModuleChecker {
#[inline(always)]
fn is_builtin_node_module(&self, module_name: &str) -> bool {
DENO_SUPPORTED_BUILTIN_NODE_MODULES
.binary_search(&module_name)
.is_ok()
}
}
/// Collection of built-in node_modules supported by Deno.
pub static DENO_SUPPORTED_BUILTIN_NODE_MODULES: &[&str] = &[
// NOTE(bartlomieju): keep this list in sync with `ext/node/polyfills/01_require.js`
"_http_agent",
"_http_common",
"_http_outgoing",
"_http_server",
"_stream_duplex",
"_stream_passthrough",
"_stream_readable",
"_stream_transform",
"_stream_writable",
"_tls_common",
"_tls_wrap",
"assert",
"assert/strict",
"async_hooks",
"buffer",
"child_process",
"cluster",
"console",
"constants",
"crypto",
"dgram",
"diagnostics_channel",
"dns",
"dns/promises",
"domain",
"events",
"fs",
"fs/promises",
"http",
"http2",
"https",
"inspector",
"inspector/promises",
"module",
"net",
"os",
"path",
"path/posix",
"path/win32",
"perf_hooks",
"process",
"punycode",
"querystring",
"readline",
"readline/promises",
"repl",
"stream",
"stream/consumers",
"stream/promises",
"stream/web",
"string_decoder",
"sys",
"test",
"timers",
"timers/promises",
"tls",
"tty",
"url",
"util",
"util/types",
"v8",
"vm",
"wasi",
"worker_threads",
"zlib",
];
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_builtins_are_sorted() {
let mut builtins_list = DENO_SUPPORTED_BUILTIN_NODE_MODULES.to_vec();
builtins_list.sort();
assert_eq!(DENO_SUPPORTED_BUILTIN_NODE_MODULES, builtins_list);
}
}

View file

@ -4,6 +4,7 @@
#![deny(clippy::print_stdout)]
pub mod analyze;
mod builtin_modules;
pub mod errors;
mod npm;
mod package_json;
@ -12,6 +13,9 @@ mod resolution;
mod sync;
pub use builtin_modules::DenoIsBuiltInNodeModuleChecker;
pub use builtin_modules::IsBuiltInNodeModuleChecker;
pub use builtin_modules::DENO_SUPPORTED_BUILTIN_NODE_MODULES;
pub use deno_package_json::PackageJson;
pub use npm::InNpmPackageChecker;
pub use npm::NpmPackageFolderResolver;
@ -22,7 +26,6 @@ pub use path::PathClean;
pub use resolution::parse_npm_pkg_name;
pub use resolution::resolve_specifier_into_node_modules;
pub use resolution::ConditionsFromResolutionMode;
pub use resolution::IsBuiltInNodeModuleChecker;
pub use resolution::NodeResolution;
pub use resolution::NodeResolutionKind;
pub use resolution::NodeResolver;

View file

@ -47,6 +47,7 @@ use crate::errors::TypesNotFoundErrorData;
use crate::errors::UnsupportedDirImportError;
use crate::errors::UnsupportedEsmUrlSchemeError;
use crate::InNpmPackageChecker;
use crate::IsBuiltInNodeModuleChecker;
use crate::NpmPackageFolderResolver;
use crate::PackageJsonResolverRc;
use crate::PathClean;
@ -55,11 +56,12 @@ pub static DEFAULT_CONDITIONS: &[&str] = &["deno", "node", "import"];
pub static REQUIRE_CONDITIONS: &[&str] = &["require", "node"];
static TYPES_ONLY_CONDITIONS: &[&str] = &["types"];
type ConditionsFromResolutionModeFn = Box<
#[allow(clippy::disallowed_types)]
type ConditionsFromResolutionModeFn = crate::sync::MaybeArc<
dyn Fn(ResolutionMode) -> &'static [&'static str] + Send + Sync + 'static,
>;
#[derive(Default)]
#[derive(Default, Clone)]
pub struct ConditionsFromResolutionMode(Option<ConditionsFromResolutionModeFn>);
impl Debug for ConditionsFromResolutionMode {
@ -132,10 +134,6 @@ impl NodeResolution {
}
}
pub trait IsBuiltInNodeModuleChecker: std::fmt::Debug {
fn is_builtin_node_module(&self, specifier: &str) -> bool;
}
#[allow(clippy::disallowed_types)]
pub type NodeResolverRc<
TInNpmPackageChecker,

View file

@ -17,7 +17,7 @@ path = "lib.rs"
async-trait.workspace = true
base64.workspace = true
boxed_error.workspace = true
deno_cache_dir.workspace = true
deno_cache_dir = { workspace = true, features = ["sync"] }
deno_error = { workspace = true, features = ["serde", "serde_json", "tokio"] }
deno_npm.workspace = true
deno_path_util.workspace = true

View file

@ -65,7 +65,7 @@ deno_os.workspace = true
deno_path_util.workspace = true
deno_permissions.workspace = true
deno_process.workspace = true
deno_resolver.workspace = true
deno_resolver = { workspace = true, features = ["sync"] }
deno_telemetry.workspace = true
deno_terminal.workspace = true
deno_tls.workspace = true

View file

@ -41,6 +41,7 @@ itest!(require_resolve_url_paths {
cwd: Some("npm/require_resolve_url/"),
copy_temp_dir: Some("npm/require_resolve_url/"),
});
#[test]
fn parallel_downloading() {
let (out, _err) = util::run_and_collect_output_with_args(