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

1
Cargo.lock generated
View file

@ -2698,6 +2698,7 @@ dependencies = [
"http 1.1.0",
"import_map",
"indexmap 2.9.0",
"jsonc-parser",
"log",
"node_resolver",
"once_cell",

View file

@ -24,8 +24,8 @@ pub use deno_config::deno_json::LintRulesConfig;
use deno_config::deno_json::NodeModulesDirMode;
pub use deno_config::deno_json::ProseWrap;
use deno_config::deno_json::TestConfig;
pub use deno_config::deno_json::TsTypeLib;
pub use deno_config::glob::FilePatterns;
pub use deno_config::workspace::TsTypeLib;
use deno_config::workspace::Workspace;
use deno_config::workspace::WorkspaceDirLintConfig;
use deno_config::workspace::WorkspaceDirectory;
@ -59,8 +59,6 @@ use thiserror::Error;
use crate::sys::CliSys;
pub type CliLockfile = deno_resolver::lockfile::LockfileLock<CliSys>;
pub type CliTsConfigResolver =
deno_resolver::deno_json::TsConfigResolver<CliSys>;
pub fn jsr_url() -> &'static Url {
static JSR_URL: Lazy<Url> = Lazy::new(|| resolve_jsr_url(&CliSys::default()));

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;

View file

@ -32,11 +32,13 @@ use deno_npm_installer::lifecycle_scripts::NullLifecycleScriptsExecutor;
use deno_npm_installer::process_state::NpmProcessStateKind;
use deno_npm_installer::NpmInstallerFactoryOptions;
use deno_resolver::cjs::IsCjsResolutionMode;
use deno_resolver::deno_json::CompilerOptionsResolver;
use deno_resolver::factory::ConfigDiscoveryOption;
use deno_resolver::factory::DenoDirPathProviderOptions;
use deno_resolver::factory::NpmProcessStateOptions;
use deno_resolver::factory::ResolverFactoryOptions;
use deno_resolver::factory::SpecifiedImportMapProvider;
use deno_resolver::factory::WorkspaceDirectoryProvider;
use deno_resolver::import_map::WorkspaceExternalImportMapLoader;
use deno_resolver::npm::DenoInNpmPackageChecker;
use deno_resolver::workspace::WorkspaceResolver;
@ -61,7 +63,6 @@ use crate::args::BundleFlags;
use crate::args::BundlePlatform;
use crate::args::CliLockfile;
use crate::args::CliOptions;
use crate::args::CliTsConfigResolver;
use crate::args::ConfigFlag;
use crate::args::DenoSubcommand;
use crate::args::Flags;
@ -693,7 +694,7 @@ impl CliFactory {
self.cjs_tracker()?.clone(),
self.emit_cache()?.clone(),
self.parsed_source_cache().clone(),
self.tsconfig_resolver()?.clone(),
self.compiler_options_resolver()?.clone(),
)))
})
}
@ -780,10 +781,10 @@ impl CliFactory {
Ok(self.resolver_factory()?.pkg_json_resolver())
}
pub fn tsconfig_resolver(
pub fn compiler_options_resolver(
&self,
) -> Result<&Arc<CliTsConfigResolver>, AnyError> {
Ok(self.workspace_factory()?.tsconfig_resolver()?)
) -> Result<&Arc<CompilerOptionsResolver>, AnyError> {
self.resolver_factory()?.compiler_options_resolver()
}
pub async fn type_checker(&self) -> Result<&Arc<TypeChecker>, AnyError> {
@ -804,7 +805,8 @@ impl CliFactory {
self.node_resolver().await?.clone(),
self.npm_resolver().await?.clone(),
self.sys(),
self.tsconfig_resolver()?.clone(),
self.workspace_directory_provider()?.clone(),
self.compiler_options_resolver()?.clone(),
if cli_options.code_cache_enabled() {
Some(self.code_cache()?.clone())
} else {
@ -843,7 +845,7 @@ impl CliFactory {
self.resolver().await?.clone(),
self.root_permissions_container()?.clone(),
self.sys(),
self.tsconfig_resolver()?.clone(),
self.compiler_options_resolver()?.clone(),
)))
}
.boxed_local(),
@ -985,6 +987,12 @@ impl CliFactory {
})
}
fn workspace_directory_provider(
&self,
) -> Result<&Arc<WorkspaceDirectoryProvider>, AnyError> {
Ok(self.workspace_factory()?.workspace_directory_provider()?)
}
fn workspace_external_import_map_loader(
&self,
) -> Result<&Arc<WorkspaceExternalImportMapLoader<CliSys>>, AnyError> {

View file

@ -34,20 +34,21 @@ use deno_graph::WorkspaceFastCheckOption;
use deno_npm_installer::graph::NpmCachingStrategy;
use deno_npm_installer::PackageCaching;
use deno_path_util::url_to_file_path;
use deno_resolver::deno_json::CompilerOptionsResolver;
use deno_resolver::deno_json::JsxImportSourceConfigResolver;
use deno_resolver::npm::DenoInNpmPackageChecker;
use deno_resolver::workspace::sloppy_imports_resolve;
use deno_resolver::workspace::ScopedJsxImportSourceConfig;
use deno_runtime::deno_node;
use deno_runtime::deno_permissions::PermissionsContainer;
use deno_semver::jsr::JsrDepPackageReq;
use deno_semver::SmallStackString;
use indexmap::IndexMap;
use sys_traits::FsMetadata;
use crate::args::config_to_deno_graph_workspace_member;
use crate::args::jsr_url;
use crate::args::CliLockfile;
use crate::args::CliOptions;
use crate::args::CliTsConfigResolver;
use crate::args::DenoSubcommand;
use crate::cache;
use crate::cache::GlobalHttpCache;
@ -689,7 +690,7 @@ pub struct ModuleGraphBuilder {
resolver: Arc<CliResolver>,
root_permissions_container: PermissionsContainer,
sys: CliSys,
tsconfig_resolver: Arc<CliTsConfigResolver>,
compiler_options_resolver: Arc<CompilerOptionsResolver>,
}
impl ModuleGraphBuilder {
@ -711,7 +712,7 @@ impl ModuleGraphBuilder {
resolver: Arc<CliResolver>,
root_permissions_container: PermissionsContainer,
sys: CliSys,
tsconfig_resolver: Arc<CliTsConfigResolver>,
compiler_options_resolver: Arc<CompilerOptionsResolver>,
) -> Self {
Self {
caches,
@ -730,7 +731,7 @@ impl ModuleGraphBuilder {
resolver,
root_permissions_container,
sys,
tsconfig_resolver,
compiler_options_resolver,
}
}
@ -760,12 +761,14 @@ impl ModuleGraphBuilder {
MutLoaderRef::Owned(self.create_graph_loader_with_root_permissions())
}
};
let scoped_jsx_config = ScopedJsxImportSourceConfig::from_workspace_dir(
&self.cli_options.start_dir,
let jsx_import_source_config_resolver =
JsxImportSourceConfigResolver::from_compiler_options_resolver(
&self.compiler_options_resolver,
)?;
let graph_resolver = self
.resolver
.as_graph_resolver(self.cjs_tracker.as_ref(), &scoped_jsx_config);
let graph_resolver = self.resolver.as_graph_resolver(
self.cjs_tracker.as_ref(),
&jsx_import_source_config_resolver,
);
let maybe_file_watcher_reporter = self
.maybe_file_watcher_reporter
.as_ref()
@ -854,19 +857,39 @@ impl ModuleGraphBuilder {
return Err(BuildGraphWithNpmResolutionError::UnsupportedNpmSpecifierEntrypointResolutionWay);
}
let imports = if graph.graph_kind().include_types() {
// Resolve all the imports from every deno.json. We'll separate
// Resolve all the imports from every config file. We'll separate
// them later based on the folder we're type checking.
let mut imports = Vec::new();
for deno_json in self.cli_options.workspace().deno_jsons() {
let maybe_imports = deno_json.to_compiler_option_types()?;
imports.extend(maybe_imports.into_iter().map(
|(referrer, imports)| deno_graph::ReferrerImports {
referrer,
imports,
},
));
let mut imports_by_referrer = IndexMap::<_, Vec<_>>::with_capacity(
self.compiler_options_resolver.size(),
);
for (referrer, files) in self
.compiler_options_resolver
.ts_configs()
.iter()
.filter_map(|t| t.files())
{
imports_by_referrer
.entry(referrer)
.or_default()
.extend(files.iter().map(|f| f.relative_specifier.clone()));
}
imports
for (referrer, types) in self
.compiler_options_resolver
.all()
.flat_map(|d| d.compiler_options_types().as_ref())
{
imports_by_referrer
.entry(referrer)
.or_default()
.extend(types.clone());
}
imports_by_referrer
.into_iter()
.map(|(referrer, imports)| deno_graph::ReferrerImports {
referrer: referrer.clone(),
imports,
})
.collect()
} else {
Vec::new()
};
@ -938,12 +961,14 @@ impl ModuleGraphBuilder {
None
};
let parser = self.parsed_source_cache.as_capturing_parser();
let scoped_jsx_config = ScopedJsxImportSourceConfig::from_workspace_dir(
&self.cli_options.start_dir,
let jsx_import_source_config_resolver =
JsxImportSourceConfigResolver::from_compiler_options_resolver(
&self.compiler_options_resolver,
)?;
let graph_resolver = self
.resolver
.as_graph_resolver(self.cjs_tracker.as_ref(), &scoped_jsx_config);
let graph_resolver = self.resolver.as_graph_resolver(
self.cjs_tracker.as_ref(),
&jsx_import_source_config_resolver,
);
graph.build_fast_check_type_graph(
deno_graph::BuildFastCheckTypeGraphOptions {
@ -1012,7 +1037,9 @@ impl ModuleGraphBuilder {
} else {
GraphKind::CodeOnly
},
check_js: CheckJsOption::Custom(self.tsconfig_resolver.as_ref()),
check_js: CheckJsOption::Custom(
self.compiler_options_resolver.as_ref(),
),
exit_integrity_errors: true,
allow_unknown_media_types,
ignore_graph_errors: matches!(

View file

@ -16,7 +16,7 @@ use std::time::Duration;
use deno_ast::EmitOptions;
use deno_ast::MediaType;
use deno_ast::ModuleSpecifier;
use deno_config::deno_json::TsTypeLib;
use deno_config::workspace::TsTypeLib;
use deno_core::error::AnyError;
use deno_core::futures::FutureExt as _;
use deno_core::resolve_url_or_path;

View file

@ -163,7 +163,7 @@ pub async fn compile_eszip(
let cli_options = factory.cli_options()?;
let module_graph_creator = factory.module_graph_creator().await?;
let parsed_source_cache = factory.parsed_source_cache();
let tsconfig_resolver = factory.tsconfig_resolver()?;
let compiler_options_resolver = factory.compiler_options_resolver()?;
let bin_name_resolver = factory.bin_name_resolver()?;
let entrypoint = cli_options.resolve_main_module()?;
let mut output_path = resolve_compile_executable_output_path(
@ -203,8 +203,9 @@ pub async fn compile_eszip(
graph
};
let transpile_and_emit_options = tsconfig_resolver
.transpile_and_emit_options(cli_options.workspace().root_dir())?;
let transpile_and_emit_options = compiler_options_resolver
.for_specifier(cli_options.workspace().root_dir())
.transpile_options()?;
let transpile_options = transpile_and_emit_options.transpile.clone();
let emit_options = transpile_and_emit_options.emit.clone();

View file

@ -73,7 +73,7 @@ pub async fn kernel(
let permissions =
PermissionsContainer::allow_all(factory.permission_desc_parser()?.clone());
let npm_installer = factory.npm_installer_if_managed().await?.cloned();
let tsconfig_resolver = factory.tsconfig_resolver()?;
let compiler_options_resolver = factory.compiler_options_resolver()?;
let resolver = factory.resolver().await?.clone();
let worker_factory = factory.create_cli_main_worker_factory().await?;
let (stdio_tx, stdio_rx) = mpsc::unbounded_channel();
@ -124,7 +124,7 @@ pub async fn kernel(
cli_options,
npm_installer,
resolver,
tsconfig_resolver,
compiler_options_resolver,
worker,
main_module,
test_event_receiver,

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)

View file

@ -640,7 +640,7 @@ impl DepManager {
&roots,
crate::module_loader::PrepareModuleLoadOptions {
is_dynamic: false,
lib: deno_config::deno_json::TsTypeLib::DenoWindow,
lib: deno_config::workspace::TsTypeLib::DenoWindow,
permissions: self.permissions_container.clone(),
ext_overwrite: None,
allow_unknown_media_types: true,

View file

@ -121,7 +121,7 @@ pub async fn publish(
cli_factory.parsed_source_cache().clone(),
specifier_unfurler,
cli_factory.sys(),
cli_factory.tsconfig_resolver()?.clone(),
cli_factory.compiler_options_resolver()?.clone(),
));
let publish_preparer = PublishPreparer::new(
GraphDiagnosticsCollector::new(cli_factory.parsed_source_cache().clone()),

View file

@ -11,7 +11,7 @@ use deno_core::anyhow::Context;
use deno_core::error::AnyError;
use deno_core::url::Url;
use deno_graph::ModuleGraph;
use deno_resolver::deno_json::TsConfigResolver;
use deno_resolver::deno_json::CompilerOptionsResolver;
use deno_resolver::workspace::ResolutionKind;
use lazy_regex::Lazy;
use sys_traits::FsMetadata;
@ -37,7 +37,7 @@ pub struct ModuleContentProvider<TSys: FsMetadata + FsRead = CliSys> {
specifier_unfurler: SpecifierUnfurler<TSys>,
parsed_source_cache: Arc<ParsedSourceCache>,
sys: TSys,
tsconfig_resolver: Arc<TsConfigResolver<TSys>>,
compiler_options_resolver: Arc<CompilerOptionsResolver>,
}
impl<TSys: FsMetadata + FsRead> ModuleContentProvider<TSys> {
@ -45,13 +45,13 @@ impl<TSys: FsMetadata + FsRead> ModuleContentProvider<TSys> {
parsed_source_cache: Arc<ParsedSourceCache>,
specifier_unfurler: SpecifierUnfurler<TSys>,
sys: TSys,
tsconfig_resolver: Arc<TsConfigResolver<TSys>>,
compiler_options_resolver: Arc<CompilerOptionsResolver>,
) -> Self {
Self {
specifier_unfurler,
parsed_source_cache,
sys,
tsconfig_resolver,
compiler_options_resolver,
}
}
@ -227,13 +227,10 @@ impl<TSys: FsMetadata + FsRead> ModuleContentProvider<TSys> {
text_info: &SourceTextInfo,
diagnostic_reporter: &mut dyn FnMut(SpecifierUnfurlerDiagnostic),
) -> Result<JsxFolderOptions<'a>, AnyError> {
let tsconfig_folder_info =
self.tsconfig_resolver.folder_for_specifier(specifier);
let jsx_config = tsconfig_folder_info
.dir
.to_maybe_jsx_import_source_config()?;
let transpile_options =
&tsconfig_folder_info.transpile_options()?.transpile;
let compiler_options =
self.compiler_options_resolver.for_specifier(specifier);
let jsx_config = compiler_options.jsx_import_source_config()?;
let transpile_options = &compiler_options.transpile_options()?.transpile;
let jsx_runtime = if transpile_options.jsx_automatic {
"automatic"
} else {
@ -254,7 +251,6 @@ impl<TSys: FsMetadata + FsRead> ModuleContentProvider<TSys> {
maybe_import_source.unwrap_or_else(|| import_source.to_string())
};
let jsx_import_source = jsx_config
.as_ref()
.and_then(|c| c.import_source.as_ref())
.map(|jsx_import_source| {
unfurl_import_source(
@ -264,7 +260,6 @@ impl<TSys: FsMetadata + FsRead> ModuleContentProvider<TSys> {
)
});
let jsx_import_source_types = jsx_config
.as_ref()
.and_then(|c| c.import_source_types.as_ref())
.map(|jsx_import_source_types| {
unfurl_import_source(
@ -289,13 +284,24 @@ mod test {
use deno_config::workspace::WorkspaceDiscoverStart;
use deno_path_util::url_from_file_path;
use deno_resolver::factory::WorkspaceDirectoryProvider;
use deno_resolver::npm::ByonmNpmResolverCreateOptions;
use deno_resolver::npm::CreateInNpmPkgCheckerOptions;
use deno_resolver::npm::DenoInNpmPackageChecker;
use deno_resolver::npm::NpmResolverCreateOptions;
use deno_resolver::workspace::WorkspaceResolver;
use node_resolver::cache::NodeResolutionSys;
use node_resolver::DenoIsBuiltInNodeModuleChecker;
use node_resolver::NodeResolver;
use node_resolver::NodeResolverOptions;
use node_resolver::PackageJsonResolver;
use pretty_assertions::assert_eq;
use sys_traits::impls::InMemorySys;
use sys_traits::FsCreateDirAll;
use sys_traits::FsWrite;
use super::*;
use crate::npm::CliNpmResolver;
#[test]
fn test_module_content_jsx() {
@ -409,15 +415,32 @@ mod test {
.unwrap(),
);
let specifier_unfurler = SpecifierUnfurler::new(resolver, false);
let tsconfig_resolver = Arc::new(TsConfigResolver::from_workspace(
let package_json_resolver =
Arc::new(PackageJsonResolver::new(sys.clone(), None));
let node_resolver = NodeResolver::new(
DenoInNpmPackageChecker::new(CreateInNpmPkgCheckerOptions::Byonm),
DenoIsBuiltInNodeModuleChecker,
CliNpmResolver::new(NpmResolverCreateOptions::Byonm(
ByonmNpmResolverCreateOptions {
root_node_modules_dir: None,
sys: NodeResolutionSys::new(sys.clone(), None),
pkg_json_resolver: package_json_resolver.clone(),
},
)),
package_json_resolver,
NodeResolutionSys::new(sys.clone(), None),
NodeResolverOptions::default(),
);
let compiler_options_resolver = Arc::new(CompilerOptionsResolver::new(
&sys,
&workspace_dir.workspace,
&WorkspaceDirectoryProvider::from_initial_dir(&Arc::new(workspace_dir)),
&node_resolver,
));
ModuleContentProvider::new(
Arc::new(ParsedSourceCache::default()),
specifier_unfurler,
sys,
tsconfig_resolver,
compiler_options_resolver,
)
}
}

View file

@ -176,7 +176,7 @@ pub async fn run(
let npm_installer = factory.npm_installer_if_managed().await?.cloned();
let resolver = factory.resolver().await?.clone();
let file_fetcher = factory.file_fetcher()?;
let tsconfig_resolver = factory.tsconfig_resolver()?;
let compiler_options_resolver = factory.compiler_options_resolver()?;
let worker_factory = factory.create_cli_main_worker_factory().await?;
let history_file_path = factory
.deno_dir()
@ -200,7 +200,7 @@ pub async fn run(
cli_options,
npm_installer,
resolver,
tsconfig_resolver,
compiler_options_resolver,
worker,
main_module.clone(),
test_event_receiver,

View file

@ -33,6 +33,7 @@ use deno_graph::analysis::SpecifierWithRange;
use deno_graph::Position;
use deno_graph::PositionRange;
use deno_lib::util::result::any_and_jserrorbox_downcast_ref;
use deno_resolver::deno_json::CompilerOptionsResolver;
use deno_runtime::worker::MainWorker;
use deno_semver::npm::NpmPackageReqReference;
use node_resolver::NodeResolutionKind;
@ -43,7 +44,6 @@ use regex::Regex;
use tokio::sync::Mutex;
use crate::args::CliOptions;
use crate::args::CliTsConfigResolver;
use crate::cdp;
use crate::cdp::RemoteObjectId;
use crate::colors;
@ -194,7 +194,7 @@ impl ReplSession {
cli_options: &CliOptions,
npm_installer: Option<Arc<CliNpmInstaller>>,
resolver: Arc<CliResolver>,
tsconfig_resolver: &CliTsConfigResolver,
compiler_options_resolver: &CompilerOptionsResolver,
mut worker: MainWorker,
main_module: ModuleSpecifier,
test_event_receiver: TestEventReceiver,
@ -250,8 +250,9 @@ impl ReplSession {
cli_options.initial_cwd().to_string_lossy(),
)
})?;
let experimental_decorators = tsconfig_resolver
.transpile_and_emit_options(&cwd_url)?
let experimental_decorators = compiler_options_resolver
.for_specifier(&cwd_url)
.transpile_options()?
.transpile
.use_ts_decorators;
let mut repl_session = ReplSession {

View file

@ -10,12 +10,14 @@ use deno_ast::MediaType;
use deno_ast::ModuleSpecifier;
use deno_config::deno_json;
use deno_config::deno_json::CompilerOptionTypesDeserializeError;
use deno_config::workspace::WorkspaceDirectory;
use deno_core::url::Url;
use deno_error::JsErrorBox;
use deno_graph::Module;
use deno_graph::ModuleGraph;
use deno_lib::util::hash::FastInsecureHasher;
use deno_resolver::deno_json::CompilerOptionsData;
use deno_resolver::deno_json::CompilerOptionsResolver;
use deno_resolver::factory::WorkspaceDirectoryProvider;
use deno_semver::npm::NpmPackageNvReference;
use deno_terminal::colors;
use indexmap::IndexMap;
@ -23,7 +25,6 @@ use once_cell::sync::Lazy;
use regex::Regex;
use crate::args::CliOptions;
use crate::args::CliTsConfigResolver;
use crate::args::CompilerOptions;
use crate::args::DenoSubcommand;
use crate::args::TsTypeLib;
@ -106,7 +107,8 @@ pub struct TypeChecker {
node_resolver: Arc<CliNodeResolver>,
npm_resolver: CliNpmResolver,
sys: CliSys,
tsconfig_resolver: Arc<CliTsConfigResolver>,
workspace_directory_provider: Arc<WorkspaceDirectoryProvider>,
compiler_options_resolver: Arc<CompilerOptionsResolver>,
code_cache: Option<Arc<crate::cache::CodeCache>>,
}
@ -120,7 +122,8 @@ impl TypeChecker {
node_resolver: Arc<CliNodeResolver>,
npm_resolver: CliNpmResolver,
sys: CliSys,
tsconfig_resolver: Arc<CliTsConfigResolver>,
workspace_directory_provider: Arc<WorkspaceDirectoryProvider>,
compiler_options_resolver: Arc<CompilerOptionsResolver>,
code_cache: Option<Arc<crate::cache::CodeCache>>,
) -> Self {
Self {
@ -131,7 +134,8 @@ impl TypeChecker {
node_resolver,
npm_resolver,
sys,
tsconfig_resolver,
workspace_directory_provider,
compiler_options_resolver,
code_cache,
}
}
@ -235,13 +239,14 @@ impl TypeChecker {
cjs_tracker: &self.cjs_tracker,
node_resolver: &self.node_resolver,
npm_resolver: &self.npm_resolver,
tsconfig_resolver: &self.tsconfig_resolver,
compiler_options_resolver: &self.compiler_options_resolver,
log_level: self.cli_options.log_level(),
npm_check_state_hash: check_state_hash(&self.npm_resolver),
type_check_cache: TypeCheckCache::new(
self.caches.type_checking_cache_db(),
),
grouped_roots,
groups: grouped_roots,
current_group_index: 0,
options,
seen_diagnotics: Default::default(),
code_cache: self.code_cache.clone(),
@ -256,102 +261,74 @@ impl TypeChecker {
&'a self,
graph: &ModuleGraph,
lib: TsTypeLib,
) -> Result<IndexMap<CheckGroupKey<'a>, CheckGroupInfo>, CheckError> {
let mut imports_for_specifier: HashMap<Arc<Url>, Rc<Vec<Url>>> =
HashMap::with_capacity(self.tsconfig_resolver.folder_count());
let mut roots_by_config: IndexMap<_, CheckGroupInfo> =
IndexMap::with_capacity(self.tsconfig_resolver.folder_count());
) -> Result<Vec<CheckGroup<'a>>, CheckError> {
let group_count = self.compiler_options_resolver.size();
let mut imports_for_specifier = HashMap::with_capacity(group_count);
let mut groups_by_key = IndexMap::with_capacity(group_count);
for root in &graph.roots {
let folder = self.tsconfig_resolver.folder_for_specifier(root);
let imports =
match imports_for_specifier.entry(folder.dir.dir_url().clone()) {
std::collections::hash_map::Entry::Occupied(entry) => {
entry.get().clone()
}
std::collections::hash_map::Entry::Vacant(vacant_entry) => {
let value = Rc::new(resolve_graph_imports_for_workspace_dir(
let compiler_options_data =
self.compiler_options_resolver.for_specifier(root);
let compiler_options =
compiler_options_data.compiler_options_for_lib(lib)?;
let imports = imports_for_specifier
.entry(compiler_options_data.sources.last().map(|s| &s.specifier))
.or_insert_with(|| {
Rc::new(resolve_graph_imports_for_compiler_options_data(
graph,
&folder.dir,
));
vacant_entry.insert(value.clone());
value
}
};
let compiler_options = folder.lib_compiler_options(lib)?;
let key = CheckGroupKey {
compiler_options_data,
))
})
.clone();
let group_key = (compiler_options, imports.clone());
let group = groups_by_key.entry(group_key).or_insert_with(|| {
let dir = self.workspace_directory_provider.for_specifier(root);
CheckGroup {
roots: Default::default(),
compiler_options,
imports,
};
let entry = roots_by_config.entry(key);
let entry = match entry {
indexmap::map::Entry::Occupied(entry) => entry.into_mut(),
indexmap::map::Entry::Vacant(entry) => entry.insert(CheckGroupInfo {
roots: Default::default(),
// this is slightly hacky. It's used as the referrer for resolving
// npm imports in the key
referrer: folder
.dir
referrer: self
.workspace_directory_provider
.for_specifier(root)
.maybe_deno_json()
.map(|d| d.specifier.clone())
.unwrap_or_else(|| folder.dir.dir_url().as_ref().clone()),
}),
};
entry.roots.push(root.clone());
.unwrap_or_else(|| dir.dir_url().as_ref().clone()),
}
Ok(roots_by_config)
});
group.roots.push(root.clone());
}
Ok(groups_by_key.into_values().collect())
}
}
fn resolve_graph_imports_for_workspace_dir(
/// This function assumes that 'graph imports' strictly refer to tsconfig
/// `files` and `compilerOptions.types` which they currently do. In fact, if
/// they were more general than that, we don't really have sufficient context to
/// group them for type-checking.
fn resolve_graph_imports_for_compiler_options_data(
graph: &ModuleGraph,
dir: &WorkspaceDirectory,
compiler_options: &CompilerOptionsData,
) -> Vec<Url> {
fn resolve_graph_imports_for_referrer<'a>(
graph: &'a ModuleGraph,
referrer: &'a Url,
) -> Option<impl Iterator<Item = Url> + 'a> {
let imports = graph.imports.get(referrer)?;
Some(
imports
.dependencies
.values()
.filter_map(|dep| dep.get_type().or_else(|| dep.get_code()))
.map(|url| graph.resolve(url))
.cloned(),
)
}
let root_deno_json = dir.workspace.root_deno_json();
let member_deno_json = dir.maybe_deno_json().filter(|c| {
Some(&c.specifier) != root_deno_json.as_ref().map(|c| &c.specifier)
});
let mut specifiers = root_deno_json
.map(|c| resolve_graph_imports_for_referrer(graph, &c.specifier))
.into_iter()
.flatten()
.flatten()
.chain(
member_deno_json
.map(|c| resolve_graph_imports_for_referrer(graph, &c.specifier))
.into_iter()
.flatten()
.flatten(),
)
let mut specifiers = compiler_options
.sources
.iter()
.map(|s| &s.specifier)
.filter_map(|s| graph.imports.get(s))
.flat_map(|i| i.dependencies.values())
.filter_map(|d| Some(graph.resolve(d.get_type().or_else(|| d.get_code())?)))
.cloned()
.collect::<Vec<_>>();
specifiers.sort();
specifiers
}
/// Key to use to group roots together by config.
#[derive(Debug, Hash, PartialEq, Eq)]
struct CheckGroupKey<'a> {
compiler_options: &'a Arc<CompilerOptions>,
imports: Rc<Vec<Url>>,
}
struct CheckGroupInfo {
#[derive(Debug)]
struct CheckGroup<'a> {
roots: Vec<Url>,
imports: Rc<Vec<Url>>,
referrer: Url,
compiler_options: &'a Arc<CompilerOptions>,
}
pub struct DiagnosticsByFolderIterator<'a>(
@ -390,9 +367,10 @@ struct DiagnosticsByFolderRealIterator<'a> {
cjs_tracker: &'a Arc<TypeCheckingCjsTracker>,
node_resolver: &'a Arc<CliNodeResolver>,
npm_resolver: &'a CliNpmResolver,
tsconfig_resolver: &'a CliTsConfigResolver,
compiler_options_resolver: &'a CompilerOptionsResolver,
type_check_cache: TypeCheckCache,
grouped_roots: IndexMap<CheckGroupKey<'a>, CheckGroupInfo>,
groups: Vec<CheckGroup<'a>>,
current_group_index: usize,
log_level: Option<log::Level>,
npm_check_state_hash: Option<u64>,
seen_diagnotics: HashSet<String>,
@ -404,8 +382,9 @@ impl Iterator for DiagnosticsByFolderRealIterator<'_> {
type Item = Result<Diagnostics, CheckError>;
fn next(&mut self) -> Option<Self::Item> {
let (group_key, group_info) = self.grouped_roots.shift_remove_index(0)?;
let mut result = self.check_diagnostics_in_folder(&group_key, group_info);
let check_group = self.groups.get(self.current_group_index)?;
self.current_group_index += 1;
let mut result = self.check_diagnostics_in_folder(check_group);
if let Ok(diagnostics) = &mut result {
diagnostics.retain(|d| {
if let (Some(file_name), Some(start)) = (&d.file_name, &d.start) {
@ -446,13 +425,12 @@ pub fn ambient_modules_to_regex_string(ambient_modules: &[String]) -> String {
regex_string
}
impl<'a> DiagnosticsByFolderRealIterator<'a> {
impl DiagnosticsByFolderRealIterator<'_> {
#[allow(clippy::too_many_arguments)]
#[allow(clippy::result_large_err)]
fn check_diagnostics_in_folder(
&self,
group_key: &'a CheckGroupKey<'a>,
group_info: CheckGroupInfo,
check_group: &CheckGroup,
) -> Result<Diagnostics, CheckError> {
fn log_provided_roots(provided_roots: &[Url]) {
for root in provided_roots {
@ -465,23 +443,21 @@ impl<'a> DiagnosticsByFolderRealIterator<'a> {
}
// walk the graph
let compiler_options = group_key.compiler_options;
let mut graph_walker = GraphWalker::new(
&self.graph,
self.sys,
self.node_resolver,
self.npm_resolver,
self.tsconfig_resolver,
self.compiler_options_resolver,
self.npm_check_state_hash,
compiler_options.as_ref(),
check_group.compiler_options,
self.options.type_check_mode,
);
let mut provided_roots = group_info.roots;
for import in group_key.imports.iter() {
graph_walker.add_config_import(import, &group_info.referrer);
for import in check_group.imports.iter() {
graph_walker.add_config_import(import, &check_group.referrer);
}
for root in &provided_roots {
for root in &check_group.roots {
graph_walker.add_root(root);
}
@ -498,7 +474,7 @@ impl<'a> DiagnosticsByFolderRealIterator<'a> {
if root_names.is_empty() {
if missing_diagnostics.has_diagnostic() {
log_provided_roots(&provided_roots);
log_provided_roots(&check_group.roots);
}
return Ok(missing_diagnostics);
}
@ -507,18 +483,21 @@ impl<'a> DiagnosticsByFolderRealIterator<'a> {
// do not type check if we know this is type checked
if let Some(check_hash) = maybe_check_hash {
if self.type_check_cache.has_check_hash(check_hash) {
log::debug!("Already type checked {}", group_info.referrer);
log::debug!("Already type checked {}", &check_group.referrer);
return Ok(Default::default());
}
}
}
// log out the roots that we're checking
log_provided_roots(&provided_roots);
log_provided_roots(&check_group.roots);
// the first root will always either be the specifier that the user provided
// or the first specifier in a directory
let first_root = provided_roots.remove(0);
let first_root = check_group
.roots
.first()
.expect("must be at least one root");
// 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
@ -526,13 +505,13 @@ impl<'a> DiagnosticsByFolderRealIterator<'a> {
let maybe_tsbuildinfo = if self.options.reload {
None
} else {
self.type_check_cache.get_tsbuildinfo(&first_root)
self.type_check_cache.get_tsbuildinfo(first_root)
};
// to make tsc build info work, we need to consistently hash modules, so that
// tsc can better determine if an emit is still valid or not, so we provide
// that data here.
let compiler_options_hash_data = FastInsecureHasher::new_deno_versioned()
.write_hashable(compiler_options)
.write_hashable(check_group.compiler_options)
.finish();
let code_cache = self.code_cache.as_ref().map(|c| {
let c: Arc<dyn deno_runtime::code_cache::CodeCache> = c.clone();
@ -540,7 +519,7 @@ impl<'a> DiagnosticsByFolderRealIterator<'a> {
});
let response = tsc::exec(
tsc::Request {
config: compiler_options.clone(),
config: check_group.compiler_options.clone(),
debug: self.log_level == Some(log::Level::Debug),
graph: self.graph.clone(),
hash_data: compiler_options_hash_data,
@ -586,7 +565,7 @@ impl<'a> DiagnosticsByFolderRealIterator<'a> {
if let Some(tsbuildinfo) = response.maybe_tsbuildinfo {
self
.type_check_cache
.set_tsbuildinfo(&first_root, &tsbuildinfo);
.set_tsbuildinfo(first_root, &tsbuildinfo);
}
if !diagnostics.has_diagnostic() {
@ -641,7 +620,7 @@ struct GraphWalker<'a> {
sys: &'a CliSys,
node_resolver: &'a CliNodeResolver,
npm_resolver: &'a CliNpmResolver,
tsconfig_resolver: &'a CliTsConfigResolver,
compiler_options_resolver: &'a CompilerOptionsResolver,
maybe_hasher: Option<FastInsecureHasher>,
seen: HashSet<&'a Url>,
pending: VecDeque<(&'a Url, bool)>,
@ -657,7 +636,7 @@ impl<'a> GraphWalker<'a> {
sys: &'a CliSys,
node_resolver: &'a CliNodeResolver,
npm_resolver: &'a CliNpmResolver,
tsconfig_resolver: &'a CliTsConfigResolver,
compiler_options_resolver: &'a CompilerOptionsResolver,
npm_cache_state_hash: Option<u64>,
compiler_options: &CompilerOptions,
type_check_mode: TypeCheckMode,
@ -679,7 +658,7 @@ impl<'a> GraphWalker<'a> {
sys,
node_resolver,
npm_resolver,
tsconfig_resolver,
compiler_options_resolver,
maybe_hasher,
seen: HashSet::with_capacity(
graph.imports.len() + graph.specifiers_count(),
@ -784,7 +763,14 @@ impl<'a> GraphWalker<'a> {
Module::Wasm(module) => {
maybe_module_dependencies = Some(&module.dependencies);
}
Module::Json(_) | Module::Npm(_) | Module::External(_) => {}
Module::Json(_) | Module::Npm(_) => {}
Module::External(module) => {
// NPM files for `"nodeModulesDir": "manual"`.
let media_type = MediaType::from_specifier(&module.specifier);
if media_type.is_declaration() {
self.roots.push((module.specifier.clone(), media_type));
}
}
Module::Node(_) => {
if !self.has_seen_node_builtin {
self.has_seen_node_builtin = true;
@ -851,8 +837,9 @@ impl<'a> GraphWalker<'a> {
| MediaType::Cjs
| MediaType::Jsx => {
if self
.tsconfig_resolver
.check_js_for_specifier(&module.specifier)
.compiler_options_resolver
.for_specifier(&module.specifier)
.check_js()
|| has_ts_check(module.media_type, &module.source.text)
{
Some((module.specifier.clone(), module.media_type))

View file

@ -19,12 +19,10 @@ use serde::de::Visitor;
use serde::Deserialize;
use serde::Deserializer;
use serde::Serialize;
use serde::Serializer;
use serde_json::json;
use serde_json::Value;
use sys_traits::FsRead;
use thiserror::Error;
use ts::parse_compiler_options;
use url::Url;
use crate::glob::FilePatterns;
@ -34,6 +32,7 @@ use crate::UrlToFilePathError;
mod ts;
pub use ts::parse_compiler_options;
pub use ts::CompilerOptions;
pub use ts::EmitConfigOptions;
pub use ts::IgnoredCompilerOptions;
@ -1887,123 +1886,12 @@ impl ConfigFile {
}
}
/// Represents the "default" type library that should be used when type
/// checking the code in the module graph. Note that a user provided config
/// of `"lib"` would override this value.
#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
pub enum TsTypeLib {
DenoWindow,
DenoWorker,
}
impl Default for TsTypeLib {
fn default() -> Self {
Self::DenoWindow
}
}
impl Serialize for TsTypeLib {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let value = match self {
Self::DenoWindow => {
vec!["deno.window".to_string(), "deno.unstable".to_string()]
}
Self::DenoWorker => {
vec!["deno.worker".to_string(), "deno.unstable".to_string()]
}
};
Serialize::serialize(&value, serializer)
}
}
/// An enum that represents the base tsc configuration to return.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CompilerOptionsType {
/// Return a configuration for bundling, using swc to emit the bundle. This is
/// independent of type checking.
Bundle,
/// Return a configuration to use tsc to type check. This
/// is independent of either bundling or emitting via swc.
Check { lib: TsTypeLib },
/// Return a configuration to use swc to emit single module files.
Emit,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CompilerOptionsWithIgnoredOptions {
pub compiler_options: CompilerOptions,
pub ignored_options: Vec<IgnoredCompilerOptions>,
}
/// For a given configuration type get the starting point CompilerOptions
/// used that can then be merged with user specified options.
pub fn get_base_compiler_options_for_emit(
config_type: CompilerOptionsType,
) -> CompilerOptions {
match config_type {
CompilerOptionsType::Bundle => CompilerOptions::new(json!({
"allowImportingTsExtensions": true,
"checkJs": false,
"emitDecoratorMetadata": false,
"experimentalDecorators": true,
"importsNotUsedAsValues": "remove",
"inlineSourceMap": false,
"inlineSources": false,
"sourceMap": false,
"jsx": "react",
"jsxFactory": "React.createElement",
"jsxFragmentFactory": "React.Fragment",
"module": "NodeNext",
"moduleResolution": "NodeNext",
})),
CompilerOptionsType::Check { lib } => CompilerOptions::new(json!({
"allowJs": true,
"allowImportingTsExtensions": true,
"allowSyntheticDefaultImports": true,
"checkJs": false,
"emitDecoratorMetadata": false,
"experimentalDecorators": false,
"incremental": true,
"jsx": "react",
"importsNotUsedAsValues": "remove",
"inlineSourceMap": true,
"inlineSources": true,
"isolatedModules": true,
"lib": lib,
"module": "NodeNext",
"moduleResolution": "NodeNext",
"moduleDetection": "force",
"noEmit": true,
"noImplicitOverride": true,
"resolveJsonModule": true,
"sourceMap": false,
"strict": true,
"target": "esnext",
"tsBuildInfoFile": "internal:///.tsbuildinfo",
"useDefineForClassFields": true,
})),
CompilerOptionsType::Emit => CompilerOptions::new(json!({
"allowImportingTsExtensions": true,
"checkJs": false,
"emitDecoratorMetadata": false,
"experimentalDecorators": false,
"importsNotUsedAsValues": "remove",
"inlineSourceMap": true,
"inlineSources": true,
"sourceMap": false,
"jsx": "react",
"jsxFactory": "React.createElement",
"jsxFragmentFactory": "React.Fragment",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"resolveJsonModule": true,
})),
}
}
#[cfg(test)]
mod tests {
use std::path::PathBuf;

View file

@ -28,6 +28,7 @@ use discovery::ConfigFolder;
use discovery::DenoOrPkgJson;
use indexmap::IndexMap;
use indexmap::IndexSet;
use serde_json::json;
use sys_traits::FsMetadata;
use sys_traits::FsRead;
use sys_traits::FsReadDir;
@ -35,12 +36,10 @@ use thiserror::Error;
use url::Url;
use crate::deno_json;
use crate::deno_json::get_base_compiler_options_for_emit;
use crate::deno_json::BenchConfig;
use crate::deno_json::CompilerOptionTypesDeserializeError;
use crate::deno_json::CompilerOptions;
use crate::deno_json::CompilerOptionsParseError;
use crate::deno_json::CompilerOptionsType;
use crate::deno_json::CompilerOptionsWithIgnoredOptions;
use crate::deno_json::ConfigFile;
use crate::deno_json::ConfigFileError;
@ -1120,6 +1119,118 @@ pub struct WorkspaceDirLintConfig {
pub files: FilePatterns,
}
/// Represents the "default" type library that should be used when type
/// checking the code in the module graph. Note that a user provided config
/// of `"lib"` would override this value.
#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
pub enum TsTypeLib {
DenoWindow,
DenoWorker,
}
impl Default for TsTypeLib {
fn default() -> Self {
Self::DenoWindow
}
}
/// An enum that represents the base tsc configuration to return.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CompilerOptionsType {
/// Return a configuration for bundling, using swc to emit the bundle. This is
/// independent of type checking.
Bundle,
/// Return a configuration to use tsc to type check. This
/// is independent of either bundling or emitting via swc.
Check { lib: TsTypeLib },
/// Return a configuration to use swc to emit single module files.
Emit,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum CompilerOptionsSourceKind {
DenoJson,
TsConfig,
}
#[derive(Debug, Clone)]
pub struct CompilerOptionsSource {
pub specifier: Url,
pub compiler_options: Option<CompilerOptions>,
}
/// For a given configuration type get the starting point CompilerOptions
/// used that can then be merged with user specified options.
pub fn get_base_compiler_options_for_emit(
config_type: CompilerOptionsType,
source_kind: CompilerOptionsSourceKind,
) -> CompilerOptions {
match config_type {
CompilerOptionsType::Bundle => CompilerOptions::new(json!({
"allowImportingTsExtensions": true,
"checkJs": false,
"emitDecoratorMetadata": false,
"experimentalDecorators": true,
"importsNotUsedAsValues": "remove",
"inlineSourceMap": false,
"inlineSources": false,
"sourceMap": false,
"jsx": "react",
"jsxFactory": "React.createElement",
"jsxFragmentFactory": "React.Fragment",
"module": "NodeNext",
"moduleResolution": "NodeNext",
})),
CompilerOptionsType::Check { lib } => CompilerOptions::new(json!({
"allowJs": true,
"allowImportingTsExtensions": true,
"allowSyntheticDefaultImports": true,
"checkJs": false,
"emitDecoratorMetadata": false,
"experimentalDecorators": false,
"incremental": true,
"jsx": "react",
"importsNotUsedAsValues": "remove",
"inlineSourceMap": true,
"inlineSources": true,
"isolatedModules": true,
"lib": match (lib, source_kind) {
(TsTypeLib::DenoWindow, CompilerOptionsSourceKind::DenoJson) => vec!["deno.window", "deno.unstable"],
(TsTypeLib::DenoWindow, CompilerOptionsSourceKind::TsConfig) => vec!["deno.window", "deno.unstable", "dom"],
(TsTypeLib::DenoWorker, CompilerOptionsSourceKind::DenoJson) => vec!["deno.worker", "deno.unstable"],
(TsTypeLib::DenoWorker, CompilerOptionsSourceKind::TsConfig) => vec!["deno.worker", "deno.unstable", "dom"],
},
"module": "NodeNext",
"moduleResolution": "NodeNext",
"moduleDetection": "force",
"noEmit": true,
"noImplicitOverride": true,
"resolveJsonModule": true,
"sourceMap": false,
"strict": true,
"target": "esnext",
"tsBuildInfoFile": "internal:///.tsbuildinfo",
"useDefineForClassFields": true,
})),
CompilerOptionsType::Emit => CompilerOptions::new(json!({
"allowImportingTsExtensions": true,
"checkJs": false,
"emitDecoratorMetadata": false,
"experimentalDecorators": false,
"importsNotUsedAsValues": "remove",
"inlineSourceMap": true,
"inlineSources": true,
"sourceMap": false,
"jsx": "react",
"jsxFactory": "React.createElement",
"jsxFragmentFactory": "React.Fragment",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"resolveJsonModule": true,
})),
}
}
#[derive(Debug, Clone)]
pub struct WorkspaceDirectory {
pub workspace: WorkspaceRc,
@ -1422,8 +1533,10 @@ impl WorkspaceDirectory {
sys: &TSys,
config_type: CompilerOptionsType,
) -> Result<CompilerOptionsWithIgnoredOptions, CompilerOptionsParseError> {
let mut base_compiler_options =
get_base_compiler_options_for_emit(config_type);
let mut base_compiler_options = get_base_compiler_options_for_emit(
config_type,
CompilerOptionsSourceKind::DenoJson,
);
let CompilerOptionsWithIgnoredOptions {
compiler_options,
ignored_options,
@ -1449,6 +1562,38 @@ impl WorkspaceDirectory {
.unwrap_or(true)
}
/// Gets a list of raw compiler options that the user provided, in a vec of
/// size 0-2 based on `[maybe_root, maybe_member].flatten()`.
pub fn to_configured_compiler_options_sources(
&self,
) -> Vec<CompilerOptionsSource> {
let Some(deno_json) = self.deno_json.as_ref() else {
return Vec::new();
};
let root = deno_json.root.as_ref().map(|d| CompilerOptionsSource {
specifier: d.specifier.clone(),
compiler_options: d
.json
.compiler_options
.as_ref()
.filter(|v| !v.is_null())
.cloned()
.map(CompilerOptions),
});
let member = CompilerOptionsSource {
specifier: deno_json.member.specifier.clone(),
compiler_options: deno_json
.member
.json
.compiler_options
.as_ref()
.filter(|v| !v.is_null())
.cloned()
.map(CompilerOptions),
};
root.into_iter().chain([member]).collect()
}
/// Gets the combined compiler options that the user provided, without any of
/// Deno's defaults. Use `to_resolved_compiler_options()` to get the resolved
/// config instead.
@ -3019,7 +3164,7 @@ pub mod test {
.to_resolved_compiler_options(
&sys,
CompilerOptionsType::Check {
lib: deno_json::TsTypeLib::DenoWindow
lib: TsTypeLib::DenoWindow
}
)
.unwrap(),

View file

@ -44,6 +44,7 @@ futures.workspace = true
http = { workspace = true, optional = true }
import_map.workspace = true
indexmap.workspace = true
jsonc-parser.workspace = true
log.workspace = true
node_resolver.workspace = true
once_cell.workspace = true

View file

@ -13,7 +13,7 @@ type UrlRc = crate::sync::MaybeArc<Url>;
/// The root directory is considered "unscoped" so values that
/// fall outside the other directories land here (ex. remote modules).
pub struct FolderScopedMap<TValue> {
unscoped: TValue,
pub unscoped: TValue,
scoped: BTreeMap<UrlRc, TValue>,
}
@ -64,9 +64,41 @@ impl<TValue> FolderScopedMap<TValue> {
.unwrap_or(&self.unscoped)
}
pub fn entry_for_specifier(
&self,
specifier: &Url,
) -> (Option<&UrlRc>, &TValue) {
self
.scoped
.iter()
.rfind(|(s, _)| specifier.as_str().starts_with(s.as_str()))
.map(|(s, v)| (Some(s), v))
.unwrap_or((None, &self.unscoped))
}
pub fn entries(&self) -> impl Iterator<Item = (Option<&UrlRc>, &TValue)> {
[(None, &self.unscoped)]
.into_iter()
.chain(self.scoped.iter().map(|(s, v)| (Some(s), v)))
}
pub fn insert(&mut self, dir_url: UrlRc, value: TValue) {
debug_assert!(dir_url.path().ends_with("/")); // must be a dir url
debug_assert_eq!(dir_url.scheme(), "file");
self.scoped.insert(dir_url, value);
}
pub fn try_map<B, E>(
&self,
mut f: impl FnMut(&TValue) -> Result<B, E>,
) -> Result<FolderScopedMap<B>, E> {
Ok(FolderScopedMap {
unscoped: f(&self.unscoped)?,
scoped: self
.scoped
.iter()
.map(|(s, v)| Ok((s.clone(), f(v)?)))
.collect::<Result<_, _>>()?,
})
}
}

View file

@ -1,13 +1,37 @@
// Copyright 2018-2025 the Deno authors. MIT license.
use std::borrow::Cow;
use std::collections::BTreeSet;
use std::collections::HashMap;
use std::io::ErrorKind;
use std::path::Path;
use std::path::PathBuf;
use std::rc::Rc;
use deno_config::deno_json::parse_compiler_options;
use deno_config::deno_json::CompilerOptions;
use deno_config::deno_json::CompilerOptionsParseError;
use deno_config::deno_json::CompilerOptionsType;
use deno_config::deno_json::CompilerOptionsWithIgnoredOptions;
use deno_config::deno_json::TsTypeLib;
use deno_config::workspace::WorkspaceDirectory;
use deno_config::glob::PathOrPatternSet;
use deno_config::workspace::get_base_compiler_options_for_emit;
use deno_config::workspace::CompilerOptionsSource;
use deno_config::workspace::CompilerOptionsSourceKind;
use deno_config::workspace::CompilerOptionsType;
use deno_config::workspace::JsxImportSourceConfig;
use deno_config::workspace::JsxImportSourceSpecifierConfig;
use deno_config::workspace::ToMaybeJsxImportSourceConfigError;
use deno_config::workspace::TsTypeLib;
use deno_path_util::normalize_path;
use deno_path_util::url_from_file_path;
use deno_path_util::url_to_file_path;
use deno_terminal::colors;
use deno_unsync::sync::AtomicFlag;
use indexmap::IndexMap;
use indexmap::IndexSet;
use node_resolver::DenoIsBuiltInNodeModuleChecker;
use node_resolver::NodeResolutionKind;
use node_resolver::NodeResolver;
use node_resolver::ResolutionMode;
#[cfg(feature = "sync")]
use once_cell::sync::OnceCell;
#[cfg(not(feature = "sync"))]
@ -16,21 +40,17 @@ use sys_traits::FsRead;
use url::Url;
use crate::collections::FolderScopedMap;
use crate::factory::WorkspaceRc;
use crate::factory::WorkspaceDirectoryProvider;
use crate::npm::DenoInNpmPackageChecker;
use crate::npm::NpmResolver;
use crate::npm::NpmResolverSys;
use crate::sync::new_rc;
#[allow(clippy::disallowed_types)]
pub type TsConfigResolverRc<TSys> =
crate::sync::MaybeArc<TsConfigResolver<TSys>>;
#[allow(clippy::disallowed_types)]
type CompilerOptionsRc = crate::sync::MaybeArc<CompilerOptions>;
#[allow(clippy::disallowed_types)]
type LoggedWarningsRc = crate::sync::MaybeArc<LoggedWarnings>;
#[cfg(feature = "deno_ast")]
#[allow(clippy::disallowed_types)]
pub type TranspileAndEmitOptionsRc =
crate::sync::MaybeArc<TranspileAndEmitOptions>;
pub type CompilerOptionsTypesRc =
crate::sync::MaybeArc<Vec<(Url, Vec<String>)>>;
#[cfg(feature = "deno_ast")]
#[derive(Debug)]
@ -41,12 +61,20 @@ pub struct TranspileAndEmitOptions {
pub pre_computed_hash: u64,
}
#[cfg(feature = "deno_ast")]
#[allow(clippy::disallowed_types)]
pub type TranspileAndEmitOptionsRc =
crate::sync::MaybeArc<TranspileAndEmitOptions>;
#[derive(Debug, Default)]
struct LoggedWarnings {
experimental_decorators: AtomicFlag,
folders: crate::sync::MaybeDashSet<Url>,
}
#[allow(clippy::disallowed_types)]
type LoggedWarningsRc = crate::sync::MaybeArc<LoggedWarnings>;
#[derive(Default, Debug)]
struct MemoizedValues {
deno_window_check_compiler_options: OnceCell<CompilerOptionsRc>,
@ -54,55 +82,87 @@ struct MemoizedValues {
emit_compiler_options: OnceCell<CompilerOptionsRc>,
#[cfg(feature = "deno_ast")]
transpile_options: OnceCell<TranspileAndEmitOptionsRc>,
compiler_options_types: OnceCell<CompilerOptionsTypesRc>,
jsx_import_source_config: OnceCell<Option<JsxImportSourceConfigRc>>,
check_js: OnceCell<bool>,
}
#[derive(Debug)]
pub struct TsConfigFolderInfo<TSys: FsRead> {
pub dir: WorkspaceDirectory,
logged_warnings: LoggedWarningsRc,
pub struct CompilerOptionsData {
pub sources: Vec<CompilerOptionsSource>,
source_kind: CompilerOptionsSourceKind,
memoized: MemoizedValues,
sys: TSys,
logged_warnings: LoggedWarningsRc,
}
impl<TSys: FsRead> TsConfigFolderInfo<TSys> {
pub fn lib_compiler_options(
impl CompilerOptionsData {
fn new(
sources: Vec<CompilerOptionsSource>,
source_kind: CompilerOptionsSourceKind,
logged_warnings: LoggedWarningsRc,
) -> Self {
Self {
sources,
source_kind,
memoized: Default::default(),
logged_warnings,
}
}
pub fn compiler_options_for_lib(
&self,
lib: TsTypeLib,
) -> Result<&CompilerOptionsRc, CompilerOptionsParseError> {
let cell = match lib {
TsTypeLib::DenoWindow => {
&self.memoized.deno_window_check_compiler_options
}
TsTypeLib::DenoWorker => {
&self.memoized.deno_worker_check_compiler_options
}
};
cell.get_or_try_init(|| {
let compiler_options_result = self.dir.to_resolved_compiler_options(
&self.sys,
CompilerOptionsType::Check { lib },
)?;
check_warn_compiler_options(
&compiler_options_result,
&self.logged_warnings,
);
Ok(new_rc(compiler_options_result.compiler_options))
})
self.compiler_options_inner(CompilerOptionsType::Check { lib })
}
pub fn emit_compiler_options(
pub fn compiler_options_for_emit(
&self,
) -> Result<&CompilerOptionsRc, CompilerOptionsParseError> {
self.memoized.emit_compiler_options.get_or_try_init(|| {
let compiler_options_result = self
.dir
.to_resolved_compiler_options(&self.sys, CompilerOptionsType::Emit)?;
check_warn_compiler_options(
&compiler_options_result,
&self.logged_warnings,
);
Ok(new_rc(compiler_options_result.compiler_options))
self.compiler_options_inner(CompilerOptionsType::Emit)
}
fn compiler_options_inner(
&self,
typ: CompilerOptionsType,
) -> Result<&CompilerOptionsRc, CompilerOptionsParseError> {
let cell = match typ {
CompilerOptionsType::Bundle => unreachable!(),
CompilerOptionsType::Check {
lib: TsTypeLib::DenoWindow,
} => &self.memoized.deno_window_check_compiler_options,
CompilerOptionsType::Check {
lib: TsTypeLib::DenoWorker,
} => &self.memoized.deno_worker_check_compiler_options,
CompilerOptionsType::Emit => &self.memoized.emit_compiler_options,
};
cell.get_or_try_init(|| {
let mut result = CompilerOptionsWithIgnoredOptions {
compiler_options: get_base_compiler_options_for_emit(
typ,
self.source_kind,
),
ignored_options: Vec::new(),
};
for source in &self.sources {
let Some(compiler_options) = source.compiler_options.as_ref() else {
continue;
};
let object = serde_json::from_value(compiler_options.0.clone())
.map_err(|err| CompilerOptionsParseError {
specifier: source.specifier.clone(),
source: err,
})?;
let parsed = parse_compiler_options(object, Some(&source.specifier));
result.compiler_options.merge_object_mut(parsed.options);
if let Some(ignored) = parsed.maybe_ignored {
result.ignored_options.push(ignored);
}
}
if self.source_kind != CompilerOptionsSourceKind::TsConfig {
check_warn_compiler_options(&result, &self.logged_warnings);
}
Ok(new_rc(result.compiler_options))
})
}
@ -111,109 +171,601 @@ impl<TSys: FsRead> TsConfigFolderInfo<TSys> {
&self,
) -> Result<&TranspileAndEmitOptionsRc, CompilerOptionsParseError> {
self.memoized.transpile_options.get_or_try_init(|| {
let compiler_options = self.emit_compiler_options()?;
let compiler_options = self.compiler_options_for_emit()?;
compiler_options_to_transpile_and_emit_options(
compiler_options.as_ref().clone(),
)
.map(new_rc)
.map_err(|source| CompilerOptionsParseError {
specifier: self
.dir
.maybe_deno_json()
.map(|d| d.specifier.clone())
.unwrap_or_else(|| {
// will never happen because each dir should have a
// deno.json if we got here
debug_assert!(false);
self.dir.dir_url().as_ref().clone()
}),
specifier: self.sources.last().map(|s| s.specifier.clone()).expect(
"Compiler options parse errors must come from a user source.",
),
source,
})
})
}
pub fn compiler_options_types(&self) -> &CompilerOptionsTypesRc {
self.memoized.compiler_options_types.get_or_init(|| {
let types = self
.sources
.iter()
.filter_map(|s| {
let types = s
.compiler_options
.as_ref()?
.0
.as_object()?
.get("types")?
.as_array()?
.iter()
.filter_map(|v| Some(v.as_str()?.to_string()))
.collect();
Some((s.specifier.clone(), types))
})
.collect();
new_rc(types)
})
}
pub fn jsx_import_source_config(
&self,
) -> Result<Option<&JsxImportSourceConfigRc>, ToMaybeJsxImportSourceConfigError>
{
self.memoized.jsx_import_source_config.get_or_try_init(|| {
let jsx = self.sources.iter().rev().find_map(|s| Some((s.compiler_options.as_ref()?.0.as_object()?.get("jsx")?.as_str()?, &s.specifier)));
let is_jsx_automatic = matches!(
jsx,
Some(("react-jsx" | "preserve" | "react-jsxdev" | "precompile", _)),
);
let import_source = self.sources.iter().rev().find_map(|s| {
Some(JsxImportSourceSpecifierConfig {
specifier: s.compiler_options.as_ref()?.0.as_object()?.get("jsxImportSource")?.as_str()?.to_string(),
base: s.specifier.clone()
})
}).or_else(|| {
if !is_jsx_automatic {
return None;
}
Some(JsxImportSourceSpecifierConfig {
base: self.sources.last()?.specifier.clone(),
specifier: "react".to_string()
})
});
let import_source_types = self.sources.iter().rev().find_map(|s| {
Some(JsxImportSourceSpecifierConfig {
specifier: s.compiler_options.as_ref()?.0.as_object()?.get("jsxImportSourceTypes")?.as_str()?.to_string(),
base: s.specifier.clone()
})
}).or_else(|| import_source.clone());
let module = match jsx {
Some(("react-jsx" | "preserve", _)) => "jsx-runtime".to_string(),
Some(("react-jsxdev", _)) => "jsx-dev-runtime".to_string(),
Some(("react", _)) | None => {
if let Some(import_source) = &import_source {
return Err(
ToMaybeJsxImportSourceConfigError::InvalidJsxImportSourceValue(
import_source.base.clone(),
),
);
}
if let Some(import_source_types) = &import_source_types {
return Err(
ToMaybeJsxImportSourceConfigError::InvalidJsxImportSourceTypesValue(
import_source_types.base.clone(),
),
);
}
return Ok(None);
}
Some(("precompile", _)) => "jsx-runtime".to_string(),
Some((setting, setting_source)) => {
return Err(
ToMaybeJsxImportSourceConfigError::InvalidJsxCompilerOption {
value: setting.to_string(),
specifier: setting_source.clone(),
},
)
}
};
Ok(Some(new_rc(JsxImportSourceConfig {
module,
import_source,
import_source_types,
})))
}).map(|c| c.as_ref())
}
pub fn check_js(&self) -> bool {
*self.memoized.check_js.get_or_init(|| {
self
.sources
.iter()
.rev()
.find_map(|s| {
s.compiler_options
.as_ref()?
.0
.as_object()?
.get("checkJs")?
.as_bool()
})
.unwrap_or(false)
})
}
}
// A resolved element of the `files` array in a tsconfig.
#[derive(Debug, Clone)]
pub struct TsConfigFile {
pub relative_specifier: String,
pub absolute_path: PathBuf,
}
impl TsConfigFile {
fn from_raw(raw: &str, dir_path: impl AsRef<Path>) -> Self {
let relative_specifier = if raw.starts_with("./")
|| raw.starts_with("../")
|| raw.starts_with('/')
{
raw.to_string()
} else {
format!("./{raw}")
};
let path = Path::new(raw);
let absolute_path = if path.is_absolute() {
normalize_path(path)
} else {
normalize_path(dir_path.as_ref().join(path))
};
Self {
relative_specifier,
absolute_path,
}
}
}
#[derive(Debug)]
struct TsConfigFileFilter {
// Note that `files`, `include` and `exclude` are overwritten, not merged,
// when using `extends`. So we only need to store one referrer for `files`.
// See: https://www.typescriptlang.org/tsconfig/#extends.
files: Option<(Url, Vec<TsConfigFile>)>,
include: Option<PathOrPatternSet>,
exclude: Option<PathOrPatternSet>,
dir_path: PathBuf,
}
impl TsConfigFileFilter {
fn includes_path(&self, path: impl AsRef<Path>) -> bool {
let path = path.as_ref();
if let Some((_, files)) = &self.files {
if files.iter().any(|f| f.absolute_path == path) {
return true;
}
}
if let Some(exclude) = &self.exclude {
if exclude.matches_path(path) {
return false;
}
}
if let Some(include) = &self.include {
if include.matches_path(path) {
return true;
}
} else if path.starts_with(&self.dir_path) {
return true;
}
false
}
}
#[allow(clippy::disallowed_types)]
type TsConfigFileFilterRc = crate::sync::MaybeArc<TsConfigFileFilter>;
#[derive(Debug)]
pub struct TsConfigData {
pub compiler_options: CompilerOptionsData,
filter: TsConfigFileFilterRc,
references: Vec<String>,
}
impl TsConfigData {
pub fn files(&self) -> Option<(&Url, &Vec<TsConfigFile>)> {
let (referrer, files) = self.filter.files.as_ref()?;
Some((referrer, files))
}
fn specifier(&self) -> &Url {
&self
.compiler_options
.sources
.last()
.expect("Tsconfigs should always have at least one source.")
.specifier
}
}
fn is_maybe_directory_error(err: &std::io::Error) -> bool {
let kind = err.kind();
kind == ErrorKind::IsADirectory
// This happens on Windows for some reason.
|| cfg!(windows) && kind == ErrorKind::PermissionDenied
}
type TsConfigNodeResolver<TSys> = NodeResolver<
DenoInNpmPackageChecker,
DenoIsBuiltInNodeModuleChecker,
NpmResolver<TSys>,
TSys,
>;
#[derive(Debug)]
struct TsConfigCollector<'a, TSys: FsRead, NSys: NpmResolverSys> {
roots: BTreeSet<PathBuf>,
collected: IndexMap<Url, Rc<TsConfigData>>,
read_cache: HashMap<PathBuf, Result<Rc<TsConfigData>, Rc<std::io::Error>>>,
currently_reading: IndexSet<PathBuf>,
sys: &'a TSys,
node_resolver: &'a TsConfigNodeResolver<NSys>,
logged_warnings: &'a LoggedWarningsRc,
}
impl<'a, TSys: FsRead, NSys: NpmResolverSys> TsConfigCollector<'a, TSys, NSys> {
fn new(
sys: &'a TSys,
node_resolver: &'a TsConfigNodeResolver<NSys>,
logged_warnings: &'a LoggedWarningsRc,
) -> Self {
Self {
roots: Default::default(),
collected: Default::default(),
read_cache: Default::default(),
currently_reading: Default::default(),
sys,
node_resolver,
logged_warnings,
}
}
fn add_root(&mut self, path: PathBuf) {
self.roots.insert(path);
}
fn collect(mut self) -> Vec<TsConfigData> {
for root in std::mem::take(&mut self.roots) {
let Ok(ts_config) = self.read_ts_config_with_cache(root) else {
continue;
};
self.visit_reference(ts_config);
}
let Self { collected, .. } = { self };
collected
.into_values()
.map(|t| {
Rc::try_unwrap(t).expect(
"No other references should be held since the read cache is dropped.",
)
})
.collect()
}
fn visit_reference(&mut self, ts_config: Rc<TsConfigData>) {
let specifier = ts_config.specifier();
if self.collected.contains_key(specifier) {
return;
}
let Some(dir_path) = url_to_file_path(specifier)
.ok()
.and_then(|p| Some(p.parent()?.to_path_buf()))
else {
return;
};
for reference in &ts_config.references {
let reference_path = Path::new(reference);
let reference_path = if reference_path.is_absolute() {
Cow::Borrowed(reference_path)
} else {
Cow::Owned(dir_path.join(reference_path))
};
match self.read_ts_config_with_cache(&reference_path) {
Ok(ts_config) => self.visit_reference(ts_config),
Err(err) if is_maybe_directory_error(&err) => {
if let Ok(ts_config) =
self.read_ts_config_with_cache(reference_path.join("tsconfig.json"))
{
self.visit_reference(ts_config)
}
}
_ => {}
}
}
self.collected.insert(specifier.clone(), ts_config);
}
fn read_ts_config_with_cache(
&mut self,
path: impl AsRef<Path>,
) -> Result<Rc<TsConfigData>, Rc<std::io::Error>> {
let path = normalize_path(path.as_ref());
self.read_cache.get(&path).cloned().unwrap_or_else(|| {
if !self.currently_reading.insert(path.clone()) {
return Err(Rc::new(std::io::Error::new(
ErrorKind::Other,
"Cycle detected while following `extends`.",
)));
}
let result = self.read_ts_config(&path).map(Rc::new).map_err(Rc::new);
self.currently_reading.pop();
self.read_cache.insert(path, result.clone());
result
})
}
fn read_ts_config(
&mut self,
path: impl AsRef<Path>,
) -> Result<TsConfigData, std::io::Error> {
let path = path.as_ref();
let warn = |err: &dyn std::fmt::Display| {
log::warn!("Failed reading {}: {}", path.display(), err);
};
let specifier = url_from_file_path(path)
.inspect_err(|e| warn(e))
.map_err(|err| std::io::Error::new(ErrorKind::InvalidInput, err))?;
let text = self.sys.fs_read_to_string(path).inspect_err(|e| {
if e.kind() != ErrorKind::NotFound && !is_maybe_directory_error(e) {
warn(e)
}
})?;
let value = jsonc_parser::parse_to_serde_value(&text, &Default::default())
.inspect_err(|e| warn(e))
.ok()
.flatten();
let object = value.as_ref().and_then(|v| v.as_object());
let extends_targets = object
.and_then(|o| o.get("extends"))
.into_iter()
.flat_map(|v| {
if let Some(s) = v.as_str() {
vec![s]
} else if let Some(a) = v.as_array() {
a.iter().filter_map(|v| v.as_str()).collect()
} else {
Vec::new()
}
})
.filter_map(|s| {
let node_resolution = self
.node_resolver
.resolve(
s,
&specifier,
ResolutionMode::Require,
NodeResolutionKind::Execution,
)
.ok()?;
let url = node_resolution.into_url().ok()?;
let path = url_to_file_path(&url).ok()?;
self.read_ts_config_with_cache(&path).ok()
})
.collect::<Vec<_>>();
let sources = extends_targets
.iter()
.flat_map(|t| &t.compiler_options.sources)
.cloned()
.chain([CompilerOptionsSource {
specifier: specifier.clone(),
compiler_options: object
.and_then(|o| o.get("compilerOptions"))
.filter(|v| !v.is_null())
.cloned()
.map(CompilerOptions),
}])
.collect();
let dir_path = path.parent().expect("file path should have a parent");
let files = object
.and_then(|o| {
let files = o
.get("files")?
.as_array()?
.iter()
.filter_map(|v| Some(TsConfigFile::from_raw(v.as_str()?, dir_path)))
.collect();
Some((specifier, files))
})
.or_else(|| {
extends_targets
.iter()
.rev()
.find_map(|t| t.filter.files.clone())
});
let include = object
.and_then(|o| {
PathOrPatternSet::from_include_relative_path_or_patterns(
dir_path,
&o.get("include")?
.as_array()?
.iter()
.filter_map(|v| Some(v.as_str()?.to_string()))
.collect::<Vec<_>>(),
)
.ok()
})
.or_else(|| {
extends_targets
.iter()
.rev()
.find_map(|t| t.filter.include.clone())
})
.or_else(|| files.is_some().then(Default::default));
let exclude = object
.and_then(|o| {
PathOrPatternSet::from_exclude_relative_path_or_patterns(
dir_path,
&o.get("exclude")?
.as_array()?
.iter()
.filter_map(|v| Some(v.as_str()?.to_string()))
.collect::<Vec<_>>(),
)
.ok()
})
.or_else(|| {
extends_targets
.iter()
.rev()
.find_map(|t| t.filter.exclude.clone())
});
let references = object
.and_then(|o| o.get("references")?.as_array())
.into_iter()
.flatten()
.filter_map(|v| Some(v.as_object()?.get("path")?.as_str()?.to_string()))
.collect();
Ok(TsConfigData {
compiler_options: CompilerOptionsData::new(
sources,
CompilerOptionsSourceKind::TsConfig,
self.logged_warnings.clone(),
),
filter: new_rc(TsConfigFileFilter {
files,
include,
exclude,
dir_path: dir_path.to_path_buf(),
}),
references,
})
}
}
#[derive(Debug)]
pub struct TsConfigResolver<TSys: FsRead> {
map: FolderScopedMap<TsConfigFolderInfo<TSys>>,
pub struct CompilerOptionsResolver {
workspace_configs: FolderScopedMap<CompilerOptionsData>,
ts_configs: Vec<TsConfigData>,
}
impl<TSys: FsRead + Clone> TsConfigResolver<TSys> {
pub fn from_workspace(sys: &TSys, workspace: &WorkspaceRc) -> Self {
// separate the workspace into directories that have compiler options
let root_dir = workspace.resolve_member_dir(workspace.root_dir());
impl CompilerOptionsResolver {
pub fn new<TSys: FsRead, NSys: NpmResolverSys>(
sys: &TSys,
workspace_directory_provider: &WorkspaceDirectoryProvider,
node_resolver: &TsConfigNodeResolver<NSys>,
) -> Self {
let logged_warnings = new_rc(LoggedWarnings::default());
let mut map = FolderScopedMap::new(TsConfigFolderInfo {
dir: root_dir,
logged_warnings: logged_warnings.clone(),
memoized: Default::default(),
sys: sys.clone(),
});
for (url, folder) in workspace.config_folders() {
let folder_has_compiler_options = folder
.deno_json
.as_ref()
.map(|d| d.json.compiler_options.is_some())
.unwrap_or(false);
if url != workspace.root_dir() && folder_has_compiler_options {
let dir = workspace.resolve_member_dir(url);
map.insert(
url.clone(),
TsConfigFolderInfo {
dir,
logged_warnings: logged_warnings.clone(),
memoized: Default::default(),
sys: sys.clone(),
},
let root_dir = workspace_directory_provider.root();
let mut workspace_configs = FolderScopedMap::new(CompilerOptionsData::new(
root_dir.to_configured_compiler_options_sources(),
CompilerOptionsSourceKind::DenoJson,
logged_warnings.clone(),
));
let mut ts_config_collector =
TsConfigCollector::new(sys, node_resolver, &logged_warnings);
for (dir_url, dir) in workspace_directory_provider.entries() {
ts_config_collector.add_root(dir.dir_path().join("tsconfig.json"));
if let Some(dir_url) = dir_url {
workspace_configs.insert(
dir_url.clone(),
CompilerOptionsData::new(
dir.to_configured_compiler_options_sources(),
CompilerOptionsSourceKind::DenoJson,
logged_warnings.clone(),
),
);
}
}
Self { map }
Self {
workspace_configs,
ts_configs: ts_config_collector.collect(),
}
}
impl<TSys: FsRead> TsConfigResolver<TSys> {
pub fn check_js_for_specifier(&self, specifier: &Url) -> bool {
self.folder_for_specifier(specifier).dir.check_js()
}
#[cfg(feature = "deno_ast")]
pub fn transpile_and_emit_options(
&self,
specifier: &Url,
) -> Result<&TranspileAndEmitOptionsRc, CompilerOptionsParseError> {
let value = self.map.get_for_specifier(specifier);
value.transpile_options()
pub fn for_specifier(&self, specifier: &Url) -> &CompilerOptionsData {
if let Ok(path) = url_to_file_path(specifier) {
for ts_config in &self.ts_configs {
if ts_config.filter.includes_path(&path) {
return &ts_config.compiler_options;
}
}
}
self.workspace_configs.get_for_specifier(specifier)
}
pub fn folder_for_specifier(
&self,
specifier: &Url,
) -> &TsConfigFolderInfo<TSys> {
self.folder_for_specifier_str(specifier.as_str())
pub fn all(&self) -> impl Iterator<Item = &CompilerOptionsData> {
self
.workspace_configs
.entries()
.map(|(_, r)| r)
.chain(self.ts_configs.iter().map(|t| &t.compiler_options))
}
pub fn folder_for_specifier_str(
&self,
specifier: &str,
) -> &TsConfigFolderInfo<TSys> {
self.map.get_for_specifier_str(specifier)
pub fn size(&self) -> usize {
self.workspace_configs.count() + self.ts_configs.len()
}
pub fn folder_count(&self) -> usize {
self.map.count()
pub fn ts_configs(&self) -> &Vec<TsConfigData> {
&self.ts_configs
}
}
#[cfg(feature = "graph")]
impl<TSys: FsRead + std::fmt::Debug> deno_graph::CheckJsResolver
for TsConfigResolver<TSys>
{
fn resolve(&self, specifier: &deno_graph::ModuleSpecifier) -> bool {
self.check_js_for_specifier(specifier)
impl deno_graph::CheckJsResolver for CompilerOptionsResolver {
fn resolve(&self, specifier: &Url) -> bool {
self.for_specifier(specifier).check_js()
}
}
#[allow(clippy::disallowed_types)]
pub type CompilerOptionsResolverRc =
crate::sync::MaybeArc<CompilerOptionsResolver>;
/// JSX config stored in `CompilerOptionsResolver`, but fallibly resolved
/// ahead of time as needed for the graph resolver.
#[derive(Debug)]
pub struct JsxImportSourceConfigResolver {
workspace_configs: FolderScopedMap<Option<JsxImportSourceConfigRc>>,
ts_configs: Vec<(Option<JsxImportSourceConfigRc>, TsConfigFileFilterRc)>,
}
impl JsxImportSourceConfigResolver {
pub fn from_compiler_options_resolver(
compiler_options_resolver: &CompilerOptionsResolver,
) -> Result<Self, ToMaybeJsxImportSourceConfigError> {
Ok(Self {
workspace_configs: compiler_options_resolver
.workspace_configs
.try_map(|d| Ok(d.jsx_import_source_config()?.cloned()))?,
ts_configs: compiler_options_resolver
.ts_configs
.iter()
.map(|t| {
Ok((
t.compiler_options.jsx_import_source_config()?.cloned(),
t.filter.clone(),
))
})
.collect::<Result<_, _>>()?,
})
}
pub fn for_specifier(
&self,
specifier: &Url,
) -> Option<&JsxImportSourceConfigRc> {
if let Ok(path) = url_to_file_path(specifier) {
for (config, filter) in &self.ts_configs {
if filter.includes_path(&path) {
return config.as_ref();
}
}
}
self.workspace_configs.get_for_specifier(specifier).as_ref()
}
}
#[allow(clippy::disallowed_types)]
pub type JsxImportSourceConfigRc = crate::sync::MaybeArc<JsxImportSourceConfig>;
#[cfg(feature = "deno_ast")]
fn compiler_options_to_transpile_and_emit_options(
config: deno_config::deno_json::CompilerOptions,

View file

@ -41,8 +41,9 @@ use url::Url;
use crate::cjs::CjsTracker;
use crate::cjs::CjsTrackerRc;
use crate::cjs::IsCjsResolutionMode;
use crate::deno_json::TsConfigResolver;
use crate::deno_json::TsConfigResolverRc;
use crate::collections::FolderScopedMap;
use crate::deno_json::CompilerOptionsResolver;
use crate::deno_json::CompilerOptionsResolverRc;
use crate::import_map::WorkspaceExternalImportMapLoader;
use crate::import_map::WorkspaceExternalImportMapLoaderRc;
use crate::lockfile::LockfileLock;
@ -83,6 +84,8 @@ type Deferred<T> = once_cell::sync::OnceCell<T>;
#[cfg(not(feature = "sync"))]
type Deferred<T> = once_cell::unsync::OnceCell<T>;
#[allow(clippy::disallowed_types)]
type UrlRc = crate::sync::MaybeArc<Url>;
#[allow(clippy::disallowed_types)]
pub type WorkspaceDirectoryRc = crate::sync::MaybeArc<WorkspaceDirectory>;
#[allow(clippy::disallowed_types)]
@ -254,8 +257,8 @@ pub struct WorkspaceFactory<TSys: WorkspaceFactorySys> {
npm_cache_dir: Deferred<NpmCacheDirRc>,
npmrc: Deferred<(ResolvedNpmRcRc, Option<PathBuf>)>,
node_modules_dir_mode: Deferred<NodeModulesDirMode>,
tsconfig_resolver: Deferred<TsConfigResolverRc<TSys>>,
workspace_directory: Deferred<WorkspaceDirectoryRc>,
workspace_directory_provider: Deferred<WorkspaceDirectoryProviderRc>,
workspace_external_import_map_loader:
Deferred<WorkspaceExternalImportMapLoaderRc<TSys>>,
workspace_npm_link_packages: Deferred<WorkspaceNpmLinkPackagesRc>,
@ -289,8 +292,8 @@ impl<TSys: WorkspaceFactorySys> WorkspaceFactory<TSys> {
npm_cache_dir: Default::default(),
npmrc: Default::default(),
node_modules_dir_mode: Default::default(),
tsconfig_resolver: Default::default(),
workspace_directory: Default::default(),
workspace_directory_provider: Default::default(),
workspace_external_import_map_loader: Default::default(),
workspace_npm_link_packages: Default::default(),
initial_cwd,
@ -536,17 +539,6 @@ impl<TSys: WorkspaceFactorySys> WorkspaceFactory<TSys> {
})
}
pub fn tsconfig_resolver(
&self,
) -> Result<&TsConfigResolverRc<TSys>, WorkspaceDiscoverError> {
self.tsconfig_resolver.get_or_try_init(|| {
Ok(new_rc(TsConfigResolver::from_workspace(
self.sys(),
&self.workspace_directory()?.workspace,
)))
})
}
pub fn sys(&self) -> &TSys {
&self.sys
}
@ -615,6 +607,16 @@ impl<TSys: WorkspaceFactorySys> WorkspaceFactory<TSys> {
})
}
pub fn workspace_directory_provider(
&self,
) -> Result<&WorkspaceDirectoryProviderRc, WorkspaceDiscoverError> {
self.workspace_directory_provider.get_or_try_init(|| {
Ok(new_rc(WorkspaceDirectoryProvider::from_initial_dir(
self.workspace_directory()?,
)))
})
}
pub fn workspace_external_import_map_loader(
&self,
) -> Result<&WorkspaceExternalImportMapLoaderRc<TSys>, WorkspaceDiscoverError>
@ -677,6 +679,7 @@ pub struct ResolverFactory<TSys: WorkspaceFactorySys> {
options: ResolverFactoryOptions,
sys: NodeResolutionSys<TSys>,
cjs_tracker: Deferred<CjsTrackerRc<DenoInNpmPackageChecker, TSys>>,
compiler_options_resolver: Deferred<CompilerOptionsResolverRc>,
#[cfg(feature = "graph")]
deno_resolver:
async_once_cell::OnceCell<crate::graph::DefaultDenoResolverRc<TSys>>,
@ -718,6 +721,7 @@ impl<TSys: WorkspaceFactorySys> ResolverFactory<TSys> {
options.node_resolution_cache.clone(),
),
cjs_tracker: Default::default(),
compiler_options_resolver: Default::default(),
raw_deno_resolver: Default::default(),
#[cfg(feature = "graph")]
deno_resolver: Default::default(),
@ -781,6 +785,18 @@ impl<TSys: WorkspaceFactorySys> ResolverFactory<TSys> {
})
}
pub fn compiler_options_resolver(
&self,
) -> Result<&CompilerOptionsResolverRc, anyhow::Error> {
self.compiler_options_resolver.get_or_try_init(|| {
Ok(new_rc(CompilerOptionsResolver::new(
&self.sys,
self.workspace_factory.workspace_directory_provider()?,
self.node_resolver()?,
)))
})
}
#[cfg(feature = "graph")]
pub fn found_package_json_dep_flag(
&self,
@ -1012,3 +1028,64 @@ impl<TSys: WorkspaceFactorySys> ResolverFactory<TSys> {
)
}
}
#[derive(Debug)]
pub struct WorkspaceDirectoryProvider {
pub workspace: WorkspaceRc,
dirs: FolderScopedMap<Deferred<WorkspaceDirectoryRc>>,
}
impl WorkspaceDirectoryProvider {
pub fn from_initial_dir(dir: &WorkspaceDirectoryRc) -> Self {
let workspace = dir.workspace.clone();
let mut dirs = FolderScopedMap::new(Deferred::default());
for dir_url in workspace.config_folders().keys() {
if dir_url == workspace.root_dir() {
continue;
} else if dir_url == dir.dir_url() {
dirs.insert(dir_url.clone(), Deferred::from(dir.clone()));
} else {
dirs.insert(dir_url.clone(), Deferred::default());
}
}
Self { workspace, dirs }
}
pub fn for_specifier(&self, specifier: &Url) -> &WorkspaceDirectoryRc {
let (dir_url, dir) = self.dirs.entry_for_specifier(specifier);
dir.get_or_init(|| {
new_rc(
self
.workspace
.resolve_member_dir(dir_url.unwrap_or(self.workspace.root_dir())),
)
})
}
pub fn root(&self) -> &WorkspaceDirectoryRc {
self.dirs.unscoped.get_or_init(|| {
new_rc(self.workspace.resolve_member_dir(self.workspace.root_dir()))
})
}
pub fn entries(
&self,
) -> impl Iterator<Item = (Option<&UrlRc>, &WorkspaceDirectoryRc)> {
self.dirs.entries().map(|(s, d)| {
(
s,
d.get_or_init(|| {
new_rc(
self
.workspace
.resolve_member_dir(s.unwrap_or(self.workspace.root_dir())),
)
}),
)
})
}
}
#[allow(clippy::disallowed_types)]
pub type WorkspaceDirectoryProviderRc =
crate::sync::MaybeArc<WorkspaceDirectoryProvider>;

View file

@ -18,9 +18,9 @@ use node_resolver::UrlOrPath;
use url::Url;
use crate::cjs::CjsTracker;
use crate::deno_json::JsxImportSourceConfigResolver;
use crate::npm;
use crate::workspace::MappedResolutionDiagnostic;
use crate::workspace::ScopedJsxImportSourceConfig;
use crate::DenoResolveError;
use crate::DenoResolverSys;
use crate::RawDenoResolverRc;
@ -405,7 +405,7 @@ impl<
pub fn as_graph_resolver<'a>(
&'a self,
cjs_tracker: &'a CjsTracker<TInNpmPackageChecker, TSys>,
scoped_jsx_import_source_config: &'a ScopedJsxImportSourceConfig,
jsx_import_source_config_resolver: &'a JsxImportSourceConfigResolver,
) -> DenoGraphResolverAdapter<
'a,
TInNpmPackageChecker,
@ -416,7 +416,7 @@ impl<
DenoGraphResolverAdapter {
cjs_tracker,
resolver: self,
scoped_jsx_import_source_config,
jsx_import_source_config_resolver,
}
}
}
@ -435,7 +435,7 @@ pub struct DenoGraphResolverAdapter<
TNpmPackageFolderResolver,
TSys,
>,
scoped_jsx_import_source_config: &'a ScopedJsxImportSourceConfig,
jsx_import_source_config_resolver: &'a JsxImportSourceConfigResolver,
}
impl<
@ -473,22 +473,22 @@ impl<
{
fn default_jsx_import_source(&self, referrer: &Url) -> Option<String> {
self
.scoped_jsx_import_source_config
.resolve_by_referrer(referrer)
.jsx_import_source_config_resolver
.for_specifier(referrer)
.and_then(|c| c.import_source.as_ref().map(|s| s.specifier.clone()))
}
fn default_jsx_import_source_types(&self, referrer: &Url) -> Option<String> {
self
.scoped_jsx_import_source_config
.resolve_by_referrer(referrer)
.jsx_import_source_config_resolver
.for_specifier(referrer)
.and_then(|c| c.import_source_types.as_ref().map(|s| s.specifier.clone()))
}
fn jsx_import_source_module(&self, referrer: &Url) -> &str {
self
.scoped_jsx_import_source_config
.resolve_by_referrer(referrer)
.jsx_import_source_config_resolver
.for_specifier(referrer)
.map(|c| c.module.as_str())
.unwrap_or(deno_graph::source::DEFAULT_JSX_IMPORT_SOURCE_MODULE)
}

View file

@ -11,11 +11,8 @@ use std::path::PathBuf;
use deno_config::deno_json::ConfigFile;
use deno_config::deno_json::ConfigFileError;
use deno_config::workspace::JsxImportSourceConfig;
use deno_config::workspace::ResolverWorkspaceJsrPackage;
use deno_config::workspace::ToMaybeJsxImportSourceConfigError;
use deno_config::workspace::Workspace;
use deno_config::workspace::WorkspaceDirectory;
use deno_error::JsError;
use deno_media_type::MediaType;
use deno_npm::registry::NpmPackageVersionInfo;
@ -1643,41 +1640,6 @@ impl BaseUrl<'_> {
}
}
#[derive(Debug, Clone)]
pub struct ScopedJsxImportSourceConfig {
unscoped: Option<JsxImportSourceConfig>,
by_scope: BTreeMap<UrlRc, Option<JsxImportSourceConfig>>,
}
impl ScopedJsxImportSourceConfig {
pub fn from_workspace_dir(
start_dir: &WorkspaceDirectory,
) -> Result<Self, ToMaybeJsxImportSourceConfigError> {
let unscoped = start_dir.to_maybe_jsx_import_source_config()?;
let mut by_scope = BTreeMap::default();
for (dir_url, _) in start_dir.workspace.config_folders() {
let dir = start_dir.workspace.resolve_member_dir(dir_url);
let jsx_import_source_config_unscoped =
dir.to_maybe_jsx_import_source_config()?;
by_scope.insert(dir_url.clone(), jsx_import_source_config_unscoped);
}
Ok(Self { unscoped, by_scope })
}
/// Resolves the `JsxImportSourceConfig` to use for the provided referrer.
pub fn resolve_by_referrer(
&self,
referrer: &Url,
) -> Option<&JsxImportSourceConfig> {
self
.by_scope
.iter()
.rfind(|(s, _)| referrer.as_str().starts_with(s.as_str()))
.map(|(_, c)| c.as_ref())
.unwrap_or(self.unscoped.as_ref())
}
}
#[allow(clippy::disallowed_types)] // ok, because definition
pub type WorkspaceNpmLinkPackagesRc =
crate::sync::MaybeArc<WorkspaceNpmLinkPackages>;

View file

@ -0,0 +1,4 @@
{
"args": "check --quiet",
"output": ""
}

View file

@ -0,0 +1 @@
{}

View file

@ -0,0 +1,2 @@
Deno.readTextFileSync("hello.txt");
document.body.innerHTML = "<div>Hello</div>";

View file

@ -0,0 +1 @@
{}

View file

@ -0,0 +1,5 @@
{
"args": "check --quiet",
"output": "main.out",
"exitCode": 1
}

View file

@ -0,0 +1 @@
{}

View file

@ -0,0 +1,2 @@
Deno.readTextFileSync("hello.txt");
document.body.innerHTML = "<div>Hello</div>";

View file

@ -0,0 +1,2 @@
Deno.readTextFileSync("hello.txt");
document.body.innerHTML = "<div>Hello</div>";

View file

@ -0,0 +1,11 @@
TS2584 [ERROR]: Cannot find name 'document'. Do you need to change your target library? Try changing the 'lib' compiler option to include 'dom'.
document.body.innerHTML = "<div>Hello</div>";
~~~~~~~~
at file:///[WILDLINE]main.deno.ts:2:1
TS2304 [ERROR]: Cannot find name 'Deno'. Do you need to change your target library? Try changing the 'lib' compiler option to include 'deno.ns' or add a triple-slash directive to the top of your entrypoint (main file): /// <reference lib="deno.ns" />
Deno.readTextFileSync("hello.txt");
~~~~
at file:///[WILDLINE]main.dom.ts:1:1
error: Type checking failed.

View file

@ -0,0 +1,6 @@
{
"exclude": ["*.deno.ts"],
"compilerOptions": {
"lib": ["esnext", "dom"]
}
}

View file

@ -0,0 +1,5 @@
{
"args": "check --quiet",
"output": "main.out",
"exitCode": 1
}

View file

@ -0,0 +1 @@
{}

View file

@ -0,0 +1,2 @@
Deno.readTextFileSync("hello.txt");
document.body.innerHTML = "<div>Hello</div>";

View file

@ -0,0 +1,2 @@
Deno.readTextFileSync("hello.txt");
document.body.innerHTML = "<div>Hello</div>";

View file

@ -0,0 +1,11 @@
TS2584 [ERROR]: Cannot find name 'document'. Do you need to change your target library? Try changing the 'lib' compiler option to include 'dom'.
document.body.innerHTML = "<div>Hello</div>";
~~~~~~~~
at file:///[WILDLINE]main.deno.ts:2:1
TS2304 [ERROR]: Cannot find name 'Deno'. Do you need to change your target library? Try changing the 'lib' compiler option to include 'deno.ns' or add a triple-slash directive to the top of your entrypoint (main file): /// <reference lib="deno.ns" />
Deno.readTextFileSync("hello.txt");
~~~~
at file:///[WILDLINE]main.dom.ts:1:1
error: Type checking failed.

View file

@ -0,0 +1,5 @@
{
"compilerOptions": {
"lib": ["esnext", "dom"]
}
}

View file

@ -0,0 +1,4 @@
{
"extends": "./tsconfig.dom.json",
"files": ["main.dom.ts"]
}

View file

@ -0,0 +1,5 @@
{
"args": "check --quiet",
"output": "main.out",
"exitCode": 1
}

View file

@ -0,0 +1 @@
{}

View file

@ -0,0 +1,2 @@
Deno.readTextFileSync("hello.txt");
document.body.innerHTML = "<div>Hello</div>";

View file

@ -0,0 +1,2 @@
Deno.readTextFileSync("hello.txt");
document.body.innerHTML = "<div>Hello</div>";

View file

@ -0,0 +1,11 @@
TS2584 [ERROR]: Cannot find name 'document'. Do you need to change your target library? Try changing the 'lib' compiler option to include 'dom'.
document.body.innerHTML = "<div>Hello</div>";
~~~~~~~~
at file:///[WILDLINE]main.deno.ts:2:1
TS2304 [ERROR]: Cannot find name 'Deno'. Do you need to change your target library? Try changing the 'lib' compiler option to include 'deno.ns' or add a triple-slash directive to the top of your entrypoint (main file): /// <reference lib="deno.ns" />
Deno.readTextFileSync("hello.txt");
~~~~
at file:///[WILDLINE]main.dom.ts:1:1
error: Type checking failed.

View file

@ -0,0 +1,5 @@
{
"compilerOptions": {
"lib": ["esnext", "dom"]
}
}

View file

@ -0,0 +1,6 @@
{
"files": ["main.dom.ts"],
"compilerOptions": {
"lib": []
}
}

View file

@ -0,0 +1,3 @@
{
"extends": ["./tsconfig.dom_files.json", "./tsconfig.dom.json"]
}

View file

@ -0,0 +1,5 @@
{
"args": "check --quiet",
"output": "main.out",
"exitCode": 1
}

View file

@ -0,0 +1,3 @@
{
"nodeModulesDir": "manual"
}

View file

@ -0,0 +1,2 @@
Deno.readTextFileSync("hello.txt");
document.body.innerHTML = "<div>Hello</div>";

View file

@ -0,0 +1,2 @@
Deno.readTextFileSync("hello.txt");
document.body.innerHTML = "<div>Hello</div>";

View file

@ -0,0 +1,11 @@
TS2584 [ERROR]: Cannot find name 'document'. Do you need to change your target library? Try changing the 'lib' compiler option to include 'dom'.
document.body.innerHTML = "<div>Hello</div>";
~~~~~~~~
at file:///[WILDLINE]main.deno.ts:2:1
TS2304 [ERROR]: Cannot find name 'Deno'. Do you need to change your target library? Try changing the 'lib' compiler option to include 'deno.ns' or add a triple-slash directive to the top of your entrypoint (main file): /// <reference lib="deno.ns" />
Deno.readTextFileSync("hello.txt");
~~~~
at file:///[WILDLINE]main.dom.ts:1:1
error: Type checking failed.

View file

@ -0,0 +1,7 @@
{
"exports": {
"./tsconfig.dom": {
"require": "./tsconfig.dom.json"
}
}
}

View file

@ -0,0 +1,5 @@
{
"compilerOptions": {
"lib": ["esnext", "dom"]
}
}

View file

@ -0,0 +1,4 @@
{
"extends": "foo/tsconfig.dom",
"files": ["main.dom.ts"]
}

View file

@ -0,0 +1,5 @@
{
"args": "check --quiet",
"output": "main.out",
"exitCode": 1
}

View file

@ -0,0 +1 @@
{}

View file

@ -0,0 +1,2 @@
Deno.readTextFileSync("hello.txt");
document.body.innerHTML = "<div>Hello</div>";

View file

@ -0,0 +1,2 @@
Deno.readTextFileSync("hello.txt");
document.body.innerHTML = "<div>Hello</div>";

View file

@ -0,0 +1,11 @@
TS2584 [ERROR]: Cannot find name 'document'. Do you need to change your target library? Try changing the 'lib' compiler option to include 'dom'.
document.body.innerHTML = "<div>Hello</div>";
~~~~~~~~
at file:///[WILDLINE]main.deno.ts:2:1
TS2304 [ERROR]: Cannot find name 'Deno'. Do you need to change your target library? Try changing the 'lib' compiler option to include 'deno.ns' or add a triple-slash directive to the top of your entrypoint (main file): /// <reference lib="deno.ns" />
Deno.readTextFileSync("hello.txt");
~~~~
at file:///[WILDLINE]main.dom.ts:1:1
error: Type checking failed.

View file

@ -0,0 +1,6 @@
{
"files": ["main.dom.ts"],
"compilerOptions": {
"lib": ["esnext", "dom"]
}
}

View file

@ -0,0 +1,5 @@
{
"args": "check main.deno.ts main.dom.ts --quiet",
"output": "main.out",
"exitCode": 1
}

View file

@ -0,0 +1 @@
{}

View file

@ -0,0 +1,3 @@
/// <reference no-default-lib="true"/>
/// <reference lib="esnext" />
/// <reference lib="dom" />

View file

@ -0,0 +1,2 @@
Deno.readTextFileSync("hello.txt");
document.body.innerHTML = "<div>Hello</div>";

View file

@ -0,0 +1,2 @@
Deno.readTextFileSync("hello.txt");
document.body.innerHTML = "<div>Hello</div>";

View file

@ -0,0 +1,11 @@
TS2584 [ERROR]: Cannot find name 'document'. Do you need to change your target library? Try changing the 'lib' compiler option to include 'dom'.
document.body.innerHTML = "<div>Hello</div>";
~~~~~~~~
at file:///[WILDLINE]main.deno.ts:2:1
TS2304 [ERROR]: Cannot find name 'Deno'. Do you need to change your target library? Try changing the 'lib' compiler option to include 'deno.ns' or add a triple-slash directive to the top of your entrypoint (main file): /// <reference lib="deno.ns" />
Deno.readTextFileSync("hello.txt");
~~~~
at file:///[WILDLINE]main.dom.ts:1:1
error: Type checking failed.

View file

@ -0,0 +1,3 @@
{
"files": ["main.dom.ts", "globals.d.ts"]
}

View file

@ -0,0 +1,5 @@
{
"args": "check --quiet",
"output": "main.out",
"exitCode": 1
}

View file

@ -0,0 +1 @@
{}

View file

@ -0,0 +1,2 @@
Deno.readTextFileSync("hello.txt");
document.body.innerHTML = "<div>Hello</div>";

View file

@ -0,0 +1,2 @@
Deno.readTextFileSync("hello.txt");
document.body.innerHTML = "<div>Hello</div>";

View file

@ -0,0 +1,11 @@
TS2584 [ERROR]: Cannot find name 'document'. Do you need to change your target library? Try changing the 'lib' compiler option to include 'dom'.
document.body.innerHTML = "<div>Hello</div>";
~~~~~~~~
at file:///[WILDLINE]main.deno.ts:2:1
TS2304 [ERROR]: Cannot find name 'Deno'. Do you need to change your target library? Try changing the 'lib' compiler option to include 'deno.ns' or add a triple-slash directive to the top of your entrypoint (main file): /// <reference lib="deno.ns" />
Deno.readTextFileSync("hello.txt");
~~~~
at file:///[WILDLINE]main.dom.ts:1:1
error: Type checking failed.

View file

@ -0,0 +1,6 @@
{
"include": ["*.dom.ts"],
"compilerOptions": {
"lib": ["esnext", "dom"]
}
}

View file

@ -0,0 +1,5 @@
{
"args": "check --quiet",
"output": "main.out",
"exitCode": 1
}

View file

@ -0,0 +1 @@
{}

View file

@ -0,0 +1,2 @@
Deno.readTextFileSync("hello.txt");
document.body.innerHTML = "<div>Hello</div>";

View file

@ -0,0 +1,2 @@
Deno.readTextFileSync("hello.txt");
document.body.innerHTML = "<div>Hello</div>";

View file

@ -0,0 +1,11 @@
TS2584 [ERROR]: Cannot find name 'document'. Do you need to change your target library? Try changing the 'lib' compiler option to include 'dom'.
document.body.innerHTML = "<div>Hello</div>";
~~~~~~~~
at file:///[WILDLINE]main.deno.ts:2:1
TS2304 [ERROR]: Cannot find name 'Deno'. Do you need to change your target library? Try changing the 'lib' compiler option to include 'deno.ns' or add a triple-slash directive to the top of your entrypoint (main file): /// <reference lib="deno.ns" />
Deno.readTextFileSync("hello.txt");
~~~~
at file:///[WILDLINE]main.dom.ts:1:1
error: Type checking failed.

View file

@ -0,0 +1,7 @@
{
"files": ["main.dom.ts"],
"compilerOptions": {
"composite": true,
"lib": ["esnext", "dom"]
}
}

View file

@ -0,0 +1,6 @@
{
"files": [],
"references": [
{ "path": "tsconfig.dom.json" }
]
}

View file

@ -0,0 +1,5 @@
{
"args": "check --quiet",
"output": "main.out",
"exitCode": 1
}

View file

@ -0,0 +1 @@
{}

View file

@ -0,0 +1,2 @@
Deno.readTextFileSync("hello.txt");
document.body.innerHTML = "<div>Hello</div>";

View file

@ -0,0 +1,2 @@
Deno.readTextFileSync("hello.txt");
document.body.innerHTML = "<div>Hello</div>";

View file

@ -0,0 +1,11 @@
TS2584 [ERROR]: Cannot find name 'document'. Do you need to change your target library? Try changing the 'lib' compiler option to include 'dom'.
document.body.innerHTML = "<div>Hello</div>";
~~~~~~~~
at file:///[WILDLINE]main.deno.ts:2:1
TS2304 [ERROR]: Cannot find name 'Deno'. Do you need to change your target library? Try changing the 'lib' compiler option to include 'deno.ns' or add a triple-slash directive to the top of your entrypoint (main file): /// <reference lib="deno.ns" />
Deno.readTextFileSync("hello.txt");
~~~~
at file:///[WILDLINE]main.dom.ts:1:1
error: Type checking failed.

View file

@ -0,0 +1,6 @@
{
"files": [],
"references": [
{ "path": "tsconfig_dom" }
]
}

View file

@ -0,0 +1,7 @@
{
"files": ["../main.dom.ts"],
"compilerOptions": {
"composite": true,
"lib": ["esnext", "dom"]
}
}

View file

@ -1,3 +1,4 @@
Check [WILDLINE]/index.mjs
Compile [WILDLINE]/index.mjs to [WILDLINE]
Embedded Files

View file

@ -1,4 +1,4 @@
{
"args": "run --reload --config config/tsconfig.json --check config/main.ts",
"args": "run --reload --config config/config.json --check config/main.ts",
"output": "config/main.out"
}

View file

@ -1,4 +1,4 @@
[WILDCARD]Unsupported compiler options in "[WILDCARD]tsconfig.json".
[WILDCARD]Unsupported compiler options in "[WILDCARD]config.json".
The following options were ignored:
module, target
Check [WILDCARD]/main.ts

View file

@ -1,4 +1,4 @@
{
"args": "run --reload --quiet --check=all --config config_types/tsconfig.json config_types/main.ts",
"args": "run --reload --quiet --check --config config_types/tsconfig.json config_types/main.ts",
"output": "config_types/main.out"
}

View file

@ -1,4 +1,4 @@
{
"args": "run --allow-import --reload --quiet --check=all --config config_types/remote.tsconfig.json config_types/main.ts",
"args": "run --allow-import --reload --quiet --check --config config_types/remote.tsconfig.json config_types/main.ts",
"output": "config_types/main.out"
}