Skip to content

fix(cli): json reporter#9221

Merged
ematipico merged 1 commit intomainfrom
fix/json-reporter
Feb 24, 2026
Merged

fix(cli): json reporter#9221
ematipico merged 1 commit intomainfrom
fix/json-reporter

Conversation

@ematipico
Copy link
Member

Summary

Found it while working on the ecosystem CI.

  • Removed unused serde implementation
  • added duration and scannerDuration to the report

Used AI to remove the code we didn't need. The rest wa me

Test Plan

We didn't have tests! I added them :)

Docs

I'll have to update the docs after we land this

@changeset-bot
Copy link

changeset-bot bot commented Feb 24, 2026

🦋 Changeset detected

Latest commit: 3e07005

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 13 packages
Name Type
@biomejs/biome Patch
@biomejs/cli-win32-x64 Patch
@biomejs/cli-win32-arm64 Patch
@biomejs/cli-darwin-x64 Patch
@biomejs/cli-darwin-arm64 Patch
@biomejs/cli-linux-x64 Patch
@biomejs/cli-linux-arm64 Patch
@biomejs/cli-linux-x64-musl Patch
@biomejs/cli-linux-arm64-musl Patch
@biomejs/wasm-web Patch
@biomejs/wasm-bundler Patch
@biomejs/wasm-nodejs Patch
@biomejs/backend-jsonrpc Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@ematipico ematipico requested review from a team February 24, 2026 08:49
@github-actions github-actions bot added the A-CLI Area: CLI label Feb 24, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 24, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9150b25 and 3e07005.

⛔ Files ignored due to path filters (9)
  • crates/biome_cli/tests/snapshots/main_cases_reporter_json/reports_diagnostics_json_check_command.snap is excluded by !**/*.snap and included by **
  • crates/biome_cli/tests/snapshots/main_cases_reporter_json/reports_diagnostics_json_check_command_file.snap is excluded by !**/*.snap and included by **
  • crates/biome_cli/tests/snapshots/main_cases_reporter_json/reports_diagnostics_json_ci_command.snap is excluded by !**/*.snap and included by **
  • crates/biome_cli/tests/snapshots/main_cases_reporter_json/reports_diagnostics_json_format_command.snap is excluded by !**/*.snap and included by **
  • crates/biome_cli/tests/snapshots/main_cases_reporter_json/reports_diagnostics_json_lint_command.snap is excluded by !**/*.snap and included by **
  • crates/biome_cli/tests/snapshots/main_commands_check/print_json.snap is excluded by !**/*.snap and included by **
  • crates/biome_cli/tests/snapshots/main_commands_check/print_json_pretty.snap is excluded by !**/*.snap and included by **
  • crates/biome_cli/tests/snapshots/main_commands_format/print_json.snap is excluded by !**/*.snap and included by **
  • crates/biome_cli/tests/snapshots/main_commands_format/print_json_pretty.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (5)
  • .changeset/breezy-parrots-fold.md
  • crates/biome_cli/src/reporter/json.rs
  • crates/biome_cli/src/reporter/mod.rs
  • crates/biome_cli/tests/cases/mod.rs
  • crates/biome_cli/tests/cases/reporter_json.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • crates/biome_cli/tests/cases/reporter_json.rs
  • crates/biome_cli/tests/cases/mod.rs

Walkthrough

This pull request refactors the JSON reporter by removing serde-derived serialization from reporter structs and switching to manual JSON construction. It adds command duration (and optional scannerDuration) to JSON output, using a placeholder string in debug builds and millisecond numbers in non-debug builds. A changeset documents a patch release for @biomejs/biome. New tests exercise the JSON reporter across multiple CLI commands and a reporter-file override.

Suggested labels

A-Diagnostic, L-JSON

Suggested reviewers

  • siketyan
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title refers to a real part of the change (JSON reporter fixes), but lacks specificity about the actual improvements (added duration fields, removed unused serde).
Description check ✅ Passed The description clearly relates to the changeset: it mentions removing unused serde implementation, adding duration and scannerDuration fields, and adding tests—all evident in the code changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/json-reporter

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/biome_cli/src/reporter/mod.rs (1)

36-54: ⚠️ Potential issue | 🟡 Minor

Remove unused Serialize derive from TraversalSummary.

The Serialize derive (and its import) is dead code—TraversalSummary is never serialised directly. json.rs manually constructs JSON output via factory functions; other reporters serialise their own structs, not TraversalSummary itself. Safe to remove both the derive and the use serde::Serialize import.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_cli/src/reporter/mod.rs` around lines 36 - 54, Remove the unused
Serialize derive and its import for TraversalSummary: delete Serialize from the
#[derive(...)] on the TraversalSummary struct and remove the corresponding use
serde::Serialize import; keep the other derives (Debug, Default, Copy, Clone)
and the #[serde(rename_all = "camelCase")] attribute if still needed for any
serde usage, and ensure json.rs continues to construct JSON via its factory
functions without relying on TraversalSummary being serialized directly.
🧹 Nitpick comments (3)
crates/biome_cli/tests/cases/reporter_json.rs (2)

8-26: MAIN_1 and MAIN_2 are byte-for-byte identical.

Two constants, one soul. If the intent is to test the reporter across multiple files, giving them different content (e.g., different lint violations) would make the snapshots more meaningful and easier to read.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_cli/tests/cases/reporter_json.rs` around lines 8 - 26, The two
test constants MAIN_1 and MAIN_2 are identical, so update one (e.g., MAIN_2) to
contain different source text to exercise different reporter output; change
MAIN_2 to introduce a distinct lint scenario (for example alter spacing, change
a token to trigger a different rule, remove or add a line, or introduce a
different error like a missing semicolon or unused variable) so the reporter
snapshots differ—locate the constants MAIN_1 and MAIN_2 in the test and replace
MAIN_2's string with the modified source that produces a different set of
diagnostics.

29-207: Heavy boilerplate repetition across all five tests — consider a small helper.

Each test recreates the same MemoryFileSystem + BufferConsole + two-file insert + run_cli + assert_cli_snapshot cycle. Extracting a helper keeps future additions down to a couple of lines.

♻️ Sketch of a shared helper
+fn run_json_reporter(verb: &str, extra_args: &[&str]) -> (MemoryFileSystem, biome_console::BufferConsole, Result<(), _>) {
+    let fs = MemoryFileSystem::default();
+    let mut console = BufferConsole::default();
+    fs.insert(Utf8Path::new("main.ts").into(), MAIN_1.as_bytes());
+    fs.insert(Utf8Path::new("index.ts").into(), MAIN_2.as_bytes());
+    let mut args = vec![verb, "--reporter=json-pretty", "main.ts", "index.ts"];
+    args.extend_from_slice(extra_args);
+    let (fs, result) = run_cli(fs, &mut console, Args::from(args.as_slice()));
+    (fs, console, result)
+}
+
 #[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", "main.ts", "index.ts"].as_slice()));
+    let (fs, console, result) = run_json_reporter("check", &[]);
     assert!(result.is_err(), "run_cli returned {result:?}");
     assert_cli_snapshot(SnapshotPayload::new(module_path!(), "reports_diagnostics_json_check_command", fs, console, result));
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_cli/tests/cases/reporter_json.rs` around lines 29 - 207, Extract
the repeated setup+execution+assertion into a small helper (e.g., fn
run_report_test(args: &[&str], snapshot_name: &str) -> () ) that creates
MemoryFileSystem and BufferConsole, inserts MAIN_1 and MAIN_2 into the fs, calls
run_cli with Args::from(args), asserts result.is_err(), and calls
assert_cli_snapshot with SnapshotPayload::new(module_path!(), snapshot_name, fs,
console, result); then replace the bodies of the tests
reports_diagnostics_json_check_command, reports_diagnostics_json_ci_command,
reports_diagnostics_json_lint_command, reports_diagnostics_json_format_command,
and reports_diagnostics_json_check_command_file to call this helper with the
appropriate argument arrays (e.g., ["check","--reporter=json-pretty", file1,
file2]) and snapshot names to remove boilerplate.
crates/biome_cli/src/reporter/mod.rs (1)

137-153: Optional: group scannerDuration adjacent to duration in the output.

Currently scannerDuration is appended after diagnosticsNotPrinted, so in the serialised JSON it sits far from its sibling duration. A reader of the output has to hunt for the related field. Consider collecting both duration fields together near the top of the members list.

♻️ Suggested restructure
-        let mut members = vec![
+        // Build duration block first so related fields stay adjacent
+        #[cfg(debug_assertions)]
+        let scanner_duration_member = self.scanner_duration.map(|_| {
+            json_member(
+                AnyJsonMemberName::JsonMemberName(json_member_name(json_string_literal("scannerDuration"))),
+                token(T![:]),
+                AnyJsonValue::JsonStringValue(json_string_value(json_string_literal("<TIME>"))),
+            )
+        });
+        #[cfg(not(debug_assertions))]
+        let scanner_duration_member = self.scanner_duration.map(|d| {
+            json_member(
+                AnyJsonMemberName::JsonMemberName(json_member_name(json_string_literal("scannerDuration"))),
+                token(T![:]),
+                AnyJsonValue::JsonNumberValue(json_number_value(json_number_literal(d.as_millis()))),
+            )
+        });
+
+        let mut members = vec![
             json_member(/* changed */...),
             json_member(/* unchanged */...),
             json_member(/* matches */...),
             json_member(/* duration */ ..., duration_value),
+            // scannerDuration immediately follows duration when present
         ];
+        if let Some(m) = scanner_duration_member {
+            members.push(m);
+        }
         members.extend([
             json_member(/* errors */...),
             // … rest of fields …
         ]);
-
-        if let Some(_scanner_duration) = self.scanner_duration {
-            // … build & push scannerDuration …
-        }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_cli/src/reporter/mod.rs` around lines 137 - 153, The
scannerDuration member is currently pushed after diagnosticsNotPrinted which
separates it from its sibling duration; move the scannerDuration construction &
members.push block (the code referencing self.scanner_duration,
_scanner_duration, AnyJsonValue::JsonStringValue/JsonNumberValue, and
json_member with "scannerDuration") so it is inserted immediately adjacent to
where duration is assembled and pushed into members (the existing
duration-related code that creates the duration member), preserving the same
cfg(debug_assertions) and value construction logic; remove the original
scannerDuration push to avoid duplication and ensure the members order places
"duration" and "scannerDuration" consecutively near the top of the members list.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@crates/biome_cli/src/reporter/mod.rs`:
- Around line 36-54: Remove the unused Serialize derive and its import for
TraversalSummary: delete Serialize from the #[derive(...)] on the
TraversalSummary struct and remove the corresponding use serde::Serialize
import; keep the other derives (Debug, Default, Copy, Clone) and the
#[serde(rename_all = "camelCase")] attribute if still needed for any serde
usage, and ensure json.rs continues to construct JSON via its factory functions
without relying on TraversalSummary being serialized directly.

---

Nitpick comments:
In `@crates/biome_cli/src/reporter/mod.rs`:
- Around line 137-153: The scannerDuration member is currently pushed after
diagnosticsNotPrinted which separates it from its sibling duration; move the
scannerDuration construction & members.push block (the code referencing
self.scanner_duration, _scanner_duration,
AnyJsonValue::JsonStringValue/JsonNumberValue, and json_member with
"scannerDuration") so it is inserted immediately adjacent to where duration is
assembled and pushed into members (the existing duration-related code that
creates the duration member), preserving the same cfg(debug_assertions) and
value construction logic; remove the original scannerDuration push to avoid
duplication and ensure the members order places "duration" and "scannerDuration"
consecutively near the top of the members list.

In `@crates/biome_cli/tests/cases/reporter_json.rs`:
- Around line 8-26: The two test constants MAIN_1 and MAIN_2 are identical, so
update one (e.g., MAIN_2) to contain different source text to exercise different
reporter output; change MAIN_2 to introduce a distinct lint scenario (for
example alter spacing, change a token to trigger a different rule, remove or add
a line, or introduce a different error like a missing semicolon or unused
variable) so the reporter snapshots differ—locate the constants MAIN_1 and
MAIN_2 in the test and replace MAIN_2's string with the modified source that
produces a different set of diagnostics.
- Around line 29-207: Extract the repeated setup+execution+assertion into a
small helper (e.g., fn run_report_test(args: &[&str], snapshot_name: &str) -> ()
) that creates MemoryFileSystem and BufferConsole, inserts MAIN_1 and MAIN_2
into the fs, calls run_cli with Args::from(args), asserts result.is_err(), and
calls assert_cli_snapshot with SnapshotPayload::new(module_path!(),
snapshot_name, fs, console, result); then replace the bodies of the tests
reports_diagnostics_json_check_command, reports_diagnostics_json_ci_command,
reports_diagnostics_json_lint_command, reports_diagnostics_json_format_command,
and reports_diagnostics_json_check_command_file to call this helper with the
appropriate argument arrays (e.g., ["check","--reporter=json-pretty", file1,
file2]) and snapshot names to remove boilerplate.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between de4c5f6 and 9150b25.

⛔ Files ignored due to path filters (5)
  • crates/biome_cli/tests/snapshots/main_cases_reporter_json/reports_diagnostics_json_check_command.snap is excluded by !**/*.snap and included by **
  • crates/biome_cli/tests/snapshots/main_cases_reporter_json/reports_diagnostics_json_check_command_file.snap is excluded by !**/*.snap and included by **
  • crates/biome_cli/tests/snapshots/main_cases_reporter_json/reports_diagnostics_json_ci_command.snap is excluded by !**/*.snap and included by **
  • crates/biome_cli/tests/snapshots/main_cases_reporter_json/reports_diagnostics_json_format_command.snap is excluded by !**/*.snap and included by **
  • crates/biome_cli/tests/snapshots/main_cases_reporter_json/reports_diagnostics_json_lint_command.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (5)
  • .changeset/breezy-parrots-fold.md
  • crates/biome_cli/src/reporter/json.rs
  • crates/biome_cli/src/reporter/mod.rs
  • crates/biome_cli/tests/cases/mod.rs
  • crates/biome_cli/tests/cases/reporter_json.rs

@ematipico ematipico merged commit 4612133 into main Feb 24, 2026
13 checks passed
@ematipico ematipico deleted the fix/json-reporter branch February 24, 2026 09:15
@github-actions github-actions bot mentioned this pull request Feb 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-CLI Area: CLI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants