Skip to content

Commit 4612133

Browse files
authored
fix(cli): json reporter (#9221)
1 parent de4c5f6 commit 4612133

14 files changed

Lines changed: 1747 additions & 25 deletions

.changeset/breezy-parrots-fold.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@biomejs/biome": patch
3+
---
4+
5+
Fixed an issue where the JSON reporter didn't contain the duration of the command.

crates/biome_cli/src/reporter/json.rs

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,8 @@ use biome_diagnostics::{
1010
use biome_json_factory::make::*;
1111
use biome_json_syntax::{AnyJsonMemberName, AnyJsonValue, JsonRoot, JsonSyntaxKind, T};
1212
use camino::{Utf8Path, Utf8PathBuf};
13-
use serde::Serialize;
1413

15-
#[derive(Debug, Default, Serialize)]
16-
#[serde(rename_all = "camelCase")]
14+
#[derive(Debug, Default)]
1715
pub(crate) struct JsonReporterVisitor {
1816
summary: TraversalSummary,
1917
diagnostics: Vec<JsonReport>,
@@ -212,13 +210,6 @@ fn report_to_json(report: &JsonReport) -> AnyJsonValue {
212210
))
213211
}
214212

215-
impl Display for JsonReporterVisitor {
216-
fn fmt(&self, fmt: &mut Formatter) -> std::io::Result<()> {
217-
let content = serde_json::to_string(&self)?;
218-
fmt.write_str(content.as_str())
219-
}
220-
}
221-
222213
pub struct JsonReporter<'a> {
223214
pub execution: &'a dyn Execution,
224215
pub diagnostics_payload: &'a DiagnosticsPayload,
@@ -344,34 +335,29 @@ fn to_location(location: &Location) -> Option<LocationReport> {
344335
})
345336
}
346337

347-
#[derive(Debug, Serialize)]
348-
#[serde(rename_all = "camelCase")]
338+
#[derive(Debug)]
349339
struct JsonReport {
350340
category: Option<&'static Category>,
351341
severity: Severity,
352342
message: String,
353343
advices: Vec<JsonSuggestion>,
354-
#[serde(skip_serializing_if = "Option::is_none")]
355344
location: Option<LocationReport>,
356345
}
357346

358-
#[derive(Debug, Serialize)]
359-
#[serde(rename_all = "camelCase")]
347+
#[derive(Debug)]
360348
struct LocationReport {
361349
path: String,
362350
start: LocationSpan,
363351
end: LocationSpan,
364352
}
365353

366-
#[derive(Debug, Serialize)]
367-
#[serde(rename_all = "camelCase")]
354+
#[derive(Debug)]
368355
struct LocationSpan {
369356
column: usize,
370357
line: usize,
371358
}
372359

373-
#[derive(Debug, Serialize)]
374-
#[serde(rename_all = "camelCase")]
360+
#[derive(Debug)]
375361
struct JsonSuggestion {
376362
start: LocationSpan,
377363
end: LocationSpan,

crates/biome_cli/src/reporter/mod.rs

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use biome_diagnostics::{Diagnostic, Error, Severity};
1616
use biome_fs::BiomePath;
1717
use biome_json_factory::make::{
1818
json_member, json_member_list, json_member_name, json_number_literal, json_number_value,
19-
json_object_value, json_string_literal, token,
19+
json_object_value, json_string_literal, json_string_value, token,
2020
};
2121
use biome_json_syntax::{AnyJsonMemberName, AnyJsonValue, JsonMember, T};
2222
use camino::Utf8Path;
@@ -55,7 +55,15 @@ pub struct TraversalSummary {
5555

5656
impl TraversalSummary {
5757
pub(crate) fn json_member(&self) -> JsonMember {
58-
let members = vec![
58+
#[cfg(debug_assertions)]
59+
let duration_value =
60+
AnyJsonValue::JsonStringValue(json_string_value(json_string_literal("<TIME>")));
61+
#[cfg(not(debug_assertions))]
62+
let duration_value = AnyJsonValue::JsonNumberValue(json_number_value(json_number_literal(
63+
self.duration.as_millis(),
64+
)));
65+
66+
let mut members = vec![
5967
json_member(
6068
AnyJsonMemberName::JsonMemberName(json_member_name(json_string_literal("changed"))),
6169
token(T![:]),
@@ -75,6 +83,13 @@ impl TraversalSummary {
7583
token(T![:]),
7684
AnyJsonValue::JsonNumberValue(json_number_value(json_number_literal(self.matches))),
7785
),
86+
json_member(
87+
AnyJsonMemberName::JsonMemberName(json_member_name(json_string_literal(
88+
"duration",
89+
))),
90+
token(T![:]),
91+
duration_value,
92+
),
7893
json_member(
7994
AnyJsonMemberName::JsonMemberName(json_member_name(json_string_literal("errors"))),
8095
token(T![:]),
@@ -119,6 +134,24 @@ impl TraversalSummary {
119134
),
120135
];
121136

137+
if let Some(_scanner_duration) = self.scanner_duration {
138+
#[cfg(debug_assertions)]
139+
let scanner_duration_value =
140+
AnyJsonValue::JsonStringValue(json_string_value(json_string_literal("<TIME>")));
141+
#[cfg(not(debug_assertions))]
142+
let scanner_duration_value = AnyJsonValue::JsonNumberValue(json_number_value(
143+
json_number_literal(_scanner_duration.as_millis()),
144+
));
145+
146+
members.push(json_member(
147+
AnyJsonMemberName::JsonMemberName(json_member_name(json_string_literal(
148+
"scannerDuration",
149+
))),
150+
token(T![:]),
151+
scanner_duration_value,
152+
));
153+
}
154+
122155
let separators = vec![token(T![,]); members.len() - 1];
123156

124157
json_member(

crates/biome_cli/tests/cases/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ mod regression_tests;
3636
mod reporter_checkstyle;
3737
mod reporter_github;
3838
mod reporter_gitlab;
39+
mod reporter_json;
3940
mod reporter_junit;
4041
mod reporter_rdjson;
4142
mod reporter_sarif;
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
use crate::run_cli;
2+
use crate::snap_test::{SnapshotPayload, assert_cli_snapshot};
3+
use biome_console::BufferConsole;
4+
use biome_fs::MemoryFileSystem;
5+
use bpaf::Args;
6+
use camino::Utf8Path;
7+
8+
const MAIN_1: &str = r#"import { z} from "z"
9+
import { z, b , a} from "lodash"
10+
11+
a ==b
12+
13+
debugger
14+
15+
let f;
16+
let f;"#;
17+
18+
const MAIN_2: &str = r#"import { z} from "z"
19+
import { z, b , a} from "lodash"
20+
21+
a ==b
22+
23+
debugger
24+
25+
let f;
26+
let f;"#;
27+
28+
#[test]
29+
fn reports_diagnostics_json_check_command() {
30+
let fs = MemoryFileSystem::default();
31+
let mut console = BufferConsole::default();
32+
33+
let file_path1 = Utf8Path::new("main.ts");
34+
fs.insert(file_path1.into(), MAIN_1.as_bytes());
35+
36+
let file_path2 = Utf8Path::new("index.ts");
37+
fs.insert(file_path2.into(), MAIN_2.as_bytes());
38+
39+
let (fs, result) = run_cli(
40+
fs,
41+
&mut console,
42+
Args::from(
43+
[
44+
"check",
45+
"--reporter=json-pretty",
46+
file_path1.as_str(),
47+
file_path2.as_str(),
48+
]
49+
.as_slice(),
50+
),
51+
);
52+
53+
assert!(result.is_err(), "run_cli returned {result:?}");
54+
55+
assert_cli_snapshot(SnapshotPayload::new(
56+
module_path!(),
57+
"reports_diagnostics_json_check_command",
58+
fs,
59+
console,
60+
result,
61+
));
62+
}
63+
64+
#[test]
65+
fn reports_diagnostics_json_ci_command() {
66+
let fs = MemoryFileSystem::default();
67+
let mut console = BufferConsole::default();
68+
69+
let file_path1 = Utf8Path::new("main.ts");
70+
fs.insert(file_path1.into(), MAIN_1.as_bytes());
71+
72+
let file_path2 = Utf8Path::new("index.ts");
73+
fs.insert(file_path2.into(), MAIN_2.as_bytes());
74+
75+
let (fs, result) = run_cli(
76+
fs,
77+
&mut console,
78+
Args::from(
79+
[
80+
"ci",
81+
"--reporter=json-pretty",
82+
file_path1.as_str(),
83+
file_path2.as_str(),
84+
]
85+
.as_slice(),
86+
),
87+
);
88+
89+
assert!(result.is_err(), "run_cli returned {result:?}");
90+
91+
assert_cli_snapshot(SnapshotPayload::new(
92+
module_path!(),
93+
"reports_diagnostics_json_ci_command",
94+
fs,
95+
console,
96+
result,
97+
));
98+
}
99+
100+
#[test]
101+
fn reports_diagnostics_json_lint_command() {
102+
let fs = MemoryFileSystem::default();
103+
let mut console = BufferConsole::default();
104+
105+
let file_path1 = Utf8Path::new("main.ts");
106+
fs.insert(file_path1.into(), MAIN_1.as_bytes());
107+
108+
let file_path2 = Utf8Path::new("index.ts");
109+
fs.insert(file_path2.into(), MAIN_2.as_bytes());
110+
111+
let (fs, result) = run_cli(
112+
fs,
113+
&mut console,
114+
Args::from(
115+
[
116+
"lint",
117+
"--reporter=json-pretty",
118+
file_path1.as_str(),
119+
file_path2.as_str(),
120+
]
121+
.as_slice(),
122+
),
123+
);
124+
125+
assert!(result.is_err(), "run_cli returned {result:?}");
126+
127+
assert_cli_snapshot(SnapshotPayload::new(
128+
module_path!(),
129+
"reports_diagnostics_json_lint_command",
130+
fs,
131+
console,
132+
result,
133+
));
134+
}
135+
136+
#[test]
137+
fn reports_diagnostics_json_format_command() {
138+
let fs = MemoryFileSystem::default();
139+
let mut console = BufferConsole::default();
140+
141+
let file_path1 = Utf8Path::new("main.ts");
142+
fs.insert(file_path1.into(), MAIN_1.as_bytes());
143+
144+
let file_path2 = Utf8Path::new("index.ts");
145+
fs.insert(file_path2.into(), MAIN_2.as_bytes());
146+
147+
let (fs, result) = run_cli(
148+
fs,
149+
&mut console,
150+
Args::from(
151+
[
152+
"format",
153+
"--reporter=json-pretty",
154+
file_path1.as_str(),
155+
file_path2.as_str(),
156+
]
157+
.as_slice(),
158+
),
159+
);
160+
161+
assert!(result.is_err(), "run_cli returned {result:?}");
162+
163+
assert_cli_snapshot(SnapshotPayload::new(
164+
module_path!(),
165+
"reports_diagnostics_json_format_command",
166+
fs,
167+
console,
168+
result,
169+
));
170+
}
171+
172+
#[test]
173+
fn reports_diagnostics_json_check_command_file() {
174+
let fs = MemoryFileSystem::default();
175+
let mut console = BufferConsole::default();
176+
177+
let file_path1 = Utf8Path::new("main.ts");
178+
fs.insert(file_path1.into(), MAIN_1.as_bytes());
179+
180+
let file_path2 = Utf8Path::new("index.ts");
181+
fs.insert(file_path2.into(), MAIN_2.as_bytes());
182+
183+
let (fs, result) = run_cli(
184+
fs,
185+
&mut console,
186+
Args::from(
187+
[
188+
"check",
189+
"--reporter=json-pretty",
190+
"--reporter-file=file.json",
191+
file_path1.as_str(),
192+
file_path2.as_str(),
193+
]
194+
.as_slice(),
195+
),
196+
);
197+
198+
assert!(result.is_err(), "run_cli returned {result:?}");
199+
200+
assert_cli_snapshot(SnapshotPayload::new(
201+
module_path!(),
202+
"reports_diagnostics_json_check_command_file",
203+
fs,
204+
console,
205+
result,
206+
));
207+
}

0 commit comments

Comments
 (0)