1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-03 04:48:52 -05:00

fix(npm): ensure npm package downloaded once per run when using --reload (#16842)

This commit is contained in:
David Sherret 2022-11-27 13:25:08 -05:00 committed by GitHub
parent a4dfc6f955
commit fb04e87387
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 71 additions and 25 deletions

View file

@ -238,22 +238,20 @@ fn create_lsp_npm_resolver(
http_client: HttpClient, http_client: HttpClient,
) -> NpmPackageResolver { ) -> NpmPackageResolver {
let registry_url = RealNpmRegistryApi::default_url(); let registry_url = RealNpmRegistryApi::default_url();
// Use an "only" cache setting in order to make the
// user do an explicit "cache" command and prevent
// the cache from being filled with lots of packages while
// the user is typing.
let cache_setting = CacheSetting::Only;
let progress_bar = ProgressBar::default(); let progress_bar = ProgressBar::default();
let npm_cache = NpmCache::from_deno_dir( let npm_cache = NpmCache::from_deno_dir(
dir, dir,
cache_setting.clone(), // Use an "only" cache setting in order to make the
// user do an explicit "cache" command and prevent
// the cache from being filled with lots of packages while
// the user is typing.
CacheSetting::Only,
http_client.clone(), http_client.clone(),
progress_bar.clone(), progress_bar.clone(),
); );
let api = RealNpmRegistryApi::new( let api = RealNpmRegistryApi::new(
registry_url, registry_url,
npm_cache.clone(), npm_cache.clone(),
cache_setting,
http_client, http_client,
progress_bar, progress_bar,
); );

View file

@ -1,14 +1,17 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
use std::collections::HashSet;
use std::fs; use std::fs;
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc;
use deno_ast::ModuleSpecifier; use deno_ast::ModuleSpecifier;
use deno_core::anyhow::bail; use deno_core::anyhow::bail;
use deno_core::anyhow::Context; use deno_core::anyhow::Context;
use deno_core::error::custom_error; use deno_core::error::custom_error;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::parking_lot::Mutex;
use deno_core::url::Url; use deno_core::url::Url;
use crate::cache::DenoDir; use crate::cache::DenoDir;
@ -317,6 +320,8 @@ pub struct NpmCache {
cache_setting: CacheSetting, cache_setting: CacheSetting,
http_client: HttpClient, http_client: HttpClient,
progress_bar: ProgressBar, progress_bar: ProgressBar,
/// ensures a package is only downloaded once per run
previously_reloaded_packages: Arc<Mutex<HashSet<String>>>,
} }
impl NpmCache { impl NpmCache {
@ -331,6 +336,7 @@ impl NpmCache {
cache_setting, cache_setting,
http_client, http_client,
progress_bar, progress_bar,
previously_reloaded_packages: Default::default(),
} }
} }
@ -338,6 +344,26 @@ impl NpmCache {
self.readonly.clone() self.readonly.clone()
} }
pub fn cache_setting(&self) -> &CacheSetting {
&self.cache_setting
}
/// Checks if the cache should be used for the provided name and version.
/// NOTE: Subsequent calls for the same package will always return `true`
/// to ensure a package is only downloaded once per run of the CLI. This
/// prevents downloads from re-occurring when someone has `--reload` and
/// and imports a dynamic import that imports the same package again for example.
fn should_use_global_cache_for_package(
&self,
package: (&str, &NpmVersion),
) -> bool {
self.cache_setting.should_use_for_npm_package(package.0)
|| !self
.previously_reloaded_packages
.lock()
.insert(format!("{}@{}", package.0, package.1))
}
pub async fn ensure_package( pub async fn ensure_package(
&self, &self,
package: (&str, &NpmVersion), package: (&str, &NpmVersion),
@ -352,10 +378,6 @@ impl NpmCache {
}) })
} }
pub fn should_use_cache_for_npm_package(&self, package_name: &str) -> bool {
self.cache_setting.should_use_for_npm_package(package_name)
}
async fn ensure_package_inner( async fn ensure_package_inner(
&self, &self,
package: (&str, &NpmVersion), package: (&str, &NpmVersion),
@ -367,11 +389,11 @@ impl NpmCache {
package.1, package.1,
registry_url, registry_url,
); );
if package_folder.exists() if self.should_use_global_cache_for_package(package)
&& package_folder.exists()
// if this file exists, then the package didn't successfully extract // if this file exists, then the package didn't successfully extract
// the first time, or another process is currently extracting the zip file // the first time, or another process is currently extracting the zip file
&& !package_folder.join(NPM_PACKAGE_SYNC_LOCK_FILENAME).exists() && !package_folder.join(NPM_PACKAGE_SYNC_LOCK_FILENAME).exists()
&& self.should_use_cache_for_npm_package(package.0)
{ {
return Ok(()); return Ok(());
} else if self.cache_setting == CacheSetting::Only { } else if self.cache_setting == CacheSetting::Only {

View file

@ -2,6 +2,7 @@
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::HashMap; use std::collections::HashMap;
use std::collections::HashSet;
use std::fs; use std::fs;
use std::io::ErrorKind; use std::io::ErrorKind;
use std::path::PathBuf; use std::path::PathBuf;
@ -248,7 +249,6 @@ impl RealNpmRegistryApi {
pub fn new( pub fn new(
base_url: Url, base_url: Url,
cache: NpmCache, cache: NpmCache,
cache_setting: CacheSetting,
http_client: HttpClient, http_client: HttpClient,
progress_bar: ProgressBar, progress_bar: ProgressBar,
) -> Self { ) -> Self {
@ -256,7 +256,7 @@ impl RealNpmRegistryApi {
base_url, base_url,
cache, cache,
mem_cache: Default::default(), mem_cache: Default::default(),
cache_setting, previously_reloaded_packages: Default::default(),
http_client, http_client,
progress_bar, progress_bar,
})) }))
@ -286,7 +286,7 @@ struct RealNpmRegistryApiInner {
base_url: Url, base_url: Url,
cache: NpmCache, cache: NpmCache,
mem_cache: Mutex<HashMap<String, Option<Arc<NpmPackageInfo>>>>, mem_cache: Mutex<HashMap<String, Option<Arc<NpmPackageInfo>>>>,
cache_setting: CacheSetting, previously_reloaded_packages: Mutex<HashSet<String>>,
http_client: HttpClient, http_client: HttpClient,
progress_bar: ProgressBar, progress_bar: ProgressBar,
} }
@ -296,12 +296,16 @@ impl RealNpmRegistryApiInner {
&self, &self,
name: &str, name: &str,
) -> Result<Option<Arc<NpmPackageInfo>>, AnyError> { ) -> Result<Option<Arc<NpmPackageInfo>>, AnyError> {
let maybe_info = self.mem_cache.lock().get(name).cloned(); let maybe_maybe_info = self.mem_cache.lock().get(name).cloned();
if let Some(info) = maybe_info { if let Some(maybe_info) = maybe_maybe_info {
Ok(info) Ok(maybe_info)
} else { } else {
let mut maybe_package_info = None; let mut maybe_package_info = None;
if self.cache_setting.should_use_for_npm_package(name) { if self.cache.cache_setting().should_use_for_npm_package(name)
// if this has been previously reloaded, then try loading from the
// file system cache
|| !self.previously_reloaded_packages.lock().insert(name.to_string())
{
// attempt to load from the file cache // attempt to load from the file cache
maybe_package_info = self.load_file_cached_package_info(name); maybe_package_info = self.load_file_cached_package_info(name);
} }
@ -409,15 +413,14 @@ impl RealNpmRegistryApiInner {
&self, &self,
name: &str, name: &str,
) -> Result<Option<NpmPackageInfo>, AnyError> { ) -> Result<Option<NpmPackageInfo>, AnyError> {
if self.cache_setting == CacheSetting::Only { if *self.cache.cache_setting() == CacheSetting::Only {
return Err(custom_error( return Err(custom_error(
"NotCached", "NotCached",
format!( format!(
"An npm specifier not found in cache: \"{}\", --cached-only is specified.", "An npm specifier not found in cache: \"{}\", --cached-only is specified.",
name name
) )
) ));
);
} }
let package_url = self.get_package_url(name); let package_url = self.get_package_url(name);

View file

@ -291,7 +291,9 @@ async fn sync_resolution_with_fs(
get_package_folder_id_folder_name(&package.get_package_cache_folder_id()); get_package_folder_id_folder_name(&package.get_package_cache_folder_id());
let folder_path = deno_local_registry_dir.join(&folder_name); let folder_path = deno_local_registry_dir.join(&folder_name);
let initialized_file = folder_path.join(".initialized"); let initialized_file = folder_path.join(".initialized");
if !cache.should_use_cache_for_npm_package(&package.id.name) if !cache
.cache_setting()
.should_use_for_npm_package(&package.id.name)
|| !initialized_file.exists() || !initialized_file.exists()
{ {
let cache = cache.clone(); let cache = cache.clone();

View file

@ -225,7 +225,6 @@ impl ProcState {
let api = RealNpmRegistryApi::new( let api = RealNpmRegistryApi::new(
registry_url, registry_url,
npm_cache.clone(), npm_cache.clone(),
cli_options.cache_setting(),
http_client, http_client,
progress_bar.clone(), progress_bar.clone(),
); );

View file

@ -155,6 +155,13 @@ mod npm {
// http_server: true, // http_server: true,
// }); // });
itest!(dynamic_import_reload_same_package {
args: "run -A --reload npm/dynamic_import_reload_same_package/main.ts",
output: "npm/dynamic_import_reload_same_package/main.out",
envs: env_vars(),
http_server: true,
});
itest!(env_var_re_export_dev { itest!(env_var_re_export_dev {
args: "run --allow-read --allow-env --quiet npm/env_var_re_export/main.js", args: "run --allow-read --allow-env --quiet npm/env_var_re_export/main.js",
output_str: Some("dev\n"), output_str: Some("dev\n"),

View file

@ -0,0 +1,5 @@
Download http://localhost:4545/npm/registry/chalk
Download http://localhost:4545/npm/registry/chalk/chalk-5.0.1.tgz
Starting...
Ran other.
Finished...

View file

@ -0,0 +1,7 @@
import chalk from "npm:chalk@5";
console.log(chalk.green("Starting..."));
// non-analyzable
const importName = "./other.ts";
await import(importName);
console.log(chalk.green("Finished..."));

View file

@ -0,0 +1,3 @@
import chalk from "npm:chalk@5";
console.log(chalk.green("Ran other."));