summaryrefslogtreecommitdiff
path: root/cli
diff options
context:
space:
mode:
authorDavid Sherret <dsherret@users.noreply.github.com>2023-08-08 10:23:02 -0400
committerGitHub <noreply@github.com>2023-08-08 14:23:02 +0000
commit05f838a57cc5b5e7b262e1640baa4d93dfb0df28 (patch)
treef13f97b5988c537023c05033bf862c52e74d080c /cli
parenta037ed77a2b2dbef1d2c4175c0401738a13f13bd (diff)
refactor: use deno_cache_dir crate (#20092)
Uses https://github.com/denoland/deno_cache/pull/26
Diffstat (limited to 'cli')
-rw-r--r--cli/Cargo.toml1
-rw-r--r--cli/cache/disk_cache.rs2
-rw-r--r--cli/cache/http_cache/common.rs42
-rw-r--r--cli/cache/http_cache/global.rs296
-rw-r--r--cli/cache/http_cache/local.rs1425
-rw-r--r--cli/cache/http_cache/mod.rs77
-rw-r--r--cli/cache/mod.rs55
-rw-r--r--cli/factory.rs1
-rw-r--r--cli/file_fetcher.rs37
-rw-r--r--cli/lsp/completions.rs10
-rw-r--r--cli/lsp/diagnostics.rs14
-rw-r--r--cli/lsp/documents.rs6
-rw-r--r--cli/lsp/language_server.rs10
-rw-r--r--cli/lsp/registries.rs5
-rw-r--r--cli/lsp/tsc.rs11
15 files changed, 123 insertions, 1869 deletions
diff --git a/cli/Cargo.toml b/cli/Cargo.toml
index 7b7066db7..4896f240c 100644
--- a/cli/Cargo.toml
+++ b/cli/Cargo.toml
@@ -46,6 +46,7 @@ winres.workspace = true
[dependencies]
deno_ast = { workspace = true, features = ["bundler", "cjs", "codegen", "dep_graph", "module_specifier", "proposal", "react", "sourcemap", "transforms", "typescript", "view", "visit"] }
+deno_cache_dir = "=0.5.0"
deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] }
deno_doc = "=0.64.0"
deno_emit = "=0.25.0"
diff --git a/cli/cache/disk_cache.rs b/cli/cache/disk_cache.rs
index 6950c056a..1375df6dc 100644
--- a/cli/cache/disk_cache.rs
+++ b/cli/cache/disk_cache.rs
@@ -1,9 +1,9 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-use super::http_cache::url_to_filename;
use super::CACHE_PERM;
use crate::util::fs::atomic_write_file;
+use deno_cache_dir::url_to_filename;
use deno_core::url::Host;
use deno_core::url::Url;
use std::ffi::OsStr;
diff --git a/cli/cache/http_cache/common.rs b/cli/cache/http_cache/common.rs
deleted file mode 100644
index 690412293..000000000
--- a/cli/cache/http_cache/common.rs
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-use std::path::Path;
-
-use deno_core::url::Url;
-
-pub fn base_url_to_filename_parts(
- url: &Url,
- port_separator: &str,
-) -> Option<Vec<String>> {
- let mut out = Vec::with_capacity(2);
-
- let scheme = url.scheme();
- out.push(scheme.to_string());
-
- match scheme {
- "http" | "https" => {
- let host = url.host_str().unwrap();
- let host_port = match url.port() {
- // underscores are not allowed in domains, so adding one here is fine
- Some(port) => format!("{host}{port_separator}{port}"),
- None => host.to_string(),
- };
- out.push(host_port);
- }
- "data" | "blob" => (),
- scheme => {
- log::debug!("Don't know how to create cache name for scheme: {}", scheme);
- return None;
- }
- };
-
- Some(out)
-}
-
-pub fn read_file_bytes(path: &Path) -> std::io::Result<Option<Vec<u8>>> {
- match std::fs::read(path) {
- Ok(s) => Ok(Some(s)),
- Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(None),
- Err(err) => Err(err),
- }
-}
diff --git a/cli/cache/http_cache/global.rs b/cli/cache/http_cache/global.rs
deleted file mode 100644
index 5c77553a8..000000000
--- a/cli/cache/http_cache/global.rs
+++ /dev/null
@@ -1,296 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-use std::io;
-use std::path::Path;
-use std::path::PathBuf;
-use std::time::SystemTime;
-
-use deno_core::error::AnyError;
-use deno_core::serde_json;
-use deno_core::url::Url;
-use thiserror::Error;
-
-use crate::cache::CACHE_PERM;
-use crate::http_util::HeadersMap;
-use crate::util;
-use crate::util::fs::atomic_write_file;
-
-use super::common::base_url_to_filename_parts;
-use super::common::read_file_bytes;
-use super::CachedUrlMetadata;
-use super::HttpCache;
-use super::HttpCacheItemKey;
-
-#[derive(Debug, Error)]
-#[error("Can't convert url (\"{}\") to filename.", .url)]
-pub struct UrlToFilenameConversionError {
- pub(super) url: String,
-}
-
-/// Turn provided `url` into a hashed filename.
-/// URLs can contain a lot of characters that cannot be used
-/// in filenames (like "?", "#", ":"), so in order to cache
-/// them properly they are deterministically hashed into ASCII
-/// strings.
-pub fn url_to_filename(
- url: &Url,
-) -> Result<PathBuf, UrlToFilenameConversionError> {
- let Some(mut cache_filename) = base_url_to_filename(url) else {
- return Err(UrlToFilenameConversionError { url: url.to_string() });
- };
-
- let mut rest_str = url.path().to_string();
- if let Some(query) = url.query() {
- rest_str.push('?');
- rest_str.push_str(query);
- }
- // NOTE: fragment is omitted on purpose - it's not taken into
- // account when caching - it denotes parts of webpage, which
- // in case of static resources doesn't make much sense
- let hashed_filename = util::checksum::gen(&[rest_str.as_bytes()]);
- cache_filename.push(hashed_filename);
- Ok(cache_filename)
-}
-
-// Turn base of url (scheme, hostname, port) into a valid filename.
-/// This method replaces port part with a special string token (because
-/// ":" cannot be used in filename on some platforms).
-/// Ex: $DENO_DIR/deps/https/deno.land/
-fn base_url_to_filename(url: &Url) -> Option<PathBuf> {
- base_url_to_filename_parts(url, "_PORT").map(|parts| {
- let mut out = PathBuf::new();
- for part in parts {
- out.push(part);
- }
- out
- })
-}
-
-#[derive(Debug)]
-pub struct GlobalHttpCache(PathBuf);
-
-impl GlobalHttpCache {
- pub fn new(path: PathBuf) -> Self {
- assert!(path.is_absolute());
- Self(path)
- }
-
- // Deprecated to discourage using this as where the file is stored and
- // how it's stored should be an implementation detail of the cache.
- #[deprecated(note = "Should only be used for deno info.")]
- pub fn get_global_cache_location(&self) -> &PathBuf {
- &self.0
- }
-
- // DEPRECATED: Where the file is stored and how it's stored should be an implementation
- // detail of the cache.
- #[deprecated(note = "Do not assume the cache will be stored at a file path.")]
- pub fn get_global_cache_filepath(
- &self,
- url: &Url,
- ) -> Result<PathBuf, AnyError> {
- Ok(self.0.join(url_to_filename(url)?))
- }
-
- fn get_cache_filepath(&self, url: &Url) -> Result<PathBuf, AnyError> {
- Ok(self.0.join(url_to_filename(url)?))
- }
-
- #[inline]
- fn key_file_path<'a>(&self, key: &'a HttpCacheItemKey) -> &'a PathBuf {
- // The key file path is always set for the global cache because
- // the file will always exist, unlike the local cache, which won't
- // have this for redirects.
- key.file_path.as_ref().unwrap()
- }
-}
-
-impl HttpCache for GlobalHttpCache {
- fn cache_item_key<'a>(
- &self,
- url: &'a Url,
- ) -> Result<HttpCacheItemKey<'a>, AnyError> {
- Ok(HttpCacheItemKey {
- #[cfg(debug_assertions)]
- is_local_key: false,
- url,
- file_path: Some(self.get_cache_filepath(url)?),
- })
- }
-
- fn contains(&self, url: &Url) -> bool {
- let Ok(cache_filepath) = self.get_cache_filepath(url) else {
- return false
- };
- cache_filepath.is_file()
- }
-
- fn read_modified_time(
- &self,
- key: &HttpCacheItemKey,
- ) -> Result<Option<SystemTime>, AnyError> {
- #[cfg(debug_assertions)]
- debug_assert!(!key.is_local_key);
-
- match std::fs::metadata(self.key_file_path(key)) {
- Ok(metadata) => Ok(Some(metadata.modified()?)),
- Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
- Err(err) => Err(err.into()),
- }
- }
-
- fn set(
- &self,
- url: &Url,
- headers: HeadersMap,
- content: &[u8],
- ) -> Result<(), AnyError> {
- let cache_filepath = self.get_cache_filepath(url)?;
- // Cache content
- atomic_write_file(&cache_filepath, content, CACHE_PERM)?;
-
- let metadata = CachedUrlMetadata {
- time: SystemTime::now(),
- url: url.to_string(),
- headers,
- };
- write_metadata(&cache_filepath, &metadata)?;
-
- Ok(())
- }
-
- fn read_file_bytes(
- &self,
- key: &HttpCacheItemKey,
- ) -> Result<Option<Vec<u8>>, AnyError> {
- #[cfg(debug_assertions)]
- debug_assert!(!key.is_local_key);
-
- Ok(read_file_bytes(self.key_file_path(key))?)
- }
-
- fn read_metadata(
- &self,
- key: &HttpCacheItemKey,
- ) -> Result<Option<CachedUrlMetadata>, AnyError> {
- #[cfg(debug_assertions)]
- debug_assert!(!key.is_local_key);
-
- match read_metadata(self.key_file_path(key))? {
- Some(metadata) => Ok(Some(metadata)),
- None => Ok(None),
- }
- }
-}
-
-fn read_metadata(path: &Path) -> Result<Option<CachedUrlMetadata>, AnyError> {
- let path = path.with_extension("metadata.json");
- match read_file_bytes(&path)? {
- Some(metadata) => Ok(Some(serde_json::from_slice(&metadata)?)),
- None => Ok(None),
- }
-}
-
-fn write_metadata(
- path: &Path,
- meta_data: &CachedUrlMetadata,
-) -> Result<(), AnyError> {
- let path = path.with_extension("metadata.json");
- let json = serde_json::to_string_pretty(meta_data)?;
- atomic_write_file(&path, json, CACHE_PERM)?;
- Ok(())
-}
-
-#[cfg(test)]
-mod test {
- use super::*;
- use std::collections::HashMap;
- use test_util::TempDir;
-
- #[test]
- fn test_url_to_filename() {
- let test_cases = [
- ("https://deno.land/x/foo.ts", "https/deno.land/2c0a064891b9e3fbe386f5d4a833bce5076543f5404613656042107213a7bbc8"),
- (
- "https://deno.land:8080/x/foo.ts",
- "https/deno.land_PORT8080/2c0a064891b9e3fbe386f5d4a833bce5076543f5404613656042107213a7bbc8",
- ),
- ("https://deno.land/", "https/deno.land/8a5edab282632443219e051e4ade2d1d5bbc671c781051bf1437897cbdfea0f1"),
- (
- "https://deno.land/?asdf=qwer",
- "https/deno.land/e4edd1f433165141015db6a823094e6bd8f24dd16fe33f2abd99d34a0a21a3c0",
- ),
- // should be the same as case above, fragment (#qwer) is ignored
- // when hashing
- (
- "https://deno.land/?asdf=qwer#qwer",
- "https/deno.land/e4edd1f433165141015db6a823094e6bd8f24dd16fe33f2abd99d34a0a21a3c0",
- ),
- (
- "data:application/typescript;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo=",
- "data/c21c7fc382b2b0553dc0864aa81a3acacfb7b3d1285ab5ae76da6abec213fb37",
- ),
- (
- "data:text/plain,Hello%2C%20Deno!",
- "data/967374e3561d6741234131e342bf5c6848b70b13758adfe23ee1a813a8131818",
- )
- ];
-
- for (url, expected) in test_cases.iter() {
- let u = Url::parse(url).unwrap();
- let p = url_to_filename(&u).unwrap();
- assert_eq!(p, PathBuf::from(expected));
- }
- }
-
- #[test]
- fn test_create_cache() {
- let dir = TempDir::new();
- let cache_path = dir.path().join("foobar");
- // HttpCache should be created lazily on first use:
- // when zipping up a local project with no external dependencies
- // "$DENO_DIR/deps" is empty. When unzipping such project
- // "$DENO_DIR/deps" might not get restored and in situation
- // when directory is owned by root we might not be able
- // to create that directory. However if it's not needed it
- // doesn't make sense to return error in such specific scenarios.
- // For more details check issue:
- // https://github.com/denoland/deno/issues/5688
- let cache = GlobalHttpCache::new(cache_path.to_path_buf());
- assert!(!cache.0.exists());
- let url = Url::parse("http://example.com/foo/bar.js").unwrap();
- cache
- .set(&url, HeadersMap::new(), b"hello world")
- .expect("Failed to add to cache");
- assert!(cache_path.is_dir());
- assert!(cache.get_cache_filepath(&url).unwrap().is_file());
- }
-
- #[test]
- fn test_get_set() {
- let dir = TempDir::new();
- let cache = GlobalHttpCache::new(dir.path().to_path_buf());
- let url = Url::parse("https://deno.land/x/welcome.ts").unwrap();
- let mut headers = HashMap::new();
- headers.insert(
- "content-type".to_string(),
- "application/javascript".to_string(),
- );
- headers.insert("etag".to_string(), "as5625rqdsfb".to_string());
- let content = b"Hello world";
- let r = cache.set(&url, headers, content);
- eprintln!("result {r:?}");
- assert!(r.is_ok());
- let key = cache.cache_item_key(&url).unwrap();
- let content =
- String::from_utf8(cache.read_file_bytes(&key).unwrap().unwrap()).unwrap();
- let headers = cache.read_metadata(&key).unwrap().unwrap().headers;
- assert_eq!(content, "Hello world");
- assert_eq!(
- headers.get("content-type").unwrap(),
- "application/javascript"
- );
- assert_eq!(headers.get("etag").unwrap(), "as5625rqdsfb");
- assert_eq!(headers.get("foobar"), None);
- }
-}
diff --git a/cli/cache/http_cache/local.rs b/cli/cache/http_cache/local.rs
deleted file mode 100644
index 04883b3ba..000000000
--- a/cli/cache/http_cache/local.rs
+++ /dev/null
@@ -1,1425 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-use std::borrow::Cow;
-use std::collections::HashMap;
-use std::collections::HashSet;
-use std::path::Path;
-use std::path::PathBuf;
-use std::sync::Arc;
-use std::time::SystemTime;
-
-use deno_ast::MediaType;
-use deno_core::error::AnyError;
-use deno_core::parking_lot::RwLock;
-use deno_core::url::Url;
-use indexmap::IndexMap;
-use once_cell::sync::Lazy;
-
-use crate::cache::CACHE_PERM;
-use crate::http_util::HeadersMap;
-use crate::util;
-use crate::util::fs::atomic_write_file;
-
-use super::common::base_url_to_filename_parts;
-use super::common::read_file_bytes;
-use super::global::GlobalHttpCache;
-use super::global::UrlToFilenameConversionError;
-use super::CachedUrlMetadata;
-use super::HttpCache;
-use super::HttpCacheItemKey;
-
-/// A vendor/ folder http cache for the lsp that provides functionality
-/// for doing a reverse mapping.
-#[derive(Debug)]
-pub struct LocalLspHttpCache {
- cache: LocalHttpCache,
-}
-
-impl LocalLspHttpCache {
- pub fn new(path: PathBuf, global_cache: Arc<GlobalHttpCache>) -> Self {
- assert!(path.is_absolute());
- let manifest = LocalCacheManifest::new_for_lsp(path.join("manifest.json"));
- Self {
- cache: LocalHttpCache {
- path,
- manifest,
- global_cache,
- },
- }
- }
-
- pub fn get_file_url(&self, url: &Url) -> Option<Url> {
- let sub_path = {
- let data = self.cache.manifest.data.read();
- let maybe_content_type =
- data.get(url).and_then(|d| d.content_type_header());
- url_to_local_sub_path(url, maybe_content_type).ok()?
- };
- let path = sub_path.as_path_from_root(&self.cache.path);
- if path.exists() {
- Url::from_file_path(path).ok()
- } else {
- None
- }
- }
-
- pub fn get_remote_url(&self, path: &Path) -> Option<Url> {
- let Ok(path) = path.strip_prefix(&self.cache.path) else {
- return None; // not in this directory
- };
- let components = path
- .components()
- .map(|c| c.as_os_str().to_string_lossy())
- .collect::<Vec<_>>();
- if components
- .last()
- .map(|c| c.starts_with('#'))
- .unwrap_or(false)
- {
- // the file itself will have an entry in the manifest
- let data = self.cache.manifest.data.read();
- data.get_reverse_mapping(path)
- } else if let Some(last_index) =
- components.iter().rposition(|c| c.starts_with('#'))
- {
- // get the mapping to the deepest hashed directory and
- // then add the remaining path components to the url
- let dir_path: PathBuf = components[..last_index + 1].iter().fold(
- PathBuf::new(),
- |mut path, c| {
- path.push(c.as_ref());
- path
- },
- );
- let dir_url = self
- .cache
- .manifest
- .data
- .read()
- .get_reverse_mapping(&dir_path)?;
- let file_url =
- dir_url.join(&components[last_index + 1..].join("/")).ok()?;
- Some(file_url)
- } else {
- // we can work backwards from the path to the url
- let mut parts = Vec::new();
- for (i, part) in path.components().enumerate() {
- let part = part.as_os_str().to_string_lossy();
- if i == 0 {
- let mut result = String::new();
- let part = if let Some(part) = part.strip_prefix("http_") {
- result.push_str("http://");
- part
- } else {
- result.push_str("https://");
- &part
- };
- if let Some((domain, port)) = part.rsplit_once('_') {
- result.push_str(&format!("{}:{}", domain, port));
- } else {
- result.push_str(part);
- }
- parts.push(result);
- } else {
- parts.push(part.to_string());
- }
- }
- Url::parse(&parts.join("/")).ok()
- }
- }
-}
-
-impl HttpCache for LocalLspHttpCache {
- fn cache_item_key<'a>(
- &self,
- url: &'a Url,
- ) -> Result<HttpCacheItemKey<'a>, AnyError> {
- self.cache.cache_item_key(url)
- }
-
- fn contains(&self, url: &Url) -> bool {
- self.cache.contains(url)
- }
-
- fn set(
- &self,
- url: &Url,
- headers: HeadersMap,
- content: &[u8],
- ) -> Result<(), AnyError> {
- self.cache.set(url, headers, content)
- }
-
- fn read_modified_time(
- &self,
- key: &HttpCacheItemKey,
- ) -> Result<Option<SystemTime>, AnyError> {
- self.cache.read_modified_time(key)
- }
-
- fn read_file_bytes(
- &self,
- key: &HttpCacheItemKey,
- ) -> Result<Option<Vec<u8>>, AnyError> {
- self.cache.read_file_bytes(key)
- }
-
- fn read_metadata(
- &self,
- key: &HttpCacheItemKey,
- ) -> Result<Option<CachedUrlMetadata>, AnyError> {
- self.cache.read_metadata(key)
- }
-}
-
-#[derive(Debug)]
-pub struct LocalHttpCache {
- path: PathBuf,
- manifest: LocalCacheManifest,
- global_cache: Arc<GlobalHttpCache>,
-}
-
-impl LocalHttpCache {
- pub fn new(path: PathBuf, global_cache: Arc<GlobalHttpCache>) -> Self {
- assert!(path.is_absolute());
- let manifest = LocalCacheManifest::new(path.join("manifest.json"));
- Self {
- path,
- manifest,
- global_cache,
- }
- }
-
- /// Copies the file from the global cache to the local cache returning
- /// if the data was successfully copied to the local cache.
- fn check_copy_global_to_local(&self, url: &Url) -> Result<bool, AnyError> {
- let global_key = self.global_cache.cache_item_key(url)?;
- let Some(metadata) = self.global_cache.read_metadata(&global_key)? else {
- return Ok(false);
- };
-
- let local_path =
- url_to_local_sub_path(url, headers_content_type(&metadata.headers))?;
-
- if !metadata.is_redirect() {
- let Some(cached_bytes) = self.global_cache.read_file_bytes(&global_key)? else {
- return Ok(false);
- };
-
- let local_file_path = local_path.as_path_from_root(&self.path);
- // if we're here, then this will be set
- atomic_write_file(&local_file_path, cached_bytes, CACHE_PERM)?;
- }
-
- self
- .manifest
- .insert_data(local_path, url.clone(), metadata.headers);
-
- Ok(true)
- }
-
- fn get_url_metadata_checking_global_cache(
- &self,
- url: &Url,
- ) -> Result<Option<CachedUrlMetadata>, AnyError> {
- if let Some(metadata) = self.manifest.get_metadata(url) {
- Ok(Some(metadata))
- } else if self.check_copy_global_to_local(url)? {
- // try again now that it's saved
- Ok(self.manifest.get_metadata(url))
- } else {
- Ok(None)
- }
- }
-}
-
-impl HttpCache for LocalHttpCache {
- fn cache_item_key<'a>(
- &self,
- url: &'a Url,
- ) -> Result<HttpCacheItemKey<'a>, AnyError> {
- Ok(HttpCacheItemKey {
- #[cfg(debug_assertions)]
- is_local_key: true,
- url,
- file_path: None, // need to compute this every time
- })
- }
-
- fn contains(&self, url: &Url) -> bool {
- self.manifest.get_metadata(url).is_some()
- }
-
- fn read_modified_time(
- &self,
- key: &HttpCacheItemKey,
- ) -> Result<Option<SystemTime>, AnyError> {
- #[cfg(debug_assertions)]
- debug_assert!(key.is_local_key);
-
- self
- .get_url_metadata_checking_global_cache(key.url)
- .map(|m| m.map(|m| m.time))
- }
-
- fn set(
- &self,
- url: &Url,
- headers: crate::http_util::HeadersMap,
- content: &[u8],
- ) -> Result<(), AnyError> {
- let is_redirect = headers.contains_key("location");
- let sub_path = url_to_local_sub_path(url, headers_content_type(&headers))?;
-
- if !is_redirect {
- // Cache content
- atomic_write_file(
- &sub_path.as_path_from_root(&self.path),
- content,
- CACHE_PERM,
- )?;
- }
-
- self.manifest.insert_data(sub_path, url.clone(), headers);
-
- Ok(())
- }
-
- fn read_file_bytes(
- &self,
- key: &HttpCacheItemKey,
- ) -> Result<Option<Vec<u8>>, AnyError> {
- #[cfg(debug_assertions)]
- debug_assert!(key.is_local_key);
-
- let metadata = self.get_url_metadata_checking_global_cache(key.url)?;
- match metadata {
- Some(data) => {
- if data.is_redirect() {
- // return back an empty file for redirect
- Ok(Some(Vec::new()))
- } else {
- // if it's not a redirect, then it should have a file path
- let cache_file_path = url_to_local_sub_path(
- key.url,
- headers_content_type(&data.headers),
- )?
- .as_path_from_root(&self.path);
- Ok(read_file_bytes(&cache_file_path)?)
- }
- }
- None => Ok(None),
- }
- }
-
- fn read_metadata(
- &self,
- key: &HttpCacheItemKey,
- ) -> Result<Option<CachedUrlMetadata>, AnyError> {
- #[cfg(debug_assertions)]
- debug_assert!(key.is_local_key);
-
- self.get_url_metadata_checking_global_cache(key.url)
- }
-}
-
-pub(super) struct LocalCacheSubPath {
- pub has_hash: bool,
- pub parts: Vec<String>,
-}
-
-impl LocalCacheSubPath {
- pub fn as_path_from_root(&self, root_path: &Path) -> PathBuf {
- let mut path = root_path.to_path_buf();
- for part in &self.parts {
- path.push(part);
- }
- path
- }
-
- pub fn as_relative_path(&self) -> PathBuf {
- let mut path = PathBuf::with_capacity(self.parts.len());
- for part in &self.parts {
- path.push(part);
- }
- path
- }
-}
-
-fn headers_content_type(headers: &HeadersMap) -> Option<&str> {
- headers.get("content-type").map(|s| s.as_str())
-}
-
-fn url_to_local_sub_path(
- url: &Url,
- content_type: Option<&str>,
-) -> Result<LocalCacheSubPath, UrlToFilenameConversionError> {
- // https://stackoverflow.com/a/31976060/188246
- static FORBIDDEN_CHARS: Lazy<HashSet<char>> = Lazy::new(|| {
- HashSet::from(['?', '<', '>', ':', '*', '|', '\\', ':', '"', '\'', '/'])
- });
- // https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file
- static FORBIDDEN_WINDOWS_NAMES: Lazy<HashSet<&'static str>> =
- Lazy::new(|| {
- let set = HashSet::from([
- "con", "prn", "aux", "nul", "com0", "com1", "com2", "com3", "com4",
- "com5", "com6", "com7", "com8", "com9", "lpt0", "lpt1", "lpt2", "lpt3",
- "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9",
- ]);
- // ensure everything is lowercase because we'll be comparing
- // lowercase filenames against this
- debug_assert!(set.iter().all(|s| s.to_lowercase() == *s));
- set
- });
-
- fn has_forbidden_chars(segment: &str) -> bool {
- segment.chars().any(|c| {
- let is_uppercase = c.is_ascii_alphabetic() && !c.is_ascii_lowercase();
- FORBIDDEN_CHARS.contains(&c)
- // do not allow uppercase letters in order to make this work
- // the same on case insensitive file systems
- || is_uppercase
- })
- }
-
- fn has_known_extension(path: &str) -> bool {
- let path = path.to_lowercase();
- path.ends_with(".js")
- || path.ends_with(".ts")
- || path.ends_with(".jsx")
- || path.ends_with(".tsx")
- || path.ends_with(".mts")
- || path.ends_with(".mjs")
- || path.ends_with(".json")
- || path.ends_with(".wasm")
- }
-
- fn get_extension(url: &Url, content_type: Option<&str>) -> &'static str {
- MediaType::from_specifier_and_content_type(url, content_type)
- .as_ts_extension()
- }
-
- fn short_hash(data: &str, last_ext: Option<&str>) -> String {
- // This function is a bit of a balancing act between readability
- // and avoiding collisions.
- let hash = util::checksum::gen(&[data.as_bytes()]);
- // keep the paths short because of windows path limit
- const MAX_LENGTH: usize = 20;
- let mut sub = String::with_capacity(MAX_LENGTH);
- for c in data.chars().take(MAX_LENGTH) {
- // don't include the query string (only use it in the hash)
- if c == '?' {
- break;
- }
- if FORBIDDEN_CHARS.contains(&c) {
- sub.push('_');
- } else {
- sub.extend(c.to_lowercase());
- }
- }
- let sub = match last_ext {
- Some(ext) => sub.strip_suffix(ext).unwrap_or(&sub),
- None => &sub,
- };
- let ext = last_ext.unwrap_or("");
- if sub.is_empty() {
- format!("#{}{}", &hash[..7], ext)
- } else {
- format!("#{}_{}{}", &sub, &hash[..5], ext)
- }
- }
-
- fn should_hash_part(part: &str, last_ext: Option<&str>) -> bool {
- if part.is_empty() || part.len() > 30 {
- // keep short due to windows path limit
- return true;
- }
- let hash_context_specific = if let Some(last_ext) = last_ext {
- // if the last part does not have a known extension, hash it in order to
- // prevent collisions with a directory of the same name
- !has_known_extension(part) || !part.ends_with(last_ext)
- } else {
- // if any non-ending path part has a known extension, hash it in order to
- // prevent collisions where a filename has the same name as a directory name
- has_known_extension(part)
- };
-
- // the hash symbol at the start designates a hash for the url part
- hash_context_specific
- || part.starts_with('#')
- || has_forbidden_chars(part)
- || last_ext.is_none() && FORBIDDEN_WINDOWS_NAMES.contains(part)
- || part.ends_with('.')
- }
-
- // get the base url
- let port_separator = "_"; // make this shorter with just an underscore
- let Some(mut base_parts) = base_url_to_filename_parts(url, port_separator) else {
- return Err(UrlToFilenameConversionError { url: url.to_string() });
- };
-
- if base_parts[0] == "https" {
- base_parts.remove(0);
- } else {
- let scheme = base_parts.remove(0);
- base_parts[0] = format!("{}_{}", scheme, base_parts[0]);
- }
-
- // first, try to get the filename of the path
- let path_segments = url_path_segments(url);
- let mut parts = base_parts
- .into_iter()
- .chain(path_segments.map(|s| s.to_string()))
- .collect::<Vec<_>>();
-
- // push the query parameter onto the last part
- if let Some(query) = url.query() {
- let last_part = parts.last_mut().unwrap();
- last_part.push('?');
- last_part.push_str(query);
- }
-
- let mut has_hash = false;
- let parts_len = parts.len();
- let parts = parts
- .into_iter()
- .enumerate()
- .map(|(i, part)| {
- let is_last = i == parts_len - 1;
- let last_ext = if is_last {
- Some(get_extension(url, content_type))
- } else {
- None
- };
- if should_hash_part(&part, last_ext) {
- has_hash = true;
- short_hash(&part, last_ext)
- } else {
- part
- }
- })
- .collect::<Vec<_>>();
-
- Ok(LocalCacheSubPath { has_hash, parts })
-}
-
-#[derive(Debug)]
-struct LocalCacheManifest {
- file_path: PathBuf,
- data: RwLock<manifest::LocalCacheManifestData>,
-}
-
-impl LocalCacheManifest {
- pub fn new(file_path: PathBuf) -> Self {
- Self::new_internal(file_path, false)
- }
-
- pub fn new_for_lsp(file_path: PathBuf) -> Self {
- Self::new_internal(file_path, true)
- }
-
- fn new_internal(file_path: PathBuf, use_reverse_mapping: bool) -> Self {
- let text = std::fs::read_to_string(&file_path).ok();
- Self {
- data: RwLock::new(manifest::LocalCacheManifestData::new(
- text.as_deref(),
- use_reverse_mapping,
- )),
- file_path,
- }
- }
-
- pub fn insert_data(
- &self,
- sub_path: LocalCacheSubPath,
- url: Url,
- mut original_headers: HashMap<String, String>,
- ) {
- fn should_keep_content_type_header(
- url: &Url,
- headers: &HashMap<String, String>,
- ) -> bool {
- // only keep the location header if it can't be derived from the url
- MediaType::from_specifier(url)
- != MediaType::from_specifier_and_headers(url, Some(headers))
- }
-
- let mut headers_subset = IndexMap::new();
-
- const HEADER_KEYS_TO_KEEP: [&str; 4] = [
- // keep alphabetical for cleanliness in the output
- "content-type",
- "location",
- "x-deno-warning",
- "x-typescript-types",
- ];
- for key in HEADER_KEYS_TO_KEEP {
- if key == "content-type"
- && !should_keep_content_type_header(&url, &original_headers)
- {
- continue;
- }
- if let Some((k, v)) = original_headers.remove_entry(key) {
- headers_subset.insert(k, v);
- }
- }
-
- let mut data = self.data.write();
- let add_module_entry = headers_subset.is_empty()
- && !sub_path
- .parts
- .last()
- .map(|s| s.starts_with('#'))
- .unwrap_or(false);
- let mut has_changed = if add_module_entry {
- data.remove(&url, &sub_path)
- } else {
- let new_data = manifest::SerializedLocalCacheManifestDataModule {
- headers: headers_subset,
- };
- if data.get(&url) == Some(&new_data) {
- false
- } else {
- data.insert(url.clone(), &sub_path, new_data);
- true
- }
- };
-
- if sub_path.has_hash {
- let url_path_parts = url_path_segments(&url).collect::<Vec<_>>();
- let base_url = {
- let mut url = url.clone();
- url.set_path("/");
- url.set_query(None);
- url.set_fragment(None);
- url
- };
- for (i, local_part) in sub_path.parts[1..sub_path.parts.len() - 1]
- .iter()
- .enumerate()
- {
- if local_part.starts_with('#') {
- let mut url = base_url.clone();
- url.set_path(&format!("{}/", url_path_parts[..i + 1].join("/")));
- if data.add_directory(url, sub_path.parts[..i + 2].join("/")) {
- has_changed = true;
- }
- }
- }
- }
-
- if has_changed {
- // don't bother ensuring the directory here because it will
- // eventually be created by files being added to the cache
- let result =
- atomic_write_file(&self.file_path, data.as_json(), CACHE_PERM);
- if let Err(err) = result {
- log::debug!("Failed saving local cache manifest: {:#}", err);
- }
- }
- }
-
- pub fn get_metadata(&self, url: &Url) -> Option<CachedUrlMetadata> {
- let data = self.data.read();
- match data.get(url) {
- Some(module) => {
- let headers = module
- .headers
- .iter()
- .map(|(k, v)| (k.to_string(), v.to_string()))
- .collect::<HashMap<_, _>>();
- let sub_path = if headers.contains_key("location") {
- Cow::Borrowed(&self.file_path)
- } else {
- let sub_path =
- url_to_local_sub_path(url, headers_content_type(&headers)).ok()?;
- let folder_path = self.file_path.parent().unwrap();
- Cow::Owned(sub_path.as_path_from_root(folder_path))
- };
-
- let Ok(metadata) = sub_path.metadata() else {
- return None;
- };
-
- Some(CachedUrlMetadata {
- headers,
- url: url.to_string(),
- time: metadata.modified().unwrap_or_else(|_| SystemTime::now()),
- })
- }
- None => {
- let folder_path = self.file_path.parent().unwrap();
- let sub_path = url_to_local_sub_path(url, None).ok()?;
- if sub_path
- .parts
- .last()
- .map(|s| s.starts_with('#'))
- .unwrap_or(false)
- {
- // only filenames without a hash are considered as in the cache
- // when they don't have a metadata entry
- return None;
- }
- let file_path = sub_path.as_path_from_root(folder_path);
- if let Ok(metadata) = file_path.metadata() {
- Some(CachedUrlMetadata {
- headers: Default::default(),
- url: url.to_string(),
- time: metadata.modified().unwrap_or_else(|_| SystemTime::now()),
- })
- } else {
- None
- }
- }
- }
- }
-}
-
-// This is in a separate module in order to enforce keeping
-// the internal implementation private.
-mod manifest {
- use std::collections::HashMap;
- use std::path::Path;
- use std::path::PathBuf;
-
- use deno_core::serde_json;
- use deno_core::url::Url;
- use indexmap::IndexMap;
- use serde::Deserialize;
- use serde::Serialize;
-
- use super::url_to_local_sub_path;
- use super::LocalCacheSubPath;
-
- #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
- pub struct SerializedLocalCacheManifestDataModule {
- #[serde(
- default = "IndexMap::new",
- skip_serializing_if = "IndexMap::is_empty"
- )]
- pub headers: IndexMap<String, String>,
- }
-
- impl SerializedLocalCacheManifestDataModule {
- pub fn content_type_header(&self) -> Option<&str> {
- self.headers.get("content-type").map(|s| s.as_str())
- }
- }
-
- #[derive(Debug, Default, Clone, Serialize, Deserialize)]
- struct SerializedLocalCacheManifestData {
- #[serde(
- default = "IndexMap::new",
- skip_serializing_if = "IndexMap::is_empty"
- )]
- pub folders: IndexMap<Url, String>,
- #[serde(
- default = "IndexMap::new",
- skip_serializing_if = "IndexMap::is_empty"
- )]
- pub modules: IndexMap<Url, SerializedLocalCacheManifestDataModule>,
- }
-
- #[derive(Debug, Default, Clone)]
- pub(super) struct LocalCacheManifestData {
- serialized: SerializedLocalCacheManifestData,
- // reverse mapping used in the lsp
- reverse_mapping: Option<HashMap<PathBuf, Url>>,
- }
-
- impl LocalCacheManifestData {
- pub fn new(maybe_text: Option<&str>, use_reverse_mapping: bool) -> Self {
- let serialized: SerializedLocalCacheManifestData = maybe_text
- .and_then(|text| match serde_json::from_str(text) {
- Ok(data) => Some(data),
- Err(err) => {
- log::debug!("Failed deserializing local cache manifest: {:#}", err);
- None
- }
- })
- .unwrap_or_default();
- let reverse_mapping = if use_reverse_mapping {
- Some(
- serialized
- .modules
- .iter()
- .filter_map(|(url, module)| {
- if module.headers.contains_key("location") {
- return None;
- }
- url_to_local_sub_path(url, module.content_type_header())
- .ok()
- .map(|local_path| {
- let path = if cfg!(windows) {
- PathBuf::from(local_path.parts.join("\\"))
- } else {
- PathBuf::from(local_path.parts.join("/"))
- };
- (path, url.clone())
- })
- })
- .chain(serialized.folders.iter().map(|(url, local_path)| {
- let path = if cfg!(windows) {
- PathBuf::from(local_path.replace('/', "\\"))
- } else {
- PathBuf::from(local_path)
- };
- (path, url.clone())
- }))
- .collect::<HashMap<_, _>>(),
- )
- } else {
- None
- };
- Self {
- serialized,
- reverse_mapping,
- }
- }
-
- pub fn get(
- &self,
- url: &Url,
- ) -> Option<&SerializedLocalCacheManifestDataModule> {
- self.serialized.modules.get(url)
- }
-
- pub fn get_reverse_mapping(&self, path: &Path) -> Option<Url> {
- debug_assert!(self.reverse_mapping.is_some()); // only call this if you're in the lsp
- self
- .reverse_mapping
- .as_ref()
- .and_then(|mapping| mapping.get(path))
- .cloned()
- }
-
- pub fn add_directory(&mut self, url: Url, local_path: String) -> bool {
- if let Some(current) = self.serialized.folders.get(&url) {
- if *current == local_path {
- return false;
- }
- }
-
- if let Some(reverse_mapping) = &mut self.reverse_mapping {
- reverse_mapping.insert(
- if cfg!(windows) {
- PathBuf::from(local_path.replace('/', "\\"))
- } else {
- PathBuf::from(&local_path)
- },
- url.clone(),
- );
- }
-
- self.serialized.folders.insert(url, local_path);
- true
- }
-
- pub fn insert(
- &mut self,
- url: Url,
- sub_path: &LocalCacheSubPath,
- new_data: SerializedLocalCacheManifestDataModule,
- ) {
- if let Some(reverse_mapping) = &mut self.reverse_mapping {
- reverse_mapping.insert(sub_path.as_relative_path(), url.clone());
- }
- self.serialized.modules.insert(url, new_data);
- }
-
- pub fn remove(&mut self, url: &Url, sub_path: &LocalCacheSubPath) -> bool {
- if self.serialized.modules.remove(url).is_some() {
- if let Some(reverse_mapping) = &mut self.reverse_mapping {
- reverse_mapping.remove(&sub_path.as_relative_path());
- }
- true
- } else {
- false
- }
- }
-
- pub fn as_json(&self) -> String {
- serde_json::to_string_pretty(&self.serialized).unwrap()
- }
- }
-}
-
-fn url_path_segments(url: &Url) -> impl Iterator<Item = &str> {
- url
- .path()
- .strip_prefix('/')
- .unwrap_or(url.path())
- .split('/')
-}
-
-#[cfg(test)]
-mod test {
- use super::*;
-
- use deno_core::serde_json::json;
- use pretty_assertions::assert_eq;
- use test_util::TempDir;
-
- #[test]
- fn test_url_to_local_sub_path() {
- run_test("https://deno.land/x/mod.ts", &[], "deno.land/x/mod.ts");
- run_test(
- "http://deno.land/x/mod.ts",
- &[],
- // http gets added to the folder name, but not https
- "http_deno.land/x/mod.ts",
- );
- run_test(
- // capital letter in filename
- "https://deno.land/x/MOD.ts",
- &[],
- "deno.land/x/#mod_fa860.ts",
- );
- run_test(
- // query string
- "https://deno.land/x/mod.ts?testing=1",
- &[],
- "deno.land/x/#mod_2eb80.ts",
- );
- run_test(
- // capital letter in directory
- "https://deno.land/OTHER/mod.ts",
- &[],
- "deno.land/#other_1c55d/mod.ts",
- );
- run_test(
- // under max of 30 chars
- "https://deno.land/x/012345678901234567890123456.js",
- &[],
- "deno.land/x/012345678901234567890123456.js",
- );
- run_test(
- // max 30 chars
- "https://deno.land/x/0123456789012345678901234567.js",
- &[],
- "deno.land/x/#01234567890123456789_836de.js",
- );
- run_test(
- // forbidden char
- "https://deno.land/x/mod's.js",
- &[],
- "deno.land/x/#mod_s_44fc8.js",
- );
- run_test(
- // no extension
- "https://deno.land/x/mod",
- &[("content-type", "application/typescript")],
- "deno.land/x/#mod_e55cf.ts",
- );
- run_test(
- // known extension in directory is not allowed
- // because it could conflict with a file of the same name
- "https://deno.land/x/mod.js/mod.js",
- &[],
- "deno.land/x/#mod.js_59c58/mod.js",
- );
- run_test(
- // slash slash in path
- "http://localhost//mod.js",
- &[],
- "http_localhost/#e3b0c44/mod.js",
- );
- run_test(
- // headers same extension
- "https://deno.land/x/mod.ts",
- &[("content-type", "application/typescript")],
- "deno.land/x/mod.ts",
- );
- run_test(
- // headers different extension... We hash this because
- // if someone deletes the manifest file, then we don't want
- // https://deno.land/x/mod.ts to resolve as a typescript file
- "https://deno.land/x/mod.ts",
- &[("content-type", "application/javascript")],
- "deno.land/x/#mod.ts_e8c36.js",
- );
- run_test(
- // not allowed windows folder name
- "https://deno.land/x/con/con.ts",
- &[],
- "deno.land/x/#con_1143d/con.ts",
- );
- run_test(
- // disallow ending a directory with a period
- // https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file
- "https://deno.land/x/test./main.ts",
- &[],
- "deno.land/x/#test._4ee3d/main.ts",
- );
-
- #[track_caller]
- fn run_test(url: &str, headers: &[(&str, &str)], expected: &str) {
- let url = Url::parse(url).unwrap();
- let headers = headers
- .iter()
- .map(|(k, v)| (k.to_string(), v.to_string()))
- .collect();
- let result =
- url_to_local_sub_path(&url, headers_content_type(&headers)).unwrap();
- let parts = result.parts.join("/");
- assert_eq!(parts, expected);
- assert_eq!(
- result.parts.iter().any(|p| p.starts_with('#')),
- result.has_hash
- )
- }
- }
-
- #[test]
- fn test_local_global_cache() {
- let temp_dir = TempDir::new();
- let global_cache_path = temp_dir.path().join("global");
- let local_cache_path = temp_dir.path().join("local");
- let global_cache =
- Arc::new(GlobalHttpCache::new(global_cache_path.to_path_buf()));
- let local_cache =
- LocalHttpCache::new(local_cache_path.to_path_buf(), global_cache.clone());
-
- let manifest_file = local_cache_path.join("manifest.json");
- // mapped url
- {
- let url = Url::parse("https://deno.land/x/mod.ts").unwrap();
- let content = "export const test = 5;";
- global_cache
- .set(
- &url,
- HashMap::from([(
- "content-type".to_string(),
- "application/typescript".to_string(),
- )]),
- content.as_bytes(),
- )
- .unwrap();
- let key = local_cache.cache_item_key(&url).unwrap();
- assert_eq!(
- String::from_utf8(local_cache.read_file_bytes(&key).unwrap().unwrap())
- .unwrap(),
- content
- );
- let metadata = local_cache.read_metadata(&key).unwrap().unwrap();
- // won't have any headers because the content-type is derivable from the url
- assert_eq!(metadata.headers, HashMap::new());
- assert_eq!(metadata.url, url.to_string());
- // no manifest file yet
- assert!(!manifest_file.exists());
-
- // now try deleting the global cache and we should still be able to load it
- global_cache_path.remove_dir_all();
- assert_eq!(
- String::from_utf8(local_cache.read_file_bytes(&key).unwrap().unwrap())
- .unwrap(),
- content
- );
- }
-
- // file that's directly mappable to a url
- {
- let content = "export const a = 1;";
- local_cache_path
- .join("deno.land")
- .join("main.js")
- .write(content);
-
- // now we should be able to read this file because it's directly mappable to a url
- let url = Url::parse("https://deno.land/main.js").unwrap();
- let key = local_cache.cache_item_key(&url).unwrap();
- assert_eq!(
- String::from_utf8(local_cache.read_file_bytes(&key).unwrap().unwrap())
- .unwrap(),
- content
- );
- let metadata = local_cache.read_metadata(&key).unwrap().unwrap();
- assert_eq!(metadata.headers, HashMap::new());
- assert_eq!(metadata.url, url.to_string());
- }
-
- // now try a file with a different content-type header
- {
- let url =
- Url::parse("https://deno.land/x/different_content_type.ts").unwrap();
- let content = "export const test = 5;";
- global_cache
- .set(
- &url,
- HashMap::from([(
- "content-type".to_string(),
- "application/javascript".to_string(),
- )]),
- content.as_bytes(),
- )
- .unwrap();
- let key = local_cache.cache_item_key(&url).unwrap();
- assert_eq!(
- String::from_utf8(local_cache.read_file_bytes(&key).unwrap().unwrap())
- .unwrap(),
- content
- );
- let metadata = local_cache.read_metadata(&key).unwrap().unwrap();
- assert_eq!(
- metadata.headers,
- HashMap::from([(
- "content-type".to_string(),
- "application/javascript".to_string(),
- )])
- );
- assert_eq!(metadata.url, url.to_string());
- assert_eq!(
- manifest_file.read_json_value(),
- json!({
- "modules": {
- "https://deno.land/x/different_content_type.ts": {
- "headers": {
- "content-type": "application/javascript"
- }
- }
- }
- })
- );
- // delete the manifest file
- manifest_file.remove_file();
-
- // Now try resolving the key again and the content type should still be application/javascript.
- // This is maintained because we hash the filename when the headers don't match the extension.
- let metadata = local_cache.read_metadata(&key).unwrap().unwrap();
- assert_eq!(
- metadata.headers,
- HashMap::from([(
- "content-type".to_string(),
- "application/javascript".to_string(),
- )])
- );
- }
-
- // reset the local cache
- local_cache_path.remove_dir_all();
- let local_cache =
- LocalHttpCache::new(local_cache_path.to_path_buf(), global_cache.clone());
-
- // now try caching a file with many headers
- {
- let url = Url::parse("https://deno.land/x/my_file.ts").unwrap();
- let content = "export const test = 5;";
- global_cache
- .set(
- &url,
- HashMap::from([
- (
- "content-type".to_string(),
- "application/typescript".to_string(),
- ),
- ("x-typescript-types".to_string(), "./types.d.ts".to_string()),
- ("x-deno-warning".to_string(), "Stop right now.".to_string()),
- (
- "x-other-header".to_string(),
- "Thank you very much.".to_string(),
- ),
- ]),
- content.as_bytes(),
- )
- .unwrap();
- let check_output = |local_cache: &LocalHttpCache| {
- let key = local_cache.cache_item_key(&url).unwrap();
- assert_eq!(
- String::from_utf8(
- local_cache.read_file_bytes(&key).unwrap().unwrap()
- )
- .unwrap(),
- content
- );
- let metadata = local_cache.read_metadata(&key).unwrap().unwrap();
- assert_eq!(
- metadata.headers,
- HashMap::from([
- ("x-typescript-types".to_string(), "./types.d.ts".to_string(),),
- ("x-deno-warning".to_string(), "Stop right now.".to_string(),)
- ])
- );
- assert_eq!(metadata.url, url.to_string());
- assert_eq!(
- manifest_file.read_json_value(),
- json!({
- "modules": {
- "https://deno.land/x/my_file.ts": {
- "headers": {
- "x-deno-warning": "Stop right now.",
- "x-typescript-types": "./types.d.ts"
- }
- }
- }
- })
- );
- };
- check_output(&local_cache);
- // now ensure it's the same when re-creating the cache
- check_output(&LocalHttpCache::new(
- local_cache_path.to_path_buf(),
- global_cache.clone(),
- ));
- }
-
- // reset the local cache
- local_cache_path.remove_dir_all();
- let local_cache =
- LocalHttpCache::new(local_cache_path.to_path_buf(), global_cache.clone());
-
- // try a file that can't be mapped to the file system
- {
- {
- let url =
- Url::parse("https://deno.land/INVALID/Module.ts?dev").unwrap();
- let content = "export const test = 5;";
- global_cache
- .set(&url, HashMap::new(), content.as_bytes())
- .unwrap();
- let key = local_cache.cache_item_key(&url).unwrap();
- assert_eq!(
- String::from_utf8(
- local_cache.read_file_bytes(&key).unwrap().unwrap()
- )
- .unwrap(),
- content
- );
- let metadata = local_cache.read_metadata(&key).unwrap().unwrap();
- // won't have any headers because the content-type is derivable from the url
- assert_eq!(metadata.headers, HashMap::new());
- assert_eq!(metadata.url, url.to_string());
- }
-
- // now try a file in the same directory, but that maps to the local filesystem
- {
- let url = Url::parse("https://deno.land/INVALID/module2.ts").unwrap();
- let content = "export const test = 4;";
- global_cache
- .set(&url, HashMap::new(), content.as_bytes())
- .unwrap();
- let key = local_cache.cache_item_key(&url).unwrap();
- assert_eq!(
- String::from_utf8(
- local_cache.read_file_bytes(&key).unwrap().unwrap()
- )
- .unwrap(),
- content
- );
- assert!(local_cache_path
- .join("deno.land/#invalid_1ee01/module2.ts")
- .exists());
-
- // ensure we can still read this file with a new local cache
- let local_cache = LocalHttpCache::new(
- local_cache_path.to_path_buf(),
- global_cache.clone(),
- );
- assert_eq!(
- String::from_utf8(
- local_cache.read_file_bytes(&key).unwrap().unwrap()
- )
- .unwrap(),
- content
- );
- }
-
- assert_eq!(
- manifest_file.read_json_value(),
- json!({
- "modules": {
- "https://deno.land/INVALID/Module.ts?dev": {
- }
- },
- "folders": {
- "https://deno.land/INVALID/": "deno.land/#invalid_1ee01",
- }
- })
- );
- }
-
- // reset the local cache
- local_cache_path.remove_dir_all();
- let local_cache =
- LocalHttpCache::new(local_cache_path.to_path_buf(), global_cache.clone());
-
- // now try a redirect
- {
- let url = Url::parse("https://deno.land/redirect.ts").unwrap();
- global_cache
- .set(
- &url,
- HashMap::from([("location".to_string(), "./x/mod.ts".to_string())]),
- "Redirecting to other url...".as_bytes(),
- )
- .unwrap();
- let key = local_cache.cache_item_key(&url).unwrap();
- let metadata = local_cache.read_metadata(&key).unwrap().unwrap();
- assert_eq!(
- metadata.headers,
- HashMap::from([("location".to_string(), "./x/mod.ts".to_string())])
- );
- assert_eq!(metadata.url, url.to_string());
- assert_eq!(
- manifest_file.read_json_value(),
- json!({
- "modules": {
- "https://deno.land/redirect.ts": {
- "headers": {
- "location": "./x/mod.ts"
- }
- }
- }
- })
- );
- }
- }
-
- #[test]
- fn test_lsp_local_cache() {
- let temp_dir = TempDir::new();
- let global_cache_path = temp_dir.path().join("global");
- let local_cache_path = temp_dir.path().join("local");
- let global_cache =
- Arc::new(GlobalHttpCache::new(global_cache_path.to_path_buf()));
- let local_cache = LocalLspHttpCache::new(
- local_cache_path.to_path_buf(),
- global_cache.clone(),
- );
-
- // mapped url
- {
- let url = Url::parse("https://deno.land/x/mod.ts").unwrap();
- let content = "export const test = 5;";
- global_cache
- .set(
- &url,
- HashMap::from([(
- "content-type".to_string(),
- "application/typescript".to_string(),
- )]),
- content.as_bytes(),
- )
- .unwrap();
- let key = local_cache.cache_item_key(&url).unwrap();
- assert_eq!(
- String::from_utf8(local_cache.read_file_bytes(&key).unwrap().unwrap())
- .unwrap(),
- content
- );
-
- // check getting the file url works
- let file_url = local_cache.get_file_url(&url);
- let expected = local_cache_path
- .uri_dir()
- .join("deno.land/x/mod.ts")
- .unwrap();
- assert_eq!(file_url, Some(expected));
-
- // get the reverse mapping
- let mapping = local_cache.get_remote_url(
- local_cache_path
- .join("deno.land")
- .join("x")
- .join("mod.ts")
- .as_path(),
- );
- assert_eq!(mapping.as_ref(), Some(&url));
- }
-
- // now try a file with a different content-type header
- {
- let url =
- Url::parse("https://deno.land/x/different_content_type.ts").unwrap();
- let content = "export const test = 5;";
- global_cache
- .set(
- &url,
- HashMap::from([(
- "content-type".to_string(),
- "application/javascript".to_string(),
- )]),
- content.as_bytes(),
- )
- .unwrap();
- let key = local_cache.cache_item_key(&url).unwrap();
- assert_eq!(
- String::from_utf8(local_cache.read_file_bytes(&key).unwrap().unwrap())
- .unwrap(),
- content
- );
-
- let file_url = local_cache.get_file_url(&url).unwrap();
- let path = file_url.to_file_path().unwrap();
- assert!(path.exists());
- let mapping = local_cache.get_remote_url(&path);
- assert_eq!(mapping.as_ref(), Some(&url));
- }
-
- // try http specifiers that can't be mapped to the file system
- {
- let urls = [
- "http://deno.land/INVALID/Module.ts?dev",
- "http://deno.land/INVALID/SubDir/Module.ts?dev",
- ];
- for url in urls {
- let url = Url::parse(url).unwrap();
- let content = "export const test = 5;";
- global_cache
- .set(&url, HashMap::new(), content.as_bytes())
- .unwrap();
- let key = local_cache.cache_item_key(&url).unwrap();
- assert_eq!(
- String::from_utf8(
- local_cache.read_file_bytes(&key).unwrap().unwrap()
- )
- .unwrap(),
- content
- );
-
- let file_url = local_cache.get_file_url(&url).unwrap();
- let path = file_url.to_file_path().unwrap();
- assert!(path.exists());
- let mapping = local_cache.get_remote_url(&path);
- assert_eq!(mapping.as_ref(), Some(&url));
- }
-
- // now try a files in the same and sub directories, that maps to the local filesystem
- let urls = [
- "http://deno.land/INVALID/module2.ts",
- "http://deno.land/INVALID/SubDir/module3.ts",
- "http://deno.land/INVALID/SubDir/sub_dir/module4.ts",
- ];
- for url in urls {
- let url = Url::parse(url).unwrap();
- let content = "export const test = 4;";
- global_cache
- .set(&url, HashMap::new(), content.as_bytes())
- .unwrap();
- let key = local_cache.cache_item_key(&url).unwrap();
- assert_eq!(
- String::from_utf8(
- local_cache.read_file_bytes(&key).unwrap().unwrap()
- )
- .unwrap(),
- content
- );
- let file_url = local_cache.get_file_url(&url).unwrap();
- let path = file_url.to_file_path().unwrap();
- assert!(path.exists());
- let mapping = local_cache.get_remote_url(&path);
- assert_eq!(mapping.as_ref(), Some(&url));
-
- // ensure we can still get this file with a new local cache
- let local_cache = LocalLspHttpCache::new(
- local_cache_path.to_path_buf(),
- global_cache.clone(),
- );
- let file_url = local_cache.get_file_url(&url).unwrap();
- let path = file_url.to_file_path().unwrap();
- assert!(path.exists());
- let mapping = local_cache.get_remote_url(&path);
- assert_eq!(mapping.as_ref(), Some(&url));
- }
- }
- }
-}
diff --git a/cli/cache/http_cache/mod.rs b/cli/cache/http_cache/mod.rs
deleted file mode 100644
index 8d09b0995..000000000
--- a/cli/cache/http_cache/mod.rs
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-use deno_core::error::AnyError;
-use deno_core::serde::Deserialize;
-use deno_core::serde::Serialize;
-use deno_core::url::Url;
-use std::path::PathBuf;
-use std::time::SystemTime;
-
-use crate::http_util::HeadersMap;
-
-mod common;
-mod global;
-mod local;
-
-pub use global::url_to_filename;
-pub use global::GlobalHttpCache;
-pub use local::LocalHttpCache;
-pub use local::LocalLspHttpCache;
-
-/// Cached metadata about a url.
-#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
-pub struct CachedUrlMetadata {
- pub headers: HeadersMap,
- pub url: String,
- #[serde(default = "SystemTime::now", rename = "now")]
- pub time: SystemTime,
-}
-
-impl CachedUrlMetadata {
- pub fn is_redirect(&self) -> bool {
- self.headers.contains_key("location")
- }
-}
-
-/// Computed cache key, which can help reduce the work of computing the cache key multiple times.
-pub struct HttpCacheItemKey<'a> {
- // The key is specific to the implementation of HttpCache,
- // so keep these private to the module. For example, the
- // fact that these may be stored in a file is an implementation
- // detail.
- #[cfg(debug_assertions)]
- pub(super) is_local_key: bool,
- pub(super) url: &'a Url,
- /// This will be set all the time for the global cache, but it
- /// won't ever be set for the local cache because that also needs
- /// header information to determine the final path.
- pub(super) file_path: Option<PathBuf>,
-}
-
-pub trait HttpCache: Send + Sync + std::fmt::Debug {
- /// A pre-computed key for looking up items in the cache.
- fn cache_item_key<'a>(
- &self,
- url: &'a Url,
- ) -> Result<HttpCacheItemKey<'a>, AnyError>;
-
- fn contains(&self, url: &Url) -> bool;
- fn set(
- &self,
- url: &Url,
- headers: HeadersMap,
- content: &[u8],
- ) -> Result<(), AnyError>;
- fn read_modified_time(
- &self,
- key: &HttpCacheItemKey,
- ) -> Result<Option<SystemTime>, AnyError>;
- fn read_file_bytes(
- &self,
- key: &HttpCacheItemKey,
- ) -> Result<Option<Vec<u8>>, AnyError>;
- fn read_metadata(
- &self,
- key: &HttpCacheItemKey,
- ) -> Result<Option<CachedUrlMetadata>, AnyError>;
-}
diff --git a/cli/cache/mod.rs b/cli/cache/mod.rs
index 2427133d1..f75761521 100644
--- a/cli/cache/mod.rs
+++ b/cli/cache/mod.rs
@@ -2,6 +2,7 @@
use crate::errors::get_error_class_name;
use crate::file_fetcher::FileFetcher;
+use crate::util::fs::atomic_write_file;
use deno_core::futures;
use deno_core::futures::FutureExt;
@@ -12,8 +13,10 @@ use deno_graph::source::LoadResponse;
use deno_graph::source::Loader;
use deno_runtime::permissions::PermissionsContainer;
use std::collections::HashMap;
+use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc;
+use std::time::SystemTime;
mod cache_db;
mod caches;
@@ -22,7 +25,6 @@ mod common;
mod deno_dir;
mod disk_cache;
mod emit;
-mod http_cache;
mod incremental;
mod node;
mod parsed_source;
@@ -34,11 +36,6 @@ pub use deno_dir::DenoDir;
pub use deno_dir::DenoDirProvider;
pub use disk_cache::DiskCache;
pub use emit::EmitCache;
-pub use http_cache::CachedUrlMetadata;
-pub use http_cache::GlobalHttpCache;
-pub use http_cache::HttpCache;
-pub use http_cache::LocalHttpCache;
-pub use http_cache::LocalLspHttpCache;
pub use incremental::IncrementalCache;
pub use node::NodeAnalysisCache;
pub use parsed_source::ParsedSourceCache;
@@ -46,6 +43,52 @@ pub use parsed_source::ParsedSourceCache;
/// Permissions used to save a file in the disk caches.
pub const CACHE_PERM: u32 = 0o644;
+#[derive(Debug, Clone)]
+pub struct RealDenoCacheEnv;
+
+impl deno_cache_dir::DenoCacheEnv for RealDenoCacheEnv {
+ fn read_file_bytes(&self, path: &Path) -> std::io::Result<Option<Vec<u8>>> {
+ match std::fs::read(path) {
+ Ok(s) => Ok(Some(s)),
+ Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(None),
+ Err(err) => Err(err),
+ }
+ }
+
+ fn atomic_write_file(
+ &self,
+ path: &Path,
+ bytes: &[u8],
+ ) -> std::io::Result<()> {
+ atomic_write_file(path, bytes, CACHE_PERM)
+ }
+
+ fn modified(&self, path: &Path) -> std::io::Result<Option<SystemTime>> {
+ match std::fs::metadata(path) {
+ Ok(metadata) => Ok(Some(
+ metadata.modified().unwrap_or_else(|_| SystemTime::now()),
+ )),
+ Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(None),
+ Err(err) => Err(err),
+ }
+ }
+
+ fn is_file(&self, path: &Path) -> bool {
+ path.is_file()
+ }
+
+ fn time_now(&self) -> SystemTime {
+ SystemTime::now()
+ }
+}
+
+pub type GlobalHttpCache = deno_cache_dir::GlobalHttpCache<RealDenoCacheEnv>;
+pub type LocalHttpCache = deno_cache_dir::LocalHttpCache<RealDenoCacheEnv>;
+pub type LocalLspHttpCache =
+ deno_cache_dir::LocalLspHttpCache<RealDenoCacheEnv>;
+pub use deno_cache_dir::CachedUrlMetadata;
+pub use deno_cache_dir::HttpCache;
+
/// A "wrapper" for the FileFetcher and DiskCache for the Deno CLI that provides
/// a concise interface to the DENO_DIR when building module graphs.
pub struct FetchCacher {
diff --git a/cli/factory.rs b/cli/factory.rs
index e9a50aada..9c553620d 100644
--- a/cli/factory.rs
+++ b/cli/factory.rs
@@ -241,6 +241,7 @@ impl CliFactory {
self.services.global_http_cache.get_or_try_init(|| {
Ok(Arc::new(GlobalHttpCache::new(
self.deno_dir()?.deps_folder_path(),
+ crate::cache::RealDenoCacheEnv,
)))
})
}
diff --git a/cli/file_fetcher.rs b/cli/file_fetcher.rs
index 864c59cb5..d79bb4368 100644
--- a/cli/file_fetcher.rs
+++ b/cli/file_fetcher.rs
@@ -696,6 +696,7 @@ async fn fetch_once<'a>(
#[cfg(test)]
mod tests {
use crate::cache::GlobalHttpCache;
+ use crate::cache::RealDenoCacheEnv;
use crate::http_util::HttpClient;
use crate::version;
@@ -727,7 +728,7 @@ mod tests {
let location = temp_dir.path().join("deps").to_path_buf();
let blob_store: Arc<BlobStore> = Default::default();
let file_fetcher = FileFetcher::new(
- Arc::new(GlobalHttpCache::new(location)),
+ Arc::new(GlobalHttpCache::new(location, RealDenoCacheEnv)),
cache_setting,
true,
Arc::new(HttpClient::new(None, None)),
@@ -1173,7 +1174,10 @@ mod tests {
// invocation and indicates to "cache bust".
let location = temp_dir.path().join("deps").to_path_buf();
let file_fetcher = FileFetcher::new(
- Arc::new(GlobalHttpCache::new(location)),
+ Arc::new(GlobalHttpCache::new(
+ location,
+ crate::cache::RealDenoCacheEnv,
+ )),
CacheSetting::ReloadAll,
true,
Arc::new(HttpClient::new(None, None)),
@@ -1202,7 +1206,10 @@ mod tests {
let file_modified_01 = {
let file_fetcher = FileFetcher::new(
- Arc::new(GlobalHttpCache::new(location.clone())),
+ Arc::new(GlobalHttpCache::new(
+ location.clone(),
+ crate::cache::RealDenoCacheEnv,
+ )),
CacheSetting::Use,
true,
Arc::new(HttpClient::new(None, None)),
@@ -1231,7 +1238,10 @@ mod tests {
let file_modified_02 = {
let file_fetcher = FileFetcher::new(
- Arc::new(GlobalHttpCache::new(location)),
+ Arc::new(GlobalHttpCache::new(
+ location,
+ crate::cache::RealDenoCacheEnv,
+ )),
CacheSetting::Use,
true,
Arc::new(HttpClient::new(None, None)),
@@ -1362,7 +1372,10 @@ mod tests {
let metadata_file_modified_01 = {
let file_fetcher = FileFetcher::new(
- Arc::new(GlobalHttpCache::new(location.clone())),
+ Arc::new(GlobalHttpCache::new(
+ location.clone(),
+ crate::cache::RealDenoCacheEnv,
+ )),
CacheSetting::Use,
true,
Arc::new(HttpClient::new(None, None)),
@@ -1394,7 +1407,10 @@ mod tests {
let metadata_file_modified_02 = {
let file_fetcher = FileFetcher::new(
- Arc::new(GlobalHttpCache::new(location)),
+ Arc::new(GlobalHttpCache::new(
+ location,
+ crate::cache::RealDenoCacheEnv,
+ )),
CacheSetting::Use,
true,
Arc::new(HttpClient::new(None, None)),
@@ -1496,7 +1512,10 @@ mod tests {
let temp_dir = TempDir::new();
let location = temp_dir.path().join("deps").to_path_buf();
let file_fetcher = FileFetcher::new(
- Arc::new(GlobalHttpCache::new(location)),
+ Arc::new(GlobalHttpCache::new(
+ location,
+ crate::cache::RealDenoCacheEnv,
+ )),
CacheSetting::Use,
false,
Arc::new(HttpClient::new(None, None)),
@@ -1521,7 +1540,7 @@ mod tests {
let temp_dir = TempDir::new();
let location = temp_dir.path().join("deps").to_path_buf();
let file_fetcher_01 = FileFetcher::new(
- Arc::new(GlobalHttpCache::new(location.clone())),
+ Arc::new(GlobalHttpCache::new(location.clone(), RealDenoCacheEnv)),
CacheSetting::Only,
true,
Arc::new(HttpClient::new(None, None)),
@@ -1529,7 +1548,7 @@ mod tests {
None,
);
let file_fetcher_02 = FileFetcher::new(
- Arc::new(GlobalHttpCache::new(location)),
+ Arc::new(GlobalHttpCache::new(location, RealDenoCacheEnv)),
CacheSetting::Use,
true,
Arc::new(HttpClient::new(None, None)),
diff --git a/cli/lsp/completions.rs b/cli/lsp/completions.rs
index a895239a6..60244f2e4 100644
--- a/cli/lsp/completions.rs
+++ b/cli/lsp/completions.rs
@@ -520,14 +520,20 @@ mod tests {
source_fixtures: &[(&str, &str)],
location: &Path,
) -> Documents {
- let cache = Arc::new(GlobalHttpCache::new(location.to_path_buf()));
+ let cache = Arc::new(GlobalHttpCache::new(
+ location.to_path_buf(),
+ crate::cache::RealDenoCacheEnv,
+ ));
let mut documents = Documents::new(cache);
for (specifier, source, version, language_id) in fixtures {
let specifier =
resolve_url(specifier).expect("failed to create specifier");
documents.open(specifier, *version, *language_id, (*source).into());
}
- let http_cache = GlobalHttpCache::new(location.to_path_buf());
+ let http_cache = GlobalHttpCache::new(
+ location.to_path_buf(),
+ crate::cache::RealDenoCacheEnv,
+ );
for (specifier, source) in source_fixtures {
let specifier =
resolve_url(specifier).expect("failed to create specifier");
diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs
index 88c4c91cb..4e24673f3 100644
--- a/cli/lsp/diagnostics.rs
+++ b/cli/lsp/diagnostics.rs
@@ -1331,6 +1331,7 @@ fn generate_deno_diagnostics(
mod tests {
use super::*;
use crate::cache::GlobalHttpCache;
+ use crate::cache::RealDenoCacheEnv;
use crate::lsp::config::ConfigSnapshot;
use crate::lsp::config::Settings;
use crate::lsp::config::SpecifierSettings;
@@ -1349,7 +1350,10 @@ mod tests {
location: &Path,
maybe_import_map: Option<(&str, &str)>,
) -> StateSnapshot {
- let cache = Arc::new(GlobalHttpCache::new(location.to_path_buf()));
+ let cache = Arc::new(GlobalHttpCache::new(
+ location.to_path_buf(),
+ RealDenoCacheEnv,
+ ));
let mut documents = Documents::new(cache);
for (specifier, source, version, language_id) in fixtures {
let specifier =
@@ -1374,7 +1378,7 @@ mod tests {
maybe_import_map,
assets: Default::default(),
cache_metadata: cache::CacheMetadata::new(Arc::new(
- GlobalHttpCache::new(location.to_path_buf()),
+ GlobalHttpCache::new(location.to_path_buf(), RealDenoCacheEnv),
)),
maybe_node_resolver: None,
maybe_npm_resolver: None,
@@ -1424,7 +1428,8 @@ let c: number = "a";
None,
);
let snapshot = Arc::new(snapshot);
- let cache = Arc::new(GlobalHttpCache::new(cache_location));
+ let cache =
+ Arc::new(GlobalHttpCache::new(cache_location, RealDenoCacheEnv));
let ts_server = TsServer::new(Default::default(), cache);
// test enabled
@@ -1517,7 +1522,8 @@ let c: number = "a";
None,
);
let snapshot = Arc::new(snapshot);
- let cache = Arc::new(GlobalHttpCache::new(cache_location));
+ let cache =
+ Arc::new(GlobalHttpCache::new(cache_location, RealDenoCacheEnv));
let ts_server = TsServer::new(Default::default(), cache);
let config = mock_config();
diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs
index 7fdce9923..439cf547d 100644
--- a/cli/lsp/documents.rs
+++ b/cli/lsp/documents.rs
@@ -1851,6 +1851,7 @@ fn sort_and_remove_non_leaf_dirs(mut dirs: Vec<PathBuf>) -> Vec<PathBuf> {
#[cfg(test)]
mod tests {
use crate::cache::GlobalHttpCache;
+ use crate::cache::RealDenoCacheEnv;
use crate::npm::NpmResolution;
use super::*;
@@ -1861,7 +1862,10 @@ mod tests {
fn setup(temp_dir: &TempDir) -> (Documents, PathRef) {
let location = temp_dir.path().join("deps");
- let cache = Arc::new(GlobalHttpCache::new(location.to_path_buf()));
+ let cache = Arc::new(GlobalHttpCache::new(
+ location.to_path_buf(),
+ RealDenoCacheEnv,
+ ));
let documents = Documents::new(cache);
(documents, location)
}
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs
index 7a13e2396..5b9ba94f1 100644
--- a/cli/lsp/language_server.rs
+++ b/cli/lsp/language_server.rs
@@ -555,7 +555,10 @@ impl Inner {
http_client.clone(),
);
let location = dir.deps_folder_path();
- let deps_http_cache = Arc::new(GlobalHttpCache::new(location));
+ let deps_http_cache = Arc::new(GlobalHttpCache::new(
+ location,
+ crate::cache::RealDenoCacheEnv,
+ ));
let documents = Documents::new(deps_http_cache.clone());
let cache_metadata = cache::CacheMetadata::new(deps_http_cache.clone());
let performance = Arc::new(Performance::default());
@@ -904,7 +907,10 @@ impl Inner {
);
self.module_registries_location = module_registries_location;
// update the cache path
- let global_cache = Arc::new(GlobalHttpCache::new(dir.deps_folder_path()));
+ let global_cache = Arc::new(GlobalHttpCache::new(
+ dir.deps_folder_path(),
+ crate::cache::RealDenoCacheEnv,
+ ));
let maybe_local_cache =
self.config.maybe_vendor_dir_path().map(|local_path| {
Arc::new(LocalLspHttpCache::new(local_path, global_cache.clone()))
diff --git a/cli/lsp/registries.rs b/cli/lsp/registries.rs
index 0219653bd..186db50b8 100644
--- a/cli/lsp/registries.rs
+++ b/cli/lsp/registries.rs
@@ -422,7 +422,10 @@ pub struct ModuleRegistry {
impl ModuleRegistry {
pub fn new(location: PathBuf, http_client: Arc<HttpClient>) -> Self {
// the http cache should always be the global one for registry completions
- let http_cache = Arc::new(GlobalHttpCache::new(location));
+ let http_cache = Arc::new(GlobalHttpCache::new(
+ location,
+ crate::cache::RealDenoCacheEnv,
+ ));
let mut file_fetcher = FileFetcher::new(
http_cache.clone(),
CacheSetting::RespectHeaders,
diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs
index 353866513..75ed8ebe3 100644
--- a/cli/lsp/tsc.rs
+++ b/cli/lsp/tsc.rs
@@ -3915,6 +3915,7 @@ mod tests {
use super::*;
use crate::cache::GlobalHttpCache;
use crate::cache::HttpCache;
+ use crate::cache::RealDenoCacheEnv;
use crate::http_util::HeadersMap;
use crate::lsp::cache::CacheMetadata;
use crate::lsp::config::WorkspaceSettings;
@@ -3931,7 +3932,10 @@ mod tests {
fixtures: &[(&str, &str, i32, LanguageId)],
location: &Path,
) -> StateSnapshot {
- let cache = Arc::new(GlobalHttpCache::new(location.to_path_buf()));
+ let cache = Arc::new(GlobalHttpCache::new(
+ location.to_path_buf(),
+ RealDenoCacheEnv,
+ ));
let mut documents = Documents::new(cache.clone());
for (specifier, source, version, language_id) in fixtures {
let specifier =
@@ -3960,7 +3964,8 @@ mod tests {
sources: &[(&str, &str, i32, LanguageId)],
) -> (JsRuntime, Arc<StateSnapshot>, PathBuf) {
let location = temp_dir.path().join("deps").to_path_buf();
- let cache = Arc::new(GlobalHttpCache::new(location.clone()));
+ let cache =
+ Arc::new(GlobalHttpCache::new(location.clone(), RealDenoCacheEnv));
let state_snapshot = Arc::new(mock_state_snapshot(sources, &location));
let mut runtime = js_runtime(Default::default(), cache);
start(&mut runtime, debug).unwrap();
@@ -4440,7 +4445,7 @@ mod tests {
LanguageId::TypeScript,
)],
);
- let cache = Arc::new(GlobalHttpCache::new(location));
+ let cache = Arc::new(GlobalHttpCache::new(location, RealDenoCacheEnv));
let specifier_dep =
resolve_url("https://deno.land/x/example/a.ts").unwrap();
cache