summaryrefslogtreecommitdiff
path: root/cli/tools/check.rs
diff options
context:
space:
mode:
authorDavid Sherret <dsherret@users.noreply.github.com>2024-01-10 17:40:30 -0500
committerGitHub <noreply@github.com>2024-01-10 22:40:30 +0000
commit70ac06138c18cf643e7e1947dee54f3adff13de3 (patch)
tree73cd8fb7fe51ecbdcf47770b15b27f6fb49b4d05 /cli/tools/check.rs
parent515a34b4de222e35c7ade1b92614d746e73d4c2e (diff)
feat(unstable): fast subset type checking of JSR dependencies (#21873)
Diffstat (limited to 'cli/tools/check.rs')
-rw-r--r--cli/tools/check.rs108
1 files changed, 77 insertions, 31 deletions
diff --git a/cli/tools/check.rs b/cli/tools/check.rs
index bde9a7612..7133f4d3f 100644
--- a/cli/tools/check.rs
+++ b/cli/tools/check.rs
@@ -1,6 +1,7 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use std::collections::HashSet;
+use std::collections::VecDeque;
use std::sync::Arc;
use deno_ast::MediaType;
@@ -23,6 +24,7 @@ use crate::cache::FastInsecureHasher;
use crate::cache::TypeCheckCache;
use crate::npm::CliNpmResolver;
use crate::tsc;
+use crate::tsc::Diagnostics;
use crate::version;
/// Options for performing a check of a module graph. Note that the decision to
@@ -68,6 +70,23 @@ impl TypeChecker {
graph: Arc<ModuleGraph>,
options: CheckOptions,
) -> Result<(), AnyError> {
+ let diagnostics = self.check_diagnostics(graph, options).await?;
+ if diagnostics.is_empty() {
+ Ok(())
+ } else {
+ Err(diagnostics.into())
+ }
+ }
+
+ /// Type check the module graph returning its diagnostics.
+ ///
+ /// It is expected that it is determined if a check and/or emit is validated
+ /// before the function is called.
+ pub async fn check_diagnostics(
+ &self,
+ graph: Arc<ModuleGraph>,
+ options: CheckOptions,
+ ) -> Result<Diagnostics, AnyError> {
// node built-in specifiers use the @types/node package to determine
// types, so inject that now (the caller should do this after the lockfile
// has been written)
@@ -100,7 +119,7 @@ impl TypeChecker {
type_check_mode,
&ts_config,
) {
- CheckHashResult::NoFiles => return Ok(()),
+ CheckHashResult::NoFiles => return Ok(Default::default()),
CheckHashResult::Hash(hash) => Some(hash),
}
}
@@ -111,7 +130,7 @@ impl TypeChecker {
if !options.reload {
if let Some(check_hash) = maybe_check_hash {
if cache.has_check_hash(check_hash) {
- return Ok(());
+ return Ok(Default::default());
}
}
}
@@ -152,7 +171,7 @@ impl TypeChecker {
check_mode: type_check_mode,
})?;
- let diagnostics = if type_check_mode == TypeCheckMode::Local {
+ let mut diagnostics = if type_check_mode == TypeCheckMode::Local {
response.diagnostics.filter(|d| {
if let Some(file_name) = &d.file_name {
if !file_name.starts_with("http") {
@@ -175,6 +194,8 @@ impl TypeChecker {
response.diagnostics
};
+ diagnostics.apply_fast_check_source_maps(&graph);
+
if let Some(tsbuildinfo) = response.maybe_tsbuildinfo {
cache.set_tsbuildinfo(&graph.roots[0], &tsbuildinfo);
}
@@ -187,11 +208,7 @@ impl TypeChecker {
log::debug!("{}", response.stats);
- if diagnostics.is_empty() {
- Ok(())
- } else {
- Err(diagnostics.into())
- }
+ Ok(diagnostics)
}
}
@@ -256,7 +273,12 @@ fn get_check_hash(
}
hasher.write_str(module.specifier.as_str());
- hasher.write_str(&module.source);
+ hasher.write_str(
+ module
+ .fast_check_module()
+ .map(|s| s.source.as_ref())
+ .unwrap_or(&module.source),
+ );
}
Module::Node(_) => {
// the @types/node package will be in the resolved
@@ -345,21 +367,18 @@ fn get_tsc_roots(
));
}
- let mut seen_roots =
- HashSet::with_capacity(graph.imports.len() + graph.roots.len());
+ let mut seen =
+ HashSet::with_capacity(graph.imports.len() + graph.specifiers_count());
+ let mut pending = VecDeque::new();
// put in the global types first so that they're resolved before anything else
for import in graph.imports.values() {
for dep in import.dependencies.values() {
let specifier = dep.get_type().or_else(|| dep.get_code());
if let Some(specifier) = &specifier {
- if seen_roots.insert(*specifier) {
- let maybe_entry = graph
- .get(specifier)
- .and_then(|m| maybe_get_check_entry(m, check_js));
- if let Some(entry) = maybe_entry {
- result.push(entry);
- }
+ let specifier = graph.resolve(specifier);
+ if seen.insert(specifier.clone()) {
+ pending.push_back(specifier);
}
}
}
@@ -367,23 +386,50 @@ fn get_tsc_roots(
// then the roots
for root in &graph.roots {
- if let Some(module) = graph.get(root) {
- if seen_roots.insert(root) {
- if let Some(entry) = maybe_get_check_entry(module, check_js) {
- result.push(entry);
- }
- }
+ let specifier = graph.resolve(root);
+ if seen.insert(specifier.clone()) {
+ pending.push_back(specifier);
}
}
- // now the rest
- result.extend(graph.modules().filter_map(|module| {
- if seen_roots.contains(module.specifier()) {
- None
- } else {
- maybe_get_check_entry(module, check_js)
+ // now walk the graph that only includes the fast check dependencies
+ while let Some(specifier) = pending.pop_front() {
+ let Some(module) = graph.get(&specifier) else {
+ continue;
+ };
+ if let Some(entry) = maybe_get_check_entry(module, check_js) {
+ result.push(entry);
+ }
+ if let Some(module) = module.esm() {
+ let deps = module.dependencies_prefer_fast_check();
+ for dep in deps.values() {
+ // walk both the code and type dependencies
+ if let Some(specifier) = dep.get_code() {
+ let specifier = graph.resolve(specifier);
+ if seen.insert(specifier.clone()) {
+ pending.push_back(specifier);
+ }
+ }
+ if let Some(specifier) = dep.get_type() {
+ let specifier = graph.resolve(specifier);
+ if seen.insert(specifier.clone()) {
+ pending.push_back(specifier);
+ }
+ }
+ }
+
+ if let Some(dep) = module
+ .maybe_types_dependency
+ .as_ref()
+ .and_then(|d| d.dependency.ok())
+ {
+ let specifier = graph.resolve(&dep.specifier);
+ if seen.insert(specifier.clone()) {
+ pending.push_back(specifier);
+ }
+ }
}
- }));
+ }
result
}