deno/libs/resolver/deno_json.rs
David Sherret 90058d6732
Some checks failed
ci / pre-build (push) Has been cancelled
ci / build libs (push) Has been cancelled
ci / test debug linux-aarch64 (push) Has been cancelled
ci / test release linux-aarch64 (push) Has been cancelled
ci / test debug macos-aarch64 (push) Has been cancelled
ci / test release macos-aarch64 (push) Has been cancelled
ci / bench release linux-x86_64 (push) Has been cancelled
ci / lint debug linux-x86_64 (push) Has been cancelled
ci / lint debug macos-x86_64 (push) Has been cancelled
ci / lint debug windows-x86_64 (push) Has been cancelled
ci / test debug linux-x86_64 (push) Has been cancelled
ci / test release linux-x86_64 (push) Has been cancelled
ci / test debug macos-x86_64 (push) Has been cancelled
ci / test release macos-x86_64 (push) Has been cancelled
ci / test debug windows-x86_64 (push) Has been cancelled
ci / test release windows-x86_64 (push) Has been cancelled
ci / publish canary (push) Has been cancelled
refactor(emitter): ability to not transpile and specify a source map base (#29996)
2025-07-04 12:51:17 -04:00

912 lines
27 KiB
Rust

// 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::CompilerOptions;
use deno_config::deno_json::CompilerOptionsParseError;
use deno_config::deno_json::CompilerOptionsWithIgnoredOptions;
use deno_config::deno_json::parse_compiler_options;
use deno_config::glob::PathOrPatternSet;
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_config::workspace::get_base_compiler_options_for_emit;
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"))]
use once_cell::unsync::OnceCell;
use sys_traits::FsRead;
use url::Url;
use crate::collections::FolderScopedMap;
use crate::factory::ConfigDiscoveryOption;
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)]
type CompilerOptionsRc = crate::sync::MaybeArc<CompilerOptions>;
#[allow(clippy::disallowed_types)]
pub type CompilerOptionsTypesRc =
crate::sync::MaybeArc<Vec<(Url, Vec<String>)>>;
#[cfg(feature = "deno_ast")]
#[derive(Debug)]
pub struct TranspileAndEmitOptions {
pub no_transpile: bool,
pub transpile: deno_ast::TranspileOptions,
pub emit: deno_ast::EmitOptions,
// stored ahead of time so we don't have to recompute this a lot
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>,
deno_worker_check_compiler_options: OnceCell<CompilerOptionsRc>,
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, Clone, Default)]
pub struct CompilerOptionsOverrides {
/// Skip transpiling in the loaders.
pub no_transpile: bool,
/// Base to use for the source map. This is useful when bundling
/// and you want to make file urls relative.
pub source_map_base: Option<Url>,
/// Preserve JSX instead of transforming it.
///
/// This may be useful when bundling.
pub preserve_jsx: bool,
}
#[derive(Debug)]
pub struct CompilerOptionsData {
pub sources: Vec<CompilerOptionsSource>,
source_kind: CompilerOptionsSourceKind,
memoized: MemoizedValues,
logged_warnings: LoggedWarningsRc,
#[cfg_attr(not(feature = "deno_ast"), allow(unused))]
overrides: CompilerOptionsOverrides,
}
impl CompilerOptionsData {
fn new(
sources: Vec<CompilerOptionsSource>,
source_kind: CompilerOptionsSourceKind,
logged_warnings: LoggedWarningsRc,
overrides: CompilerOptionsOverrides,
) -> Self {
Self {
sources,
source_kind,
memoized: Default::default(),
logged_warnings,
overrides,
}
}
pub fn compiler_options_for_lib(
&self,
lib: TsTypeLib,
) -> Result<&CompilerOptionsRc, CompilerOptionsParseError> {
self.compiler_options_inner(CompilerOptionsType::Check { lib })
}
pub fn compiler_options_for_emit(
&self,
) -> Result<&CompilerOptionsRc, CompilerOptionsParseError> {
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))
})
}
#[cfg(feature = "deno_ast")]
pub fn transpile_options(
&self,
) -> Result<&TranspileAndEmitOptionsRc, CompilerOptionsParseError> {
self.memoized.transpile_options.get_or_try_init(|| {
let compiler_options = self.compiler_options_for_emit()?;
compiler_options_to_transpile_and_emit_options(
compiler_options.as_ref().clone(),
&self.overrides,
)
.map(new_rc)
.map_err(|source| CompilerOptionsParseError {
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,
overrides: CompilerOptionsOverrides,
}
impl<'a, TSys: FsRead, NSys: NpmResolverSys> TsConfigCollector<'a, TSys, NSys> {
fn new(
sys: &'a TSys,
node_resolver: &'a TsConfigNodeResolver<NSys>,
logged_warnings: &'a LoggedWarningsRc,
overrides: CompilerOptionsOverrides,
) -> Self {
Self {
roots: Default::default(),
collected: Default::default(),
read_cache: Default::default(),
currently_reading: Default::default(),
sys,
node_resolver,
logged_warnings,
overrides,
}
}
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(),
self.overrides.clone(),
),
filter: new_rc(TsConfigFileFilter {
files,
include,
exclude,
dir_path: dir_path.to_path_buf(),
}),
references,
})
}
}
#[derive(Debug)]
pub struct CompilerOptionsResolver {
workspace_configs: FolderScopedMap<CompilerOptionsData>,
ts_configs: Vec<TsConfigData>,
}
impl CompilerOptionsResolver {
pub fn new<TSys: FsRead, NSys: NpmResolverSys>(
sys: &TSys,
workspace_directory_provider: &WorkspaceDirectoryProvider,
node_resolver: &TsConfigNodeResolver<NSys>,
config_discover: &ConfigDiscoveryOption,
overrides: &CompilerOptionsOverrides,
) -> Self {
if matches!(config_discover, ConfigDiscoveryOption::Disabled) {
return Self {
workspace_configs: FolderScopedMap::new(CompilerOptionsData::new(
Vec::new(),
CompilerOptionsSourceKind::DenoJson,
Default::default(),
overrides.clone(),
)),
ts_configs: Vec::new(),
};
}
let logged_warnings = new_rc(LoggedWarnings::default());
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(),
overrides.clone(),
));
let mut ts_config_collector = TsConfigCollector::new(
sys,
node_resolver,
&logged_warnings,
overrides.clone(),
);
for (dir_url, dir) in workspace_directory_provider.entries() {
if dir.has_deno_or_pkg_json() {
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(),
overrides.clone(),
),
);
}
}
Self {
workspace_configs,
ts_configs: ts_config_collector.collect(),
}
}
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 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 size(&self) -> usize {
self.workspace_configs.count() + self.ts_configs.len()
}
pub fn ts_configs(&self) -> &Vec<TsConfigData> {
&self.ts_configs
}
}
#[cfg(feature = "graph")]
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,
overrides: &CompilerOptionsOverrides,
) -> Result<TranspileAndEmitOptions, serde_json::Error> {
let options: deno_config::deno_json::EmitConfigOptions =
serde_json::from_value(config.0)?;
let imports_not_used_as_values =
match options.imports_not_used_as_values.as_str() {
"preserve" => deno_ast::ImportsNotUsedAsValues::Preserve,
"error" => deno_ast::ImportsNotUsedAsValues::Error,
_ => deno_ast::ImportsNotUsedAsValues::Remove,
};
let (transform_jsx, jsx_automatic, jsx_development, precompile_jsx) =
match overrides.preserve_jsx {
true => (false, false, false, false),
false => match options.jsx.as_str() {
"react" => (true, false, false, false),
"react-jsx" => (true, true, false, false),
"react-jsxdev" => (true, true, true, false),
"precompile" => (false, false, false, true),
_ => (false, false, false, false),
},
};
let source_map = if options.inline_source_map {
deno_ast::SourceMapOption::Inline
} else if options.source_map {
deno_ast::SourceMapOption::Separate
} else {
deno_ast::SourceMapOption::None
};
let transpile = deno_ast::TranspileOptions {
use_ts_decorators: options.experimental_decorators,
use_decorators_proposal: !options.experimental_decorators,
emit_metadata: options.emit_decorator_metadata,
imports_not_used_as_values,
jsx_automatic,
jsx_development,
jsx_factory: options.jsx_factory,
jsx_fragment_factory: options.jsx_fragment_factory,
jsx_import_source: options.jsx_import_source,
precompile_jsx,
precompile_jsx_skip_elements: options.jsx_precompile_skip_elements,
precompile_jsx_dynamic_props: None,
transform_jsx,
var_decl_imports: false,
// todo(dsherret): support verbatim_module_syntax here properly
verbatim_module_syntax: false,
};
let emit = deno_ast::EmitOptions {
inline_sources: options.inline_sources,
remove_comments: false,
source_map,
source_map_base: None,
source_map_file: None,
};
let transpile_and_emit_options_hash = {
use std::hash::Hash;
use std::hash::Hasher;
let mut hasher = twox_hash::XxHash64::default();
transpile.hash(&mut hasher);
emit.hash(&mut hasher);
hasher.finish()
};
Ok(TranspileAndEmitOptions {
no_transpile: overrides.no_transpile,
transpile,
emit,
pre_computed_hash: transpile_and_emit_options_hash,
})
}
fn check_warn_compiler_options(
compiler_options: &CompilerOptionsWithIgnoredOptions,
logged_warnings: &LoggedWarnings,
) {
for ignored_options in &compiler_options.ignored_options {
if ignored_options
.maybe_specifier
.as_ref()
.map(|s| logged_warnings.folders.insert(s.clone()))
.unwrap_or(true)
{
log::warn!("{}", ignored_options);
}
}
let serde_json::Value::Object(obj) = &compiler_options.compiler_options.0
else {
return;
};
if obj.get("experimentalDecorators") == Some(&serde_json::Value::Bool(true))
&& logged_warnings.experimental_decorators.raise()
{
log::warn!(
"{} experimentalDecorators compiler option is deprecated and may be removed at any time",
colors::yellow("Warning"),
);
}
}