Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main' into root-dirs
Browse files Browse the repository at this point in the history
  • Loading branch information
nayeemrmn committed Jan 27, 2025
2 parents 914ea81 + 732bdbd commit b8f116e
Show file tree
Hide file tree
Showing 3 changed files with 204 additions and 217 deletions.
102 changes: 64 additions & 38 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 @@ -64,6 +63,15 @@ pub enum IntoResolvedErrorKind {
InvalidExclude(crate::glob::FromExcludeRelativePathOrPatternsError),
}

#[derive(Debug, Error, JsError)]
#[class(generic)]
#[error("Failed deserilaizing \"compilerOptions\".\"types\" in {}", self.specifier)]
pub struct CompilerOptionTypesDeserializeError {
specifier: Url,
#[source]
source: serde_json::Error,
}

#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
#[serde(default, deny_unknown_fields)]
struct SerializedFilesConfig {
Expand Down Expand Up @@ -705,11 +713,20 @@ pub trait DenoJsonCache {
fn set(&self, path: PathBuf, deno_json: ConfigFileRc);
}

#[derive(Debug, Error, JsError)]
#[class(type)]
#[error("compilerOptions should be an object at '{specifier}'")]
pub struct CompilerOptionsParseError {
pub specifier: Url,
#[source]
pub source: serde_json::Error,
}

#[derive(Debug, Error, JsError)]
pub enum ConfigFileError {
#[class(type)]
#[error("compilerOptions should be an object: {0}")]
CompilerOptionsShouldBeObject(serde_json::Error),
#[class(inherit)]
#[error(transparent)]
CompilerOptionsParseError(CompilerOptionsParseError),
#[class(type)]
#[error("Only file: specifiers are supported for security reasons in import maps stored in a deno.json. To use a remote import map, use the --import-map flag and \"deno.importMap\" in the language server config")]
OnlyFileSpecifiersSupported,
Expand Down Expand Up @@ -1014,26 +1031,29 @@ impl ConfigFile {
.to_path_buf()
}

/// Returns true if the configuration indicates that JavaScript should be
/// type checked, otherwise false.
pub fn get_check_js(&self) -> bool {
/// Returns if the configuration indicates that JavaScript should be
/// type checked, otherwise None if not set.
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`.
/// The result also contains any options that were ignored.
pub fn to_compiler_options(
&self,
) -> Result<ParsedTsConfigOptions, ConfigFileError> {
) -> Result<ParsedTsConfigOptions, CompilerOptionsParseError> {
if let Some(compiler_options) = self.json.compiler_options.clone() {
let options: serde_json::Map<String, Value> =
serde_json::from_value(compiler_options)
.map_err(ConfigFileError::CompilerOptionsShouldBeObject)?;
serde_json::from_value(compiler_options).map_err(|source| {
CompilerOptionsParseError {
specifier: self.specifier.clone(),
source,
}
})?;
Ok(parse_compiler_options(options, Some(&self.specifier)))
} else {
Ok(Default::default())
Expand Down Expand Up @@ -1580,20 +1600,27 @@ impl ConfigFile {

pub fn to_compiler_option_types(
&self,
) -> Result<Vec<(Url, Vec<String>)>, serde_json::Error> {
) -> 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())?;
let imports: Vec<String> =
serde_json::from_value(types.clone()).map_err(|source| {
CompilerOptionTypesDeserializeError {
specifier: self.specifier.clone(),
source,
}
})?;
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 @@ -1603,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 @@ -1745,6 +1780,7 @@ impl Serialize for TsTypeLib {
}

/// An enum that represents the base tsc configuration to return.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum TsConfigType {
/// Return a configuration for bundling, using swc to emit the bundle. This is
/// independent of type checking.
Expand All @@ -1757,19 +1793,15 @@ pub enum TsConfigType {
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TsConfigForEmit {
pub struct TsConfigWithIgnoredOptions {
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, ConfigFileError> {
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 @@ -1827,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
97 changes: 16 additions & 81 deletions src/deno_json/ts.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
// Copyright 2018-2024 the Deno authors. MIT license.

use crate::deno_json::ConfigFileError;
use serde::Deserialize;
use serde::Serialize;
use serde::Serializer;
use serde_json::json;
use serde_json::Value;
use std::collections::BTreeMap;
use std::fmt;
use url::Url;

Expand Down Expand Up @@ -55,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 @@ -143,7 +129,7 @@ pub fn parse_compiler_options(
) -> ParsedTsConfigOptions {
let mut allowed: serde_json::Map<String, Value> =
serde_json::Map::with_capacity(compiler_options.len());
let mut ignored: Vec<String> = Vec::with_capacity(compiler_options.len());
let mut ignored: Vec<String> = Vec::new(); // don't pre-allocate because it's rare

for (key, value) in compiler_options {
// We don't pass "types" entries to typescript via the compiler
Expand Down Expand Up @@ -177,63 +163,31 @@ pub fn parse_compiler_options(
}

/// A structure for managing the configuration of TypeScript
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TsConfig(pub Value);

impl Default for TsConfig {
fn default() -> Self {
Self(serde_json::Value::Object(Default::default()))
}
}

impl TsConfig {
/// Create a new `TsConfig` with the base being the `value` supplied.
pub fn new(value: Value) -> Self {
TsConfig(value)
}

pub fn as_bytes(&self) -> Vec<u8> {
let map = self.0.as_object().expect("invalid tsconfig");
let ordered: BTreeMap<_, _> = map.iter().collect();
let value = json!(ordered);
value.to_string().as_bytes().to_owned()
}

/// Return the value of the `checkJs` compiler option, defaulting to `false`
/// if not present.
pub fn get_check_js(&self) -> bool {
if let Some(check_js) = self.0.get("checkJs") {
check_js.as_bool().unwrap_or(false)
} else {
false
}
}

pub fn get_declaration(&self) -> bool {
if let Some(declaration) = self.0.get("declaration") {
declaration.as_bool().unwrap_or(false)
} else {
false
}
pub fn merge_mut(&mut self, value: TsConfig) {
json_merge(&mut self.0, value.0);
}

/// 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_object_mut(
&mut self,
maybe_config_file: Option<&super::ConfigFile>,
) -> Result<Option<IgnoredCompilerOptions>, ConfigFileError> {
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 Expand Up @@ -263,28 +217,9 @@ fn json_merge(a: &mut Value, b: Value) {

#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;

#[test]
fn test_tsconfig_as_bytes() {
let mut tsconfig1 = TsConfig::new(json!({
"strict": true,
"target": "esnext",
}));
tsconfig1.merge(json!({
"target": "es5",
"module": "amd",
}));
let mut tsconfig2 = TsConfig::new(json!({
"target": "esnext",
"strict": true,
}));
tsconfig2.merge(json!({
"module": "amd",
"target": "es5",
}));
assert_eq!(tsconfig1.as_bytes(), tsconfig2.as_bytes());
}
use super::*;

#[test]
fn test_json_merge() {
Expand Down
Loading

0 comments on commit b8f116e

Please sign in to comment.