perf(jsr): fast check cache and lazy fast check graph (#22485)

This commit is contained in:
David Sherret 2024-02-20 16:29:57 -05:00 committed by GitHub
parent dbc4a4d632
commit f90889e5ee
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 669 additions and 220 deletions

View file

@ -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<ModuleSpecifier>,
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<CliOptions>,
fs: Arc<dyn FileSystem>,
resolver: Arc<CliGraphResolver>,
npm_resolver: Arc<dyn CliNpmResolver>,
module_info_cache: Arc<ModuleInfoCache>,
parsed_source_cache: Arc<ParsedSourceCache>,
module_graph_builder: Arc<ModuleGraphBuilder>,
lockfile: Option<Arc<Mutex<Lockfile>>>,
maybe_file_watcher_reporter: Option<FileWatcherReporter>,
emit_cache: cache::EmitCache,
file_fetcher: Arc<FileFetcher>,
global_http_cache: Arc<GlobalHttpCache>,
type_checker: Arc<TypeChecker>,
}
impl ModuleGraphBuilder {
#[allow(clippy::too_many_arguments)]
impl ModuleGraphCreator {
pub fn new(
options: Arc<CliOptions>,
fs: Arc<dyn FileSystem>,
resolver: Arc<CliGraphResolver>,
npm_resolver: Arc<dyn CliNpmResolver>,
module_info_cache: Arc<ModuleInfoCache>,
parsed_source_cache: Arc<ParsedSourceCache>,
module_graph_builder: Arc<ModuleGraphBuilder>,
lockfile: Option<Arc<Mutex<Lockfile>>>,
maybe_file_watcher_reporter: Option<FileWatcherReporter>,
emit_cache: cache::EmitCache,
file_fetcher: Arc<FileFetcher>,
global_http_cache: Arc<GlobalHttpCache>,
type_checker: Arc<TypeChecker>,
) -> 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<ModuleSpecifier>,
) -> Result<deno_graph::ModuleGraph, AnyError> {
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<Vec<deno_graph::WorkspaceMember>, 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<CliOptions>,
caches: Arc<cache::Caches>,
fs: Arc<dyn FileSystem>,
resolver: Arc<CliGraphResolver>,
npm_resolver: Arc<dyn CliNpmResolver>,
module_info_cache: Arc<ModuleInfoCache>,
parsed_source_cache: Arc<ParsedSourceCache>,
lockfile: Option<Arc<Mutex<Lockfile>>>,
maybe_file_watcher_reporter: Option<FileWatcherReporter>,
emit_cache: cache::EmitCache,
file_fetcher: Arc<FileFetcher>,
global_http_cache: Arc<GlobalHttpCache>,
}
impl ModuleGraphBuilder {
#[allow(clippy::too_many_arguments)]
pub fn new(
options: Arc<CliOptions>,
caches: Arc<cache::Caches>,
fs: Arc<dyn FileSystem>,
resolver: Arc<CliGraphResolver>,
npm_resolver: Arc<dyn CliNpmResolver>,
module_info_cache: Arc<ModuleInfoCache>,
parsed_source_cache: Arc<ParsedSourceCache>,
lockfile: Option<Arc<Mutex<Lockfile>>>,
maybe_file_watcher_reporter: Option<FileWatcherReporter>,
emit_cache: cache::EmitCache,
file_fetcher: Arc<FileFetcher>,
global_http_cache: Arc<GlobalHttpCache>,
) -> 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<Vec<deno_graph::WorkspaceMember>, 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<deno_graph::WorkspaceMember, AnyError> {
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;