diff --git a/cli/args/config_file.rs b/cli/args/config_file.rs index 61f7778d9e151b..06943b29e13d86 100644 --- a/cli/args/config_file.rs +++ b/cli/args/config_file.rs @@ -16,6 +16,7 @@ use deno_core::serde::Serializer; use deno_core::serde_json; use deno_core::serde_json::json; use deno_core::serde_json::Value; +use deno_core::url::Url; use deno_core::ModuleSpecifier; use indexmap::IndexMap; use std::borrow::Cow; @@ -664,6 +665,7 @@ pub struct ConfigFileJson { pub lock: Option, pub exclude: Option, pub node_modules_dir: Option, + pub npm_registry: Option, } #[derive(Clone, Debug)] @@ -859,6 +861,24 @@ impl ConfigFile { self.json.node_modules_dir } + pub fn to_npm_registry(&self) -> Option { + self.json.npm_registry.as_ref().and_then(|registry_url| { + let registry_url = format!("{}/", registry_url.trim_end_matches('/')); + match Url::parse(®istry_url) { + Ok(url) => { + return Some(url); + } + Err(err) => { + log::debug!( + "Invalid \"npmRegistry\" configuration option: {:#}", + err, + ); + } + } + None + }) + } + pub fn to_import_map_value(&self) -> Value { let mut value = serde_json::Map::with_capacity(2); if let Some(imports) = &self.json.imports { @@ -1329,7 +1349,8 @@ mod tests { "tasks": { "build": "deno run --allow-read --allow-write build.ts", "server": "deno run --allow-net --allow-read server.ts" - } + }, + "npmRegistry": "https://deno.land" }"#; let config_dir = ModuleSpecifier::parse("file:///deno/").unwrap(); let config_specifier = config_dir.join("tsconfig.json").unwrap(); @@ -1390,6 +1411,11 @@ mod tests { tasks_config["server"], "deno run --allow-net --allow-read server.ts" ); + + assert_eq!( + config_file.to_npm_registry().unwrap(), + Url::parse("https://deno.land/").unwrap() + ); } /// if either "include" or "exclude" is specified, "files" is ignored diff --git a/cli/args/mod.rs b/cli/args/mod.rs index 6dab0c973b1ed4..417fef01c6156a 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -655,6 +655,13 @@ impl CliOptions { &self.initial_cwd } + pub fn maybe_npm_registry(&self) -> Option { + self + .maybe_config_file + .as_ref() + .and_then(|f| f.to_npm_registry()) + } + pub fn maybe_config_file_specifier(&self) -> Option { self.maybe_config_file.as_ref().map(|f| f.specifier.clone()) } diff --git a/cli/factory.rs b/cli/factory.rs index 17d141be14c0d0..4168740e4827fc 100644 --- a/cli/factory.rs +++ b/cli/factory.rs @@ -46,6 +46,7 @@ use crate::worker::HasNodeSpecifierChecker; use deno_core::error::AnyError; use deno_core::parking_lot::Mutex; +use deno_core::url::Url; use deno_runtime::deno_fs; use deno_runtime::deno_node::analyze::NodeCodeTranslator; @@ -266,6 +267,17 @@ impl CliFactory { .get_or_init(|| self.options.maybe_lockfile()) } + fn npm_registry(&self) -> Url { + if let Some(url) = CliNpmRegistryApi::npm_registry_override() { + url.to_owned() + } else { + self + .options + .maybe_npm_registry() + .unwrap_or_else(|| CliNpmRegistryApi::default_npm_registry().to_owned()) + } + } + pub fn npm_cache(&self) -> Result<&Arc, AnyError> { self.services.npm_cache.get_or_try_init(|| { Ok(Arc::new(NpmCache::new( @@ -280,7 +292,7 @@ impl CliFactory { pub fn npm_api(&self) -> Result<&Arc, AnyError> { self.services.npm_api.get_or_try_init(|| { Ok(Arc::new(CliNpmRegistryApi::new( - CliNpmRegistryApi::default_url().to_owned(), + self.npm_registry(), self.npm_cache()?.clone(), self.http_client().clone(), self.text_only_progress_bar().clone(), @@ -317,7 +329,7 @@ impl CliFactory { fs.clone(), self.npm_cache()?.clone(), self.text_only_progress_bar(), - CliNpmRegistryApi::default_url().to_owned(), + self.npm_registry(), npm_resolution.clone(), self.options.node_modules_dir_path(), self.options.npm_system_info(), @@ -340,7 +352,7 @@ impl CliFactory { self.fs().clone(), self.npm_cache()?.clone(), self.text_only_progress_bar(), - CliNpmRegistryApi::default_url().to_owned(), + self.npm_registry(), self.npm_resolution().await?.clone(), // when an explicit path is provided here, it will create the // local node_modules variant of an npm fs resolver diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 7c4191c82db0db..347b53ccc75841 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -966,12 +966,13 @@ impl Inner { return; // no need to do anything } - let registry_url = CliNpmRegistryApi::default_url(); + let registry_url = self.npm_registry(); + let progress_bar = ProgressBar::new(ProgressBarStyle::TextOnly); (self.npm.api, self.npm.cache) = create_npm_api_and_cache( &deno_dir, self.http_client.clone(), - registry_url, + ®istry_url, &progress_bar, ); let maybe_snapshot = match self.maybe_lockfile() { @@ -988,7 +989,7 @@ impl Inner { }; (self.npm.resolver, self.npm.resolution) = create_npm_resolver_and_resolution( - registry_url, + ®istry_url, progress_bar, self.npm.api.clone(), self.npm.cache.clone(), @@ -1218,6 +1219,17 @@ impl Inner { self.maybe_config_file_info.as_ref().map(|c| &c.config_file) } + fn npm_registry(&self) -> Url { + if let Some(url) = CliNpmRegistryApi::npm_registry_override() { + url.to_owned() + } else { + self + .maybe_config_file() + .and_then(|f| f.to_npm_registry()) + .unwrap_or_else(|| CliNpmRegistryApi::default_npm_registry().clone()) + } + } + fn maybe_lockfile(&self) -> Option<&Arc>> { self .maybe_config_file_info diff --git a/cli/npm/registry.rs b/cli/npm/registry.rs index 40d7f62191964f..4f93304df815ce 100644 --- a/cli/npm/registry.rs +++ b/cli/npm/registry.rs @@ -34,31 +34,43 @@ use crate::util::sync::AtomicFlag; use super::cache::should_sync_download; use super::cache::NpmCache; -static NPM_REGISTRY_DEFAULT_URL: Lazy = Lazy::new(|| { +static NPM_REGISTRY_OVERRIDE: Lazy> = 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(®istry_url) { Ok(url) => { - return url; + return Some(url); } Err(err) => { log::debug!("Invalid {} environment variable: {:#}", env_var_name, err,); } } } - - Url::parse("https://registry.npmjs.org").unwrap() + None }); +static NPM_REGISTRY_DEFAULT_URL: Lazy = + Lazy::new(|| Url::parse("https://registry.npmjs.org").unwrap()); + #[derive(Debug)] pub struct CliNpmRegistryApi(Option>); impl CliNpmRegistryApi { - pub fn default_url() -> &'static Url { + pub fn npm_registry_override() -> &'static Option { + &NPM_REGISTRY_OVERRIDE + } + pub fn default_npm_registry() -> &'static Url { &NPM_REGISTRY_DEFAULT_URL } + pub fn default_url() -> &'static Url { + if let Some(url) = Self::npm_registry_override() { + url + } else { + Self::default_npm_registry() + } + } pub fn new( base_url: Url, diff --git a/cli/schemas/config-file.v1.json b/cli/schemas/config-file.v1.json index 73723c52bd9291..91bb63ed9d5d19 100644 --- a/cli/schemas/config-file.v1.json +++ b/cli/schemas/config-file.v1.json @@ -426,6 +426,10 @@ "description": "Enables or disables the use of a local node_modules folder for npm packages. Alternatively, use the `--node-modules-dir` or `--node-modules-dir=false` flag. Requires Deno 1.34 or later.", "type": "boolean" }, + "npmRegistry": { + "description": "The npm registry to use for npm specifiers.", + "type": "string" + }, "tasks": { "description": "Configuration for deno task", "type": "object",