forked from denoland/deno
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: integrate deno_graph into CLI (denoland#12369)
- Loading branch information
Showing
78 changed files
with
2,972 additions
and
5,118 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,349 @@ | ||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. | ||
|
||
use crate::disk_cache::DiskCache; | ||
use crate::file_fetcher::FileFetcher; | ||
|
||
use deno_core::error::AnyError; | ||
use deno_core::futures::future; | ||
use deno_core::futures::FutureExt; | ||
use deno_core::serde::Deserialize; | ||
use deno_core::serde::Serialize; | ||
use deno_core::serde_json; | ||
use deno_core::ModuleSpecifier; | ||
use deno_graph::source::CacheInfo; | ||
use deno_graph::source::LoadFuture; | ||
use deno_graph::source::LoadResponse; | ||
use deno_graph::source::Loader; | ||
use deno_runtime::permissions::Permissions; | ||
use std::collections::HashMap; | ||
use std::sync::Arc; | ||
|
||
#[derive(Debug, Deserialize, Serialize)] | ||
pub struct EmitMetadata { | ||
pub version_hash: String, | ||
} | ||
|
||
pub(crate) enum CacheType { | ||
Declaration, | ||
Emit, | ||
SourceMap, | ||
TypeScriptBuildInfo, | ||
Version, | ||
} | ||
|
||
/// A trait which provides a concise implementation to getting and setting | ||
/// values in a cache. | ||
pub(crate) trait Cacher { | ||
/// Get a value from the cache. | ||
fn get( | ||
&self, | ||
cache_type: CacheType, | ||
specifier: &ModuleSpecifier, | ||
) -> Option<String>; | ||
/// Set a value in the cache. | ||
fn set( | ||
&mut self, | ||
cache_type: CacheType, | ||
specifier: &ModuleSpecifier, | ||
value: String, | ||
) -> Result<(), AnyError>; | ||
} | ||
|
||
/// Combines the cacher trait along with the deno_graph Loader trait to provide | ||
/// a single interface to be able to load and cache modules when building a | ||
/// graph. | ||
pub(crate) trait CacherLoader: Cacher + Loader { | ||
fn as_cacher(&self) -> &dyn Cacher; | ||
fn as_mut_loader(&mut self) -> &mut dyn Loader; | ||
fn as_mut_cacher(&mut self) -> &mut dyn Cacher; | ||
} | ||
|
||
/// 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(crate) struct FetchCacher { | ||
disk_cache: DiskCache, | ||
dynamic_permissions: Permissions, | ||
file_fetcher: Arc<FileFetcher>, | ||
root_permissions: Permissions, | ||
} | ||
|
||
impl FetchCacher { | ||
pub fn new( | ||
disk_cache: DiskCache, | ||
file_fetcher: FileFetcher, | ||
root_permissions: Permissions, | ||
dynamic_permissions: Permissions, | ||
) -> Self { | ||
let file_fetcher = Arc::new(file_fetcher); | ||
|
||
Self { | ||
disk_cache, | ||
dynamic_permissions, | ||
file_fetcher, | ||
root_permissions, | ||
} | ||
} | ||
|
||
fn get_emit_metadata( | ||
&self, | ||
specifier: &ModuleSpecifier, | ||
) -> Option<EmitMetadata> { | ||
let filename = self | ||
.disk_cache | ||
.get_cache_filename_with_extension(specifier, "meta")?; | ||
let bytes = self.disk_cache.get(&filename).ok()?; | ||
serde_json::from_slice(&bytes).ok() | ||
} | ||
|
||
fn set_emit_metadata( | ||
&self, | ||
specifier: &ModuleSpecifier, | ||
data: EmitMetadata, | ||
) -> Result<(), AnyError> { | ||
let filename = self | ||
.disk_cache | ||
.get_cache_filename_with_extension(specifier, "meta") | ||
.unwrap(); | ||
let bytes = serde_json::to_vec(&data)?; | ||
self.disk_cache.set(&filename, &bytes).map_err(|e| e.into()) | ||
} | ||
} | ||
|
||
impl Loader for FetchCacher { | ||
fn get_cache_info(&self, specifier: &ModuleSpecifier) -> Option<CacheInfo> { | ||
let local = self.file_fetcher.get_local_path(specifier)?; | ||
if local.is_file() { | ||
let location = &self.disk_cache.location; | ||
let emit = self | ||
.disk_cache | ||
.get_cache_filename_with_extension(specifier, "js") | ||
.map(|p| location.join(p)) | ||
.filter(|p| p.is_file()); | ||
let map = self | ||
.disk_cache | ||
.get_cache_filename_with_extension(specifier, "js.map") | ||
.map(|p| location.join(p)) | ||
.filter(|p| p.is_file()); | ||
Some(CacheInfo { | ||
local: Some(local), | ||
emit, | ||
map, | ||
}) | ||
} else { | ||
None | ||
} | ||
} | ||
|
||
fn load( | ||
&mut self, | ||
specifier: &ModuleSpecifier, | ||
is_dynamic: bool, | ||
) -> LoadFuture { | ||
let specifier = specifier.clone(); | ||
let mut permissions = if is_dynamic { | ||
self.dynamic_permissions.clone() | ||
} else { | ||
self.root_permissions.clone() | ||
}; | ||
let file_fetcher = self.file_fetcher.clone(); | ||
|
||
async move { | ||
let load_result = file_fetcher | ||
.fetch(&specifier, &mut permissions) | ||
.await | ||
.map_or_else( | ||
|err| { | ||
if let Some(err) = err.downcast_ref::<std::io::Error>() { | ||
if err.kind() == std::io::ErrorKind::NotFound { | ||
return Ok(None); | ||
} | ||
} | ||
Err(err) | ||
}, | ||
|file| { | ||
Ok(Some(LoadResponse { | ||
specifier: file.specifier, | ||
maybe_headers: file.maybe_headers, | ||
content: file.source, | ||
})) | ||
}, | ||
); | ||
|
||
(specifier, load_result) | ||
} | ||
.boxed() | ||
} | ||
} | ||
|
||
impl Cacher for FetchCacher { | ||
fn get( | ||
&self, | ||
cache_type: CacheType, | ||
specifier: &ModuleSpecifier, | ||
) -> Option<String> { | ||
let extension = match cache_type { | ||
CacheType::Declaration => "d.ts", | ||
CacheType::Emit => "js", | ||
CacheType::SourceMap => "js.map", | ||
CacheType::TypeScriptBuildInfo => "buildinfo", | ||
CacheType::Version => { | ||
return self.get_emit_metadata(specifier).map(|d| d.version_hash) | ||
} | ||
}; | ||
let filename = self | ||
.disk_cache | ||
.get_cache_filename_with_extension(specifier, extension)?; | ||
self | ||
.disk_cache | ||
.get(&filename) | ||
.ok() | ||
.map(|b| String::from_utf8(b).ok()) | ||
.flatten() | ||
} | ||
|
||
fn set( | ||
&mut self, | ||
cache_type: CacheType, | ||
specifier: &ModuleSpecifier, | ||
value: String, | ||
) -> Result<(), AnyError> { | ||
let extension = match cache_type { | ||
CacheType::Declaration => "d.ts", | ||
CacheType::Emit => "js", | ||
CacheType::SourceMap => "js.map", | ||
CacheType::TypeScriptBuildInfo => "buildinfo", | ||
CacheType::Version => { | ||
let data = if let Some(mut data) = self.get_emit_metadata(specifier) { | ||
data.version_hash = value; | ||
data | ||
} else { | ||
EmitMetadata { | ||
version_hash: value, | ||
} | ||
}; | ||
return self.set_emit_metadata(specifier, data); | ||
} | ||
}; | ||
let filename = self | ||
.disk_cache | ||
.get_cache_filename_with_extension(specifier, extension) | ||
.unwrap(); | ||
self | ||
.disk_cache | ||
.set(&filename, value.as_bytes()) | ||
.map_err(|e| e.into()) | ||
} | ||
} | ||
|
||
impl CacherLoader for FetchCacher { | ||
fn as_cacher(&self) -> &dyn Cacher { | ||
self | ||
} | ||
|
||
fn as_mut_loader(&mut self) -> &mut dyn Loader { | ||
self | ||
} | ||
|
||
fn as_mut_cacher(&mut self) -> &mut dyn Cacher { | ||
self | ||
} | ||
} | ||
|
||
/// An in memory cache that is used by the runtime `Deno.emit()` API to provide | ||
/// the same behavior as the disk cache when sources are provided. | ||
#[derive(Debug)] | ||
pub(crate) struct MemoryCacher { | ||
sources: HashMap<String, Arc<String>>, | ||
declarations: HashMap<ModuleSpecifier, String>, | ||
emits: HashMap<ModuleSpecifier, String>, | ||
maps: HashMap<ModuleSpecifier, String>, | ||
build_infos: HashMap<ModuleSpecifier, String>, | ||
versions: HashMap<ModuleSpecifier, String>, | ||
} | ||
|
||
impl MemoryCacher { | ||
pub fn new(sources: HashMap<String, Arc<String>>) -> Self { | ||
Self { | ||
sources, | ||
declarations: HashMap::default(), | ||
emits: HashMap::default(), | ||
maps: HashMap::default(), | ||
build_infos: HashMap::default(), | ||
versions: HashMap::default(), | ||
} | ||
} | ||
} | ||
|
||
impl Loader for MemoryCacher { | ||
fn load( | ||
&mut self, | ||
specifier: &ModuleSpecifier, | ||
_is_dynamic: bool, | ||
) -> LoadFuture { | ||
let mut specifier_str = specifier.to_string(); | ||
if !self.sources.contains_key(&specifier_str) { | ||
specifier_str = specifier_str.replace("file:https:///", "/"); | ||
if !self.sources.contains_key(&specifier_str) { | ||
specifier_str = specifier_str[3..].to_string(); | ||
} | ||
} | ||
let response = self.sources.get(&specifier_str).map(|c| LoadResponse { | ||
specifier: specifier.clone(), | ||
maybe_headers: None, | ||
content: c.to_owned(), | ||
}); | ||
Box::pin(future::ready((specifier.clone(), Ok(response)))) | ||
} | ||
} | ||
|
||
impl Cacher for MemoryCacher { | ||
fn get( | ||
&self, | ||
cache_type: CacheType, | ||
specifier: &ModuleSpecifier, | ||
) -> Option<String> { | ||
match cache_type { | ||
CacheType::Declaration => self.declarations.get(specifier).cloned(), | ||
CacheType::Emit => self.emits.get(specifier).cloned(), | ||
CacheType::SourceMap => self.maps.get(specifier).cloned(), | ||
CacheType::TypeScriptBuildInfo => { | ||
self.build_infos.get(specifier).cloned() | ||
} | ||
CacheType::Version => self.versions.get(specifier).cloned(), | ||
} | ||
} | ||
|
||
fn set( | ||
&mut self, | ||
cache_type: CacheType, | ||
specifier: &ModuleSpecifier, | ||
value: String, | ||
) -> Result<(), AnyError> { | ||
match cache_type { | ||
CacheType::Declaration => { | ||
self.declarations.insert(specifier.clone(), value) | ||
} | ||
CacheType::Emit => self.emits.insert(specifier.clone(), value), | ||
CacheType::SourceMap => self.maps.insert(specifier.clone(), value), | ||
CacheType::TypeScriptBuildInfo => { | ||
self.build_infos.insert(specifier.clone(), value) | ||
} | ||
CacheType::Version => self.versions.insert(specifier.clone(), value), | ||
}; | ||
Ok(()) | ||
} | ||
} | ||
|
||
impl CacherLoader for MemoryCacher { | ||
fn as_cacher(&self) -> &dyn Cacher { | ||
self | ||
} | ||
|
||
fn as_mut_loader(&mut self) -> &mut dyn Loader { | ||
self | ||
} | ||
|
||
fn as_mut_cacher(&mut self) -> &mut dyn Cacher { | ||
self | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.