Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/breezy-parrots-fold.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@biomejs/biome": patch
---

Fixed an issue where the JSON reporter didn't contain the duration of the command.
24 changes: 5 additions & 19 deletions crates/biome_cli/src/reporter/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,8 @@ use biome_diagnostics::{
use biome_json_factory::make::*;
use biome_json_syntax::{AnyJsonMemberName, AnyJsonValue, JsonRoot, JsonSyntaxKind, T};
use camino::{Utf8Path, Utf8PathBuf};
use serde::Serialize;

#[derive(Debug, Default, Serialize)]
#[serde(rename_all = "camelCase")]
#[derive(Debug, Default)]
pub(crate) struct JsonReporterVisitor {
summary: TraversalSummary,
diagnostics: Vec<JsonReport>,
Expand Down Expand Up @@ -212,13 +210,6 @@ fn report_to_json(report: &JsonReport) -> AnyJsonValue {
))
}

impl Display for JsonReporterVisitor {
fn fmt(&self, fmt: &mut Formatter) -> std::io::Result<()> {
let content = serde_json::to_string(&self)?;
fmt.write_str(content.as_str())
}
}

pub struct JsonReporter<'a> {
pub execution: &'a dyn Execution,
pub diagnostics_payload: &'a DiagnosticsPayload,
Expand Down Expand Up @@ -344,34 +335,29 @@ fn to_location(location: &Location) -> Option<LocationReport> {
})
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
#[derive(Debug)]
struct JsonReport {
category: Option<&'static Category>,
severity: Severity,
message: String,
advices: Vec<JsonSuggestion>,
#[serde(skip_serializing_if = "Option::is_none")]
location: Option<LocationReport>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
#[derive(Debug)]
struct LocationReport {
path: String,
start: LocationSpan,
end: LocationSpan,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
#[derive(Debug)]
struct LocationSpan {
column: usize,
line: usize,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
#[derive(Debug)]
struct JsonSuggestion {
start: LocationSpan,
end: LocationSpan,
Expand Down
37 changes: 35 additions & 2 deletions crates/biome_cli/src/reporter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use biome_diagnostics::{Diagnostic, Error, Severity};
use biome_fs::BiomePath;
use biome_json_factory::make::{
json_member, json_member_list, json_member_name, json_number_literal, json_number_value,
json_object_value, json_string_literal, token,
json_object_value, json_string_literal, json_string_value, token,
};
use biome_json_syntax::{AnyJsonMemberName, AnyJsonValue, JsonMember, T};
use camino::Utf8Path;
Expand Down Expand Up @@ -55,7 +55,15 @@ pub struct TraversalSummary {

impl TraversalSummary {
pub(crate) fn json_member(&self) -> JsonMember {
let members = vec![
#[cfg(debug_assertions)]
let duration_value =
AnyJsonValue::JsonStringValue(json_string_value(json_string_literal("<TIME>")));
#[cfg(not(debug_assertions))]
let duration_value = AnyJsonValue::JsonNumberValue(json_number_value(json_number_literal(
self.duration.as_millis(),
)));

let mut members = vec![
json_member(
AnyJsonMemberName::JsonMemberName(json_member_name(json_string_literal("changed"))),
token(T![:]),
Expand All @@ -75,6 +83,13 @@ impl TraversalSummary {
token(T![:]),
AnyJsonValue::JsonNumberValue(json_number_value(json_number_literal(self.matches))),
),
json_member(
AnyJsonMemberName::JsonMemberName(json_member_name(json_string_literal(
"duration",
))),
token(T![:]),
duration_value,
),
json_member(
AnyJsonMemberName::JsonMemberName(json_member_name(json_string_literal("errors"))),
token(T![:]),
Expand Down Expand Up @@ -119,6 +134,24 @@ impl TraversalSummary {
),
];

if let Some(_scanner_duration) = self.scanner_duration {
#[cfg(debug_assertions)]
let scanner_duration_value =
AnyJsonValue::JsonStringValue(json_string_value(json_string_literal("<TIME>")));
#[cfg(not(debug_assertions))]
let scanner_duration_value = AnyJsonValue::JsonNumberValue(json_number_value(
json_number_literal(_scanner_duration.as_millis()),
));

members.push(json_member(
AnyJsonMemberName::JsonMemberName(json_member_name(json_string_literal(
"scannerDuration",
))),
token(T![:]),
scanner_duration_value,
));
}

let separators = vec![token(T![,]); members.len() - 1];

json_member(
Expand Down
1 change: 1 addition & 0 deletions crates/biome_cli/tests/cases/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ mod regression_tests;
mod reporter_checkstyle;
mod reporter_github;
mod reporter_gitlab;
mod reporter_json;
mod reporter_junit;
mod reporter_rdjson;
mod reporter_sarif;
Expand Down
207 changes: 207 additions & 0 deletions crates/biome_cli/tests/cases/reporter_json.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
use crate::run_cli;
use crate::snap_test::{SnapshotPayload, assert_cli_snapshot};
use biome_console::BufferConsole;
use biome_fs::MemoryFileSystem;
use bpaf::Args;
use camino::Utf8Path;

const MAIN_1: &str = r#"import { z} from "z"
import { z, b , a} from "lodash"
a ==b
debugger
let f;
let f;"#;

const MAIN_2: &str = r#"import { z} from "z"
import { z, b , a} from "lodash"
a ==b
debugger
let f;
let f;"#;

#[test]
fn reports_diagnostics_json_check_command() {
let fs = MemoryFileSystem::default();
let mut console = BufferConsole::default();

let file_path1 = Utf8Path::new("main.ts");
fs.insert(file_path1.into(), MAIN_1.as_bytes());

let file_path2 = Utf8Path::new("index.ts");
fs.insert(file_path2.into(), MAIN_2.as_bytes());

let (fs, result) = run_cli(
fs,
&mut console,
Args::from(
[
"check",
"--reporter=json-pretty",
file_path1.as_str(),
file_path2.as_str(),
]
.as_slice(),
),
);

assert!(result.is_err(), "run_cli returned {result:?}");

assert_cli_snapshot(SnapshotPayload::new(
module_path!(),
"reports_diagnostics_json_check_command",
fs,
console,
result,
));
}

#[test]
fn reports_diagnostics_json_ci_command() {
let fs = MemoryFileSystem::default();
let mut console = BufferConsole::default();

let file_path1 = Utf8Path::new("main.ts");
fs.insert(file_path1.into(), MAIN_1.as_bytes());

let file_path2 = Utf8Path::new("index.ts");
fs.insert(file_path2.into(), MAIN_2.as_bytes());

let (fs, result) = run_cli(
fs,
&mut console,
Args::from(
[
"ci",
"--reporter=json-pretty",
file_path1.as_str(),
file_path2.as_str(),
]
.as_slice(),
),
);

assert!(result.is_err(), "run_cli returned {result:?}");

assert_cli_snapshot(SnapshotPayload::new(
module_path!(),
"reports_diagnostics_json_ci_command",
fs,
console,
result,
));
}

#[test]
fn reports_diagnostics_json_lint_command() {
let fs = MemoryFileSystem::default();
let mut console = BufferConsole::default();

let file_path1 = Utf8Path::new("main.ts");
fs.insert(file_path1.into(), MAIN_1.as_bytes());

let file_path2 = Utf8Path::new("index.ts");
fs.insert(file_path2.into(), MAIN_2.as_bytes());

let (fs, result) = run_cli(
fs,
&mut console,
Args::from(
[
"lint",
"--reporter=json-pretty",
file_path1.as_str(),
file_path2.as_str(),
]
.as_slice(),
),
);

assert!(result.is_err(), "run_cli returned {result:?}");

assert_cli_snapshot(SnapshotPayload::new(
module_path!(),
"reports_diagnostics_json_lint_command",
fs,
console,
result,
));
}

#[test]
fn reports_diagnostics_json_format_command() {
let fs = MemoryFileSystem::default();
let mut console = BufferConsole::default();

let file_path1 = Utf8Path::new("main.ts");
fs.insert(file_path1.into(), MAIN_1.as_bytes());

let file_path2 = Utf8Path::new("index.ts");
fs.insert(file_path2.into(), MAIN_2.as_bytes());

let (fs, result) = run_cli(
fs,
&mut console,
Args::from(
[
"format",
"--reporter=json-pretty",
file_path1.as_str(),
file_path2.as_str(),
]
.as_slice(),
),
);

assert!(result.is_err(), "run_cli returned {result:?}");

assert_cli_snapshot(SnapshotPayload::new(
module_path!(),
"reports_diagnostics_json_format_command",
fs,
console,
result,
));
}

#[test]
fn reports_diagnostics_json_check_command_file() {
let fs = MemoryFileSystem::default();
let mut console = BufferConsole::default();

let file_path1 = Utf8Path::new("main.ts");
fs.insert(file_path1.into(), MAIN_1.as_bytes());

let file_path2 = Utf8Path::new("index.ts");
fs.insert(file_path2.into(), MAIN_2.as_bytes());

let (fs, result) = run_cli(
fs,
&mut console,
Args::from(
[
"check",
"--reporter=json-pretty",
"--reporter-file=file.json",
file_path1.as_str(),
file_path2.as_str(),
]
.as_slice(),
),
);

assert!(result.is_err(), "run_cli returned {result:?}");

assert_cli_snapshot(SnapshotPayload::new(
module_path!(),
"reports_diagnostics_json_check_command_file",
fs,
console,
result,
));
}
Loading