diff options
author | David Sherret <dsherret@users.noreply.github.com> | 2023-04-14 18:05:46 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-04-14 18:05:46 -0400 |
commit | 0a67a3965fd7c0041b818a5cb1068dee19ae25b1 (patch) | |
tree | 2ea4de052e7c193ae53b60867b5775f0eff83bb6 /cli/tools/check.rs | |
parent | f6a28e3e629b5e98905c876f38999107b9ec6ea9 (diff) |
refactor: add `TypeChecker` struct (#18709)
Adds a `TypeChecker` struct and pushes more shared functionality into
it.
Diffstat (limited to 'cli/tools/check.rs')
-rw-r--r-- | cli/tools/check.rs | 239 |
1 files changed, 138 insertions, 101 deletions
diff --git a/cli/tools/check.rs b/cli/tools/check.rs index dcc9b2843..eee82adcf 100644 --- a/cli/tools/check.rs +++ b/cli/tools/check.rs @@ -12,136 +12,172 @@ use deno_runtime::colors; use once_cell::sync::Lazy; use regex::Regex; +use crate::args::CliOptions; use crate::args::TsConfig; +use crate::args::TsConfigType; +use crate::args::TsTypeLib; use crate::args::TypeCheckMode; +use crate::cache::Caches; +use crate::cache::DenoDir; use crate::cache::FastInsecureHasher; use crate::cache::TypeCheckCache; use crate::npm::NpmPackageResolver; use crate::tsc; -use crate::tsc::Diagnostics; -use crate::tsc::Stats; use crate::version; /// Options for performing a check of a module graph. Note that the decision to /// emit or not is determined by the `ts_config` settings. pub struct CheckOptions { - /// The check flag from the option which can effect the filtering of - /// diagnostics in the emit result. - pub type_check_mode: TypeCheckMode, - /// Set the debug flag on the TypeScript type checker. - pub debug: bool, - /// The module specifier to the configuration file, passed to tsc so that - /// configuration related diagnostics are properly formed. - pub maybe_config_specifier: Option<ModuleSpecifier>, - /// The derived tsconfig that should be used when checking. - pub ts_config: TsConfig, - /// If true, `Check <specifier>` will be written to stdout for each root. - pub log_checks: bool, + /// Default type library to type check with. + pub lib: TsTypeLib, + /// Whether to log about any ignored compiler options. + pub log_ignored_options: bool, /// If true, valid `.tsbuildinfo` files will be ignored and type checking /// will always occur. pub reload: bool, } -/// The result of a check of a module graph. -#[derive(Debug, Default)] -pub struct CheckResult { - pub diagnostics: Diagnostics, - pub stats: Stats, +pub struct TypeChecker { + deno_dir: DenoDir, + caches: Arc<Caches>, + cli_options: Arc<CliOptions>, + npm_resolver: Arc<NpmPackageResolver>, } -/// Given a set of roots and graph data, type check the module graph. -/// -/// It is expected that it is determined if a check and/or emit is validated -/// before the function is called. -pub fn check( - graph: Arc<ModuleGraph>, - cache: &TypeCheckCache, - npm_resolver: Arc<NpmPackageResolver>, - options: CheckOptions, -) -> Result<CheckResult, AnyError> { - let check_js = options.ts_config.get_check_js(); - let check_hash = match get_check_hash(&graph, &options) { - CheckHashResult::NoFiles => return Ok(Default::default()), - CheckHashResult::Hash(hash) => hash, - }; - - // do not type check if we know this is type checked - if !options.reload && cache.has_check_hash(check_hash) { - return Ok(Default::default()); +impl TypeChecker { + pub fn new( + deno_dir: DenoDir, + caches: Arc<Caches>, + cli_options: Arc<CliOptions>, + npm_resolver: Arc<NpmPackageResolver>, + ) -> Self { + Self { + deno_dir, + caches, + cli_options, + npm_resolver, + } } - if options.log_checks { + /// Type check the module graph. + /// + /// It is expected that it is determined if a check and/or emit is validated + /// before the function is called. + pub async fn check( + &self, + graph: Arc<ModuleGraph>, + options: CheckOptions, + ) -> Result<(), AnyError> { + // node built-in specifiers use the @types/node package to determine + // types, so inject that now (the caller should do this after the lockfile + // has been written) + if graph.has_node_specifier { + self + .npm_resolver + .inject_synthetic_types_node_package() + .await?; + } + + log::debug!("Type checking."); + let ts_config_result = self + .cli_options + .resolve_ts_config_for_emit(TsConfigType::Check { lib: options.lib })?; + if options.log_ignored_options { + if let Some(ignored_options) = ts_config_result.maybe_ignored_options { + log::warn!("{}", ignored_options); + } + } + + let ts_config = ts_config_result.ts_config; + let type_check_mode = self.cli_options.type_check_mode(); + let debug = self.cli_options.log_level() == Some(log::Level::Debug); + let cache = + TypeCheckCache::new(self.caches.type_checking_cache_db(&self.deno_dir)); + let check_js = ts_config.get_check_js(); + let check_hash = match get_check_hash(&graph, type_check_mode, &ts_config) { + CheckHashResult::NoFiles => return Ok(()), + CheckHashResult::Hash(hash) => hash, + }; + + // do not type check if we know this is type checked + if !options.reload && cache.has_check_hash(check_hash) { + return Ok(()); + } + for root in &graph.roots { let root_str = root.as_str(); log::info!("{} {}", colors::green("Check"), root_str); } - } - let root_names = get_tsc_roots(&graph, check_js); - // while there might be multiple roots, we can't "merge" the build info, so we - // try to retrieve the build info for first root, which is the most common use - // case. - let maybe_tsbuildinfo = if options.reload { - None - } else { - cache.get_tsbuildinfo(&graph.roots[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 - // that data here. - let hash_data = { - let mut hasher = FastInsecureHasher::new(); - hasher.write(&options.ts_config.as_bytes()); - hasher.write_str(version::deno()); - hasher.finish() - }; - - let response = tsc::exec(tsc::Request { - config: options.ts_config, - debug: options.debug, - graph: graph.clone(), - hash_data, - maybe_npm_resolver: Some(npm_resolver.clone()), - maybe_tsbuildinfo, - root_names, - check_mode: options.type_check_mode, - })?; - - let diagnostics = if options.type_check_mode == TypeCheckMode::Local { - response.diagnostics.filter(|d| { - if let Some(file_name) = &d.file_name { - if !file_name.starts_with("http") { - if ModuleSpecifier::parse(file_name) - .map(|specifier| !npm_resolver.in_npm_package(&specifier)) - .unwrap_or(true) - { - Some(d.clone()) + let root_names = get_tsc_roots(&graph, check_js); + // while there might be multiple roots, we can't "merge" the build info, so we + // try to retrieve the build info for first root, which is the most common use + // case. + let maybe_tsbuildinfo = if options.reload { + None + } else { + cache.get_tsbuildinfo(&graph.roots[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 + // that data here. + let hash_data = { + let mut hasher = FastInsecureHasher::new(); + hasher.write(&ts_config.as_bytes()); + hasher.write_str(version::deno()); + hasher.finish() + }; + + let response = tsc::exec(tsc::Request { + config: ts_config, + debug, + graph: graph.clone(), + hash_data, + maybe_npm_resolver: Some(self.npm_resolver.clone()), + maybe_tsbuildinfo, + root_names, + check_mode: type_check_mode, + })?; + + let diagnostics = if type_check_mode == TypeCheckMode::Local { + response.diagnostics.filter(|d| { + if let Some(file_name) = &d.file_name { + if !file_name.starts_with("http") { + if ModuleSpecifier::parse(file_name) + .map(|specifier| !self.npm_resolver.in_npm_package(&specifier)) + .unwrap_or(true) + { + Some(d.clone()) + } else { + None + } } else { None } } else { - None + Some(d.clone()) } - } else { - Some(d.clone()) - } - }) - } else { - response.diagnostics - }; + }) + } else { + response.diagnostics + }; - if let Some(tsbuildinfo) = response.maybe_tsbuildinfo { - cache.set_tsbuildinfo(&graph.roots[0], &tsbuildinfo); - } + if let Some(tsbuildinfo) = response.maybe_tsbuildinfo { + cache.set_tsbuildinfo(&graph.roots[0], &tsbuildinfo); + } - if diagnostics.is_empty() { - cache.add_check_hash(check_hash); - } + if diagnostics.is_empty() { + cache.add_check_hash(check_hash); + } + + log::debug!("{}", response.stats); - Ok(CheckResult { - diagnostics, - stats: response.stats, - }) + if diagnostics.is_empty() { + Ok(()) + } else { + Err(diagnostics.into()) + } + } } enum CheckHashResult { @@ -153,17 +189,18 @@ enum CheckHashResult { /// be used to tell fn get_check_hash( graph: &ModuleGraph, - options: &CheckOptions, + type_check_mode: TypeCheckMode, + ts_config: &TsConfig, ) -> CheckHashResult { let mut hasher = FastInsecureHasher::new(); - hasher.write_u8(match options.type_check_mode { + hasher.write_u8(match type_check_mode { TypeCheckMode::All => 0, TypeCheckMode::Local => 1, TypeCheckMode::None => 2, }); - hasher.write(&options.ts_config.as_bytes()); + hasher.write(&ts_config.as_bytes()); - let check_js = options.ts_config.get_check_js(); + let check_js = ts_config.get_check_js(); let mut sorted_modules = graph.modules().collect::<Vec<_>>(); sorted_modules.sort_by_key(|m| m.specifier().as_str()); // make it deterministic let mut has_file = false; |