Skip to content

Commit 8ef94af

Browse files
scarf005chatgpt-codex-connector[bot]bartlomieju
authored
feat(fmt): add JSON trailing comma config (#33383)
Fixes #27856 Fixes #28522 --------- Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com> Co-authored-by: Bartek Iwańczuk <bartek@deno.com>
1 parent 2fd8967 commit 8ef94af

8 files changed

Lines changed: 170 additions & 1 deletion

File tree

cli/schemas/config-file.v1.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -765,6 +765,11 @@
765765
"default": "onlyMultiLine",
766766
"enum": ["never", "always", "onlyMultiLine"]
767767
},
768+
"json.trailingCommas": {
769+
"description": "Whether to add trailing commas in JSON and JSONC. Defaults to \"never\". The \"jsonc\" option adds trailing commas in JSONC files and omits them in JSON files.",
770+
"default": "never",
771+
"enum": ["never", "always", "maintain", "jsonc"]
772+
},
768773
"operatorPosition": {
769774
"description": "Where to place the operator for expressions that span multiple lines in JavaScript and TypeScript.",
770775
"default": "sameLine",

cli/tools/fmt.rs

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1515,10 +1515,21 @@ fn get_resolved_markdown_config(
15151515
fn get_resolved_json_config(
15161516
options: &FmtOptionsConfig,
15171517
) -> dprint_plugin_json::configuration::Configuration {
1518+
use deno_config::deno_json::JsonTrailingCommaKind;
1519+
use dprint_plugin_json::configuration::TrailingCommaKind;
1520+
15181521
let mut builder =
15191522
dprint_plugin_json::configuration::ConfigurationBuilder::new();
15201523

15211524
builder.deno();
1525+
if let Some(json_trailing_commas) = options.json_trailing_commas {
1526+
builder.trailing_commas(match json_trailing_commas {
1527+
JsonTrailingCommaKind::Always => TrailingCommaKind::Always,
1528+
JsonTrailingCommaKind::Jsonc => TrailingCommaKind::Jsonc,
1529+
JsonTrailingCommaKind::Maintain => TrailingCommaKind::Maintain,
1530+
JsonTrailingCommaKind::Never => TrailingCommaKind::Never,
1531+
});
1532+
}
15221533

15231534
if let Some(use_tabs) = options.use_tabs {
15241535
builder.use_tabs(use_tabs);
@@ -1879,6 +1890,7 @@ fn is_supported_ext_fmt(path: &Path) -> bool {
18791890

18801891
#[cfg(test)]
18811892
mod test {
1893+
use deno_config::deno_json::JsonTrailingCommaKind;
18821894
use test_util::assert_starts_with;
18831895

18841896
use super::*;
@@ -2061,4 +2073,102 @@ mod test {
20612073
.unwrap();
20622074
assert_eq!(file_text, "let a = 1;\n",);
20632075
}
2076+
2077+
#[test]
2078+
fn test_jsonc_does_not_add_trailing_commas_by_default() {
2079+
let file_text = format_file(
2080+
Path::new("test.jsonc"),
2081+
&FileContents {
2082+
had_bom: false,
2083+
text: r#"{
2084+
"a": 1,
2085+
"b": 2
2086+
}
2087+
"#
2088+
.into(),
2089+
},
2090+
&FmtOptionsConfig::default(),
2091+
&UnstableFmtOptions::default(),
2092+
None,
2093+
)
2094+
.unwrap();
2095+
assert_eq!(file_text, None);
2096+
}
2097+
2098+
#[test]
2099+
fn test_json_does_not_add_trailing_commas() {
2100+
let file_text = format_file(
2101+
Path::new("test.json"),
2102+
&FileContents {
2103+
had_bom: false,
2104+
text: r#"{
2105+
"a": 1,
2106+
"b": 2
2107+
}
2108+
"#
2109+
.into(),
2110+
},
2111+
&FmtOptionsConfig::default(),
2112+
&UnstableFmtOptions::default(),
2113+
None,
2114+
)
2115+
.unwrap();
2116+
assert_eq!(file_text, None);
2117+
}
2118+
2119+
#[test]
2120+
fn test_jsonc_trailing_commas_can_be_disabled() {
2121+
let file_text = format_file(
2122+
Path::new("test.jsonc"),
2123+
&FileContents {
2124+
had_bom: false,
2125+
text: r#"{
2126+
"a": 1,
2127+
"b": 2
2128+
}
2129+
"#
2130+
.into(),
2131+
},
2132+
&FmtOptionsConfig {
2133+
json_trailing_commas: Some(JsonTrailingCommaKind::Never),
2134+
..Default::default()
2135+
},
2136+
&UnstableFmtOptions::default(),
2137+
None,
2138+
)
2139+
.unwrap();
2140+
assert_eq!(file_text, None);
2141+
}
2142+
2143+
#[test]
2144+
fn test_json_trailing_commas_can_be_enabled() {
2145+
let file_text = format_file(
2146+
Path::new("test.json"),
2147+
&FileContents {
2148+
had_bom: false,
2149+
text: r#"{
2150+
"a": 1,
2151+
"b": 2
2152+
}
2153+
"#
2154+
.into(),
2155+
},
2156+
&FmtOptionsConfig {
2157+
json_trailing_commas: Some(JsonTrailingCommaKind::Always),
2158+
..Default::default()
2159+
},
2160+
&UnstableFmtOptions::default(),
2161+
None,
2162+
)
2163+
.unwrap()
2164+
.unwrap();
2165+
assert_eq!(
2166+
file_text,
2167+
r#"{
2168+
"a": 1,
2169+
"b": 2,
2170+
}
2171+
"#
2172+
);
2173+
}
20642174
}

libs/config/deno_json/mod.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,15 @@ pub enum TrailingCommas {
337337
OnlyMultiLine,
338338
}
339339

340+
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Hash, PartialEq)]
341+
#[serde(deny_unknown_fields, rename_all = "camelCase")]
342+
pub enum JsonTrailingCommaKind {
343+
Always,
344+
Jsonc,
345+
Maintain,
346+
Never,
347+
}
348+
340349
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Hash, PartialEq)]
341350
#[serde(deny_unknown_fields, rename_all = "camelCase")]
342351
pub enum OperatorPosition {
@@ -394,6 +403,7 @@ pub struct FmtOptionsConfig {
394403
pub single_body_position: Option<SingleBodyPosition>,
395404
pub next_control_flow_position: Option<NextControlFlowPosition>,
396405
pub trailing_commas: Option<TrailingCommas>,
406+
pub json_trailing_commas: Option<JsonTrailingCommaKind>,
397407
pub operator_position: Option<OperatorPosition>,
398408
pub jsx_bracket_position: Option<BracketPosition>,
399409
pub jsx_force_new_lines_surrounding_content: Option<bool>,
@@ -420,6 +430,7 @@ impl FmtOptionsConfig {
420430
&& self.single_body_position.is_none()
421431
&& self.next_control_flow_position.is_none()
422432
&& self.trailing_commas.is_none()
433+
&& self.json_trailing_commas.is_none()
423434
&& self.operator_position.is_none()
424435
&& self.jsx_bracket_position.is_none()
425436
&& self.jsx_force_new_lines_surrounding_content.is_none()
@@ -491,6 +502,8 @@ struct SerializedFmtConfig {
491502
pub single_body_position: Option<SingleBodyPosition>,
492503
pub next_control_flow_position: Option<NextControlFlowPosition>,
493504
pub trailing_commas: Option<TrailingCommas>,
505+
#[serde(rename = "json.trailingCommas")]
506+
pub json_trailing_commas: Option<JsonTrailingCommaKind>,
494507
pub operator_position: Option<OperatorPosition>,
495508
#[serde(rename = "jsx.bracketPosition")]
496509
pub jsx_bracket_position: Option<BracketPosition>,
@@ -533,6 +546,7 @@ impl SerializedFmtConfig {
533546
single_body_position: self.single_body_position,
534547
next_control_flow_position: self.next_control_flow_position,
535548
trailing_commas: self.trailing_commas,
549+
json_trailing_commas: self.json_trailing_commas,
536550
operator_position: self.operator_position,
537551
jsx_bracket_position: self.jsx_bracket_position,
538552
jsx_force_new_lines_surrounding_content: self
@@ -2529,6 +2543,7 @@ mod tests {
25292543
"singleBodyPosition": "nextLine",
25302544
"nextControlFlowPosition": "sameLine",
25312545
"trailingCommas": "never",
2546+
"json.trailingCommas": "maintain",
25322547
"operatorPosition": "maintain",
25332548
"jsx.bracketPosition": "maintain",
25342549
"jsx.forceNewLinesSurroundingContent": true,
@@ -2604,6 +2619,7 @@ mod tests {
26042619
single_body_position: Some(SingleBodyPosition::NextLine),
26052620
next_control_flow_position: Some(NextControlFlowPosition::SameLine),
26062621
trailing_commas: Some(TrailingCommas::Never),
2622+
json_trailing_commas: Some(JsonTrailingCommaKind::Maintain),
26072623
operator_position: Some(OperatorPosition::Maintain),
26082624
jsx_bracket_position: Some(BracketPosition::Maintain),
26092625
jsx_force_new_lines_surrounding_content: Some(true),

libs/config/workspace/mod.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2301,6 +2301,10 @@ impl WorkspaceDirectory {
23012301
.options
23022302
.trailing_commas
23032303
.or(root_config.options.trailing_commas),
2304+
json_trailing_commas: member_config
2305+
.options
2306+
.json_trailing_commas
2307+
.or(root_config.options.json_trailing_commas),
23042308
operator_position: member_config
23052309
.options
23062310
.operator_position
@@ -3064,6 +3068,7 @@ pub mod test {
30643068
use crate::deno_json::BracePosition;
30653069
use crate::deno_json::BracketPosition;
30663070
use crate::deno_json::DenoJsonCache;
3071+
use crate::deno_json::JsonTrailingCommaKind;
30673072
use crate::deno_json::MultiLineParens;
30683073
use crate::deno_json::NewLineKind;
30693074
use crate::deno_json::NextControlFlowPosition;
@@ -4067,6 +4072,7 @@ pub mod test {
40674072
"singleBodyPosition": "sameLine",
40684073
"nextControlFlowPosition": "nextLine",
40694074
"trailingCommas": "always",
4075+
"json.trailingCommas": "never",
40704076
"operatorPosition": "sameLine",
40714077
"jsx.bracketPosition": "sameLine",
40724078
"jsx.forceNewLinesSurroundingContent": false,
@@ -4092,6 +4098,7 @@ pub mod test {
40924098
"singleBodyPosition": "maintain",
40934099
"nextControlFlowPosition": "maintain",
40944100
"trailingCommas": "onlyMultiLine",
4101+
"json.trailingCommas": "maintain",
40954102
"operatorPosition": "nextLine",
40964103
"jsx.bracketPosition": "nextLine",
40974104
"jsx.forceNewLinesSurroundingContent": true,
@@ -4123,6 +4130,7 @@ pub mod test {
41234130
single_body_position: Some(SingleBodyPosition::Maintain),
41244131
next_control_flow_position: Some(NextControlFlowPosition::Maintain),
41254132
trailing_commas: Some(TrailingCommas::OnlyMultiLine),
4133+
json_trailing_commas: Some(JsonTrailingCommaKind::Maintain),
41264134
operator_position: Some(OperatorPosition::NextLine),
41274135
jsx_bracket_position: Some(BracketPosition::NextLine),
41284136
jsx_force_new_lines_surrounding_content: Some(true),
@@ -4167,6 +4175,7 @@ pub mod test {
41674175
single_body_position: Some(SingleBodyPosition::SameLine),
41684176
next_control_flow_position: Some(NextControlFlowPosition::NextLine),
41694177
trailing_commas: Some(TrailingCommas::Always),
4178+
json_trailing_commas: Some(JsonTrailingCommaKind::Never),
41704179
operator_position: Some(OperatorPosition::SameLine),
41714180
jsx_bracket_position: Some(BracketPosition::SameLine),
41724181
jsx_force_new_lines_surrounding_content: Some(false),
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"tests": {
3+
"fmt_jsonc_default_trailing_commas": {
4+
"args": "fmt --ext=jsonc -",
5+
"input": "{\n \"a\": 1,\n \"b\": 2\n}\n",
6+
"output": "{\n \"a\": 1,\n \"b\": 2\n}\n"
7+
},
8+
"fmt_jsonc_trailing_commas_never": {
9+
"args": "fmt --config deno.never.jsonc --ext=jsonc -",
10+
"input": "{\n \"a\": 1,\n \"b\": 2\n}\n",
11+
"output": "{\n \"a\": 1,\n \"b\": 2\n}\n"
12+
},
13+
"fmt_json_trailing_commas_always": {
14+
"args": "fmt --config deno.always.jsonc --ext=json -",
15+
"input": "{\n \"a\": 1,\n \"b\": 2\n}\n",
16+
"output": "{\n \"a\": 1,\n \"b\": 2,\n}\n"
17+
}
18+
}
19+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"fmt": {
3+
"json.trailingCommas": "always"
4+
}
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"fmt": {
3+
"json.trailingCommas": "never"
4+
}
5+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
error: Failed to parse "fmt" configuration
22

33
Caused by:
4-
unknown field `dont_know_this_field`, expected one of `useTabs`, `lineWidth`, `indentWidth`, `singleQuote`, `proseWrap`, `semiColons`, `quoteProps`, `newLineKind`, `useBraces`, `bracePosition`, `singleBodyPosition`, `nextControlFlowPosition`, `trailingCommas`, `operatorPosition`, `jsx.bracketPosition`, `jsx.forceNewLinesSurroundingContent`, `jsx.multiLineParens`, `typeLiteral.separatorKind`, `spaceAround`, `spaceSurroundingProperties`, `vueComponentCase`, `angularNextControlFlowSameLine`, `options`, `include`, `exclude`, `files`
4+
unknown field `dont_know_this_field`, expected one of `useTabs`, `lineWidth`, `indentWidth`, `singleQuote`, `proseWrap`, `semiColons`, `quoteProps`, `newLineKind`, `useBraces`, `bracePosition`, `singleBodyPosition`, `nextControlFlowPosition`, `trailingCommas`, `json.trailingCommas`, `operatorPosition`, `jsx.bracketPosition`, `jsx.forceNewLinesSurroundingContent`, `jsx.multiLineParens`, `typeLiteral.separatorKind`, `spaceAround`, `spaceSurroundingProperties`, `vueComponentCase`, `angularNextControlFlowSameLine`, `options`, `include`, `exclude`, `files`

0 commit comments

Comments
 (0)