@@ -11,6 +11,7 @@ import type { BenchmarkRun } from '../../types/index.js';
1111import type { CliContext } from '../index.js' ;
1212
1313import { ErrorCodes } from '../../constants.js' ;
14+ import { resolveOutputPath } from '../../core/output-path-resolver.js' ;
1415import {
1516 InvalidArgumentError ,
1617 type ModestBenchError ,
@@ -32,6 +33,7 @@ interface RunOptions {
3233 json ?: boolean | undefined ;
3334 noColor ?: boolean | undefined ;
3435 outputDir ?: string | undefined ;
36+ outputFile ?: string | undefined ;
3537 pattern : string [ ] ;
3638 progress ?: boolean | undefined ;
3739 quiet ?: boolean | undefined ;
@@ -61,6 +63,18 @@ export const handleRunCommand = async (
6163 const showCliMessages = verbose && ! options . quiet ;
6264
6365 try {
66+ // Validate --output-file usage
67+ if (
68+ options . outputFile &&
69+ options . reporters &&
70+ options . reporters . length > 1
71+ ) {
72+ throw new InvalidArgumentError (
73+ '--output-file can only be used with a single reporter. ' +
74+ 'Use --output <dir> for multiple reporters.' ,
75+ ) ;
76+ }
77+
6478 // Step 1: Load and merge configuration
6579 if ( showCliMessages ) {
6680 console . error ( 'Loading configuration...' ) ;
@@ -79,6 +93,7 @@ export const handleRunCommand = async (
7993 showCliMessages ,
8094 options . quiet ?? false ,
8195 options . outputDir ,
96+ options . outputFile ,
8297 options . progress ,
8398 ) ;
8499
@@ -161,10 +176,13 @@ export const handleRunCommand = async (
161176
162177 return handleResults ( executionResult , options , shouldBeQuiet ) ;
163178 } catch ( error ) {
164- // Re-throw FileDiscoveryError so yargs fail handler can show help
179+ // Re-throw CLI errors so yargs fail handler can show help
165180 if ( ( error as ModestBenchError ) . code === ErrorCodes . FILE_DISCOVERY_FAILED ) {
166181 throw error ;
167182 }
183+ if ( ( error as ModestBenchError ) . code === ErrorCodes . CLI_INVALID_ARGUMENT ) {
184+ throw error ;
185+ }
168186
169187 if ( ! shouldBeQuiet ) {
170188 console . error (
@@ -298,6 +316,7 @@ const setupReporters = async (
298316 showCliMessages : boolean ,
299317 explicitQuiet : boolean ,
300318 explicitOutputDir ?: string ,
319+ explicitOutputFile ?: string ,
301320 progressOption ?: boolean ,
302321) => {
303322 try {
@@ -328,17 +347,27 @@ const setupReporters = async (
328347 verbose : isVerbose ,
329348 } ) ;
330349 } else if ( reporterName === 'json' ) {
350+ const outputPath = resolveOutputPath (
351+ outputDir ,
352+ explicitOutputFile ,
353+ 'results.json' ,
354+ ) ;
331355 reporter = new JsonReporter ( {
332- ...( outputDir ? { outputPath : ` ${ outputDir } /results.json` } : { } ) ,
356+ ...( outputPath ? { outputPath } : { } ) ,
333357 prettyPrint : true ,
334358 quiet : shouldBeQuiet , // JSON uses shouldBeQuiet to avoid polluting stdout
335359 verbose : isVerbose ,
336360 } ) ;
337361 } else if ( reporterName === 'csv' ) {
362+ const outputPath = resolveOutputPath (
363+ outputDir ,
364+ explicitOutputFile ,
365+ 'results.csv' ,
366+ ) ;
338367 reporter = new CsvReporter ( {
339368 includeHeaders : true ,
340369 includeMetadata : true ,
341- ...( outputDir ? { outputPath : ` ${ outputDir } /results.csv` } : { } ) ,
370+ ...( outputPath ? { outputPath } : { } ) ,
342371 quiet : explicitQuiet , // Only applies explicit --quiet flag; CSV output can coexist with progress messages on different streams
343372 verbose : isVerbose ,
344373 } ) ;
0 commit comments