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

@ -21,9 +21,9 @@ use deno_graph::MediaType;
use deno_graph::Module;
use deno_graph::ModuleGraph;
use deno_lib::util::hash::FastInsecureHasher;
use deno_resolver::deno_json::CompilerOptionsResolver;
use deno_resolver::deno_json::TranspileAndEmitOptions;
use crate::args::CliTsConfigResolver;
use crate::cache::EmitCache;
use crate::cache::ParsedSourceCache;
use crate::resolver::CliCjsTracker;
@ -33,7 +33,7 @@ pub struct Emitter {
cjs_tracker: Arc<CliCjsTracker>,
emit_cache: Arc<EmitCache>,
parsed_source_cache: Arc<ParsedSourceCache>,
tsconfig_resolver: Arc<CliTsConfigResolver>,
compiler_options_resolver: Arc<CompilerOptionsResolver>,
}
impl Emitter {
@ -41,13 +41,13 @@ impl Emitter {
cjs_tracker: Arc<CliCjsTracker>,
emit_cache: Arc<EmitCache>,
parsed_source_cache: Arc<ParsedSourceCache>,
tsconfig_resolver: Arc<CliTsConfigResolver>,
compiler_options_resolver: Arc<CompilerOptionsResolver>,
) -> Self {
Self {
cjs_tracker,
emit_cache,
parsed_source_cache,
tsconfig_resolver,
compiler_options_resolver,
}
}
@ -96,8 +96,9 @@ impl Emitter {
source: &str,
) -> Result<Option<String>, AnyError> {
let transpile_and_emit_options = self
.tsconfig_resolver
.transpile_and_emit_options(specifier)?;
.compiler_options_resolver
.for_specifier(specifier)
.transpile_options()?;
let source_hash =
self.get_source_hash(module_kind, transpile_and_emit_options, source);
Ok(self.emit_cache.get_emit_code(specifier, source_hash))
@ -111,8 +112,9 @@ impl Emitter {
source: &Arc<str>,
) -> Result<String, EmitParsedSourceHelperError> {
let transpile_and_emit_options = self
.tsconfig_resolver
.transpile_and_emit_options(specifier)?;
.compiler_options_resolver
.for_specifier(specifier)
.transpile_options()?;
// Note: keep this in sync with the sync version below
let helper = EmitParsedSourceHelper(self);
match helper.pre_emit_parsed_source(
@ -162,8 +164,9 @@ impl Emitter {
source: &Arc<str>,
) -> Result<String, EmitParsedSourceHelperError> {
let transpile_and_emit_options = self
.tsconfig_resolver
.transpile_and_emit_options(specifier)?;
.compiler_options_resolver
.for_specifier(specifier)
.transpile_options()?;
// Note: keep this in sync with the async version above
let helper = EmitParsedSourceHelper(self);
match helper.pre_emit_parsed_source(
@ -202,8 +205,9 @@ impl Emitter {
source: &Arc<str>,
) -> Result<(String, String), AnyError> {
let transpile_and_emit_options = self
.tsconfig_resolver
.transpile_and_emit_options(specifier)?;
.compiler_options_resolver
.for_specifier(specifier)
.transpile_options()?;
let mut emit_options = transpile_and_emit_options.emit.clone();
emit_options.inline_sources = false;
emit_options.source_map = SourceMapOption::Separate;
@ -247,8 +251,9 @@ impl Emitter {
// the option to not use them (though you should test this out because
// this statement is probably wrong)
let transpile_and_emit_options = self
.tsconfig_resolver
.transpile_and_emit_options(specifier)
.compiler_options_resolver
.for_specifier(specifier)
.transpile_options()
.map_err(JsErrorBox::from_err)?;
let mut options = transpile_and_emit_options.emit.clone();
options.source_map = SourceMapOption::None;