diff options
author | David Sherret <dsherret@users.noreply.github.com> | 2022-06-20 17:59:52 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-06-20 17:59:52 -0400 |
commit | a7339f756c5f871026ab080e849d3dd37f2ca124 (patch) | |
tree | 900d033db951a5e43d67b0ab0cc582fe3cc23b1a /cli | |
parent | a7a64438e2cb054700cb35936c941b7444b8bd2d (diff) |
refactor: add `EmitCache` trait (#14925)
Diffstat (limited to 'cli')
-rw-r--r-- | cli/cache.rs | 107 | ||||
-rw-r--r-- | cli/disk_cache.rs | 79 | ||||
-rw-r--r-- | cli/emit.rs | 176 | ||||
-rw-r--r-- | cli/lsp/cache.rs | 3 | ||||
-rw-r--r-- | cli/main.rs | 2 | ||||
-rw-r--r-- | cli/proc_state.rs | 46 | ||||
-rw-r--r-- | cli/tools/bench.rs | 3 | ||||
-rw-r--r-- | cli/tools/test.rs | 3 |
8 files changed, 242 insertions, 177 deletions
diff --git a/cli/cache.rs b/cli/cache.rs index 8f8e1ade1..bdb38e393 100644 --- a/cli/cache.rs +++ b/cli/cache.rs @@ -8,7 +8,6 @@ use deno_core::error::AnyError; 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; @@ -40,22 +39,13 @@ pub trait Cacher { ) -> Option<String>; /// Set a value in the cache. fn set( - &mut self, + &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 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 struct FetchCacher { @@ -81,30 +71,6 @@ impl FetchCacher { 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 { @@ -172,74 +138,3 @@ impl Loader for FetchCacher { .boxed() } } - -impl Cacher for FetchCacher { - fn get( - &self, - cache_type: CacheType, - specifier: &ModuleSpecifier, - ) -> Option<String> { - let extension = match cache_type { - 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() - .and_then(|b| String::from_utf8(b).ok()) - } - - fn set( - &mut self, - cache_type: CacheType, - specifier: &ModuleSpecifier, - value: String, - ) -> Result<(), AnyError> { - let extension = match cache_type { - 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 - } -} diff --git a/cli/disk_cache.rs b/cli/disk_cache.rs index 19369274e..c23f7f4df 100644 --- a/cli/disk_cache.rs +++ b/cli/disk_cache.rs @@ -1,7 +1,13 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. +use crate::cache::CacheType; +use crate::cache::Cacher; +use crate::cache::EmitMetadata; use crate::fs_util; use crate::http_cache::url_to_filename; +use deno_ast::ModuleSpecifier; +use deno_core::error::AnyError; +use deno_core::serde_json; use deno_core::url::{Host, Url}; use std::ffi::OsStr; use std::fs; @@ -145,6 +151,79 @@ impl DiskCache { fs_util::atomic_write_file(&path, data, crate::http_cache::CACHE_PERM) .map_err(|e| with_io_context(&e, format!("{:#?}", &path))) } + + fn get_emit_metadata( + &self, + specifier: &ModuleSpecifier, + ) -> Option<EmitMetadata> { + let filename = self.get_cache_filename_with_extension(specifier, "meta")?; + let bytes = self.get(&filename).ok()?; + serde_json::from_slice(&bytes).ok() + } + + fn set_emit_metadata( + &self, + specifier: &ModuleSpecifier, + data: EmitMetadata, + ) -> Result<(), AnyError> { + let filename = self + .get_cache_filename_with_extension(specifier, "meta") + .unwrap(); + let bytes = serde_json::to_vec(&data)?; + self.set(&filename, &bytes).map_err(|e| e.into()) + } +} + +// todo(13302): remove and replace with sqlite database +impl Cacher for DiskCache { + fn get( + &self, + cache_type: CacheType, + specifier: &ModuleSpecifier, + ) -> Option<String> { + let extension = match cache_type { + 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.get_cache_filename_with_extension(specifier, extension)?; + self + .get(&filename) + .ok() + .and_then(|b| String::from_utf8(b).ok()) + } + + fn set( + &self, + cache_type: CacheType, + specifier: &ModuleSpecifier, + value: String, + ) -> Result<(), AnyError> { + let extension = match cache_type { + 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 + .get_cache_filename_with_extension(specifier, extension) + .unwrap(); + self.set(&filename, value.as_bytes()).map_err(|e| e.into()) + } } #[cfg(test)] diff --git a/cli/emit.rs b/cli/emit.rs index 1722cd881..10089bc8a 100644 --- a/cli/emit.rs +++ b/cli/emit.rs @@ -43,6 +43,90 @@ use std::result; use std::sync::Arc; use std::time::Instant; +/// Emit cache for a single file. +#[derive(Debug, Clone, PartialEq)] +pub struct SpecifierEmitCacheData { + pub source_hash: String, + pub text: String, + pub map: Option<String>, +} + +pub trait EmitCache { + /// Gets the emit data from the cache. + fn get_emit_data( + &self, + specifier: &ModuleSpecifier, + ) -> Option<SpecifierEmitCacheData>; + /// Gets the stored hash of the source of the provider specifier + /// to tell if the emit is out of sync with the source. + /// TODO(13302): this is actually not reliable and should be removed + /// once switching to an sqlite db + fn get_source_hash(&self, specifier: &ModuleSpecifier) -> Option<String>; + /// Gets the emitted JavaScript of the TypeScript source. + /// TODO(13302): remove this once switching to an sqlite db + fn get_emit_text(&self, specifier: &ModuleSpecifier) -> Option<String>; + /// Sets the emit data in the cache. + fn set_emit_data( + &self, + specifier: ModuleSpecifier, + data: SpecifierEmitCacheData, + ) -> Result<(), AnyError>; + /// Gets the .tsbuildinfo file from the cache. + fn get_tsbuildinfo(&self, specifier: &ModuleSpecifier) -> Option<String>; + /// Sets the .tsbuildinfo file in the cache. + fn set_tsbuildinfo( + &self, + specifier: ModuleSpecifier, + text: String, + ) -> Result<(), AnyError>; +} + +impl<T: Cacher> EmitCache for T { + fn get_emit_data( + &self, + specifier: &ModuleSpecifier, + ) -> Option<SpecifierEmitCacheData> { + Some(SpecifierEmitCacheData { + source_hash: self.get_source_hash(specifier)?, + text: self.get_emit_text(specifier)?, + map: self.get(CacheType::SourceMap, specifier), + }) + } + + fn get_source_hash(&self, specifier: &ModuleSpecifier) -> Option<String> { + self.get(CacheType::Version, specifier) + } + + fn get_emit_text(&self, specifier: &ModuleSpecifier) -> Option<String> { + self.get(CacheType::Emit, specifier) + } + + fn set_emit_data( + &self, + specifier: ModuleSpecifier, + data: SpecifierEmitCacheData, + ) -> Result<(), AnyError> { + self.set(CacheType::Version, &specifier, data.source_hash)?; + self.set(CacheType::Emit, &specifier, data.text)?; + if let Some(map) = data.map { + self.set(CacheType::SourceMap, &specifier, map)?; + } + Ok(()) + } + + fn get_tsbuildinfo(&self, specifier: &ModuleSpecifier) -> Option<String> { + self.get(CacheType::TypeScriptBuildInfo, specifier) + } + + fn set_tsbuildinfo( + &self, + specifier: ModuleSpecifier, + text: String, + ) -> Result<(), AnyError> { + self.set(CacheType::TypeScriptBuildInfo, &specifier, text) + } +} + /// Represents the "default" type library that should be used when type /// checking the code in the module graph. Note that a user provided config /// of `"lib"` would override this value. @@ -325,7 +409,7 @@ pub struct CheckEmitResult { pub fn check_and_maybe_emit( roots: &[(ModuleSpecifier, ModuleKind)], graph_data: Arc<RwLock<GraphData>>, - cache: &mut dyn Cacher, + cache: &dyn EmitCache, options: CheckOptions, ) -> Result<CheckEmitResult, AnyError> { let check_js = options.ts_config.get_check_js(); @@ -358,7 +442,7 @@ pub fn check_and_maybe_emit( let maybe_tsbuildinfo = if options.reload { None } else { - cache.get(CacheType::TypeScriptBuildInfo, &roots[0].0) + cache.get_tsbuildinfo(&roots[0].0) }; // to make tsc build info work, we need to consistently hash modules, so that // tsc can better determine if an emit is still valid or not, so we provide @@ -400,9 +484,29 @@ pub fn check_and_maybe_emit( // while we retrieve the build info for just the first module, it can be // used for all the roots in the graph, so we will cache it for all roots for (root, _) in roots { - cache.set(CacheType::TypeScriptBuildInfo, root, info.clone())?; + cache.set_tsbuildinfo(root.clone(), info.to_string())?; + } + } + + struct SpecifierEmitData { + pub version_hash: String, + pub text: Option<String>, + pub map: Option<String>, + } + + impl SpecifierEmitData { + fn into_cache_data(self) -> Option<SpecifierEmitCacheData> { + self.text.map(|text| SpecifierEmitCacheData { + source_hash: self.version_hash, + text, + map: self.map, + }) } } + + // combine the emitted files into groups based on their specifier and media type + let mut emit_data_items: HashMap<ModuleSpecifier, SpecifierEmitData> = + HashMap::with_capacity(response.emitted_files.len()); for emit in response.emitted_files.into_iter() { if let Some(specifiers) = emit.maybe_specifiers { assert!(specifiers.len() == 1); @@ -437,14 +541,21 @@ pub fn check_and_maybe_emit( log::debug!("skipping emit for {}", specifier); continue; } + + let mut emit_data_item = emit_data_items + .entry(specifier.clone()) + .or_insert_with(|| SpecifierEmitData { + version_hash: get_version(source_bytes, &config_bytes), + text: None, + map: None, + }); + match emit.media_type { MediaType::JavaScript | MediaType::Mjs | MediaType::Cjs => { - let version = get_version(source_bytes, &config_bytes); - cache.set(CacheType::Version, &specifier, version)?; - cache.set(CacheType::Emit, &specifier, emit.data)?; + emit_data_item.text = Some(emit.data); } MediaType::SourceMap => { - cache.set(CacheType::SourceMap, &specifier, emit.data)?; + emit_data_item.map = Some(emit.data); } _ => unreachable!( "unexpected media_type {} {}", @@ -453,6 +564,13 @@ pub fn check_and_maybe_emit( } } } + + // now insert these items into the cache + for (specifier, data) in emit_data_items.into_iter() { + if let Some(cache_data) = data.into_cache_data() { + cache.set_emit_data(specifier, cache_data)?; + } + } } Ok(CheckEmitResult { @@ -472,7 +590,7 @@ pub struct EmitOptions { // `check_and_maybe_emit()`, but the AST isn't stored in that. Cleanup. pub fn emit( graph: &ModuleGraph, - cache: &mut dyn Cacher, + cache: &dyn EmitCache, options: EmitOptions, ) -> Result<CheckEmitResult, AnyError> { let start = Instant::now(); @@ -493,15 +611,9 @@ pub fn emit( module.maybe_source.as_ref().map(|s| s.as_bytes()).unwrap(), &config_bytes, ); - let is_valid = - cache - .get(CacheType::Version, &module.specifier) - .map_or(false, |v| { - v == get_version( - module.maybe_source.as_ref().map(|s| s.as_bytes()).unwrap(), - &config_bytes, - ) - }); + let is_valid = cache + .get_source_hash(&module.specifier) + .map_or(false, |v| v == version); if is_valid && !needs_reload { continue; } @@ -511,13 +623,14 @@ pub fn emit( .map(|ps| ps.transpile(&emit_options)) .unwrap()?; emit_count += 1; - cache.set(CacheType::Emit, &module.specifier, transpiled_source.text)?; - if let Some(map) = transpiled_source.source_map { - cache.set(CacheType::SourceMap, &module.specifier, map)?; - } - if !is_valid { - cache.set(CacheType::Version, &module.specifier, version)?; - } + cache.set_emit_data( + module.specifier.clone(), + SpecifierEmitCacheData { + source_hash: version, + text: transpiled_source.text, + map: transpiled_source.source_map, + }, + )?; } let stats = Stats(vec![ @@ -538,7 +651,7 @@ pub fn emit( /// a valid emit in the cache. fn valid_emit( graph_data: &GraphData, - cache: &dyn Cacher, + cache: &dyn EmitCache, ts_config: &TsConfig, reload: bool, reload_exclusions: &HashSet<ModuleSpecifier>, @@ -564,13 +677,20 @@ fn valid_emit( continue; } } - _ => continue, + MediaType::Json + | MediaType::TsBuildInfo + | MediaType::SourceMap + | MediaType::Dts + | MediaType::Dmts + | MediaType::Dcts + | MediaType::Wasm + | MediaType::Unknown => continue, } if reload && !reload_exclusions.contains(specifier) { return false; } - if let Some(version) = cache.get(CacheType::Version, specifier) { - if version != get_version(code.as_bytes(), &config_bytes) { + if let Some(source_hash) = cache.get_source_hash(specifier) { + if source_hash != get_version(code.as_bytes(), &config_bytes) { return false; } } else { diff --git a/cli/lsp/cache.rs b/cli/lsp/cache.rs index bdf9db607..249177a64 100644 --- a/cli/lsp/cache.rs +++ b/cli/lsp/cache.rs @@ -1,6 +1,5 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. -use crate::cache::CacherLoader; use crate::cache::FetchCacher; use crate::config_file::ConfigFile; use crate::flags::Flags; @@ -87,7 +86,7 @@ impl CacheServer { roots, false, maybe_imports.clone(), - cache.as_mut_loader(), + &mut cache, maybe_resolver, None, None, diff --git a/cli/main.rs b/cli/main.rs index 1f545d51f..471afdffd 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -719,7 +719,7 @@ async fn create_graph_and_maybe_check( let check_result = emit::check_and_maybe_emit( &graph.roots, Arc::new(RwLock::new(graph.as_ref().into())), - &mut cache, + &ps.dir.gen_cache, emit::CheckOptions { type_check_mode: ps.flags.type_check_mode.clone(), debug, diff --git a/cli/proc_state.rs b/cli/proc_state.rs index 0ad0cd093..e06c3f772 100644 --- a/cli/proc_state.rs +++ b/cli/proc_state.rs @@ -9,6 +9,7 @@ use crate::config_file::ConfigFile; use crate::config_file::MaybeImportsResult; use crate::deno_dir; use crate::emit; +use crate::emit::EmitCache; use crate::file_fetcher::get_root_cert_store; use crate::file_fetcher::CacheSetting; use crate::file_fetcher::FileFetcher; @@ -499,7 +500,7 @@ impl ProcState { reload: self.flags.reload, reload_exclusions, }; - let emit_result = emit::emit(&graph, &mut cache, options)?; + let emit_result = emit::emit(&graph, &self.dir.gen_cache, options)?; log::debug!("{}", emit_result.stats); } else { let maybe_config_specifier = self @@ -519,7 +520,7 @@ impl ProcState { let emit_result = emit::check_and_maybe_emit( &roots, self.graph_data.clone(), - &mut cache, + &self.dir.gen_cache, options, )?; if !emit_result.diagnostics.is_empty() { @@ -633,16 +634,10 @@ impl ProcState { | MediaType::Cts | MediaType::Jsx | MediaType::Tsx => { - let emit_path = self - .dir - .gen_cache - .get_cache_filename_with_extension(&found_url, "js") - .unwrap_or_else(|| { - unreachable!("Unable to get cache filename: {}", &found_url) - }); - match self.dir.gen_cache.get(&emit_path) { - Ok(b) => String::from_utf8(b).unwrap(), - Err(_) => unreachable!("Unexpected missing emit: {}\n\nTry reloading with the --reload CLI flag or deleting your DENO_DIR.", found_url), + let cached_text = self.dir.gen_cache.get_emit_text(&found_url); + match cached_text { + Some(text) => text, + None => unreachable!("Unexpected missing emit: {}\n\nTry reloading with the --reload CLI flag or deleting your DENO_DIR.", found_url), } } MediaType::TsBuildInfo | MediaType::Wasm | MediaType::SourceMap => { @@ -665,28 +660,6 @@ impl ProcState { )), } } - - // TODO(@kitsonk) this should be refactored to get it from the module graph - fn get_emit(&self, url: &Url) -> Option<(Vec<u8>, Option<Vec<u8>>)> { - let emit_path = self - .dir - .gen_cache - .get_cache_filename_with_extension(url, "js")?; - let emit_map_path = self - .dir - .gen_cache - .get_cache_filename_with_extension(url, "js.map")?; - if let Ok(code) = self.dir.gen_cache.get(&emit_path) { - let maybe_map = if let Ok(map) = self.dir.gen_cache.get(&emit_map_path) { - Some(map) - } else { - None - }; - Some((code, maybe_map)) - } else { - None - } - } } // TODO(@kitsonk) this is only temporary, but should be refactored to somewhere @@ -700,8 +673,9 @@ impl SourceMapGetter for ProcState { "wasm" | "file" | "http" | "https" | "data" | "blob" => (), _ => return None, } - if let Some((code, maybe_map)) = self.get_emit(&specifier) { - source_map_from_code(&code).or(maybe_map) + if let Some(cache_data) = self.dir.gen_cache.get_emit_data(&specifier) { + source_map_from_code(cache_data.text.as_bytes()) + .or_else(|| cache_data.map.map(|t| t.into_bytes())) } else if let Ok(source) = self.load(specifier, None, false) { source_map_from_code(&source.code) } else { diff --git a/cli/tools/bench.rs b/cli/tools/bench.rs index d9e9c38c3..e88151648 100644 --- a/cli/tools/bench.rs +++ b/cli/tools/bench.rs @@ -1,7 +1,6 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. use crate::cache; -use crate::cache::CacherLoader; use crate::colors; use crate::compat; use crate::create_main_worker; @@ -632,7 +631,7 @@ pub async fn run_benchmarks_with_watch( .collect(), false, maybe_imports, - cache.as_mut_loader(), + &mut cache, maybe_resolver, maybe_locker, None, diff --git a/cli/tools/test.rs b/cli/tools/test.rs index ef4d35cce..170c1a12d 100644 --- a/cli/tools/test.rs +++ b/cli/tools/test.rs @@ -1,7 +1,6 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. use crate::cache; -use crate::cache::CacherLoader; use crate::colors; use crate::compat; use crate::create_main_worker; @@ -1453,7 +1452,7 @@ pub async fn run_tests_with_watch( .collect(), false, maybe_imports, - cache.as_mut_loader(), + &mut cache, maybe_resolver, maybe_locker, None, |