summaryrefslogtreecommitdiff
path: root/cli
diff options
context:
space:
mode:
Diffstat (limited to 'cli')
-rw-r--r--cli/cache/check.rs215
-rw-r--r--cli/cache/common.rs31
-rw-r--r--cli/cache/disk_cache.rs (renamed from cli/disk_cache.rs)13
-rw-r--r--cli/cache/emit.rs71
-rw-r--r--cli/cache/incremental.rs (renamed from cli/tools/incremental_cache.rs)33
-rw-r--r--cli/cache/mod.rs (renamed from cli/cache.rs)14
-rw-r--r--cli/deno_dir.rs9
-rw-r--r--cli/emit.rs329
-rw-r--r--cli/graph_util.rs6
-rw-r--r--cli/main.rs11
-rw-r--r--cli/proc_state.rs70
-rw-r--r--cli/tests/integration/check_tests.rs63
-rw-r--r--cli/tests/integration/mod.rs46
-rw-r--r--cli/tests/integration/run_tests.rs82
-rw-r--r--cli/tests/testdata/check/cache_config_on_off/deno.json5
-rw-r--r--cli/tests/testdata/check/cache_config_on_off/main.ts1
-rw-r--r--cli/tests/testdata/coverage/branch_expected.lcov4
-rw-r--r--cli/tests/testdata/coverage/branch_expected.out3
-rw-r--r--cli/tests/testdata/coverage/complex_expected.lcov22
-rw-r--r--cli/tests/testdata/coverage/complex_expected.out4
-rw-r--r--cli/tests/testdata/run/remote_type_error/main.ts3
-rw-r--r--cli/tests/testdata/run/remote_type_error/remote.ts5
-rw-r--r--cli/tools/coverage/mod.rs14
-rw-r--r--cli/tools/fmt.rs2
-rw-r--r--cli/tools/lint.rs2
-rw-r--r--cli/tools/mod.rs1
-rw-r--r--cli/tsc.rs112
-rw-r--r--cli/tsc/99_main_compiler.js16
28 files changed, 663 insertions, 524 deletions
diff --git a/cli/cache/check.rs b/cli/cache/check.rs
new file mode 100644
index 000000000..0ff86ef6c
--- /dev/null
+++ b/cli/cache/check.rs
@@ -0,0 +1,215 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+use std::path::Path;
+
+use deno_ast::ModuleSpecifier;
+use deno_core::anyhow::Context;
+use deno_core::error::AnyError;
+use deno_runtime::deno_webstorage::rusqlite::params;
+use deno_runtime::deno_webstorage::rusqlite::Connection;
+
+use super::common::run_sqlite_pragma;
+
+/// The cache used to tell whether type checking should occur again.
+///
+/// This simply stores a hash of the inputs of each successful type check
+/// and only clears them out when changing CLI versions.
+pub struct TypeCheckCache {
+ conn: Connection,
+}
+
+impl TypeCheckCache {
+ pub fn new(db_file_path: &Path) -> Result<Self, AnyError> {
+ let conn = Connection::open(db_file_path).with_context(|| {
+ format!(
+ concat!(
+ "Error opening type checking cache at {} -- ",
+ "Perhaps it's corrupt. Maybe try deleting it."
+ ),
+ db_file_path.display()
+ )
+ })?;
+ Self::from_connection(conn, crate::version::deno())
+ }
+
+ fn from_connection(
+ conn: Connection,
+ cli_version: String,
+ ) -> Result<Self, AnyError> {
+ run_sqlite_pragma(&conn)?;
+ create_tables(&conn, cli_version)?;
+
+ Ok(Self { conn })
+ }
+
+ pub fn has_check_hash(&self, hash: u64) -> bool {
+ match self.hash_check_hash_result(hash) {
+ Ok(val) => val,
+ Err(err) => {
+ if cfg!(debug_assertions) {
+ panic!("Error retrieving hash: {}", err);
+ } else {
+ log::debug!("Error retrieving hash: {}", err);
+ // fail silently when not debugging
+ false
+ }
+ }
+ }
+ }
+
+ fn hash_check_hash_result(&self, hash: u64) -> Result<bool, AnyError> {
+ let query = "SELECT * FROM checkcache WHERE check_hash=?1 LIMIT 1";
+ let mut stmt = self.conn.prepare_cached(query)?;
+ Ok(stmt.exists(params![hash.to_string()])?)
+ }
+
+ pub fn add_check_hash(&self, check_hash: u64) {
+ if let Err(err) = self.add_check_hash_result(check_hash) {
+ if cfg!(debug_assertions) {
+ panic!("Error saving check hash: {}", err);
+ } else {
+ log::debug!("Error saving check hash: {}", err);
+ }
+ }
+ }
+
+ fn add_check_hash_result(&self, check_hash: u64) -> Result<(), AnyError> {
+ let sql = "
+ INSERT OR REPLACE INTO
+ checkcache (check_hash)
+ VALUES
+ (?1)";
+ let mut stmt = self.conn.prepare_cached(sql)?;
+ stmt.execute(params![&check_hash.to_string(),])?;
+ Ok(())
+ }
+
+ pub fn get_tsbuildinfo(&self, specifier: &ModuleSpecifier) -> Option<String> {
+ let mut stmt = self
+ .conn
+ .prepare_cached("SELECT text FROM tsbuildinfo WHERE specifier=?1 LIMIT 1")
+ .ok()?;
+ let mut rows = stmt.query(params![specifier.to_string()]).ok()?;
+ let row = rows.next().ok().flatten()?;
+
+ row.get(0).ok()
+ }
+
+ pub fn set_tsbuildinfo(&self, specifier: &ModuleSpecifier, text: &str) {
+ if let Err(err) = self.set_tsbuildinfo_result(specifier, text) {
+ // should never error here, but if it ever does don't fail
+ if cfg!(debug_assertions) {
+ panic!("Error saving tsbuildinfo: {}", err);
+ } else {
+ log::debug!("Error saving tsbuildinfo: {}", err);
+ }
+ }
+ }
+
+ fn set_tsbuildinfo_result(
+ &self,
+ specifier: &ModuleSpecifier,
+ text: &str,
+ ) -> Result<(), AnyError> {
+ let mut stmt = self.conn.prepare_cached(
+ "INSERT OR REPLACE INTO tsbuildinfo (specifier, text) VALUES (?1, ?2)",
+ )?;
+ stmt.execute(params![specifier.to_string(), text])?;
+ Ok(())
+ }
+}
+
+fn create_tables(
+ conn: &Connection,
+ cli_version: String,
+) -> Result<(), AnyError> {
+ // INT doesn't store up to u64, so use TEXT
+ conn.execute(
+ "CREATE TABLE IF NOT EXISTS checkcache (
+ check_hash TEXT PRIMARY KEY
+ )",
+ [],
+ )?;
+ conn.execute(
+ "CREATE TABLE IF NOT EXISTS tsbuildinfo (
+ specifier TEXT PRIMARY KEY,
+ text TEXT NOT NULL
+ )",
+ [],
+ )?;
+ conn.execute(
+ "CREATE TABLE IF NOT EXISTS info (
+ key TEXT PRIMARY KEY,
+ value TEXT NOT NULL
+ )",
+ [],
+ )?;
+
+ // delete the cache when the CLI version changes
+ let data_cli_version: Option<String> = conn
+ .query_row(
+ "SELECT value FROM info WHERE key='CLI_VERSION' LIMIT 1",
+ [],
+ |row| row.get(0),
+ )
+ .ok();
+ if data_cli_version != Some(cli_version.to_string()) {
+ conn.execute("DELETE FROM checkcache", params![])?;
+ conn.execute("DELETE FROM tsbuildinfo", params![])?;
+ let mut stmt = conn
+ .prepare("INSERT OR REPLACE INTO info (key, value) VALUES (?1, ?2)")?;
+ stmt.execute(params!["CLI_VERSION", &cli_version])?;
+ }
+
+ Ok(())
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ pub fn check_cache_general_use() {
+ let conn = Connection::open_in_memory().unwrap();
+ let cache =
+ TypeCheckCache::from_connection(conn, "1.0.0".to_string()).unwrap();
+
+ assert!(!cache.has_check_hash(1));
+ cache.add_check_hash(1);
+ assert!(cache.has_check_hash(1));
+ assert!(!cache.has_check_hash(2));
+
+ let specifier1 = ModuleSpecifier::parse("file:///test.json").unwrap();
+ assert_eq!(cache.get_tsbuildinfo(&specifier1), None);
+ cache.set_tsbuildinfo(&specifier1, "test");
+ assert_eq!(cache.get_tsbuildinfo(&specifier1), Some("test".to_string()));
+
+ // try changing the cli version (should clear)
+ let conn = cache.conn;
+ let cache =
+ TypeCheckCache::from_connection(conn, "2.0.0".to_string()).unwrap();
+ assert!(!cache.has_check_hash(1));
+ cache.add_check_hash(1);
+ assert!(cache.has_check_hash(1));
+ assert_eq!(cache.get_tsbuildinfo(&specifier1), None);
+ cache.set_tsbuildinfo(&specifier1, "test");
+ assert_eq!(cache.get_tsbuildinfo(&specifier1), Some("test".to_string()));
+
+ // recreating the cache should not remove the data because the CLI version and state hash is the same
+ let conn = cache.conn;
+ let cache =
+ TypeCheckCache::from_connection(conn, "2.0.0".to_string()).unwrap();
+ assert!(cache.has_check_hash(1));
+ assert!(!cache.has_check_hash(2));
+ assert_eq!(cache.get_tsbuildinfo(&specifier1), Some("test".to_string()));
+
+ // adding when already exists should not cause issue
+ cache.add_check_hash(1);
+ assert!(cache.has_check_hash(1));
+ cache.set_tsbuildinfo(&specifier1, "other");
+ assert_eq!(
+ cache.get_tsbuildinfo(&specifier1),
+ Some("other".to_string())
+ );
+ }
+}
diff --git a/cli/cache/common.rs b/cli/cache/common.rs
new file mode 100644
index 000000000..c01c1ab9a
--- /dev/null
+++ b/cli/cache/common.rs
@@ -0,0 +1,31 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::error::AnyError;
+use deno_runtime::deno_webstorage::rusqlite::Connection;
+
+/// Very fast non-cryptographically secure hash.
+pub fn fast_insecure_hash(bytes: &[u8]) -> u64 {
+ use std::hash::Hasher;
+ use twox_hash::XxHash64;
+
+ let mut hasher = XxHash64::default();
+ hasher.write(bytes);
+ hasher.finish()
+}
+
+/// Runs the common sqlite pragma.
+pub fn run_sqlite_pragma(conn: &Connection) -> Result<(), AnyError> {
+ // Enable write-ahead-logging and tweak some other stuff
+ let initial_pragmas = "
+ -- enable write-ahead-logging mode
+ PRAGMA journal_mode=WAL;
+ PRAGMA synchronous=NORMAL;
+ PRAGMA temp_store=memory;
+ PRAGMA page_size=4096;
+ PRAGMA mmap_size=6000000;
+ PRAGMA optimize;
+ ";
+
+ conn.execute_batch(initial_pragmas)?;
+ Ok(())
+}
diff --git a/cli/disk_cache.rs b/cli/cache/disk_cache.rs
index c23f7f4df..01352c398 100644
--- a/cli/disk_cache.rs
+++ b/cli/cache/disk_cache.rs
@@ -1,14 +1,17 @@
// 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 super::CacheType;
+use super::Cacher;
+use super::EmitMetadata;
+
use deno_ast::ModuleSpecifier;
use deno_core::error::AnyError;
use deno_core::serde_json;
-use deno_core::url::{Host, Url};
+use deno_core::url::Host;
+use deno_core::url::Url;
use std::ffi::OsStr;
use std::fs;
use std::io;
@@ -184,7 +187,6 @@ impl Cacher for DiskCache {
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)
}
@@ -206,7 +208,6 @@ impl Cacher for DiskCache {
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;
diff --git a/cli/cache/emit.rs b/cli/cache/emit.rs
new file mode 100644
index 000000000..e1469b862
--- /dev/null
+++ b/cli/cache/emit.rs
@@ -0,0 +1,71 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+use deno_ast::ModuleSpecifier;
+use deno_core::error::AnyError;
+
+use super::CacheType;
+use super::Cacher;
+
+/// 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>;
+ /// Sets the emit data in the cache.
+ fn set_emit_data(
+ &self,
+ specifier: ModuleSpecifier,
+ data: SpecifierEmitCacheData,
+ ) -> Result<(), AnyError>;
+ /// 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>;
+}
+
+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(())
+ }
+}
diff --git a/cli/tools/incremental_cache.rs b/cli/cache/incremental.rs
index 476c46b29..b5fff0734 100644
--- a/cli/tools/incremental_cache.rs
+++ b/cli/cache/incremental.rs
@@ -1,3 +1,5 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
use std::collections::HashMap;
use std::path::Path;
use std::path::PathBuf;
@@ -10,6 +12,9 @@ use deno_runtime::deno_webstorage::rusqlite::Connection;
use serde::Serialize;
use tokio::task::JoinHandle;
+use super::common::fast_insecure_hash;
+use super::common::run_sqlite_pragma;
+
/// Cache used to skip formatting/linting a file again when we
/// know it is already formatted or has no lint diagnostics.
pub struct IncrementalCache(Option<IncrementalCacheInner>);
@@ -165,7 +170,7 @@ impl SqlIncrementalCache {
state_hash: u64,
cli_version: String,
) -> Result<Self, AnyError> {
- run_pragma(&conn)?;
+ run_sqlite_pragma(&conn)?;
create_tables(&conn, cli_version)?;
Ok(Self { conn, state_hash })
@@ -229,22 +234,6 @@ impl SqlIncrementalCache {
}
}
-fn run_pragma(conn: &Connection) -> Result<(), AnyError> {
- // Enable write-ahead-logging and tweak some other stuff
- let initial_pragmas = "
- -- enable write-ahead-logging mode
- PRAGMA journal_mode=WAL;
- PRAGMA synchronous=NORMAL;
- PRAGMA temp_store=memory;
- PRAGMA page_size=4096;
- PRAGMA mmap_size=6000000;
- PRAGMA optimize;
- ";
-
- conn.execute_batch(initial_pragmas)?;
- Ok(())
-}
-
fn create_tables(
conn: &Connection,
cli_version: String,
@@ -284,16 +273,6 @@ fn create_tables(
Ok(())
}
-/// Very fast non-cryptographically secure hash.
-fn fast_insecure_hash(bytes: &[u8]) -> u64 {
- use std::hash::Hasher;
- use twox_hash::XxHash64;
-
- let mut hasher = XxHash64::default();
- hasher.write(bytes);
- hasher.finish()
-}
-
#[cfg(test)]
mod test {
use std::path::PathBuf;
diff --git a/cli/cache.rs b/cli/cache/mod.rs
index bdb38e393..f363d8fa8 100644
--- a/cli/cache.rs
+++ b/cli/cache/mod.rs
@@ -1,6 +1,5 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
-use crate::disk_cache::DiskCache;
use crate::errors::get_error_class_name;
use crate::file_fetcher::FileFetcher;
@@ -16,6 +15,18 @@ use deno_graph::source::Loader;
use deno_runtime::permissions::Permissions;
use std::sync::Arc;
+mod check;
+mod common;
+mod disk_cache;
+mod emit;
+mod incremental;
+
+pub use check::TypeCheckCache;
+pub use disk_cache::DiskCache;
+pub use emit::EmitCache;
+pub use emit::SpecifierEmitCacheData;
+pub use incremental::IncrementalCache;
+
#[derive(Debug, Deserialize, Serialize)]
pub struct EmitMetadata {
pub version_hash: String,
@@ -24,7 +35,6 @@ pub struct EmitMetadata {
pub enum CacheType {
Emit,
SourceMap,
- TypeScriptBuildInfo,
Version,
}
diff --git a/cli/deno_dir.rs b/cli/deno_dir.rs
index c6a5eca29..0a1864f2c 100644
--- a/cli/deno_dir.rs
+++ b/cli/deno_dir.rs
@@ -1,6 +1,7 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
-use crate::disk_cache::DiskCache;
+use crate::cache::DiskCache;
+
use std::path::PathBuf;
/// `DenoDir` serves as coordinator for multiple `DiskCache`s containing them
@@ -56,6 +57,12 @@ impl DenoDir {
// bump this version name to invalidate the entire cache
self.root.join("lint_incremental_cache_v1")
}
+
+ /// Path for the incremental cache used for linting.
+ pub fn type_checking_cache_db_file_path(&self) -> PathBuf {
+ // bump this version name to invalidate the entire cache
+ self.root.join("check_cache_v1")
+ }
}
/// To avoid the poorly managed dirs crate
diff --git a/cli/emit.rs b/cli/emit.rs
index 329eb4f5d..a530dbcb9 100644
--- a/cli/emit.rs
+++ b/cli/emit.rs
@@ -9,8 +9,9 @@ use crate::args::ConfigFile;
use crate::args::EmitConfigOptions;
use crate::args::TsConfig;
use crate::args::TypeCheckMode;
-use crate::cache::CacheType;
-use crate::cache::Cacher;
+use crate::cache::EmitCache;
+use crate::cache::SpecifierEmitCacheData;
+use crate::cache::TypeCheckCache;
use crate::colors;
use crate::diagnostics::Diagnostics;
use crate::graph_util::GraphData;
@@ -35,97 +36,12 @@ use deno_graph::ModuleGraph;
use deno_graph::ModuleGraphError;
use deno_graph::ModuleKind;
use deno_graph::ResolutionError;
-use std::collections::HashMap;
use std::collections::HashSet;
use std::fmt;
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)
- }
-}
-
/// A structure representing stats from an emit operation for a graph.
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Stats(pub Vec<(String, u32)>);
@@ -236,13 +152,17 @@ pub fn get_ts_config_for_emit(
let mut ts_config = TsConfig::new(json!({
"allowJs": true,
"allowSyntheticDefaultImports": true,
+ "checkJs": false,
"experimentalDecorators": true,
"incremental": true,
"jsx": "react",
+ "jsxFactory": "React.createElement",
+ "jsxFragmentFactory": "React.Fragment",
"isolatedModules": true,
"lib": lib,
"module": "esnext",
"resolveJsonModule": true,
+ "sourceMap": false,
"strict": true,
"target": "esnext",
"tsBuildInfoFile": "deno:///.tsbuildinfo",
@@ -378,10 +298,6 @@ pub struct CheckOptions {
pub type_check_mode: TypeCheckMode,
/// Set the debug flag on the TypeScript type checker.
pub debug: bool,
- /// If true, any files emitted will be cached, even if there are diagnostics
- /// produced. If false, if there are diagnostics, caching emitted files will
- /// be skipped.
- pub emit_with_diagnostics: 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>,
@@ -389,46 +305,43 @@ pub struct CheckOptions {
pub ts_config: TsConfig,
/// If true, `Check <specifier>` will be written to stdout for each root.
pub log_checks: bool,
- /// If true, valid existing emits and `.tsbuildinfo` files will be ignored.
+ /// If true, valid `.tsbuildinfo` files will be ignored and type checking
+ /// will always occur.
pub reload: bool,
- pub reload_exclusions: HashSet<ModuleSpecifier>,
}
-/// The result of a check or emit of a module graph. Note that the actual
-/// emitted sources are stored in the cache and are not returned in the result.
+/// The result of a check of a module graph.
#[derive(Debug, Default)]
-pub struct CheckEmitResult {
+pub struct CheckResult {
pub diagnostics: Diagnostics,
pub stats: Stats,
}
-/// Given a set of roots and graph data, type check the module graph and
-/// optionally emit modules, updating the cache as appropriate. Emitting is
-/// determined by the `ts_config` supplied in the options, and if emitting, the
-/// files are stored in the cache.
+/// 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_and_maybe_emit(
+pub fn check(
roots: &[(ModuleSpecifier, ModuleKind)],
graph_data: Arc<RwLock<GraphData>>,
- cache: &dyn EmitCache,
+ cache: &TypeCheckCache,
options: CheckOptions,
-) -> Result<CheckEmitResult, AnyError> {
+) -> Result<CheckResult, AnyError> {
let check_js = options.ts_config.get_check_js();
let segment_graph_data = {
let graph_data = graph_data.read();
graph_data.graph_segment(roots).unwrap()
};
- if valid_emit(
- &segment_graph_data,
- cache,
- &options.ts_config,
- options.reload,
- &options.reload_exclusions,
- ) {
+ let check_hash = match get_check_hash(&segment_graph_data, &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());
}
+
let root_names = get_tsc_roots(roots, &segment_graph_data, check_js);
if options.log_checks {
for (root, _) in roots {
@@ -454,12 +367,11 @@ pub fn check_and_maybe_emit(
options.ts_config.as_bytes(),
version::deno().as_bytes().to_owned(),
];
- let config_bytes = options.ts_config.as_bytes();
let response = tsc::exec(tsc::Request {
config: options.ts_config,
debug: options.debug,
- graph_data: graph_data.clone(),
+ graph_data,
hash_data,
maybe_config_specifier: options.maybe_config_specifier,
maybe_tsbuildinfo,
@@ -478,105 +390,15 @@ pub fn check_and_maybe_emit(
response.diagnostics
};
- // sometimes we want to emit when there are diagnostics, and sometimes we
- // don't. tsc will always return an emit if there are diagnostics
- if (diagnostics.is_empty() || options.emit_with_diagnostics)
- && !response.emitted_files.is_empty()
- {
- if let Some(info) = &response.maybe_tsbuildinfo {
- // 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_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);
- // The emitted specifier might not be the file specifier we want, so we
- // resolve it via the graph.
- let graph_data = graph_data.read();
- let specifier = graph_data.follow_redirect(&specifiers[0]);
- let (source_bytes, media_type, ts_check) =
- match graph_data.get(&specifier) {
- Some(ModuleEntry::Module {
- code,
- media_type,
- ts_check,
- ..
- }) => (code.as_bytes(), *media_type, *ts_check),
- _ => {
- log::debug!("skipping emit for {}", specifier);
- continue;
- }
- };
- // Sometimes if `tsc` sees a CommonJS file or a JSON module, it will
- // _helpfully_ output it, which we don't really want to do unless
- // someone has enabled check_js.
- if matches!(media_type, MediaType::Json)
- || (!check_js
- && !ts_check
- && matches!(
- media_type,
- MediaType::JavaScript | MediaType::Cjs | MediaType::Mjs
- ))
- {
- 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 => {
- emit_data_item.text = Some(emit.data);
- }
- MediaType::SourceMap => {
- emit_data_item.map = Some(emit.data);
- }
- _ => unreachable!(
- "unexpected media_type {} {}",
- emit.media_type, specifier
- ),
- }
- }
- }
+ if let Some(tsbuildinfo) = response.maybe_tsbuildinfo {
+ cache.set_tsbuildinfo(&roots[0].0, &tsbuildinfo);
+ }
- // 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)?;
- }
- }
+ if diagnostics.is_empty() {
+ cache.add_check_hash(check_hash);
}
- Ok(CheckEmitResult {
+ Ok(CheckResult {
diagnostics,
stats: response.stats,
})
@@ -590,12 +412,12 @@ pub struct EmitOptions {
/// Given a module graph, emit any appropriate modules and cache them.
// TODO(nayeemrmn): This would ideally take `GraphData` like
-// `check_and_maybe_emit()`, but the AST isn't stored in that. Cleanup.
+// `check()`, but the AST isn't stored in that. Cleanup.
pub fn emit(
graph: &ModuleGraph,
cache: &dyn EmitCache,
options: EmitOptions,
-) -> Result<CheckEmitResult, AnyError> {
+) -> Result<CheckResult, AnyError> {
let start = Instant::now();
let config_bytes = options.ts_config.as_bytes();
let include_js = options.ts_config.get_check_js();
@@ -623,7 +445,7 @@ pub fn emit(
let transpiled_source = module
.maybe_parsed_source
.as_ref()
- .map(|ps| ps.transpile(&emit_options))
+ .map(|source| source.transpile(&emit_options))
.unwrap()?;
emit_count += 1;
cache.set_emit_data(
@@ -642,26 +464,41 @@ pub fn emit(
("Total time".to_string(), start.elapsed().as_millis() as u32),
]);
- Ok(CheckEmitResult {
+ Ok(CheckResult {
diagnostics: Diagnostics::default(),
stats,
})
}
-/// Check a module graph to determine if the graph contains anything that
-/// is required to be emitted to be valid. It determines what modules in the
-/// graph are emittable and for those that are emittable, if there is currently
-/// a valid emit in the cache.
-fn valid_emit(
+enum CheckHashResult {
+ Hash(u64),
+ NoFiles,
+}
+
+/// Gets a hash of the inputs for type checking. This can then
+/// be used to tell
+fn get_check_hash(
graph_data: &GraphData,
- cache: &dyn EmitCache,
- ts_config: &TsConfig,
- reload: bool,
- reload_exclusions: &HashSet<ModuleSpecifier>,
-) -> bool {
- let config_bytes = ts_config.as_bytes();
- let check_js = ts_config.get_check_js();
- for (specifier, module_entry) in graph_data.entries() {
+ options: &CheckOptions,
+) -> CheckHashResult {
+ // twox hash is insecure, but fast so it works for our purposes
+ use std::hash::Hasher;
+ use twox_hash::XxHash64;
+
+ let mut hasher = XxHash64::default();
+ hasher.write_u8(match options.type_check_mode {
+ TypeCheckMode::All => 0,
+ TypeCheckMode::Local => 1,
+ TypeCheckMode::None => 2,
+ });
+ hasher.write(&options.ts_config.as_bytes());
+
+ let check_js = options.ts_config.get_check_js();
+ let mut sorted_entries = graph_data.entries().collect::<Vec<_>>();
+ sorted_entries.sort_by_key(|(s, _)| s.as_str()); // make it deterministic
+ let mut has_file = false;
+ let mut has_file_to_type_check = false;
+ for (specifier, module_entry) in sorted_entries {
if let ModuleEntry::Module {
code,
media_type,
@@ -669,13 +506,26 @@ fn valid_emit(
..
} = module_entry
{
+ if *ts_check {
+ has_file_to_type_check = true;
+ }
+
match media_type {
MediaType::TypeScript
+ | MediaType::Dts
+ | MediaType::Dmts
+ | MediaType::Dcts
| MediaType::Mts
| MediaType::Cts
- | MediaType::Tsx
- | MediaType::Jsx => {}
- MediaType::JavaScript | MediaType::Mjs | MediaType::Cjs => {
+ | MediaType::Tsx => {
+ has_file = true;
+ has_file_to_type_check = true;
+ }
+ MediaType::JavaScript
+ | MediaType::Mjs
+ | MediaType::Cjs
+ | MediaType::Jsx => {
+ has_file = true;
if !check_js && !ts_check {
continue;
}
@@ -683,25 +533,20 @@ fn valid_emit(
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(source_hash) = cache.get_source_hash(specifier) {
- if source_hash != get_version(code.as_bytes(), &config_bytes) {
- return false;
- }
- } else {
- return false;
- }
+ hasher.write(specifier.as_str().as_bytes());
+ hasher.write(code.as_bytes());
}
}
- true
+
+ if !has_file || !check_js && !has_file_to_type_check {
+ // no files to type check
+ CheckHashResult::NoFiles
+ } else {
+ CheckHashResult::Hash(hasher.finish())
+ }
}
/// An adapter struct to make a deno_graph::ModuleGraphError display as expected
diff --git a/cli/graph_util.rs b/cli/graph_util.rs
index 991115319..de418edd7 100644
--- a/cli/graph_util.rs
+++ b/cli/graph_util.rs
@@ -162,8 +162,10 @@ impl GraphData {
}
}
- pub fn entries(&self) -> HashMap<&ModuleSpecifier, &ModuleEntry> {
- self.modules.iter().collect()
+ pub fn entries(
+ &self,
+ ) -> impl Iterator<Item = (&ModuleSpecifier, &ModuleEntry)> {
+ self.modules.iter()
}
/// Walk dependencies from `roots` and return every encountered specifier.
diff --git a/cli/main.rs b/cli/main.rs
index c6188fec8..4ed44c9bd 100644
--- a/cli/main.rs
+++ b/cli/main.rs
@@ -9,7 +9,6 @@ mod compat;
mod deno_dir;
mod diagnostics;
mod diff;
-mod disk_cache;
mod display;
mod emit;
mod errors;
@@ -59,6 +58,7 @@ use crate::args::TypeCheckMode;
use crate::args::UninstallFlags;
use crate::args::UpgradeFlags;
use crate::args::VendorFlags;
+use crate::cache::TypeCheckCache;
use crate::emit::TsConfigType;
use crate::file_fetcher::File;
use crate::file_watcher::ResolutionResult;
@@ -661,19 +661,20 @@ async fn create_graph_and_maybe_check(
eprintln!("{}", ignored_options);
}
let maybe_config_specifier = ps.options.maybe_config_file_specifier();
- let check_result = emit::check_and_maybe_emit(
+ // todo: don't use anything on failure
+ let cache =
+ TypeCheckCache::new(&ps.dir.type_checking_cache_db_file_path())?;
+ let check_result = emit::check(
&graph.roots,
Arc::new(RwLock::new(graph.as_ref().into())),
- &ps.dir.gen_cache,
+ &cache,
emit::CheckOptions {
type_check_mode: ps.options.type_check_mode(),
debug,
- emit_with_diagnostics: false,
maybe_config_specifier,
ts_config: ts_config_result.ts_config,
log_checks: true,
reload: ps.options.reload_flag(),
- reload_exclusions: Default::default(),
},
)?;
debug!("{}", check_result.stats);
diff --git a/cli/proc_state.rs b/cli/proc_state.rs
index 1a32b01a4..75630e47b 100644
--- a/cli/proc_state.rs
+++ b/cli/proc_state.rs
@@ -5,11 +5,12 @@ use crate::args::DenoSubcommand;
use crate::args::Flags;
use crate::args::TypeCheckMode;
use crate::cache;
+use crate::cache::EmitCache;
+use crate::cache::TypeCheckCache;
use crate::compat;
use crate::compat::NodeEsmResolver;
use crate::deno_dir;
use crate::emit;
-use crate::emit::EmitCache;
use crate::emit::TsConfigType;
use crate::emit::TsTypeLib;
use crate::file_fetcher::FileFetcher;
@@ -394,7 +395,7 @@ impl ProcState {
// should be skipped.
let reload_exclusions: HashSet<ModuleSpecifier> = {
let graph_data = self.graph_data.read();
- graph_data.entries().into_keys().cloned().collect()
+ graph_data.entries().map(|(s, _)| s).cloned().collect()
};
{
@@ -426,36 +427,45 @@ impl ProcState {
log::warn!("{}", ignored_options);
}
- if self.options.type_check_mode() == TypeCheckMode::None {
- let options = emit::EmitOptions {
- ts_config: ts_config_result.ts_config,
- reload: self.options.reload_flag(),
- reload_exclusions,
- };
- let emit_result = emit::emit(&graph, &self.dir.gen_cache, options)?;
- log::debug!("{}", emit_result.stats);
- } else {
- let maybe_config_specifier = self.options.maybe_config_file_specifier();
- let options = emit::CheckOptions {
- type_check_mode: self.options.type_check_mode(),
- debug: self.options.log_level() == Some(log::Level::Debug),
- emit_with_diagnostics: false,
- maybe_config_specifier,
- ts_config: ts_config_result.ts_config,
- log_checks: true,
- reload: self.options.reload_flag(),
- reload_exclusions,
+ // start type checking if necessary
+ let type_checking_task =
+ if self.options.type_check_mode() != TypeCheckMode::None {
+ let maybe_config_specifier = self.options.maybe_config_file_specifier();
+ let roots = roots.clone();
+ let options = emit::CheckOptions {
+ type_check_mode: self.options.type_check_mode(),
+ debug: self.options.log_level() == Some(log::Level::Debug),
+ maybe_config_specifier,
+ ts_config: ts_config_result.ts_config.clone(),
+ log_checks: true,
+ reload: self.options.reload_flag()
+ && !roots.iter().all(|r| reload_exclusions.contains(&r.0)),
+ };
+ // todo(THIS PR): don't use a cache on failure
+ let check_cache =
+ TypeCheckCache::new(&self.dir.type_checking_cache_db_file_path())?;
+ let graph_data = self.graph_data.clone();
+ Some(tokio::task::spawn_blocking(move || {
+ emit::check(&roots, graph_data, &check_cache, options)
+ }))
+ } else {
+ None
};
- let emit_result = emit::check_and_maybe_emit(
- &roots,
- self.graph_data.clone(),
- &self.dir.gen_cache,
- options,
- )?;
- if !emit_result.diagnostics.is_empty() {
- return Err(anyhow!(emit_result.diagnostics));
+
+ let options = emit::EmitOptions {
+ ts_config: ts_config_result.ts_config,
+ reload: self.options.reload_flag(),
+ reload_exclusions,
+ };
+ let emit_result = emit::emit(&graph, &self.dir.gen_cache, options)?;
+ log::debug!("{}", emit_result.stats);
+
+ if let Some(type_checking_task) = type_checking_task {
+ let type_check_result = type_checking_task.await??;
+ if !type_check_result.diagnostics.is_empty() {
+ return Err(anyhow!(type_check_result.diagnostics));
}
- log::debug!("{}", emit_result.stats);
+ log::debug!("{}", type_check_result.stats);
}
if self.options.type_check_mode() != TypeCheckMode::None {
diff --git a/cli/tests/integration/check_tests.rs b/cli/tests/integration/check_tests.rs
index 8000ddc9d..5ceaffe51 100644
--- a/cli/tests/integration/check_tests.rs
+++ b/cli/tests/integration/check_tests.rs
@@ -1,7 +1,11 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+use std::process::Stdio;
+
use crate::itest;
+use test_util as util;
+
itest!(_095_check_with_bare_import {
args: "check 095_cache_with_bare_import.ts",
output: "095_cache_with_bare_import.ts.out",
@@ -43,3 +47,62 @@ itest!(declaration_header_file_with_no_exports {
args: "check --quiet declaration_header_file_with_no_exports.ts",
output_str: Some(""),
});
+
+#[test]
+fn cache_switching_config_then_no_config() {
+ let deno_dir = util::new_deno_dir();
+ assert!(does_type_checking(&deno_dir, true));
+ assert!(does_type_checking(&deno_dir, false));
+
+ // should now not do type checking even when it changes
+ // configs because it previously did
+ assert!(!does_type_checking(&deno_dir, true));
+ assert!(!does_type_checking(&deno_dir, false));
+
+ fn does_type_checking(deno_dir: &util::TempDir, with_config: bool) -> bool {
+ let mut cmd = util::deno_cmd_with_deno_dir(deno_dir);
+ cmd
+ .current_dir(util::testdata_path())
+ .stderr(Stdio::piped())
+ .arg("check")
+ .arg("check/cache_config_on_off/main.ts");
+ if with_config {
+ cmd
+ .arg("--config")
+ .arg("check/cache_config_on_off/deno.json");
+ }
+ let output = cmd.spawn().unwrap().wait_with_output().unwrap();
+ assert!(output.status.success());
+
+ let stderr = std::str::from_utf8(&output.stderr).unwrap();
+ stderr.contains("Check")
+ }
+}
+
+#[test]
+fn reload_flag() {
+ // should do type checking whenever someone specifies --reload
+ let deno_dir = util::new_deno_dir();
+ assert!(does_type_checking(&deno_dir, false));
+ assert!(!does_type_checking(&deno_dir, false));
+ assert!(does_type_checking(&deno_dir, true));
+ assert!(does_type_checking(&deno_dir, true));
+ assert!(!does_type_checking(&deno_dir, false));
+
+ fn does_type_checking(deno_dir: &util::TempDir, reload: bool) -> bool {
+ let mut cmd = util::deno_cmd_with_deno_dir(deno_dir);
+ cmd
+ .current_dir(util::testdata_path())
+ .stderr(Stdio::piped())
+ .arg("check")
+ .arg("check/cache_config_on_off/main.ts");
+ if reload {
+ cmd.arg("--reload");
+ }
+ let output = cmd.spawn().unwrap().wait_with_output().unwrap();
+ assert!(output.status.success());
+
+ let stderr = std::str::from_utf8(&output.stderr).unwrap();
+ stderr.contains("Check")
+ }
+}
diff --git a/cli/tests/integration/mod.rs b/cli/tests/integration/mod.rs
index 42ae24142..277b6a5d6 100644
--- a/cli/tests/integration/mod.rs
+++ b/cli/tests/integration/mod.rs
@@ -158,12 +158,6 @@ fn cache_test() {
.expect("Failed to spawn script");
assert!(output.status.success());
- let out = std::str::from_utf8(&output.stderr).unwrap();
- // Check if file and dependencies are written successfully
- assert!(out.contains("host.writeFile(\"deno://subdir/print_hello.js\")"));
- assert!(out.contains("host.writeFile(\"deno://subdir/mod2.js\")"));
- assert!(out.contains("host.writeFile(\"deno://006_url_imports.js\")"));
-
let prg = util::deno_exe_path();
let output = Command::new(&prg)
.env("DENO_DIR", deno_dir.path())
@@ -370,46 +364,6 @@ fn ts_no_recheck_on_redirect() {
}
#[test]
-fn ts_reload() {
- let hello_ts = util::testdata_path().join("002_hello.ts");
- assert!(hello_ts.is_file());
-
- let deno_dir = TempDir::new();
- let mut initial = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(util::testdata_path())
- .arg("cache")
- .arg("--check=all")
- .arg(&hello_ts)
- .spawn()
- .expect("failed to spawn script");
- let status_initial =
- initial.wait().expect("failed to wait for child process");
- assert!(status_initial.success());
-
- let output = util::deno_cmd_with_deno_dir(&deno_dir)
- .current_dir(util::testdata_path())
- .arg("cache")
- .arg("--check=all")
- .arg("--reload")
- .arg("-L")
- .arg("debug")
- .arg(&hello_ts)
- .output()
- .expect("failed to spawn script");
-
- // check the output of the the bundle program.
- let output_path = hello_ts.canonicalize().unwrap();
- assert!(
- dbg!(std::str::from_utf8(&output.stderr).unwrap().trim()).contains(
- &format!(
- "host.getSourceFile(\"{}\", Latest)",
- url::Url::from_file_path(&output_path).unwrap().as_str()
- )
- )
- );
-}
-
-#[test]
fn timeout_clear() {
// https://github.com/denoland/deno/issues/7599
diff --git a/cli/tests/integration/run_tests.rs b/cli/tests/integration/run_tests.rs
index d9c20907d..e8bf3682a 100644
--- a/cli/tests/integration/run_tests.rs
+++ b/cli/tests/integration/run_tests.rs
@@ -2,8 +2,10 @@
use deno_core::url;
use std::process::Command;
+use std::process::Stdio;
use test_util as util;
use test_util::TempDir;
+use util::assert_contains;
itest!(stdout_write_all {
args: "run --quiet stdout_write_all.ts",
@@ -268,7 +270,7 @@ fn webstorage_location_shares_origin() {
.arg("--location")
.arg("https://example.com/a.ts")
.arg("webstorage/fixture.ts")
- .stdout(std::process::Stdio::piped())
+ .stdout(Stdio::piped())
.spawn()
.unwrap()
.wait_with_output()
@@ -283,7 +285,7 @@ fn webstorage_location_shares_origin() {
.arg("--location")
.arg("https://example.com/b.ts")
.arg("webstorage/logger.ts")
- .stdout(std::process::Stdio::piped())
+ .stdout(Stdio::piped())
.spawn()
.unwrap()
.wait_with_output()
@@ -305,7 +307,7 @@ fn webstorage_config_file() {
.arg("--config")
.arg("webstorage/config_a.jsonc")
.arg("webstorage/fixture.ts")
- .stdout(std::process::Stdio::piped())
+ .stdout(Stdio::piped())
.spawn()
.unwrap()
.wait_with_output()
@@ -320,7 +322,7 @@ fn webstorage_config_file() {
.arg("--config")
.arg("webstorage/config_b.jsonc")
.arg("webstorage/logger.ts")
- .stdout(std::process::Stdio::piped())
+ .stdout(Stdio::piped())
.spawn()
.unwrap()
.wait_with_output()
@@ -335,7 +337,7 @@ fn webstorage_config_file() {
.arg("--config")
.arg("webstorage/config_a.jsonc")
.arg("webstorage/logger.ts")
- .stdout(std::process::Stdio::piped())
+ .stdout(Stdio::piped())
.spawn()
.unwrap()
.wait_with_output()
@@ -359,7 +361,7 @@ fn webstorage_location_precedes_config() {
.arg("--config")
.arg("webstorage/config_a.jsonc")
.arg("webstorage/fixture.ts")
- .stdout(std::process::Stdio::piped())
+ .stdout(Stdio::piped())
.spawn()
.unwrap()
.wait_with_output()
@@ -376,7 +378,7 @@ fn webstorage_location_precedes_config() {
.arg("--config")
.arg("webstorage/config_b.jsonc")
.arg("webstorage/logger.ts")
- .stdout(std::process::Stdio::piped())
+ .stdout(Stdio::piped())
.spawn()
.unwrap()
.wait_with_output()
@@ -396,7 +398,7 @@ fn webstorage_main_module() {
.current_dir(util::testdata_path())
.arg("run")
.arg("webstorage/fixture.ts")
- .stdout(std::process::Stdio::piped())
+ .stdout(Stdio::piped())
.spawn()
.unwrap()
.wait_with_output()
@@ -409,7 +411,7 @@ fn webstorage_main_module() {
.current_dir(util::testdata_path())
.arg("run")
.arg("webstorage/logger.ts")
- .stdout(std::process::Stdio::piped())
+ .stdout(Stdio::piped())
.spawn()
.unwrap()
.wait_with_output()
@@ -422,7 +424,7 @@ fn webstorage_main_module() {
.current_dir(util::testdata_path())
.arg("run")
.arg("webstorage/fixture.ts")
- .stdout(std::process::Stdio::piped())
+ .stdout(Stdio::piped())
.spawn()
.unwrap()
.wait_with_output()
@@ -1632,8 +1634,8 @@ fn no_validate_asm() {
.current_dir(util::testdata_path())
.arg("run")
.arg("no_validate_asm.js")
- .stderr(std::process::Stdio::piped())
- .stdout(std::process::Stdio::piped())
+ .stderr(Stdio::piped())
+ .stdout(Stdio::piped())
.spawn()
.unwrap()
.wait_with_output()
@@ -1650,7 +1652,7 @@ fn exec_path() {
.arg("run")
.arg("--allow-read")
.arg("exec_path.ts")
- .stdout(std::process::Stdio::piped())
+ .stdout(Stdio::piped())
.spawn()
.unwrap()
.wait_with_output()
@@ -1776,7 +1778,7 @@ fn rust_log() {
.current_dir(util::testdata_path())
.arg("run")
.arg("001_hello.js")
- .stderr(std::process::Stdio::piped())
+ .stderr(Stdio::piped())
.spawn()
.unwrap()
.wait_with_output()
@@ -1790,7 +1792,7 @@ fn rust_log() {
.arg("run")
.arg("001_hello.js")
.env("RUST_LOG", "debug")
- .stderr(std::process::Stdio::piped())
+ .stderr(Stdio::piped())
.spawn()
.unwrap()
.wait_with_output()
@@ -1810,7 +1812,7 @@ fn dont_cache_on_check_fail() {
.arg("--check=all")
.arg("--reload")
.arg("error_003_typescript.ts")
- .stderr(std::process::Stdio::piped())
+ .stderr(Stdio::piped())
.spawn()
.unwrap()
.wait_with_output()
@@ -1824,7 +1826,7 @@ fn dont_cache_on_check_fail() {
.arg("run")
.arg("--check=all")
.arg("error_003_typescript.ts")
- .stderr(std::process::Stdio::piped())
+ .stderr(Stdio::piped())
.spawn()
.unwrap()
.wait_with_output()
@@ -2374,8 +2376,8 @@ fn issue12740() {
.current_dir(util::testdata_path())
.arg("run")
.arg(&mod1_path)
- .stderr(std::process::Stdio::null())
- .stdout(std::process::Stdio::null())
+ .stderr(Stdio::null())
+ .stdout(Stdio::null())
.spawn()
.unwrap()
.wait()
@@ -2387,8 +2389,8 @@ fn issue12740() {
.current_dir(util::testdata_path())
.arg("run")
.arg(&mod1_path)
- .stderr(std::process::Stdio::null())
- .stdout(std::process::Stdio::null())
+ .stderr(Stdio::null())
+ .stdout(Stdio::null())
.spawn()
.unwrap()
.wait()
@@ -2411,8 +2413,8 @@ fn issue12807() {
.arg("run")
.arg("--check")
.arg(&mod1_path)
- .stderr(std::process::Stdio::null())
- .stdout(std::process::Stdio::null())
+ .stderr(Stdio::null())
+ .stdout(Stdio::null())
.spawn()
.unwrap()
.wait()
@@ -2425,8 +2427,8 @@ fn issue12807() {
.arg("run")
.arg("--check")
.arg(&mod1_path)
- .stderr(std::process::Stdio::null())
- .stdout(std::process::Stdio::null())
+ .stderr(Stdio::null())
+ .stdout(Stdio::null())
.spawn()
.unwrap()
.wait()
@@ -2663,6 +2665,36 @@ itest!(js_root_with_ts_check {
exit_code: 1,
});
+#[test]
+fn check_local_then_remote() {
+ let _http_guard = util::http_server();
+ let deno_dir = util::new_deno_dir();
+ let output = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--check")
+ .arg("run/remote_type_error/main.ts")
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ let output = util::deno_cmd_with_deno_dir(&deno_dir)
+ .current_dir(util::testdata_path())
+ .arg("run")
+ .arg("--check=all")
+ .arg("run/remote_type_error/main.ts")
+ .env("NO_COLOR", "1")
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(!output.status.success());
+ let stderr = std::str::from_utf8(&output.stderr).unwrap();
+ assert_contains!(stderr, "Type 'string' is not assignable to type 'number'.");
+}
+
itest!(no_prompt_flag {
args: "run --quiet --unstable --no-prompt no_prompt.ts",
output_str: Some(""),
diff --git a/cli/tests/testdata/check/cache_config_on_off/deno.json b/cli/tests/testdata/check/cache_config_on_off/deno.json
new file mode 100644
index 000000000..8ad9c9801
--- /dev/null
+++ b/cli/tests/testdata/check/cache_config_on_off/deno.json
@@ -0,0 +1,5 @@
+{
+ "compilerOptions": {
+ "strict": false
+ }
+}
diff --git a/cli/tests/testdata/check/cache_config_on_off/main.ts b/cli/tests/testdata/check/cache_config_on_off/main.ts
new file mode 100644
index 000000000..0f3785f91
--- /dev/null
+++ b/cli/tests/testdata/check/cache_config_on_off/main.ts
@@ -0,0 +1 @@
+console.log(5);
diff --git a/cli/tests/testdata/coverage/branch_expected.lcov b/cli/tests/testdata/coverage/branch_expected.lcov
index 31da70224..fb3454210 100644
--- a/cli/tests/testdata/coverage/branch_expected.lcov
+++ b/cli/tests/testdata/coverage/branch_expected.lcov
@@ -11,7 +11,7 @@ BRH:0
DA:1,1
DA:2,2
DA:3,2
-DA:4,2
+DA:4,0
DA:5,0
DA:6,0
DA:7,2
@@ -22,6 +22,6 @@ DA:12,0
DA:13,0
DA:14,0
DA:15,0
-LH:5
+LH:4
LF:14
end_of_record
diff --git a/cli/tests/testdata/coverage/branch_expected.out b/cli/tests/testdata/coverage/branch_expected.out
index 2ff5e911e..630ea93b2 100644
--- a/cli/tests/testdata/coverage/branch_expected.out
+++ b/cli/tests/testdata/coverage/branch_expected.out
@@ -1,4 +1,5 @@
-cover [WILDCARD]/coverage/branch.ts ... 35.714% (5/14)
+cover [WILDCARD]/coverage/branch.ts ... 28.571% (4/14)
+ 4 | } else {
5 | return false;
6 | }
-----|-----
diff --git a/cli/tests/testdata/coverage/complex_expected.lcov b/cli/tests/testdata/coverage/complex_expected.lcov
index 7a3cd8d92..c6f9a2578 100644
--- a/cli/tests/testdata/coverage/complex_expected.lcov
+++ b/cli/tests/testdata/coverage/complex_expected.lcov
@@ -11,44 +11,62 @@ FNF:4
FNH:2
BRF:0
BRH:0
+DA:13,1
+DA:14,1
+DA:15,1
+DA:16,1
DA:17,2
DA:18,2
DA:19,2
DA:20,2
+DA:21,2
DA:22,2
DA:23,2
DA:24,2
DA:25,2
DA:26,2
DA:27,2
+DA:29,1
+DA:30,1
+DA:31,1
DA:32,1
DA:33,1
DA:34,1
DA:35,1
+DA:36,1
DA:37,2
DA:38,2
DA:39,2
DA:40,2
DA:41,2
DA:42,2
+DA:44,1
+DA:45,1
DA:46,0
DA:47,0
DA:48,0
DA:49,0
+DA:50,0
DA:51,0
DA:52,0
DA:53,0
DA:54,0
DA:55,0
DA:56,0
+DA:58,1
+DA:59,1
DA:60,1
+DA:62,1
+DA:63,1
DA:64,0
DA:65,0
DA:66,0
DA:67,0
DA:68,0
+DA:70,1
DA:71,0
+DA:73,1
DA:74,1
-LH:22
-LF:38
+LH:39
+LF:56
end_of_record
diff --git a/cli/tests/testdata/coverage/complex_expected.out b/cli/tests/testdata/coverage/complex_expected.out
index 3f7c89e9b..aeff4cd60 100644
--- a/cli/tests/testdata/coverage/complex_expected.out
+++ b/cli/tests/testdata/coverage/complex_expected.out
@@ -1,9 +1,9 @@
-cover [WILDCARD]/coverage/complex.ts ... 57.895% (22/38)
+cover [WILDCARD]/coverage/complex.ts ... 69.643% (39/56)
46 | export function unused(
47 | foo: string,
48 | bar: string,
49 | baz: string,
------|-----
+ 50 | ): Complex {
51 | return complex(
52 | foo,
53 | bar,
diff --git a/cli/tests/testdata/run/remote_type_error/main.ts b/cli/tests/testdata/run/remote_type_error/main.ts
new file mode 100644
index 000000000..00f8a52df
--- /dev/null
+++ b/cli/tests/testdata/run/remote_type_error/main.ts
@@ -0,0 +1,3 @@
+import { doAction } from "http://localhost:4545/run/remote_type_error/remote.ts";
+
+doAction();
diff --git a/cli/tests/testdata/run/remote_type_error/remote.ts b/cli/tests/testdata/run/remote_type_error/remote.ts
new file mode 100644
index 000000000..6e9bf4adb
--- /dev/null
+++ b/cli/tests/testdata/run/remote_type_error/remote.ts
@@ -0,0 +1,5 @@
+export function doAction() {
+ // this is an intentional type error
+ const val: number = "test";
+ console.log(val);
+}
diff --git a/cli/tools/coverage/mod.rs b/cli/tools/coverage/mod.rs
index bba7271f5..d2c6c1894 100644
--- a/cli/tools/coverage/mod.rs
+++ b/cli/tools/coverage/mod.rs
@@ -2,6 +2,7 @@
use crate::args::CoverageFlags;
use crate::args::Flags;
+use crate::cache::EmitCache;
use crate::colors;
use crate::fs_util::collect_files;
use crate::proc_state::ProcState;
@@ -676,16 +677,9 @@ pub async fn cover_files(
| MediaType::Mts
| MediaType::Cts
| MediaType::Tsx => {
- let emit_path = ps
- .dir
- .gen_cache
- .get_cache_filename_with_extension(&file.specifier, "js")
- .unwrap_or_else(|| {
- unreachable!("Unable to get cache filename: {}", &file.specifier)
- });
- match ps.dir.gen_cache.get(&emit_path) {
- Ok(b) => String::from_utf8(b).unwrap(),
- Err(_) => {
+ match ps.dir.gen_cache.get_emit_text(&file.specifier) {
+ Some(source) => source,
+ None => {
return Err(anyhow!(
"Missing transpiled source code for: \"{}\".
Before generating coverage report, run `deno test --coverage` to ensure consistent state.",
diff --git a/cli/tools/fmt.rs b/cli/tools/fmt.rs
index 585bf6e92..334e46c34 100644
--- a/cli/tools/fmt.rs
+++ b/cli/tools/fmt.rs
@@ -39,7 +39,7 @@ use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::sync::Arc;
-use super::incremental_cache::IncrementalCache;
+use crate::cache::IncrementalCache;
/// Format JavaScript/TypeScript files.
pub async fn format(
diff --git a/cli/tools/lint.rs b/cli/tools/lint.rs
index 61129b752..1c79ff8f6 100644
--- a/cli/tools/lint.rs
+++ b/cli/tools/lint.rs
@@ -41,7 +41,7 @@ use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::sync::Mutex;
-use super::incremental_cache::IncrementalCache;
+use crate::cache::IncrementalCache;
static STDIN_FILE_NAME: &str = "_stdin.ts";
diff --git a/cli/tools/mod.rs b/cli/tools/mod.rs
index a6eaeb70e..7c5d79744 100644
--- a/cli/tools/mod.rs
+++ b/cli/tools/mod.rs
@@ -4,7 +4,6 @@ pub mod bench;
pub mod coverage;
pub mod doc;
pub mod fmt;
-pub mod incremental_cache;
pub mod installer;
pub mod lint;
pub mod repl;
diff --git a/cli/tsc.rs b/cli/tsc.rs
index 4065c6354..e3001583b 100644
--- a/cli/tsc.rs
+++ b/cli/tsc.rs
@@ -252,8 +252,6 @@ pub struct Request {
pub struct Response {
/// Any diagnostics that have been returned from the checker.
pub diagnostics: Diagnostics,
- /// Any files that were emitted during the check.
- pub emitted_files: Vec<EmittedFile>,
/// If there was any build info associated with the exec request.
pub maybe_tsbuildinfo: Option<String>,
/// Statistics from the check.
@@ -263,7 +261,6 @@ pub struct Response {
#[derive(Debug)]
struct State {
hash_data: Vec<Vec<u8>>,
- emitted_files: Vec<EmittedFile>,
graph_data: Arc<RwLock<GraphData>>,
maybe_config_specifier: Option<ModuleSpecifier>,
maybe_tsbuildinfo: Option<String>,
@@ -283,7 +280,6 @@ impl State {
) -> Self {
State {
hash_data,
- emitted_files: Default::default(),
graph_data,
maybe_config_specifier,
maybe_tsbuildinfo,
@@ -337,10 +333,6 @@ struct EmitArgs {
/// The _internal_ filename for the file. This will be used to determine how
/// the file is cached and stored.
file_name: String,
- /// A string representation of the specifier that was associated with a
- /// module. This should be present on every module that represents a module
- /// that was requested to be transformed.
- maybe_specifiers: Option<Vec<String>>,
}
#[op]
@@ -349,43 +341,9 @@ fn op_emit(state: &mut OpState, args: EmitArgs) -> bool {
match args.file_name.as_ref() {
"deno:///.tsbuildinfo" => state.maybe_tsbuildinfo = Some(args.data),
_ => {
- let media_type = MediaType::from(&args.file_name);
- let media_type = if matches!(
- media_type,
- MediaType::JavaScript
- | MediaType::Mjs
- | MediaType::Cjs
- | MediaType::Dts
- | MediaType::Dmts
- | MediaType::Dcts
- | MediaType::SourceMap
- | MediaType::TsBuildInfo
- ) {
- media_type
- } else {
- MediaType::JavaScript
- };
- state.emitted_files.push(EmittedFile {
- data: args.data,
- maybe_specifiers: if let Some(specifiers) = &args.maybe_specifiers {
- let specifiers = specifiers
- .iter()
- .map(|s| {
- if let Some(data_specifier) = state.remapped_specifiers.get(s) {
- data_specifier.clone()
- } else if let Some(remapped_specifier) = state.root_map.get(s) {
- remapped_specifier.clone()
- } else {
- normalize_specifier(s).unwrap()
- }
- })
- .collect();
- Some(specifiers)
- } else {
- None
- },
- media_type,
- })
+ if cfg!(debug_assertions) {
+ panic!("Unhandled emit write: {}", args.file_name);
+ }
}
}
@@ -703,13 +661,11 @@ pub fn exec(request: Request) -> Result<Response, AnyError> {
if let Some(response) = state.maybe_response {
let diagnostics = response.diagnostics;
- let emitted_files = state.emitted_files;
let maybe_tsbuildinfo = state.maybe_tsbuildinfo;
let stats = response.stats;
Ok(Response {
diagnostics,
- emitted_files,
maybe_tsbuildinfo,
stats,
})
@@ -908,64 +864,6 @@ mod tests {
}
#[tokio::test]
- async fn test_emit() {
- let mut state = setup(None, None, None).await;
- let actual = op_emit::call(
- &mut state,
- EmitArgs {
- data: "some file content".to_string(),
- file_name: "cache:///some/file.js".to_string(),
- maybe_specifiers: Some(vec!["file:///some/file.ts".to_string()]),
- },
- );
- assert!(actual);
- let state = state.borrow::<State>();
- assert_eq!(state.emitted_files.len(), 1);
- assert!(state.maybe_tsbuildinfo.is_none());
- assert_eq!(
- state.emitted_files[0],
- EmittedFile {
- data: "some file content".to_string(),
- maybe_specifiers: Some(vec![resolve_url_or_path(
- "file:///some/file.ts"
- )
- .unwrap()]),
- media_type: MediaType::JavaScript,
- }
- );
- }
-
- #[tokio::test]
- async fn test_emit_strange_specifier() {
- let mut state = setup(None, None, None).await;
- let actual = op_emit::call(
- &mut state,
- EmitArgs {
- data: "some file content".to_string(),
- file_name: "deno:///some.file.ts?q=.json".to_string(),
- maybe_specifiers: Some(
- vec!["file:///some/file.ts?q=.json".to_string()],
- ),
- },
- );
- assert!(actual);
- let state = state.borrow::<State>();
- assert_eq!(state.emitted_files.len(), 1);
- assert!(state.maybe_tsbuildinfo.is_none());
- assert_eq!(
- state.emitted_files[0],
- EmittedFile {
- data: "some file content".to_string(),
- maybe_specifiers: Some(vec![resolve_url_or_path(
- "file:///some/file.ts?q=.json"
- )
- .unwrap()]),
- media_type: MediaType::JavaScript,
- }
- );
- }
-
- #[tokio::test]
async fn test_emit_tsbuildinfo() {
let mut state = setup(None, None, None).await;
let actual = op_emit::call(
@@ -973,12 +871,10 @@ mod tests {
EmitArgs {
data: "some file content".to_string(),
file_name: "deno:///.tsbuildinfo".to_string(),
- maybe_specifiers: None,
},
);
assert!(actual);
let state = state.borrow::<State>();
- assert_eq!(state.emitted_files.len(), 0);
assert_eq!(
state.maybe_tsbuildinfo,
Some("some file content".to_string())
@@ -1169,7 +1065,6 @@ mod tests {
.expect("exec should not have errored");
eprintln!("diagnostics {:#?}", actual.diagnostics);
assert!(actual.diagnostics.is_empty());
- assert!(actual.emitted_files.is_empty());
assert!(actual.maybe_tsbuildinfo.is_some());
assert_eq!(actual.stats.0.len(), 12);
}
@@ -1182,7 +1077,6 @@ mod tests {
.expect("exec should not have errored");
eprintln!("diagnostics {:#?}", actual.diagnostics);
assert!(actual.diagnostics.is_empty());
- assert!(actual.emitted_files.is_empty());
assert!(actual.maybe_tsbuildinfo.is_some());
assert_eq!(actual.stats.0.len(), 12);
}
diff --git a/cli/tsc/99_main_compiler.js b/cli/tsc/99_main_compiler.js
index 85ab38ccc..1c6679e84 100644
--- a/cli/tsc/99_main_compiler.js
+++ b/cli/tsc/99_main_compiler.js
@@ -336,15 +336,11 @@ delete Object.prototype.__proto__;
getDefaultLibLocation() {
return ASSETS;
},
- writeFile(fileName, data, _writeByteOrderMark, _onError, sourceFiles) {
+ writeFile(fileName, data, _writeByteOrderMark, _onError, _sourceFiles) {
debug(`host.writeFile("${fileName}")`);
- let maybeSpecifiers;
- if (sourceFiles) {
- maybeSpecifiers = sourceFiles.map((sf) => sf.moduleName);
- }
return core.opSync(
"op_emit",
- { maybeSpecifiers, fileName, data },
+ { fileName, data },
);
},
getCurrentDirectory() {
@@ -557,16 +553,18 @@ delete Object.prototype.__proto__;
configFileParsingDiagnostics,
});
- const { diagnostics: emitDiagnostics } = program.emit();
-
const diagnostics = [
...program.getConfigFileParsingDiagnostics(),
...program.getSyntacticDiagnostics(),
...program.getOptionsDiagnostics(),
...program.getGlobalDiagnostics(),
...program.getSemanticDiagnostics(),
- ...emitDiagnostics,
].filter(({ code }) => !IGNORED_DIAGNOSTICS.includes(code));
+
+ // emit the tsbuildinfo file
+ // @ts-ignore: emitBuildInfo is not exposed (https://github.com/microsoft/TypeScript/issues/49871)
+ program.emitBuildInfo(host.writeFile);
+
performanceProgram({ program });
core.opSync("op_respond", {