diff --git a/Cargo.lock b/Cargo.lock index 673712362c..a288763721 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1324,9 +1324,9 @@ dependencies = [ [[package]] name = "deno_doc" -version = "0.107.0" +version = "0.110.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f82478f27de7958eb6a1e48e447b8cb030a1294097ef510eec190d29e81f330f" +checksum = "87316f90c7a0d58043b3a06291c3c75228f6b14b8f3d17ebeb1bc98edc0ebd78" dependencies = [ "ammonia", "anyhow", @@ -1348,9 +1348,9 @@ dependencies = [ [[package]] name = "deno_emit" -version = "0.37.0" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a670c56f233f85f18f1d4a3288c5241505d8aea559fe3870b45e00d4c0e731dc" +checksum = "f3871940b8ad61336c4de4645349236d12d1c3be4cb959487b2e3a3eaa498d12" dependencies = [ "anyhow", "base64", @@ -1418,9 +1418,9 @@ dependencies = [ [[package]] name = "deno_graph" -version = "0.66.2" +version = "0.68.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10efbd226fb00e97c04350051cbb025957b2de025117493ee5b9e53cc7e230f" +checksum = "47a122de1af66e3b5389a914ce0bf6296271138d259fb78259b839ca7e0722a7" dependencies = [ "anyhow", "async-trait", @@ -1440,6 +1440,7 @@ dependencies = [ "serde_json", "sha2", "thiserror", + "twox-hash", "url", ] @@ -2429,9 +2430,9 @@ dependencies = [ [[package]] name = "eszip" -version = "0.63.0" +version = "0.64.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "731a0e44e886cb8efbbd63b8121341d505e9dab855fe487249d70c362a6bd774" +checksum = "13ed72fda5ff9a13b0710f6e4bf52c9b56f170ef2c85bdab124f9671aeb124db" dependencies = [ "anyhow", "base64", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index a7d6780e20..77bb27f625 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -66,9 +66,9 @@ deno_ast = { workspace = true, features = ["bundler", "cjs", "codegen", "proposa deno_cache_dir = { workspace = true } deno_config = "=0.10.0" deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] } -deno_doc = { version = "=0.107.0", features = ["html"] } -deno_emit = "=0.37.0" -deno_graph = "=0.66.2" +deno_doc = { version = "=0.110.0", features = ["html"] } +deno_emit = "=0.38.0" +deno_graph = "=0.68.0" deno_lint = { version = "=0.56.0", features = ["docs"] } deno_lockfile.workspace = true deno_npm = "=0.17.0" @@ -76,7 +76,7 @@ deno_runtime = { workspace = true, features = ["include_js_files_for_snapshottin deno_semver = "=0.5.4" deno_task_shell = "=0.14.3" deno_terminal.workspace = true -eszip = "=0.63.0" +eszip = "=0.64.0" napi_sym.workspace = true async-trait.workspace = true diff --git a/cli/args/mod.rs b/cli/args/mod.rs index d7ff91b849..2a779e04b0 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -1304,6 +1304,67 @@ impl CliOptions { ) } + pub fn resolve_deno_graph_workspace_members( + &self, + ) -> Result, AnyError> { + fn workspace_config_to_workspace_members( + workspace_config: &deno_config::WorkspaceConfig, + ) -> Result, AnyError> { + workspace_config + .members + .iter() + .map(|member| { + config_to_workspace_member(&member.config_file).with_context(|| { + format!( + "Failed to resolve configuration for '{}' workspace member at '{}'", + member.member_name, + member.config_file.specifier.as_str() + ) + }) + }) + .collect() + } + + fn config_to_workspace_member( + config: &ConfigFile, + ) -> Result { + let nv = deno_semver::package::PackageNv { + name: match &config.json.name { + Some(name) => name.clone(), + None => bail!("Missing 'name' field in config file."), + }, + version: match &config.json.version { + Some(name) => deno_semver::Version::parse_standard(name)?, + None => bail!("Missing 'version' field in config file."), + }, + }; + Ok(deno_graph::WorkspaceMember { + base: config.specifier.join("./").unwrap(), + nv, + exports: config.to_exports_config()?.into_map(), + }) + } + + let maybe_workspace_config = self.maybe_workspace_config(); + if let Some(wc) = maybe_workspace_config { + workspace_config_to_workspace_members(wc) + } else { + Ok( + self + .maybe_config_file() + .as_ref() + .and_then(|c| match config_to_workspace_member(c) { + Ok(m) => Some(vec![m]), + Err(e) => { + log::debug!("Deno config was not a package: {:#}", e); + None + } + }) + .unwrap_or_default(), + ) + } + } + /// Vector of user script CLI arguments. pub fn argv(&self) -> &Vec { &self.flags.argv diff --git a/cli/cache/caches.rs b/cli/cache/caches.rs index 7220a2f9d6..dc97f02d55 100644 --- a/cli/cache/caches.rs +++ b/cli/cache/caches.rs @@ -9,6 +9,7 @@ use super::cache_db::CacheDB; use super::cache_db::CacheDBConfiguration; use super::check::TYPE_CHECK_CACHE_DB; use super::deno_dir::DenoDirProvider; +use super::fast_check::FAST_CHECK_CACHE_DB; use super::incremental::INCREMENTAL_CACHE_DB; use super::module_info::MODULE_INFO_CACHE_DB; use super::node::NODE_ANALYSIS_CACHE_DB; @@ -18,6 +19,7 @@ pub struct Caches { fmt_incremental_cache_db: OnceCell, lint_incremental_cache_db: OnceCell, dep_analysis_db: OnceCell, + fast_check_db: OnceCell, node_analysis_db: OnceCell, type_checking_cache_db: OnceCell, } @@ -29,6 +31,7 @@ impl Caches { fmt_incremental_cache_db: Default::default(), lint_incremental_cache_db: Default::default(), dep_analysis_db: Default::default(), + fast_check_db: Default::default(), node_analysis_db: Default::default(), type_checking_cache_db: Default::default(), } @@ -86,6 +89,18 @@ impl Caches { ) } + pub fn fast_check_db(&self) -> CacheDB { + Self::make_db( + &self.fast_check_db, + &FAST_CHECK_CACHE_DB, + self + .dir_provider + .get_or_create() + .ok() + .map(|dir| dir.fast_check_cache_db_file_path()), + ) + } + pub fn node_analysis_db(&self) -> CacheDB { Self::make_db( &self.node_analysis_db, diff --git a/cli/cache/deno_dir.rs b/cli/cache/deno_dir.rs index 72f8987bd6..ee8c35684e 100644 --- a/cli/cache/deno_dir.rs +++ b/cli/cache/deno_dir.rs @@ -98,6 +98,12 @@ impl DenoDir { self.root.join("dep_analysis_cache_v1") } + /// Path for the cache used for fast check. + pub fn fast_check_cache_db_file_path(&self) -> PathBuf { + // bump this version name to invalidate the entire cache + self.root.join("fast_check_cache_v1") + } + /// Path for caching node analysis. pub fn node_analysis_db_file_path(&self) -> PathBuf { // bump this version name to invalidate the entire cache diff --git a/cli/cache/fast_check.rs b/cli/cache/fast_check.rs new file mode 100644 index 0000000000..f8335d5ea2 --- /dev/null +++ b/cli/cache/fast_check.rs @@ -0,0 +1,162 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use deno_core::error::AnyError; +use deno_graph::FastCheckCacheItem; +use deno_graph::FastCheckCacheKey; +use deno_runtime::deno_webstorage::rusqlite::params; + +use super::cache_db::CacheDB; +use super::cache_db::CacheDBConfiguration; +use super::cache_db::CacheFailure; + +pub static FAST_CHECK_CACHE_DB: CacheDBConfiguration = CacheDBConfiguration { + table_initializer: "CREATE TABLE IF NOT EXISTS fastcheckcache ( + hash TEXT PRIMARY KEY, + data TEXT NOT NULL + );", + on_version_change: "DELETE FROM fastcheckcache;", + preheat_queries: &[], + on_failure: CacheFailure::Blackhole, +}; + +#[derive(Clone)] +pub struct FastCheckCache { + inner: FastCheckCacheInner, +} + +impl FastCheckCache { + pub fn new(db: CacheDB) -> Self { + Self { + inner: FastCheckCacheInner::new(db), + } + } + + fn ensure_ok(res: Result) -> T { + match res { + Ok(x) => x, + Err(err) => { + // TODO(mmastrac): This behavior was inherited from before the refactoring but it probably makes sense to move it into the cache + // at some point. + // should never error here, but if it ever does don't fail + if cfg!(debug_assertions) { + panic!("Error using fast check cache: {err:#}"); + } else { + log::debug!("Error using fast check cache: {:#}", err); + } + T::default() + } + } + } +} + +impl deno_graph::FastCheckCache for FastCheckCache { + fn get(&self, key: FastCheckCacheKey) -> Option { + Self::ensure_ok(self.inner.get(key)) + } + + fn set(&self, key: FastCheckCacheKey, value: FastCheckCacheItem) { + Self::ensure_ok(self.inner.set(key, &value)); + } +} + +#[derive(Clone)] +struct FastCheckCacheInner { + conn: CacheDB, +} + +impl FastCheckCacheInner { + pub fn new(conn: CacheDB) -> Self { + Self { conn } + } + + pub fn get( + &self, + key: FastCheckCacheKey, + ) -> Result, AnyError> { + let query = " + SELECT + data + FROM + fastcheckcache + WHERE + hash=?1 + LIMIT 1"; + let res = self + .conn + // key is a string because SQLite can't handle u64 + .query_row(query, params![key.as_u64().to_string()], |row| { + let value: Vec = row.get(0)?; + Ok(bincode::deserialize::(&value)?) + })?; + Ok(res) + } + + pub fn set( + &self, + key: FastCheckCacheKey, + data: &FastCheckCacheItem, + ) -> Result<(), AnyError> { + let sql = " + INSERT OR REPLACE INTO + fastcheckcache (hash, data) + VALUES + (?1, ?2)"; + self.conn.execute( + sql, + params![key.as_u64().to_string(), &bincode::serialize(data)?], + )?; + Ok(()) + } +} + +#[cfg(test)] +mod test { + use std::collections::BTreeSet; + + use deno_ast::ModuleSpecifier; + use deno_graph::FastCheckCacheModuleItem; + use deno_graph::FastCheckCacheModuleItemDiagnostic; + use deno_semver::package::PackageNv; + + use super::*; + + #[test] + pub fn cache_general_use() { + let conn = CacheDB::in_memory(&FAST_CHECK_CACHE_DB, "1.0.0"); + let cache = FastCheckCacheInner::new(conn); + + let key = FastCheckCacheKey::build( + &PackageNv::from_str("@scope/a@1.0.0").unwrap(), + &Default::default(), + ); + assert!(cache.get(key).unwrap().is_none()); + let value = FastCheckCacheItem { + dependencies: BTreeSet::from([ + PackageNv::from_str("@scope/b@1.0.0").unwrap() + ]), + modules: vec![( + ModuleSpecifier::parse("https://jsr.io/test.ts").unwrap(), + FastCheckCacheModuleItem::Diagnostic( + FastCheckCacheModuleItemDiagnostic { source_hash: 123 }, + ), + )], + }; + cache.set(key, &value).unwrap(); + let stored_value = cache.get(key).unwrap().unwrap(); + assert_eq!(stored_value, value); + + // adding when already exists should not cause issue + cache.set(key, &value).unwrap(); + + // recreating with same cli version should still have it + let conn = cache.conn.recreate_with_version("1.0.0"); + let cache = FastCheckCacheInner::new(conn); + let stored_value = cache.get(key).unwrap().unwrap(); + assert_eq!(stored_value, value); + + // now changing the cli version should clear it + let conn = cache.conn.recreate_with_version("2.0.0"); + let cache = FastCheckCacheInner::new(conn); + assert!(cache.get(key).unwrap().is_none()); + } +} diff --git a/cli/cache/mod.rs b/cli/cache/mod.rs index e9d2251466..7d95a6423e 100644 --- a/cli/cache/mod.rs +++ b/cli/cache/mod.rs @@ -1,6 +1,5 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::args::jsr_url; use crate::args::CacheSetting; use crate::errors::get_error_class_name; use crate::file_fetcher::FetchOptions; @@ -11,7 +10,6 @@ use crate::util::fs::atomic_write_file; use deno_ast::MediaType; use deno_core::futures; use deno_core::futures::FutureExt; -use deno_core::url::Url; use deno_core::ModuleSpecifier; use deno_graph::source::CacheInfo; use deno_graph::source::LoadFuture; @@ -31,6 +29,7 @@ mod common; mod deno_dir; mod disk_cache; mod emit; +mod fast_check; mod incremental; mod module_info; mod node; @@ -43,6 +42,7 @@ pub use deno_dir::DenoDir; pub use deno_dir::DenoDirProvider; pub use disk_cache::DiskCache; pub use emit::EmitCache; +pub use fast_check::FastCheckCache; pub use incremental::IncrementalCache; pub use module_info::ModuleInfoCache; pub use node::NodeAnalysisCache; @@ -167,10 +167,6 @@ impl FetchCacher { } impl Loader for FetchCacher { - fn registry_url(&self) -> &Url { - jsr_url() - } - fn get_cache_info(&self, specifier: &ModuleSpecifier) -> Option { if !self.cache_info_enabled { return None; diff --git a/cli/factory.rs b/cli/factory.rs index 2797afb294..9f3719510f 100644 --- a/cli/factory.rs +++ b/cli/factory.rs @@ -22,6 +22,7 @@ use crate::file_fetcher::FileFetcher; use crate::graph_util::FileWatcherReporter; use crate::graph_util::ModuleGraphBuilder; use crate::graph_util::ModuleGraphContainer; +use crate::graph_util::ModuleGraphCreator; use crate::http_util::HttpClient; use crate::module_loader::CliModuleLoaderFactory; use crate::module_loader::ModuleLoadPreparer; @@ -55,6 +56,7 @@ use crate::worker::CliMainWorkerOptions; use std::path::PathBuf; use deno_core::error::AnyError; +use deno_core::futures::FutureExt; use deno_core::parking_lot::Mutex; use deno_core::FeatureChecker; @@ -116,6 +118,7 @@ impl Default for Deferred { } impl Deferred { + #[inline(always)] pub fn get_or_try_init( &self, create: impl FnOnce() -> Result, @@ -123,12 +126,16 @@ impl Deferred { self.0.get_or_try_init(create) } + #[inline(always)] pub fn get_or_init(&self, create: impl FnOnce() -> T) -> &T { self.0.get_or_init(create) } pub async fn get_or_try_init_async( &self, + // some futures passed here are boxed because it was discovered + // that they were called a lot, causing other futures to get + // really big causing stack overflows on Windows create: impl Future>, ) -> Result<&T, AnyError> { if self.0.get().is_none() { @@ -164,6 +171,7 @@ struct CliFactoryServices { resolver: Deferred>, maybe_file_watcher_reporter: Deferred>, module_graph_builder: Deferred>, + module_graph_creator: Deferred>, module_load_preparer: Deferred>, node_code_translator: Deferred>, node_resolver: Deferred>, @@ -212,14 +220,16 @@ impl CliFactory { let caches = Arc::new(Caches::new(self.deno_dir_provider().clone())); // Warm up the caches we know we'll likely need based on the CLI mode match self.options.sub_command() { - DenoSubcommand::Run(_) => { + DenoSubcommand::Run(_) + | DenoSubcommand::Bench(_) + | DenoSubcommand::Test(_) + | DenoSubcommand::Check(_) => { _ = caches.dep_analysis_db(); _ = caches.node_analysis_db(); - } - DenoSubcommand::Check(_) => { - _ = caches.dep_analysis_db(); - _ = caches.node_analysis_db(); - _ = caches.type_checking_cache_db(); + if self.options.type_check_mode().is_true() { + _ = caches.fast_check_db(); + _ = caches.type_checking_cache_db(); + } } _ => {} } @@ -437,7 +447,7 @@ impl CliFactory { npm_registry_url: crate::args::npm_registry_default_url().to_owned(), }) }).await - }) + }.boxed_local()) .await } @@ -471,32 +481,37 @@ impl CliFactory { self .services .resolver - .get_or_try_init_async(async { - Ok(Arc::new(CliGraphResolver::new(CliGraphResolverOptions { - fs: self.fs().clone(), - cjs_resolutions: Some(self.cjs_resolutions().clone()), - sloppy_imports_resolver: if self.options.unstable_sloppy_imports() { - Some(SloppyImportsResolver::new(self.fs().clone())) - } else { - None - }, - node_resolver: Some(self.node_resolver().await?.clone()), - npm_resolver: if self.options.no_npm() { - None - } else { - Some(self.npm_resolver().await?.clone()) - }, - package_json_deps_provider: self.package_json_deps_provider().clone(), - maybe_jsx_import_source_config: self - .options - .to_maybe_jsx_import_source_config()?, - maybe_import_map: self.maybe_import_map().await?.clone(), - maybe_vendor_dir: self.options.vendor_dir_path(), - bare_node_builtins_enabled: self - .options - .unstable_bare_node_builtins(), - }))) - }) + .get_or_try_init_async( + async { + Ok(Arc::new(CliGraphResolver::new(CliGraphResolverOptions { + fs: self.fs().clone(), + cjs_resolutions: Some(self.cjs_resolutions().clone()), + sloppy_imports_resolver: if self.options.unstable_sloppy_imports() { + Some(SloppyImportsResolver::new(self.fs().clone())) + } else { + None + }, + node_resolver: Some(self.node_resolver().await?.clone()), + npm_resolver: if self.options.no_npm() { + None + } else { + Some(self.npm_resolver().await?.clone()) + }, + package_json_deps_provider: self + .package_json_deps_provider() + .clone(), + maybe_jsx_import_source_config: self + .options + .to_maybe_jsx_import_source_config()?, + maybe_import_map: self.maybe_import_map().await?.clone(), + maybe_vendor_dir: self.options.vendor_dir_path(), + bare_node_builtins_enabled: self + .options + .unstable_bare_node_builtins(), + }))) + } + .boxed_local(), + ) .await } @@ -554,12 +569,15 @@ impl CliFactory { self .services .node_resolver - .get_or_try_init_async(async { - Ok(Arc::new(NodeResolver::new( - self.fs().clone(), - self.npm_resolver().await?.clone().into_npm_resolver(), - ))) - }) + .get_or_try_init_async( + async { + Ok(Arc::new(NodeResolver::new( + self.fs().clone(), + self.npm_resolver().await?.clone().into_npm_resolver(), + ))) + } + .boxed_local(), + ) .await } @@ -594,6 +612,7 @@ impl CliFactory { Ok(Arc::new(TypeChecker::new( self.caches()?.clone(), self.options.clone(), + self.module_graph_builder().await?.clone(), self.node_resolver().await?.clone(), self.npm_resolver().await?.clone(), ))) @@ -610,6 +629,7 @@ impl CliFactory { .get_or_try_init_async(async { Ok(Arc::new(ModuleGraphBuilder::new( self.options.clone(), + self.caches()?.clone(), self.fs().clone(), self.resolver().await?.clone(), self.npm_resolver().await?.clone(), @@ -620,6 +640,24 @@ impl CliFactory { self.emit_cache()?.clone(), self.file_fetcher()?.clone(), self.global_http_cache()?.clone(), + ))) + }) + .await + } + + pub async fn module_graph_creator( + &self, + ) -> Result<&Arc, AnyError> { + self + .services + .module_graph_creator + .get_or_try_init_async(async { + Ok(Arc::new(ModuleGraphCreator::new( + self.options.clone(), + self.fs().clone(), + self.npm_resolver().await?.clone(), + self.module_graph_builder().await?.clone(), + self.maybe_lockfile().clone(), self.type_checker().await?.clone(), ))) }) diff --git a/cli/graph_util.rs b/cli/graph_util.rs index 380d82cbf7..de13f25ba0 100644 --- a/cli/graph_util.rs +++ b/cli/graph_util.rs @@ -22,10 +22,8 @@ use crate::util::path::specifier_to_file_path; use crate::util::sync::TaskQueue; use crate::util::sync::TaskQueuePermit; -use deno_config::ConfigFile; use deno_config::WorkspaceMemberConfig; use deno_core::anyhow::bail; -use deno_core::anyhow::Context; use deno_core::error::custom_error; use deno_core::error::AnyError; use deno_core::parking_lot::Mutex; @@ -208,56 +206,34 @@ pub struct CreateGraphOptions<'a> { pub graph_kind: GraphKind, pub roots: Vec, pub is_dynamic: bool, - /// Whether to do fast check on workspace members. This is mostly only - /// useful when publishing. - pub workspace_fast_check: bool, /// Specify `None` to use the default CLI loader. pub loader: Option<&'a mut dyn Loader>, } -pub struct ModuleGraphBuilder { +pub struct ModuleGraphCreator { options: Arc, fs: Arc, - resolver: Arc, npm_resolver: Arc, - module_info_cache: Arc, - parsed_source_cache: Arc, + module_graph_builder: Arc, lockfile: Option>>, - maybe_file_watcher_reporter: Option, - emit_cache: cache::EmitCache, - file_fetcher: Arc, - global_http_cache: Arc, type_checker: Arc, } -impl ModuleGraphBuilder { - #[allow(clippy::too_many_arguments)] +impl ModuleGraphCreator { pub fn new( options: Arc, fs: Arc, - resolver: Arc, npm_resolver: Arc, - module_info_cache: Arc, - parsed_source_cache: Arc, + module_graph_builder: Arc, lockfile: Option>>, - maybe_file_watcher_reporter: Option, - emit_cache: cache::EmitCache, - file_fetcher: Arc, - global_http_cache: Arc, type_checker: Arc, ) -> Self { Self { options, fs, - resolver, npm_resolver, - module_info_cache, - parsed_source_cache, lockfile, - maybe_file_watcher_reporter, - emit_cache, - file_fetcher, - global_http_cache, + module_graph_builder, type_checker, } } @@ -267,7 +243,7 @@ impl ModuleGraphBuilder { graph_kind: GraphKind, roots: Vec, ) -> Result { - let mut cache = self.create_graph_loader(); + let mut cache = self.module_graph_builder.create_graph_loader(); self .create_graph_with_loader(graph_kind, roots, &mut cache) .await @@ -285,7 +261,6 @@ impl ModuleGraphBuilder { graph_kind, roots, loader: Some(loader), - workspace_fast_check: false, }) .await } @@ -298,15 +273,21 @@ impl ModuleGraphBuilder { for package in packages { roots.extend(package.config_file.resolve_export_value_urls()?); } - self + let mut graph = self .create_graph_with_options(CreateGraphOptions { is_dynamic: false, graph_kind: deno_graph::GraphKind::All, roots, - workspace_fast_check: true, loader: None, }) - .await + .await?; + self.module_graph_builder.build_fast_check_graph( + &mut graph, + BuildFastCheckGraphOptions { + workspace_fast_check: true, + }, + )?; + Ok(graph) } pub async fn create_graph_with_options( @@ -316,6 +297,7 @@ impl ModuleGraphBuilder { let mut graph = ModuleGraph::new(options.graph_kind); self + .module_graph_builder .build_graph_with_npm_resolution(&mut graph, options) .await?; @@ -340,11 +322,9 @@ impl ModuleGraphBuilder { graph_kind, roots, loader: None, - workspace_fast_check: false, }) .await?; - let graph = Arc::new(graph); graph_valid_with_cli_options( &graph, self.fs.as_ref(), @@ -356,43 +336,76 @@ impl ModuleGraphBuilder { } if self.options.type_check_mode().is_true() { - self + // provide the graph to the type checker, then get it back after it's done + let graph = self .type_checker .check( - graph.clone(), + graph, check::CheckOptions { + build_fast_check_graph: true, lib: self.options.ts_type_lib_window(), log_ignored_options: true, reload: self.options.reload_flag(), }, ) .await?; - } - - Ok(graph) - } - - fn get_deno_graph_workspace_members( - &self, - ) -> Result, AnyError> { - let maybe_workspace_config = self.options.maybe_workspace_config(); - if let Some(wc) = maybe_workspace_config { - workspace_config_to_workspace_members(wc) + Ok(graph) } else { - Ok( - self - .options - .maybe_config_file() - .as_ref() - .and_then(|c| match config_to_workspace_member(c) { - Ok(m) => Some(vec![m]), - Err(e) => { - log::debug!("Deno config was not a package: {:#}", e); - None - } - }) - .unwrap_or_default(), - ) + Ok(Arc::new(graph)) + } + } +} + +pub struct BuildFastCheckGraphOptions { + /// Whether to do fast check on workspace members. This + /// is mostly only useful when publishing. + pub workspace_fast_check: bool, +} + +pub struct ModuleGraphBuilder { + options: Arc, + caches: Arc, + fs: Arc, + resolver: Arc, + npm_resolver: Arc, + module_info_cache: Arc, + parsed_source_cache: Arc, + lockfile: Option>>, + maybe_file_watcher_reporter: Option, + emit_cache: cache::EmitCache, + file_fetcher: Arc, + global_http_cache: Arc, +} + +impl ModuleGraphBuilder { + #[allow(clippy::too_many_arguments)] + pub fn new( + options: Arc, + caches: Arc, + fs: Arc, + resolver: Arc, + npm_resolver: Arc, + module_info_cache: Arc, + parsed_source_cache: Arc, + lockfile: Option>>, + maybe_file_watcher_reporter: Option, + emit_cache: cache::EmitCache, + file_fetcher: Arc, + global_http_cache: Arc, + ) -> Self { + Self { + options, + caches, + fs, + resolver, + npm_resolver, + module_info_cache, + parsed_source_cache, + lockfile, + maybe_file_watcher_reporter, + emit_cache, + file_fetcher, + global_http_cache, } } @@ -422,13 +435,15 @@ impl ModuleGraphBuilder { Some(loader) => MutLoaderRef::Borrowed(loader), None => MutLoaderRef::Owned(self.create_graph_loader()), }; - let cli_resolver = self.resolver.clone(); + let cli_resolver = &self.resolver; let graph_resolver = cli_resolver.as_graph_resolver(); let graph_npm_resolver = cli_resolver.as_graph_npm_resolver(); let maybe_file_watcher_reporter = self .maybe_file_watcher_reporter .as_ref() .map(|r| r.as_reporter()); + let workspace_members = + self.options.resolve_deno_graph_workspace_members()?; self .build_graph_with_npm_resolution_and_build_options( graph, @@ -436,6 +451,7 @@ impl ModuleGraphBuilder { loader.as_mut_loader(), deno_graph::BuildOptions { is_dynamic: options.is_dynamic, + jsr_url_provider: Some(&CliJsrUrlProvider), imports: maybe_imports, resolver: Some(graph_resolver), file_system: Some(&DenoGraphFsAdapter(self.fs.as_ref())), @@ -443,8 +459,7 @@ impl ModuleGraphBuilder { module_analyzer: Some(&analyzer), module_parser: Some(&parser), reporter: maybe_file_watcher_reporter, - workspace_fast_check: options.workspace_fast_check, - workspace_members: self.get_deno_graph_workspace_members()?, + workspace_members: &workspace_members, }, ) .await @@ -567,6 +582,49 @@ impl ModuleGraphBuilder { Ok(()) } + pub fn build_fast_check_graph( + &self, + graph: &mut ModuleGraph, + options: BuildFastCheckGraphOptions, + ) -> Result<(), AnyError> { + if !graph.graph_kind().include_types() { + return Ok(()); + } + + log::debug!("Building fast check graph"); + let fast_check_cache = if !options.workspace_fast_check { + Some(cache::FastCheckCache::new(self.caches.fast_check_db())) + } else { + None + }; + let parser = self.parsed_source_cache.as_capturing_parser(); + let cli_resolver = &self.resolver; + let graph_resolver = cli_resolver.as_graph_resolver(); + let graph_npm_resolver = cli_resolver.as_graph_npm_resolver(); + let workspace_members = if options.workspace_fast_check { + Some(self.options.resolve_deno_graph_workspace_members()?) + } else { + None + }; + + graph.build_fast_check_type_graph( + deno_graph::BuildFastCheckTypeGraphOptions { + jsr_url_provider: Some(&CliJsrUrlProvider), + fast_check_cache: fast_check_cache.as_ref().map(|c| c as _), + fast_check_dts: false, + module_parser: Some(&parser), + resolver: Some(graph_resolver), + npm_resolver: Some(graph_npm_resolver), + workspace_fast_check: if let Some(members) = &workspace_members { + deno_graph::WorkspaceFastCheckOption::Enabled(members) + } else { + deno_graph::WorkspaceFastCheckOption::Disabled + }, + }, + ); + Ok(()) + } + /// Creates the default loader used for creating a graph. pub fn create_graph_loader(&self) -> cache::FetchCacher { self.create_fetch_cacher(PermissionsContainer::allow_all()) @@ -851,44 +909,6 @@ impl deno_graph::source::Reporter for FileWatcherReporter { } } -fn workspace_config_to_workspace_members( - workspace_config: &deno_config::WorkspaceConfig, -) -> Result, AnyError> { - workspace_config - .members - .iter() - .map(|member| { - config_to_workspace_member(&member.config_file).with_context(|| { - format!( - "Failed to resolve configuration for '{}' workspace member at '{}'", - member.member_name, - member.config_file.specifier.as_str() - ) - }) - }) - .collect() -} - -fn config_to_workspace_member( - config: &ConfigFile, -) -> Result { - let nv = deno_semver::package::PackageNv { - name: match &config.json.name { - Some(name) => name.clone(), - None => bail!("Missing 'name' field in config file."), - }, - version: match &config.json.version { - Some(name) => deno_semver::Version::parse_standard(name)?, - None => bail!("Missing 'version' field in config file."), - }, - }; - Ok(deno_graph::WorkspaceMember { - base: config.specifier.join("./").unwrap(), - nv, - exports: config.to_exports_config()?.into_map(), - }) -} - pub struct DenoGraphFsAdapter<'a>( pub &'a dyn deno_runtime::deno_fs::FileSystem, ); @@ -963,6 +983,15 @@ pub fn format_range_with_colors(range: &deno_graph::Range) -> String { ) } +#[derive(Debug, Default, Clone, Copy)] +struct CliJsrUrlProvider; + +impl deno_graph::source::JsrUrlProvider for CliJsrUrlProvider { + fn url(&self) -> &'static ModuleSpecifier { + jsr_url() + } +} + #[cfg(test)] mod test { use std::sync::Arc; diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs index 1253077573..c727d0fc93 100644 --- a/cli/lsp/documents.rs +++ b/cli/lsp/documents.rs @@ -51,7 +51,6 @@ use deno_runtime::permissions::PermissionsContainer; use deno_semver::npm::NpmPackageReqReference; use deno_semver::package::PackageReq; use indexmap::IndexMap; -use lsp::Url; use once_cell::sync::Lazy; use package_json::PackageJsonDepsProvider; use std::borrow::Cow; @@ -1817,10 +1816,6 @@ impl<'a> OpenDocumentsGraphLoader<'a> { } impl<'a> deno_graph::source::Loader for OpenDocumentsGraphLoader<'a> { - fn registry_url(&self) -> &Url { - self.inner_loader.registry_url() - } - fn load( &mut self, specifier: &ModuleSpecifier, diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 7aa4fdc993..7e24c6e517 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -299,13 +299,14 @@ impl LanguageServer { let cli_options = Arc::new(cli_options); let factory = CliFactory::from_cli_options(cli_options.clone()); let module_graph_builder = factory.module_graph_builder().await?; + let module_graph_creator = factory.module_graph_creator().await?; let mut inner_loader = module_graph_builder.create_graph_loader(); let mut loader = crate::lsp::documents::OpenDocumentsGraphLoader { inner_loader: &mut inner_loader, open_docs: &open_docs, unstable_sloppy_imports: cli_options.unstable_sloppy_imports(), }; - let graph = module_graph_builder + let graph = module_graph_creator .create_graph_with_loader(GraphKind::All, roots.clone(), &mut loader) .await?; graph_util::graph_valid( diff --git a/cli/module_loader.rs b/cli/module_loader.rs index 4e0fe7142c..d2742d1ba4 100644 --- a/cli/module_loader.rs +++ b/cli/module_loader.rs @@ -127,7 +127,6 @@ impl ModuleLoadPreparer { graph_kind: graph.graph_kind(), roots: roots.clone(), loader: Some(&mut cache), - workspace_fast_check: false, }, ) .await?; @@ -157,12 +156,13 @@ impl ModuleLoadPreparer { if self.options.type_check_mode().is_true() && !self.graph_container.is_type_checked(&roots, lib) { - let graph = Arc::new(graph.segment(&roots)); + let graph = graph.segment(&roots); self .type_checker .check( graph, check::CheckOptions { + build_fast_check_graph: true, lib, log_ignored_options: false, reload: self.options.reload_flag() diff --git a/cli/tools/bench/mod.rs b/cli/tools/bench/mod.rs index 146c9e8bd0..2f0d59f49d 100644 --- a/cli/tools/bench/mod.rs +++ b/cli/tools/bench/mod.rs @@ -511,7 +511,7 @@ pub async fn run_benchmarks_with_watch( } let graph_kind = cli_options.type_check_mode().as_graph_kind(); - let module_graph_builder = factory.module_graph_builder().await?; + let module_graph_creator = factory.module_graph_creator().await?; let module_load_preparer = factory.module_load_preparer().await?; let bench_modules = collect_specifiers( @@ -525,7 +525,7 @@ pub async fn run_benchmarks_with_watch( let permissions = Permissions::from_options(&cli_options.permissions_options())?; - let graph = module_graph_builder + let graph = module_graph_creator .create_graph(graph_kind, bench_modules.clone()) .await?; graph_valid_with_cli_options( diff --git a/cli/tools/bundle.rs b/cli/tools/bundle.rs index 4099ad41ae..c0b1ce31bc 100644 --- a/cli/tools/bundle.rs +++ b/cli/tools/bundle.rs @@ -62,10 +62,10 @@ async fn bundle_action( let cli_options = factory.cli_options(); let module_specifier = cli_options.resolve_main_module()?; log::debug!(">>>>> bundle START"); - let module_graph_builder = factory.module_graph_builder().await?; + let module_graph_creator = factory.module_graph_creator().await?; let cli_options = factory.cli_options(); - let graph = module_graph_builder + let graph = module_graph_creator .create_graph_and_maybe_check(vec![module_specifier.clone()]) .await?; diff --git a/cli/tools/check.rs b/cli/tools/check.rs index 6e14d09f50..08fc6f0874 100644 --- a/cli/tools/check.rs +++ b/cli/tools/check.rs @@ -22,6 +22,8 @@ use crate::args::TypeCheckMode; use crate::cache::Caches; use crate::cache::FastInsecureHasher; use crate::cache::TypeCheckCache; +use crate::graph_util::BuildFastCheckGraphOptions; +use crate::graph_util::ModuleGraphBuilder; use crate::npm::CliNpmResolver; use crate::tsc; use crate::tsc::Diagnostics; @@ -30,6 +32,11 @@ 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 { + /// Whether to build the fast check type graph if necessary. + /// + /// Note: For perf reasons, the fast check type graph is only + /// built if type checking is necessary. + pub build_fast_check_graph: bool, /// Default type library to type check with. pub lib: TsTypeLib, /// Whether to log about any ignored compiler options. @@ -42,6 +49,7 @@ pub struct CheckOptions { pub struct TypeChecker { caches: Arc, cli_options: Arc, + module_graph_builder: Arc, node_resolver: Arc, npm_resolver: Arc, } @@ -50,12 +58,14 @@ impl TypeChecker { pub fn new( caches: Arc, cli_options: Arc, + module_graph_builder: Arc, node_resolver: Arc, npm_resolver: Arc, ) -> Self { Self { caches, cli_options, + module_graph_builder, node_resolver, npm_resolver, } @@ -67,12 +77,12 @@ impl TypeChecker { /// before the function is called. pub async fn check( &self, - graph: Arc, + graph: ModuleGraph, options: CheckOptions, - ) -> Result<(), AnyError> { - let diagnostics = self.check_diagnostics(graph, options).await?; + ) -> Result, AnyError> { + let (graph, diagnostics) = self.check_diagnostics(graph, options).await?; if diagnostics.is_empty() { - Ok(()) + Ok(graph) } else { Err(diagnostics.into()) } @@ -84,11 +94,11 @@ impl TypeChecker { /// before the function is called. pub async fn check_diagnostics( &self, - graph: Arc, + mut graph: ModuleGraph, options: CheckOptions, - ) -> Result { + ) -> Result<(Arc, Diagnostics), AnyError> { if graph.roots.is_empty() { - return Ok(Default::default()); + return Ok((graph.into(), Default::default())); } // node built-in specifiers use the @types/node package to determine @@ -112,9 +122,6 @@ impl TypeChecker { 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()); - let check_js = ts_config.get_check_js(); let maybe_check_hash = match self.npm_resolver.check_state_hash() { Some(npm_check_hash) => { match get_check_hash( @@ -123,7 +130,9 @@ impl TypeChecker { type_check_mode, &ts_config, ) { - CheckHashResult::NoFiles => return Ok(Default::default()), + CheckHashResult::NoFiles => { + return Ok((graph.into(), Default::default())) + } CheckHashResult::Hash(hash) => Some(hash), } } @@ -131,10 +140,12 @@ impl TypeChecker { }; // do not type check if we know this is type checked + let cache = TypeCheckCache::new(self.caches.type_checking_cache_db()); if !options.reload { if let Some(check_hash) = maybe_check_hash { if cache.has_check_hash(check_hash) { - return Ok(Default::default()); + log::debug!("Already type checked."); + return Ok((graph.into(), Default::default())); } } } @@ -144,7 +155,7 @@ impl TypeChecker { log::info!("{} {}", colors::green("Check"), root_str); } - let root_names = get_tsc_roots(&graph, check_js); + let check_js = ts_config.get_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. @@ -161,9 +172,21 @@ impl TypeChecker { .write_str(version::deno()) .finish(); + // add fast check to the graph before getting the roots + if options.build_fast_check_graph { + self.module_graph_builder.build_fast_check_graph( + &mut graph, + BuildFastCheckGraphOptions { + workspace_fast_check: false, + }, + )?; + } + + let root_names = get_tsc_roots(&graph, check_js); + let graph = Arc::new(graph); let response = tsc::exec(tsc::Request { config: ts_config, - debug, + debug: self.cli_options.log_level() == Some(log::Level::Debug), graph: graph.clone(), hash_data, maybe_npm: Some(tsc::RequestNpmState { @@ -212,7 +235,7 @@ impl TypeChecker { log::debug!("{}", response.stats); - Ok(diagnostics) + Ok((graph, diagnostics)) } } @@ -277,12 +300,7 @@ fn get_check_hash( } hasher.write_str(module.specifier.as_str()); - hasher.write_str( - module - .fast_check_module() - .map(|s| s.source.as_ref()) - .unwrap_or(&module.source), - ); + hasher.write_str(&module.source); } Module::Node(_) => { // the @types/node package will be in the resolved diff --git a/cli/tools/compile.rs b/cli/tools/compile.rs index 70baed6696..0a5e75f9ac 100644 --- a/cli/tools/compile.rs +++ b/cli/tools/compile.rs @@ -24,7 +24,7 @@ pub async fn compile( ) -> Result<(), AnyError> { let factory = CliFactory::from_flags(flags).await?; let cli_options = factory.cli_options(); - let module_graph_builder = factory.module_graph_builder().await?; + let module_graph_creator = factory.module_graph_creator().await?; let parsed_source_cache = factory.parsed_source_cache(); let binary_writer = factory.create_compile_binary_writer().await?; let module_specifier = cli_options.resolve_main_module()?; @@ -56,7 +56,7 @@ pub async fn compile( .await?; let graph = Arc::try_unwrap( - module_graph_builder + module_graph_creator .create_graph_and_maybe_check(module_roots.clone()) .await?, ) @@ -65,7 +65,7 @@ pub async fn compile( // In this case, the previous graph creation did type checking, which will // create a module graph with types information in it. We don't want to // store that in the eszip so create a code only module graph from scratch. - module_graph_builder + module_graph_creator .create_graph(GraphKind::CodeOnly, module_roots) .await? } else { diff --git a/cli/tools/doc.rs b/cli/tools/doc.rs index 5044e73d32..0b7b26e314 100644 --- a/cli/tools/doc.rs +++ b/cli/tools/doc.rs @@ -23,6 +23,7 @@ use deno_graph::GraphKind; use deno_graph::ModuleAnalyzer; use deno_graph::ModuleParser; use deno_graph::ModuleSpecifier; +use doc::html::ShortPath; use doc::DocDiagnostic; use indexmap::IndexMap; use std::collections::BTreeMap; @@ -89,7 +90,7 @@ pub async fn doc(flags: Flags, doc_flags: DocFlags) -> Result<(), AnyError> { .await? } DocSourceFileFlag::Paths(ref source_files) => { - let module_graph_builder = factory.module_graph_builder().await?; + let module_graph_creator = factory.module_graph_creator().await?; let maybe_lockfile = factory.maybe_lockfile(); let module_specifiers = collect_specifiers( @@ -103,7 +104,7 @@ pub async fn doc(flags: Flags, doc_flags: DocFlags) -> Result<(), AnyError> { }, |_, _| true, )?; - let graph = module_graph_builder + let graph = module_graph_creator .create_graph(GraphKind::TypesOnly, module_specifiers.clone()) .await?; @@ -211,9 +212,9 @@ impl deno_doc::html::HrefResolver for DocResolver { fn resolve_usage( &self, _current_specifier: &ModuleSpecifier, - current_file: Option<&str>, + current_file: Option<&ShortPath>, ) -> Option { - current_file.map(|f| f.to_string()) + current_file.map(|f| f.as_str().to_string()) } fn resolve_source(&self, location: &deno_doc::Location) -> Option { diff --git a/cli/tools/info.rs b/cli/tools/info.rs index 0ad7d89208..ca08003ad9 100644 --- a/cli/tools/info.rs +++ b/cli/tools/info.rs @@ -40,6 +40,7 @@ pub async fn info(flags: Flags, info_flags: InfoFlags) -> Result<(), AnyError> { let cli_options = factory.cli_options(); if let Some(specifier) = info_flags.file { let module_graph_builder = factory.module_graph_builder().await?; + let module_graph_creator = factory.module_graph_creator().await?; let npm_resolver = factory.npm_resolver().await?; let maybe_lockfile = factory.maybe_lockfile(); let maybe_imports_map = factory.maybe_import_map().await?; @@ -63,7 +64,7 @@ pub async fn info(flags: Flags, info_flags: InfoFlags) -> Result<(), AnyError> { let mut loader = module_graph_builder.create_graph_loader(); loader.enable_loading_cache_info(); // for displaying the cache information - let graph = module_graph_builder + let graph = module_graph_creator .create_graph_with_loader(GraphKind::All, vec![specifier], &mut loader) .await?; diff --git a/cli/tools/lint/mod.rs b/cli/tools/lint/mod.rs index e4a88f91c4..1240b391f8 100644 --- a/cli/tools/lint/mod.rs +++ b/cli/tools/lint/mod.rs @@ -178,13 +178,13 @@ async fn lint_files( let members = config_file.to_workspace_members()?; let has_error = has_error.clone(); let reporter_lock = reporter_lock.clone(); - let module_graph_builder = factory.module_graph_builder().await?.clone(); + let module_graph_creator = factory.module_graph_creator().await?.clone(); let path_urls = paths .iter() .filter_map(|p| ModuleSpecifier::from_file_path(p).ok()) .collect::>(); futures.push(deno_core::unsync::spawn(async move { - let graph = module_graph_builder.create_publish_graph(&members).await?; + let graph = module_graph_creator.create_publish_graph(&members).await?; // todo(dsherret): this isn't exactly correct as linting isn't properly // setup to handle workspaces. Iterating over the workspace members // should be done at a higher level because it also needs to take into diff --git a/cli/tools/registry/mod.rs b/cli/tools/registry/mod.rs index 37bd3616b4..951ac4944e 100644 --- a/cli/tools/registry/mod.rs +++ b/cli/tools/registry/mod.rs @@ -31,7 +31,7 @@ use crate::args::PublishFlags; use crate::cache::LazyGraphSourceParser; use crate::cache::ParsedSourceCache; use crate::factory::CliFactory; -use crate::graph_util::ModuleGraphBuilder; +use crate::graph_util::ModuleGraphCreator; use crate::http_util::HttpClient; use crate::tools::check::CheckOptions; use crate::tools::lint::no_slow_types; @@ -656,7 +656,7 @@ async fn prepare_packages_for_publishing( import_map: Arc, ) -> Result { let members = deno_json.to_workspace_members()?; - let module_graph_builder = cli_factory.module_graph_builder().await?.as_ref(); + let module_graph_creator = cli_factory.module_graph_creator().await?.as_ref(); let source_cache = cli_factory.parsed_source_cache(); let type_checker = cli_factory.type_checker().await?; let cli_options = cli_factory.cli_options(); @@ -667,7 +667,7 @@ async fn prepare_packages_for_publishing( // create the module graph let graph = build_and_check_graph_for_publish( - module_graph_builder, + module_graph_creator, type_checker, cli_options, allow_slow_types, @@ -715,15 +715,14 @@ async fn prepare_packages_for_publishing( } async fn build_and_check_graph_for_publish( - module_graph_builder: &ModuleGraphBuilder, + module_graph_creator: &ModuleGraphCreator, type_checker: &TypeChecker, cli_options: &CliOptions, allow_slow_types: bool, diagnostics_collector: &PublishDiagnosticsCollector, packages: &[WorkspaceMemberConfig], ) -> Result, deno_core::anyhow::Error> { - let graph = - Arc::new(module_graph_builder.create_publish_graph(packages).await?); + let graph = module_graph_creator.create_publish_graph(packages).await?; graph.valid()?; // todo(dsherret): move to lint rule @@ -740,6 +739,7 @@ async fn build_and_check_graph_for_publish( ), colors::yellow("Warning"), ); + Ok(Arc::new(graph)) } else { log::info!("Checking for slow types in the public API..."); let mut any_pkg_had_diagnostics = false; @@ -755,12 +755,16 @@ async fn build_and_check_graph_for_publish( } } - if !any_pkg_had_diagnostics { - // this is a temporary measure until we know that fast check is reliable and stable - let check_diagnostics = type_checker + if any_pkg_had_diagnostics { + Ok(Arc::new(graph)) + } else { + // fast check passed, type check the output as a temporary measure + // until we know that it's reliable and stable + let (graph, check_diagnostics) = type_checker .check_diagnostics( - graph.clone(), + graph, CheckOptions { + build_fast_check_graph: false, // already built lib: cli_options.ts_type_lib_window(), log_ignored_options: false, reload: cli_options.reload_flag(), @@ -778,10 +782,9 @@ async fn build_and_check_graph_for_publish( check_diagnostics ); } + Ok(graph) } } - - Ok(graph) } pub async fn publish( diff --git a/cli/tools/test/mod.rs b/cli/tools/test/mod.rs index 6e22b894af..b088cf7a32 100644 --- a/cli/tools/test/mod.rs +++ b/cli/tools/test/mod.rs @@ -1490,7 +1490,7 @@ pub async fn run_tests_with_watch( let graph_kind = cli_options.type_check_mode().as_graph_kind(); let log_level = cli_options.log_level(); let cli_options = cli_options.clone(); - let module_graph_builder = factory.module_graph_builder().await?; + let module_graph_creator = factory.module_graph_creator().await?; let file_fetcher = factory.file_fetcher()?; let test_modules = if test_options.doc { collect_specifiers(test_options.files.clone(), |p, _| { @@ -1505,7 +1505,7 @@ pub async fn run_tests_with_watch( let permissions = Permissions::from_options(&cli_options.permissions_options())?; - let graph = module_graph_builder + let graph = module_graph_creator .create_graph(graph_kind, test_modules.clone()) .await?; graph_valid_with_cli_options( diff --git a/cli/tools/vendor/mod.rs b/cli/tools/vendor/mod.rs index e2c04305a2..f168f84a2e 100644 --- a/cli/tools/vendor/mod.rs +++ b/cli/tools/vendor/mod.rs @@ -51,12 +51,12 @@ pub async fn vendor( let entry_points = resolve_entry_points(&vendor_flags, cli_options.initial_cwd())?; let jsx_import_source = cli_options.to_maybe_jsx_import_source_config()?; - let module_graph_builder = factory.module_graph_builder().await?.clone(); + let module_graph_creator = factory.module_graph_creator().await?.clone(); let output = build::build(build::BuildInput { entry_points, build_graph: move |entry_points| { async move { - module_graph_builder + module_graph_creator .create_graph(GraphKind::All, entry_points) .await } diff --git a/tests/integration/info_tests.rs b/tests/integration/info_tests.rs index 8fae81a61e..62e169c9e6 100644 --- a/tests/integration/info_tests.rs +++ b/tests/integration/info_tests.rs @@ -175,6 +175,7 @@ itest!(info_import_map { args: "info preact/debug", output: "info/with_import_map/with_import_map.out", cwd: Some("info/with_import_map"), + copy_temp_dir: Some("info/with_import_map"), exit_code: 0, }); diff --git a/tests/integration/jsr_tests.rs b/tests/integration/jsr_tests.rs index b6e4d8a4f8..fa8a9d8b9a 100644 --- a/tests/integration/jsr_tests.rs +++ b/tests/integration/jsr_tests.rs @@ -6,6 +6,8 @@ use deno_lockfile::Lockfile; use test_util as util; use test_util::itest; use url::Url; +use util::assert_contains; +use util::assert_not_contains; use util::env_vars_for_jsr_tests; use util::TestContextBuilder; @@ -66,6 +68,118 @@ itest!(subset_type_graph { exit_code: 1, }); +#[test] +fn fast_check_cache() { + let test_context = TestContextBuilder::for_jsr().use_temp_cwd().build(); + let deno_dir = test_context.deno_dir(); + let temp_dir = test_context.temp_dir(); + let type_check_cache_path = deno_dir.path().join("check_cache_v1"); + + temp_dir.write( + "main.ts", + r#"import { add } from "jsr:@denotest/add@1"; + const value: number = add(1, 2); + console.log(value);"#, + ); + temp_dir.path().join("deno.json").write_json(&json!({ + "vendor": true + })); + + test_context + .new_command() + .args("check main.ts") + .run() + .skip_output_check(); + + type_check_cache_path.remove_file(); + let check_debug_cmd = test_context + .new_command() + .args("check --log-level=debug main.ts"); + let output = check_debug_cmd.run(); + assert_contains!( + output.combined_output(), + "Using FastCheck cache for: @denotest/add@1.0.0" + ); + + // modify the file in the vendor folder + let vendor_dir = temp_dir.path().join("vendor"); + let pkg_dir = vendor_dir.join("http_127.0.0.1_4250/@denotest/add/1.0.0/"); + pkg_dir + .join("mod.ts") + .append("\nexport * from './other.ts';"); + let nested_pkg_file = pkg_dir.join("other.ts"); + nested_pkg_file.write("export function other(): string { return ''; }"); + + // invalidated + let output = check_debug_cmd.run(); + assert_not_contains!( + output.combined_output(), + "Using FastCheck cache for: @denotest/add@1.0.0" + ); + + // ensure cache works + let output = check_debug_cmd.run(); + assert_contains!(output.combined_output(), "Already type checked."); + let building_fast_check_msg = "Building fast check graph"; + assert_not_contains!(output.combined_output(), building_fast_check_msg); + + // now validated + type_check_cache_path.remove_file(); + let output = check_debug_cmd.run(); + assert_contains!(output.combined_output(), building_fast_check_msg); + assert_contains!( + output.combined_output(), + "Using FastCheck cache for: @denotest/add@1.0.0" + ); + + // cause a fast check error in the nested package + nested_pkg_file + .append("\nexport function asdf(a: number) { let err: number = ''; return Math.random(); }"); + check_debug_cmd.run().skip_output_check(); + + // ensure the cache still picks it up for this file + type_check_cache_path.remove_file(); + let output = check_debug_cmd.run(); + assert_contains!(output.combined_output(), building_fast_check_msg); + assert_contains!( + output.combined_output(), + "Using FastCheck cache for: @denotest/add@1.0.0" + ); + + // see that the type checking error in the internal function gets surfaced with --all + test_context + .new_command() + .args("check --all main.ts") + .run() + .assert_matches_text( + "Check file:///[WILDCARD]main.ts +error: TS2322 [ERROR]: Type 'string' is not assignable to type 'number'. +export function asdf(a: number) { let err: number = ''; return Math.random(); } + ~~~ + at http://127.0.0.1:4250/@denotest/add/1.0.0/other.ts:2:39 +", + ) + .assert_exit_code(1); + + // now fix the package + nested_pkg_file.write("export function test() {}"); + let output = check_debug_cmd.run(); + assert_contains!(output.combined_output(), building_fast_check_msg); + assert_not_contains!( + output.combined_output(), + "Using FastCheck cache for: @denotest/add@1.0.0" + ); + + // finally ensure it uses the cache + type_check_cache_path.remove_file(); + let output = check_debug_cmd.run(); + assert_contains!(output.combined_output(), building_fast_check_msg); + assert_contains!( + output.combined_output(), + "Using FastCheck cache for: @denotest/add@1.0.0" + ); +} + itest!(version_not_found { args: "run jsr/version_not_found/main.ts", output: "jsr/version_not_found/main.out", diff --git a/tests/util/server/src/fs.rs b/tests/util/server/src/fs.rs index 0e47a75035..d99572b06f 100644 --- a/tests/util/server/src/fs.rs +++ b/tests/util/server/src/fs.rs @@ -4,6 +4,8 @@ use pretty_assertions::assert_eq; use std::borrow::Cow; use std::ffi::OsStr; use std::fs; +use std::fs::OpenOptions; +use std::io::Write; use std::path::Path; use std::path::PathBuf; use std::process::Command; @@ -134,6 +136,11 @@ impl PathRef { fs::rename(self, self.join(to)).unwrap(); } + pub fn append(&self, text: impl AsRef) { + let mut file = OpenOptions::new().append(true).open(self).unwrap(); + file.write_all(text.as_ref().as_bytes()).unwrap(); + } + pub fn write(&self, text: impl AsRef) { fs::write(self, text.as_ref()).unwrap(); }