refactor(cli): add Emitter struct (#18690)

Removes the functions in the `emit` module and replaces them with an
`Emitter` struct that can have "ctor dependencies" injected rather than
using functions to pass along the dependencies.

This is part of a long term refactor to move more functionality out of
proc state.
This commit is contained in:
David Sherret 2023-04-13 14:03:07 -04:00 committed by GitHub
parent 6e8618ae0f
commit d192d84a0e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 102 additions and 80 deletions

View file

@ -8,41 +8,99 @@ use deno_core::error::AnyError;
use deno_core::ModuleCode;
use deno_core::ModuleSpecifier;
use deno_graph::MediaType;
use deno_graph::Module;
use deno_graph::ModuleGraph;
use std::sync::Arc;
/// A hashing function that takes the source code and emit options
/// hash then generates a string hash which can be stored to
/// determine if the cached emit is valid or not.
pub fn get_source_hash(source_text: &str, emit_options_hash: u64) -> u64 {
FastInsecureHasher::new()
.write_str(source_text)
.write_u64(emit_options_hash)
.finish()
#[derive(Clone)]
pub struct Emitter {
emit_cache: EmitCache,
parsed_source_cache: ParsedSourceCache,
emit_options: deno_ast::EmitOptions,
// cached hash of the emit options
emit_options_hash: u64,
}
pub fn emit_parsed_source(
emit_cache: &EmitCache,
parsed_source_cache: &ParsedSourceCache,
specifier: &ModuleSpecifier,
media_type: MediaType,
source: &Arc<str>,
emit_options: &deno_ast::EmitOptions,
emit_config_hash: u64,
) -> Result<ModuleCode, AnyError> {
let source_hash = get_source_hash(source, emit_config_hash);
impl Emitter {
pub fn new(
emit_cache: EmitCache,
parsed_source_cache: ParsedSourceCache,
emit_options: deno_ast::EmitOptions,
) -> Self {
let emit_options_hash = FastInsecureHasher::new()
.write_hashable(&emit_options)
.finish();
Self {
emit_cache,
parsed_source_cache,
emit_options,
emit_options_hash,
}
}
if let Some(emit_code) = emit_cache.get_emit_code(specifier, source_hash) {
Ok(emit_code.into())
} else {
// this will use a cached version if it exists
let parsed_source = parsed_source_cache.get_or_parse_module(
specifier,
source.clone(),
media_type,
)?;
let transpiled_source = parsed_source.transpile(emit_options)?;
debug_assert!(transpiled_source.source_map.is_none());
emit_cache.set_emit_code(specifier, source_hash, &transpiled_source.text);
Ok(transpiled_source.text.into())
pub fn cache_module_emits(
&self,
graph: &ModuleGraph,
) -> Result<(), AnyError> {
for module in graph.modules() {
if let Module::Esm(module) = module {
let is_emittable = matches!(
module.media_type,
MediaType::TypeScript
| MediaType::Mts
| MediaType::Cts
| MediaType::Jsx
| MediaType::Tsx
);
if is_emittable {
self.emit_parsed_source(
&module.specifier,
module.media_type,
&module.source,
)?;
}
}
}
Ok(())
}
pub fn emit_parsed_source(
&self,
specifier: &ModuleSpecifier,
media_type: MediaType,
source: &Arc<str>,
) -> Result<ModuleCode, AnyError> {
let source_hash = self.get_source_hash(source);
if let Some(emit_code) =
self.emit_cache.get_emit_code(specifier, source_hash)
{
Ok(emit_code.into())
} else {
// this will use a cached version if it exists
let parsed_source = self.parsed_source_cache.get_or_parse_module(
specifier,
source.clone(),
media_type,
)?;
let transpiled_source = parsed_source.transpile(&self.emit_options)?;
debug_assert!(transpiled_source.source_map.is_none());
self.emit_cache.set_emit_code(
specifier,
source_hash,
&transpiled_source.text,
);
Ok(transpiled_source.text.into())
}
}
/// A hashing function that takes the source code and uses the global emit
/// options then generates a string hash which can be stored to
/// determine if the cached emit is valid or not.
pub fn get_source_hash(&self, source_text: &str) -> u64 {
FastInsecureHasher::new()
.write_str(source_text)
.write_u64(self.emit_options_hash)
.finish()
}
}

View file

@ -71,7 +71,7 @@ async fn run_subcommand(flags: Flags) -> Result<i32, AnyError> {
DenoSubcommand::Cache(cache_flags) => {
let ps = ProcState::from_flags(flags).await?;
ps.load_and_type_check_files(&cache_flags.files).await?;
ps.cache_module_emits()?;
ps.emitter.cache_module_emits(&ps.graph())?;
Ok(0)
}
DenoSubcommand::Check(check_flags) => {

View file

@ -1,7 +1,6 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
use crate::args::TsTypeLib;
use crate::emit::emit_parsed_source;
use crate::node;
use crate::proc_state::ProcState;
use crate::util::text_encoding::code_without_source_map;
@ -117,14 +116,10 @@ impl CliModuleLoader {
| MediaType::Jsx
| MediaType::Tsx => {
// get emit text
emit_parsed_source(
&self.ps.emit_cache,
&self.ps.parsed_source_cache,
self.ps.emitter.emit_parsed_source(
specifier,
*media_type,
source,
&self.ps.emit_options,
self.ps.emit_options_hash,
)?
}
MediaType::TsBuildInfo | MediaType::Wasm | MediaType::SourceMap => {

View file

@ -11,12 +11,11 @@ use crate::cache;
use crate::cache::Caches;
use crate::cache::DenoDir;
use crate::cache::EmitCache;
use crate::cache::FastInsecureHasher;
use crate::cache::HttpCache;
use crate::cache::NodeAnalysisCache;
use crate::cache::ParsedSourceCache;
use crate::cache::TypeCheckCache;
use crate::emit::emit_parsed_source;
use crate::emit::Emitter;
use crate::file_fetcher::FileFetcher;
use crate::graph_util::build_graph_with_npm_resolution;
use crate::graph_util::graph_lock_or_exit;
@ -36,7 +35,6 @@ use crate::tools::check;
use crate::util::progress_bar::ProgressBar;
use crate::util::progress_bar::ProgressBarStyle;
use deno_ast::MediaType;
use deno_core::anyhow::anyhow;
use deno_core::anyhow::Context;
use deno_core::error::custom_error;
@ -80,8 +78,7 @@ pub struct Inner {
pub http_client: HttpClient,
pub options: Arc<CliOptions>,
pub emit_cache: EmitCache,
pub emit_options: deno_ast::EmitOptions,
pub emit_options_hash: u64,
pub emitter: Emitter,
graph_container: ModuleGraphContainer,
pub lockfile: Option<Arc<Mutex<Lockfile>>>,
pub maybe_import_map: Option<Arc<ImportMap>>,
@ -143,8 +140,7 @@ impl ProcState {
caches: self.caches.clone(),
options: self.options.clone(),
emit_cache: self.emit_cache.clone(),
emit_options_hash: self.emit_options_hash,
emit_options: self.emit_options.clone(),
emitter: self.emitter.clone(),
file_fetcher: self.file_fetcher.clone(),
http_client: self.http_client.clone(),
graph_container: Default::default(),
@ -301,6 +297,12 @@ impl ProcState {
let emit_cache = EmitCache::new(dir.gen_cache.clone());
let parsed_source_cache =
ParsedSourceCache::new(caches.dep_analysis_db(&dir));
let emit_options: deno_ast::EmitOptions = ts_config_result.ts_config.into();
let emitter = Emitter::new(
emit_cache.clone(),
parsed_source_cache.clone(),
emit_options,
);
let npm_cache = NpmCache::from_deno_dir(
&dir,
cli_options.cache_setting(),
@ -310,16 +312,12 @@ impl ProcState {
let node_analysis_cache =
NodeAnalysisCache::new(caches.node_analysis_db(&dir));
let emit_options: deno_ast::EmitOptions = ts_config_result.ts_config.into();
Ok(ProcState(Arc::new(Inner {
dir,
caches,
options: cli_options,
emit_cache,
emit_options_hash: FastInsecureHasher::new()
.write_hashable(&emit_options)
.finish(),
emit_options,
emitter,
file_fetcher: Arc::new(file_fetcher),
http_client,
graph_container: Default::default(),
@ -626,34 +624,6 @@ impl ProcState {
resolution
}
pub fn cache_module_emits(&self) -> Result<(), AnyError> {
let graph = self.graph();
for module in graph.modules() {
if let Module::Esm(module) = module {
let is_emittable = matches!(
module.media_type,
MediaType::TypeScript
| MediaType::Mts
| MediaType::Cts
| MediaType::Jsx
| MediaType::Tsx
);
if is_emittable {
emit_parsed_source(
&self.emit_cache,
&self.parsed_source_cache,
&module.specifier,
module.media_type,
&module.source,
&self.emit_options,
self.emit_options_hash,
)?;
}
}
}
Ok(())
}
/// Creates the default loader used for creating a graph.
pub fn create_graph_loader(&self) -> cache::FetchCacher {
cache::FetchCacher::new(

View file

@ -4,7 +4,6 @@ use crate::args::CoverageFlags;
use crate::args::FileFlags;
use crate::args::Flags;
use crate::colors;
use crate::emit::get_source_hash;
use crate::proc_state::ProcState;
use crate::tools::fmt::format_json;
use crate::util::fs::FileCollector;
@ -698,7 +697,7 @@ pub async fn cover_files(
| MediaType::Mts
| MediaType::Cts
| MediaType::Tsx => {
let source_hash = get_source_hash(&file.source, ps.emit_options_hash);
let source_hash = ps.emitter.get_source_hash(&file.source);
match ps.emit_cache.get_emit_code(&file.specifier, source_hash) {
Some(code) => code.into(),
None => {