feat(check): tsconfig "references", "extends", "files", "include" and "exclude" (#29843)

- Each workspace directory is probed for a `tsconfig.json`.
- These and any that are included by their `references` are put into a
list ordered by priority.
  - A tsconfig has lower priority than its `references`.
- An earlier listed entry in `references` has higher priority than a
later one.
- A probed tsconfig in an inner directory has higher priority than an
outer one. Their `references` would be interspersed between them.
- Each tsconfig has a filter based on its `files`, `include` and
`exclude` fields. If it doesn't have `files` or `include`, it will match
any path in its containing directory not exempted by `exclude`.
- For type-checking, each root path will be allocated compiler options
based on the first tsconfig it whose filter it matches from this list.
- Only if it doesn't match any tsconfig, it will fall back to using the
nearest `deno.json`. If it's a workspace member and the root `deno.json`
has `compilerOptions`, these will be merged using the same logic from
`extends`.

Inheritance between configs strictly occurs via `extends` in a
`tsconfig.json`, and between workspace member and root `deno.json`s'
`compilerOptions`. There is no implicit inheritance between
`tsconfig.json` and `deno.json`.

The default compiler options currently applied against tsconfigs are
Deno's normal defaults, with the exception of `lib`. The default value
for `lib` is `["deno.window", "deno.unstable", "dom"]` for files in the
scope of a tsconfig with `lib` unspecified. This behaviour is depended
on by, for example, the template project created by `create-vite ->
svelte`. I expect we'll add more such exceptions over time with other
fields.
This commit is contained in:
Nayeem Rahman 2025-06-26 22:17:07 +01:00 committed by GitHub
parent 9913311860
commit 7a9ab843bd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
95 changed files with 1512 additions and 514 deletions

View file

@ -29,12 +29,12 @@ use deno_core::unsync::future::SharedLocal;
use deno_graph::ModuleGraph;
use deno_lib::util::hash::FastInsecureHasher;
use deno_lint::diagnostic::LintDiagnostic;
use deno_resolver::deno_json::CompilerOptionsResolver;
use log::debug;
use reporters::create_reporter;
use reporters::LintReporter;
use crate::args::CliOptions;
use crate::args::CliTsConfigResolver;
use crate::args::Flags;
use crate::args::LintFlags;
use crate::args::LintOptions;
@ -90,7 +90,7 @@ pub async fn lint(
let cli_options = factory.cli_options()?;
let lint_rule_provider = factory.lint_rule_provider().await?;
let is_stdin = lint_flags.is_stdin();
let tsconfig_resolver = factory.tsconfig_resolver()?;
let compiler_options_resolver = factory.compiler_options_resolver()?;
let workspace_lint_options =
cli_options.resolve_workspace_lint_options(&lint_flags)?;
let success = if is_stdin {
@ -99,14 +99,14 @@ pub async fn lint(
lint_rule_provider,
workspace_lint_options,
lint_flags,
tsconfig_resolver,
compiler_options_resolver,
)?
} else {
let mut linter = WorkspaceLinter::new(
factory.caches()?.clone(),
lint_rule_provider,
factory.module_graph_creator().await?.clone(),
tsconfig_resolver.clone(),
compiler_options_resolver.clone(),
cli_options.start_dir.clone(),
&workspace_lint_options,
);
@ -165,7 +165,7 @@ async fn lint_with_watch_inner(
factory.caches()?.clone(),
factory.lint_rule_provider().await?,
factory.module_graph_creator().await?.clone(),
factory.tsconfig_resolver()?.clone(),
factory.compiler_options_resolver()?.clone(),
cli_options.start_dir.clone(),
&cli_options.resolve_workspace_lint_options(&lint_flags)?,
);
@ -245,7 +245,7 @@ struct WorkspaceLinter {
caches: Arc<Caches>,
lint_rule_provider: LintRuleProvider,
module_graph_creator: Arc<ModuleGraphCreator>,
tsconfig_resolver: Arc<CliTsConfigResolver>,
compiler_options_resolver: Arc<CompilerOptionsResolver>,
workspace_dir: Arc<WorkspaceDirectory>,
reporter_lock: Arc<Mutex<Box<dyn LintReporter + Send>>>,
workspace_module_graph: Option<WorkspaceModuleGraphFuture>,
@ -258,7 +258,7 @@ impl WorkspaceLinter {
caches: Arc<Caches>,
lint_rule_provider: LintRuleProvider,
module_graph_creator: Arc<ModuleGraphCreator>,
tsconfig_resolver: Arc<CliTsConfigResolver>,
compiler_options_resolver: Arc<CompilerOptionsResolver>,
workspace_dir: Arc<WorkspaceDirectory>,
workspace_options: &WorkspaceLintOptions,
) -> Self {
@ -268,7 +268,7 @@ impl WorkspaceLinter {
caches,
lint_rule_provider,
module_graph_creator,
tsconfig_resolver,
compiler_options_resolver,
workspace_dir,
reporter_lock,
workspace_module_graph: None,
@ -341,7 +341,7 @@ impl WorkspaceLinter {
configured_rules: lint_rules,
fix: lint_options.fix,
deno_lint_config: resolve_lint_config(
&self.tsconfig_resolver,
&self.compiler_options_resolver,
member_dir.dir_url(),
)?,
maybe_plugin_runner: plugin_runner,
@ -577,7 +577,7 @@ fn lint_stdin(
lint_rule_provider: LintRuleProvider,
workspace_lint_options: WorkspaceLintOptions,
lint_flags: LintFlags,
tsconfig_resolver: &CliTsConfigResolver,
compiler_options_resolver: &CompilerOptionsResolver,
) -> Result<bool, AnyError> {
let start_dir = &cli_options.start_dir;
let reporter_lock = Arc::new(Mutex::new(create_reporter(
@ -586,7 +586,7 @@ fn lint_stdin(
let lint_config = start_dir
.to_lint_config(FilePatterns::new_with_base(start_dir.dir_path()))?;
let deno_lint_config =
resolve_lint_config(tsconfig_resolver, start_dir.dir_url())?;
resolve_lint_config(compiler_options_resolver, start_dir.dir_url())?;
let lint_options = LintOptions::resolve(lint_config, &lint_flags)?;
let configured_rules = lint_rule_provider.resolve_lint_rules_err_empty(
lint_options.rules,
@ -654,11 +654,12 @@ fn handle_lint_result(
}
fn resolve_lint_config(
tsconfig_resolver: &CliTsConfigResolver,
compiler_options_resolver: &CompilerOptionsResolver,
specifier: &ModuleSpecifier,
) -> Result<deno_lint::linter::LintConfig, AnyError> {
let transpile_options = &tsconfig_resolver
.transpile_and_emit_options(specifier)?
let transpile_options = &compiler_options_resolver
.for_specifier(specifier)
.transpile_options()?
.transpile;
Ok(deno_lint::linter::LintConfig {
default_jsx_factory: (!transpile_options.jsx_automatic)