Skip to content

Commit

Permalink
merge compiler options
Browse files Browse the repository at this point in the history
  • Loading branch information
dsherret committed Jan 24, 2025
1 parent 89f85e0 commit 09b9296
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 85 deletions.
55 changes: 26 additions & 29 deletions src/deno_json/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ use crate::UrlToFilePathError;

mod ts;

pub use ts::CompilerOptions;
pub use ts::EmitConfigOptions;
pub use ts::IgnoredCompilerOptions;
pub use ts::JsxImportSourceConfig;
Expand Down Expand Up @@ -716,7 +715,7 @@ pub trait DenoJsonCache {

#[derive(Debug, Error, JsError)]
#[class(type)]
#[error("compilerOptions should be an object in '{specifier}'")]
#[error("compilerOptions should be an object at '{specifier}'")]
pub struct CompilerOptionsParseError {
pub specifier: Url,
#[source]
Expand Down Expand Up @@ -1034,13 +1033,12 @@ impl ConfigFile {

/// Returns true if the configuration indicates that JavaScript should be
/// type checked, otherwise false.
pub fn get_check_js(&self) -> bool {
pub fn check_js(&self) -> Option<bool> {
self
.json
.compiler_options
.as_ref()
.and_then(|co| co.get("checkJs").and_then(|v| v.as_bool()))
.unwrap_or(false)
}

/// Parse `compilerOptions` and return a serde `Value`.
Expand Down Expand Up @@ -1602,13 +1600,14 @@ impl ConfigFile {

pub fn to_compiler_option_types(
&self,
) -> Result<Vec<(Url, Vec<String>)>, CompilerOptionTypesDeserializeError> {
) -> Result<Option<(Url, Vec<String>)>, CompilerOptionTypesDeserializeError>
{
let Some(compiler_options_value) = self.json.compiler_options.as_ref()
else {
return Ok(Vec::new());
return Ok(None);
};
let Some(types) = compiler_options_value.get("types") else {
return Ok(Vec::new());
return Ok(None);
};
let imports: Vec<String> =
serde_json::from_value(types.clone()).map_err(|source| {
Expand All @@ -1619,9 +1618,9 @@ impl ConfigFile {
})?;
if !imports.is_empty() {
let referrer = self.specifier.clone();
Ok(vec![(referrer, imports)])
Ok(Some((referrer, imports)))
} else {
Ok(Vec::new())
Ok(None)
}
}

Expand All @@ -1631,14 +1630,22 @@ impl ConfigFile {
&self,
) -> Result<Option<JsxImportSourceConfig>, ToMaybeJsxImportSourceConfigError>
{
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct JsxCompilerOptions {
pub jsx: Option<String>,
pub jsx_import_source: Option<String>,
pub jsx_import_source_types: Option<String>,
}

let Some(compiler_options_value) = self.json.compiler_options.as_ref()
else {
return Ok(None);
};
let Some(compiler_options) =
serde_json::from_value::<CompilerOptions>(compiler_options_value.clone())
.ok()
else {
let Some(compiler_options) = serde_json::from_value::<JsxCompilerOptions>(
compiler_options_value.clone(),
)
.ok() else {
return Ok(None);
};
let module = match compiler_options.jsx.as_deref() {
Expand Down Expand Up @@ -1788,17 +1795,13 @@ pub enum TsConfigType {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TsConfigForEmit {
pub ts_config: TsConfig,
pub maybe_ignored_options: Option<IgnoredCompilerOptions>,
pub ignored_options: Vec<IgnoredCompilerOptions>,
}

/// For a given configuration type and optionally a configuration file,
/// return a `TsConfig` struct and optionally any user configuration
/// options that were ignored.
pub fn get_ts_config_for_emit(
config_type: TsConfigType,
maybe_config_file: Option<&ConfigFile>,
) -> Result<TsConfigForEmit, CompilerOptionsParseError> {
let mut ts_config = match config_type {
/// For a given configuration type get the starting point TsConfig
/// used that can then be merged with user specified tsconfigs.
pub fn get_base_ts_config_for_emit(config_type: TsConfigType) -> TsConfig {
match config_type {
TsConfigType::Bundle => TsConfig::new(json!({
"allowImportingTsExtensions": true,
"checkJs": false,
Expand Down Expand Up @@ -1856,13 +1859,7 @@ pub fn get_ts_config_for_emit(
"moduleResolution": "NodeNext",
"resolveJsonModule": true,
})),
};
let maybe_ignored_options =
ts_config.merge_tsconfig_from_config_file(maybe_config_file)?;
Ok(TsConfigForEmit {
ts_config,
maybe_ignored_options,
})
}
}

#[cfg(test)]
Expand Down
38 changes: 4 additions & 34 deletions src/deno_json/ts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ use serde_json::Value;
use std::fmt;
use url::Url;

use super::CompilerOptionsParseError;

#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct JsxImportSourceConfig {
pub default_specifier: Option<String>,
Expand Down Expand Up @@ -54,17 +52,6 @@ pub struct EmitConfigOptions {
pub jsx_precompile_skip_elements: Option<Vec<String>>,
}

/// There are certain compiler options that can impact what modules are part of
/// a module graph, which need to be deserialized into a structure for analysis.
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CompilerOptions {
pub jsx: Option<String>,
pub jsx_import_source: Option<String>,
pub jsx_import_source_types: Option<String>,
pub types: Option<Vec<String>>,
}

/// A structure that represents a set of options that were ignored and the
/// path those options came from.
#[derive(Debug, Clone, Eq, PartialEq)]
Expand Down Expand Up @@ -204,28 +191,11 @@ impl TsConfig {
}

/// Merge a serde_json value into the configuration.
pub fn merge(&mut self, value: serde_json::Value) {
json_merge(&mut self.0, value);
}

/// Take an optional user provided config file
/// which was passed in via the `--config` flag and merge `compilerOptions` with
/// the configuration. Returning the result which optionally contains any
/// compiler options that were ignored.
pub fn merge_tsconfig_from_config_file(
pub fn merge_mut(
&mut self,
maybe_config_file: Option<&super::ConfigFile>,
) -> Result<Option<IgnoredCompilerOptions>, CompilerOptionsParseError> {
if let Some(config_file) = maybe_config_file {
let ParsedTsConfigOptions {
options,
maybe_ignored,
} = config_file.to_compiler_options()?;
self.merge(serde_json::Value::Object(options));
Ok(maybe_ignored)
} else {
Ok(None)
}
value: serde_json::Map<String, serde_json::Value>,
) {
json_merge(&mut self.0, serde_json::Value::Object(value));
}
}

Expand Down
82 changes: 60 additions & 22 deletions src/workspace/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use thiserror::Error;
use url::Url;

use crate::deno_json;
use crate::deno_json::get_ts_config_for_emit;
use crate::deno_json::get_base_ts_config_for_emit;
use crate::deno_json::BenchConfig;
use crate::deno_json::CompilerOptionTypesDeserializeError;
use crate::deno_json::CompilerOptionsParseError;
Expand Down Expand Up @@ -1361,36 +1361,64 @@ impl WorkspaceDirectory {
})
}

pub fn deno_json_for_compiler_options(&self) -> Option<&ConfigFileRc> {
self
.maybe_deno_json()
.filter(|c| c.json.compiler_options.is_some())
.or_else(|| self.workspace.root_deno_json())
.filter(|c| c.json.compiler_options.is_some())
}

pub fn check_js(&self) -> bool {
self
.deno_json_for_compiler_options()
.map(|c| c.get_check_js())
.deno_json
.as_ref()
.and_then(|c| {
// prefer member, then root
c.member
.check_js()
.or_else(|| c.root.as_ref().and_then(|d| d.check_js()))
})
.unwrap_or(false)
}

pub fn to_ts_config_for_emit(
&self,
config_type: TsConfigType,
) -> Result<TsConfigForEmit, CompilerOptionsParseError> {
let deno_json = self.deno_json_for_compiler_options();
get_ts_config_for_emit(config_type, deno_json.map(|c| c.as_ref()))
let mut ts_config = get_base_ts_config_for_emit(config_type);
let mut ignored_options = Vec::new();
if let Some(config) = &self.deno_json {
// root first
if let Some(root) = &config.root {
let root_options = root.to_compiler_options()?;
if let Some(options) = root_options.maybe_ignored {
ignored_options.push(options);
}
ts_config.merge_mut(root_options.options);
}

// then member overwrites
let member_options = config.member.to_compiler_options()?;
if let Some(options) = member_options.maybe_ignored {
ignored_options.push(options);
}
ts_config.merge_mut(member_options.options);
}
Ok(TsConfigForEmit {
ts_config,
ignored_options,
})
}

pub fn to_compiler_option_types(
&self,
) -> Result<Vec<(Url, Vec<String>)>, CompilerOptionTypesDeserializeError> {
self
.deno_json_for_compiler_options()
.map(|c| c.to_compiler_option_types())
.unwrap_or(Ok(Vec::new()))
let Some(config) = &self.deno_json else {
return Ok(Vec::new());
};
let mut result = Vec::with_capacity(2);
if let Some(root) = &config.root {
if let Some(types) = root.to_compiler_option_types()? {
result.push(types);
}
}
if let Some(types) = config.member.to_compiler_option_types()? {
result.push(types);
}
Ok(result)
}

pub fn to_maybe_jsx_import_source_config(
Expand All @@ -1399,10 +1427,20 @@ impl WorkspaceDirectory {
Option<JsxImportSourceConfig>,
deno_json::ToMaybeJsxImportSourceConfigError,
> {
self
.deno_json_for_compiler_options()
.map(|c| c.to_maybe_jsx_import_source_config())
.unwrap_or(Ok(None))
let Some(config) = &self.deno_json else {
return Ok(None);
};
if let Some(jsx_config) =
config.member.to_maybe_jsx_import_source_config()?
{
return Ok(Some(jsx_config));
}
if let Some(root) = &config.root {
if let Some(jsx_config) = root.to_maybe_jsx_import_source_config()? {
return Ok(Some(jsx_config));
}
}
Ok(None)
}

pub fn to_lint_config(
Expand Down Expand Up @@ -2442,7 +2480,7 @@ mod test {
"resolveJsonModule": true,
"jsxImportSource": "npm:react"
})),
maybe_ignored_options: None,
ignored_options: Vec::new(),
}
);
assert_eq!(workspace_dir.workspace.diagnostics(), vec![]);
Expand Down

0 comments on commit 09b9296

Please sign in to comment.