Skip to content

Commit e23f494

Browse files
committed
feat(cli): multi reporters
1 parent 7c85bf0 commit e23f494

File tree

3 files changed

+131
-115
lines changed

3 files changed

+131
-115
lines changed

.changeset/slimy-files-feel.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+
Support multiple `--reporter` options

crates/biome_cli/src/cli_options.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,10 @@ pub struct CliOptions {
5757
#[bpaf(
5858
long("reporter"),
5959
argument("json|json-pretty|github|junit|summary|gitlab|checkstyle|rdjson"),
60-
fallback(CliReporter::default())
60+
fallback(CliReporter::default()),
61+
many
6162
)]
62-
pub reporter: CliReporter,
63+
pub reporter: Vec<CliReporter>,
6364

6465
/// Optional path to redirect log messages to.
6566
///

crates/biome_cli/src/execute/mod.rs

Lines changed: 123 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ use tracing::{info, instrument};
4141
#[derive(Debug, Clone)]
4242
pub struct Execution {
4343
/// How the information should be collected and reported
44-
report_mode: ReportMode,
44+
report_mode: Vec<ReportMode>,
4545

4646
/// The modality of execution of the traversal
4747
traversal_mode: TraversalMode,
@@ -283,7 +283,7 @@ impl From<CliReporter> for ReportMode {
283283
impl Execution {
284284
pub(crate) fn new(mode: TraversalMode) -> Self {
285285
Self {
286-
report_mode: ReportMode::default(),
286+
report_mode: Vec::from([ReportMode::default()]),
287287
traversal_mode: mode,
288288
max_diagnostics: 20,
289289
}
@@ -300,7 +300,7 @@ impl Execution {
300300
.is_some_and(|value| value == "true");
301301

302302
Self {
303-
report_mode: ReportMode::default(),
303+
report_mode: Vec::from([ReportMode::default()]),
304304
traversal_mode: TraversalMode::CI {
305305
environment: if is_github {
306306
Some(ExecutionEnvironment::GitHub)
@@ -317,7 +317,11 @@ impl Execution {
317317

318318
/// It sets the reporting mode by reading the [CliOptions]
319319
pub(crate) fn set_report(mut self, cli_options: &CliOptions) -> Self {
320-
self.report_mode = cli_options.reporter.clone().into();
320+
self.report_mode = cli_options
321+
.reporter
322+
.iter()
323+
.map(|reporter| reporter.clone().into())
324+
.collect::<Vec<ReportMode>>();
321325
self
322326
}
323327

@@ -455,12 +459,12 @@ impl Execution {
455459
stdin: None,
456460
vcs_targeted,
457461
},
458-
report_mode: ReportMode::default(),
462+
report_mode: Vec::from([ReportMode::default()]),
459463
max_diagnostics: 0,
460464
}
461465
}
462466

463-
pub fn report_mode(&self) -> &ReportMode {
467+
pub fn report_mode(&self) -> &Vec<ReportMode> {
464468
&self.report_mode
465469
}
466470
pub(crate) fn to_feature(&self) -> FeatureName {
@@ -532,12 +536,15 @@ pub fn execute_mode(
532536
project_key: ProjectKey,
533537
) -> Result<(), CliDiagnostic> {
534538
// If a custom reporter was provided, let's lift the limit so users can see all of them
535-
execution.max_diagnostics = if cli_options.reporter.is_default() {
539+
execution.max_diagnostics = if cli_options
540+
.reporter
541+
.iter()
542+
.any(|reporter| reporter.is_default())
543+
{
536544
cli_options.max_diagnostics.into()
537545
} else {
538546
info!(
539-
"Removing the limit of --max-diagnostics, because of a reporter different from the default one: {}",
540-
cli_options.reporter
547+
"Removing the limit of --max-diagnostics, because reporter(s) does not include the default one"
541548
);
542549
u32::MAX
543550
};
@@ -612,124 +619,127 @@ pub fn execute_mode(
612619
max_diagnostics: cli_options.max_diagnostics,
613620
};
614621

615-
match execution.report_mode {
616-
ReportMode::Terminal { with_summary } => {
617-
if with_summary {
618-
let reporter = SummaryReporter {
622+
for report_mode in execution.report_mode {
623+
match report_mode {
624+
ReportMode::Terminal { with_summary } => {
625+
if with_summary {
626+
let reporter = SummaryReporter {
627+
summary,
628+
diagnostics_payload,
629+
execution: execution.clone(),
630+
verbose: cli_options.verbose,
631+
working_directory: fs.working_directory().clone(),
632+
evaluated_paths: evaluated_paths.clone(),
633+
};
634+
reporter.write(&mut SummaryReporterVisitor(console))?;
635+
} else {
636+
let reporter = ConsoleReporter {
637+
summary,
638+
diagnostics_payload,
639+
execution: execution.clone(),
640+
evaluated_paths: evaluated_paths.clone(),
641+
verbose: cli_options.verbose,
642+
working_directory: fs.working_directory().clone(),
643+
};
644+
reporter.write(&mut ConsoleReporterVisitor(console))?;
645+
}
646+
}
647+
ReportMode::Json { pretty } => {
648+
console.error(markup! {
649+
<Warn>"The "<Emphasis>"--json"</Emphasis>" option is "<Underline>"unstable/experimental"</Underline>" and its output might change between patches/minor releases."</Warn>
650+
});
651+
let reporter = JsonReporter {
619652
summary,
653+
diagnostics: diagnostics_payload,
654+
execution: execution.clone(),
655+
verbose: cli_options.verbose,
656+
working_directory: fs.working_directory().clone(),
657+
};
658+
let mut buffer = JsonReporterVisitor::new(summary);
659+
reporter.write(&mut buffer)?;
660+
if pretty {
661+
let content = serde_json::to_string(&buffer).map_err(|error| {
662+
CliDiagnostic::Report(ReportDiagnostic::Serialization(
663+
SerdeJsonError::from(error),
664+
))
665+
})?;
666+
let report_file = BiomePath::new(TEMPORARY_INTERNAL_REPORTER_FILE);
667+
session.app.workspace.open_file(OpenFileParams {
668+
project_key,
669+
content: FileContent::from_client(content),
670+
path: report_file.clone(),
671+
document_file_source: None,
672+
persist_node_cache: false,
673+
})?;
674+
let code = session.app.workspace.format_file(FormatFileParams {
675+
project_key,
676+
path: report_file.clone(),
677+
})?;
678+
console.log(markup! {
679+
{code.as_code()}
680+
});
681+
session.app.workspace.close_file(CloseFileParams {
682+
project_key,
683+
path: report_file,
684+
})?;
685+
} else {
686+
console.log(markup! {
687+
{buffer}
688+
});
689+
}
690+
}
691+
ReportMode::GitHub => {
692+
let reporter = GithubReporter {
620693
diagnostics_payload,
621694
execution: execution.clone(),
622695
verbose: cli_options.verbose,
623696
working_directory: fs.working_directory().clone(),
624-
evaluated_paths,
625697
};
626-
reporter.write(&mut SummaryReporterVisitor(console))?;
627-
} else {
628-
let reporter = ConsoleReporter {
698+
reporter.write(&mut GithubReporterVisitor(console))?;
699+
}
700+
ReportMode::GitLab => {
701+
let reporter = GitLabReporter {
702+
diagnostics: diagnostics_payload,
703+
execution: execution.clone(),
704+
verbose: cli_options.verbose,
705+
working_directory: fs.working_directory().clone(),
706+
};
707+
reporter.write(&mut GitLabReporterVisitor::new(
708+
console,
709+
session.app.workspace.fs().working_directory(),
710+
))?;
711+
}
712+
ReportMode::Junit => {
713+
let reporter = JunitReporter {
629714
summary,
630715
diagnostics_payload,
631716
execution: execution.clone(),
632-
evaluated_paths,
633717
verbose: cli_options.verbose,
634718
working_directory: fs.working_directory().clone(),
635719
};
636-
reporter.write(&mut ConsoleReporterVisitor(console))?;
720+
reporter.write(&mut JunitReporterVisitor::new(console))?;
637721
}
638-
}
639-
ReportMode::Json { pretty } => {
640-
console.error(markup! {
641-
<Warn>"The "<Emphasis>"--json"</Emphasis>" option is "<Underline>"unstable/experimental"</Underline>" and its output might change between patches/minor releases."</Warn>
642-
});
643-
let reporter = JsonReporter {
644-
summary,
645-
diagnostics: diagnostics_payload,
646-
execution: execution.clone(),
647-
verbose: cli_options.verbose,
648-
working_directory: fs.working_directory().clone(),
649-
};
650-
let mut buffer = JsonReporterVisitor::new(summary);
651-
reporter.write(&mut buffer)?;
652-
if pretty {
653-
let content = serde_json::to_string(&buffer).map_err(|error| {
654-
CliDiagnostic::Report(ReportDiagnostic::Serialization(SerdeJsonError::from(
655-
error,
656-
)))
657-
})?;
658-
let report_file = BiomePath::new(TEMPORARY_INTERNAL_REPORTER_FILE);
659-
session.app.workspace.open_file(OpenFileParams {
660-
project_key,
661-
content: FileContent::from_client(content),
662-
path: report_file.clone(),
663-
document_file_source: None,
664-
persist_node_cache: false,
665-
})?;
666-
let code = session.app.workspace.format_file(FormatFileParams {
667-
project_key,
668-
path: report_file.clone(),
669-
})?;
670-
console.log(markup! {
671-
{code.as_code()}
672-
});
673-
session.app.workspace.close_file(CloseFileParams {
674-
project_key,
675-
path: report_file,
676-
})?;
677-
} else {
678-
console.log(markup! {
679-
{buffer}
680-
});
722+
ReportMode::Checkstyle => {
723+
let reporter = CheckstyleReporter {
724+
summary,
725+
diagnostics_payload,
726+
execution: execution.clone(),
727+
verbose: cli_options.verbose,
728+
working_directory: fs.working_directory().clone(),
729+
};
730+
reporter.write(
731+
&mut crate::reporter::checkstyle::CheckstyleReporterVisitor::new(console),
732+
)?;
733+
}
734+
ReportMode::RdJson => {
735+
let reporter = RdJsonReporter {
736+
diagnostics_payload,
737+
execution: execution.clone(),
738+
verbose: cli_options.verbose,
739+
working_directory: fs.working_directory().clone(),
740+
};
741+
reporter.write(&mut RdJsonReporterVisitor(console))?;
681742
}
682-
}
683-
ReportMode::GitHub => {
684-
let reporter = GithubReporter {
685-
diagnostics_payload,
686-
execution: execution.clone(),
687-
verbose: cli_options.verbose,
688-
working_directory: fs.working_directory().clone(),
689-
};
690-
reporter.write(&mut GithubReporterVisitor(console))?;
691-
}
692-
ReportMode::GitLab => {
693-
let reporter = GitLabReporter {
694-
diagnostics: diagnostics_payload,
695-
execution: execution.clone(),
696-
verbose: cli_options.verbose,
697-
working_directory: fs.working_directory().clone(),
698-
};
699-
reporter.write(&mut GitLabReporterVisitor::new(
700-
console,
701-
session.app.workspace.fs().working_directory(),
702-
))?;
703-
}
704-
ReportMode::Junit => {
705-
let reporter = JunitReporter {
706-
summary,
707-
diagnostics_payload,
708-
execution: execution.clone(),
709-
verbose: cli_options.verbose,
710-
working_directory: fs.working_directory().clone(),
711-
};
712-
reporter.write(&mut JunitReporterVisitor::new(console))?;
713-
}
714-
ReportMode::Checkstyle => {
715-
let reporter = CheckstyleReporter {
716-
summary,
717-
diagnostics_payload,
718-
execution: execution.clone(),
719-
verbose: cli_options.verbose,
720-
working_directory: fs.working_directory().clone(),
721-
};
722-
reporter
723-
.write(&mut crate::reporter::checkstyle::CheckstyleReporterVisitor::new(console))?;
724-
}
725-
ReportMode::RdJson => {
726-
let reporter = RdJsonReporter {
727-
diagnostics_payload,
728-
execution: execution.clone(),
729-
verbose: cli_options.verbose,
730-
working_directory: fs.working_directory().clone(),
731-
};
732-
reporter.write(&mut RdJsonReporterVisitor(console))?;
733743
}
734744
}
735745

0 commit comments

Comments
 (0)