diff options
author | David Sherret <dsherret@users.noreply.github.com> | 2022-07-19 11:58:18 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-07-19 11:58:18 -0400 |
commit | 0ab262b901348e9251262a02bef17d14ed13b997 (patch) | |
tree | fc5a6e3926ea7480714cbc844098eca6c43c1ab5 /cli/emit.rs | |
parent | e99d64acedb6e111d33f53599da494865978f1aa (diff) |
feat: emit files on demand and fix racy emit (#15220)
Diffstat (limited to 'cli/emit.rs')
-rw-r--r-- | cli/emit.rs | 154 |
1 files changed, 26 insertions, 128 deletions
diff --git a/cli/emit.rs b/cli/emit.rs index 584593869..1d8ecca43 100644 --- a/cli/emit.rs +++ b/cli/emit.rs @@ -10,7 +10,7 @@ use crate::args::EmitConfigOptions; use crate::args::TsConfig; use crate::args::TypeCheckMode; use crate::cache::EmitCache; -use crate::cache::SpecifierEmitCacheData; +use crate::cache::FastInsecureHasher; use crate::cache::TypeCheckCache; use crate::colors; use crate::diagnostics::Diagnostics; @@ -22,6 +22,7 @@ use crate::version; use deno_ast::swc::bundler::Hook; use deno_ast::swc::bundler::ModuleRecord; use deno_ast::swc::common::Span; +use deno_ast::ParsedSource; use deno_core::error::AnyError; use deno_core::parking_lot::RwLock; use deno_core::serde::Deserialize; @@ -32,14 +33,11 @@ use deno_core::serde_json; use deno_core::serde_json::json; use deno_core::ModuleSpecifier; use deno_graph::MediaType; -use deno_graph::ModuleGraph; use deno_graph::ModuleGraphError; use deno_graph::ModuleKind; use deno_graph::ResolutionError; -use std::collections::HashSet; use std::fmt; use std::sync::Arc; -use std::time::Instant; /// A structure representing stats from an emit operation for a graph. #[derive(Clone, Debug, Default, Eq, PartialEq)] @@ -116,8 +114,8 @@ pub enum TsConfigType { /// Return a configuration for bundling, using swc to emit the bundle. This is /// independent of type checking. Bundle, - /// Return a configuration to use tsc to type check and optionally emit. This - /// is independent of either bundling or just emitting via swc + /// Return a configuration to use tsc to type check. This + /// is independent of either bundling or emitting via swc. Check { lib: TsTypeLib }, /// Return a configuration to use swc to emit single module files. Emit, @@ -234,31 +232,30 @@ fn get_tsc_roots( /// A hashing function that takes the source code, version and optionally a /// user provided config and generates a string hash which can be stored to /// determine if the cached emit is valid or not. -fn get_version(source_bytes: &[u8], config_bytes: &[u8]) -> String { - crate::checksum::gen(&[ - source_bytes, - version::deno().as_bytes(), - config_bytes, - ]) +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() } -/// Determine if a given module kind and media type is emittable or not. -pub fn is_emittable( - kind: &ModuleKind, - media_type: &MediaType, - include_js: bool, -) -> bool { - if matches!(kind, ModuleKind::Synthetic) { - return false; - } - match &media_type { - MediaType::TypeScript - | MediaType::Mts - | MediaType::Cts - | MediaType::Tsx - | MediaType::Jsx => true, - MediaType::JavaScript | MediaType::Mjs | MediaType::Cjs => include_js, - _ => false, +pub fn emit_parsed_source( + cache: &EmitCache, + specifier: &ModuleSpecifier, + parsed_source: &ParsedSource, + emit_options: &deno_ast::EmitOptions, + emit_config_hash: u64, +) -> Result<String, AnyError> { + let source_hash = + get_source_hash(parsed_source.text_info().text_str(), emit_config_hash); + + if let Some(emit_code) = cache.get_emit_code(specifier, source_hash) { + Ok(emit_code) + } else { + let transpiled_source = parsed_source.transpile(emit_options)?; + debug_assert!(transpiled_source.source_map.is_none()); + cache.set_emit_code(specifier, source_hash, &transpiled_source.text); + Ok(transpiled_source.text) } } @@ -376,72 +373,6 @@ pub fn check( }) } -pub struct EmitOptions { - pub ts_config: TsConfig, - pub reload: bool, - pub reload_exclusions: HashSet<ModuleSpecifier>, -} - -/// Given a module graph, emit any appropriate modules and cache them. -// TODO(nayeemrmn): This would ideally take `GraphData` like -// `check()`, but the AST isn't stored in that. Cleanup. -pub fn emit( - graph: &ModuleGraph, - cache: &dyn EmitCache, - options: EmitOptions, -) -> Result<CheckResult, AnyError> { - let start = Instant::now(); - let config_bytes = options.ts_config.as_bytes(); - let include_js = options.ts_config.get_check_js(); - let emit_options = options.ts_config.into(); - - let mut emit_count = 0_u32; - let mut file_count = 0_u32; - for module in graph.modules() { - file_count += 1; - if !is_emittable(&module.kind, &module.media_type, include_js) { - continue; - } - let needs_reload = - options.reload && !options.reload_exclusions.contains(&module.specifier); - let version = 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; - } - let transpiled_source = module - .maybe_parsed_source - .as_ref() - .map(|source| source.transpile(&emit_options)) - .unwrap()?; - emit_count += 1; - cache.set_emit_data( - module.specifier.clone(), - SpecifierEmitCacheData { - source_hash: version, - text: transpiled_source.text, - map: transpiled_source.source_map, - }, - )?; - } - - let stats = Stats(vec![ - ("Files".to_string(), file_count), - ("Emitted".to_string(), emit_count), - ("Total time".to_string(), start.elapsed().as_millis() as u32), - ]); - - Ok(CheckResult { - diagnostics: Diagnostics::default(), - stats, - }) -} - enum CheckHashResult { Hash(u64), NoFiles, @@ -624,36 +555,3 @@ impl From<TsConfig> for deno_ast::EmitOptions { } } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_is_emittable() { - assert!(is_emittable( - &ModuleKind::Esm, - &MediaType::TypeScript, - false - )); - assert!(!is_emittable( - &ModuleKind::Synthetic, - &MediaType::TypeScript, - false - )); - assert!(!is_emittable(&ModuleKind::Esm, &MediaType::Dts, false)); - assert!(!is_emittable(&ModuleKind::Esm, &MediaType::Dcts, false)); - assert!(!is_emittable(&ModuleKind::Esm, &MediaType::Dmts, false)); - assert!(is_emittable(&ModuleKind::Esm, &MediaType::Tsx, false)); - assert!(!is_emittable( - &ModuleKind::Esm, - &MediaType::JavaScript, - false - )); - assert!(!is_emittable(&ModuleKind::Esm, &MediaType::Cjs, false)); - assert!(!is_emittable(&ModuleKind::Esm, &MediaType::Mjs, false)); - assert!(is_emittable(&ModuleKind::Esm, &MediaType::JavaScript, true)); - assert!(is_emittable(&ModuleKind::Esm, &MediaType::Jsx, false)); - assert!(!is_emittable(&ModuleKind::Esm, &MediaType::Json, false)); - } -} |