Problem
Vitest now documents .vitest/ as the shared location for generated reports and artifacts. The blob reporter already follows this (.vitest/blob/), but json and junit do not.
Currently, when no outputFile is configured:
json prints the report to stdout (json.ts:231-248)
junit prints the report to stdout (junit.ts:249-273)
This is inconsistent with the .vitest/ convention and with the blob reporter, and means these reporters produce no on-disk artifact by default.
In practice, "print to stdout" pushes users into awkward workarounds to get the file they actually wanted. Common patterns in the wild:
- Redirect to a file, then upload as a CI artifact:
vitest run --reporter=json > vitest-report.json then upload it (e.g. mizchi/flaker .github/workflows/ci.yml, which redirects → uploads → re-imports the report in a later job).
- Pipe into a post-processor:
vitest --reporter=json | jq '.numFailedTests'.
- Redirect a bench/report run:
vitest bench --run --reporter=json > benchmark-results.json (e.g. thibmeu/act-ts).
A scoped .vitest/ default serves all of these better, with zero config:
- Artifact upload: just run
vitest --reporter=json and upload .vitest/json/output.json — no redirect.
- Post-analysis:
jq '.numFailedTests' .vitest/json/output.json — and it's re-runnable without re-running the tests, since the artifact persists.
So the stdout default isn't a feature these workflows rely on; it's the limitation they work around. Moving to .vitest/ removes the workaround.
Goal
When the json and junit reporters use their default output (no outputFile), write to a scoped directory under .vitest/:
.vitest/
json/
output.json
junit/
output.xml
This mirrors the existing blob behavior (blob.ts:88-116): explicit outputFile always wins; otherwise fall back to createReport(scope) + a default filename.
Desired behavior
- No
outputFile configured:
json writes .vitest/json/output.json
junit writes .vitest/junit/output.xml
- Explicit
outputFile (string or outputFile.json / outputFile.junit) is respected and still resolved against root, unchanged.
Breaking change
The default changes from "print to stdout" to "write to file". This affects:
- The documented piping workflow
vitest --reporter=json > out.json (docs/guide/reporters.md:63-67, 319, 423).
- Any CI that consumes json/junit from stdout.
Decision: drop stdout entirely; no sentinel opt-in. Every workflow stdout enabled (artifact upload, | jq post-processing) is served better by reading .vitest/json/output.json after a plain vitest --reporter=json. Adding an outputFile: 'stdout' sentinel would just preserve the worse path. Explicit outputFile (file path) is unchanged.
Scope / notes
Problem
Vitest now documents
.vitest/as the shared location for generated reports and artifacts. Theblobreporter already follows this (.vitest/blob/), butjsonandjunitdo not.Currently, when no
outputFileis configured:jsonprints the report to stdout (json.ts:231-248)junitprints the report to stdout (junit.ts:249-273)This is inconsistent with the
.vitest/convention and with theblobreporter, and means these reporters produce no on-disk artifact by default.In practice, "print to stdout" pushes users into awkward workarounds to get the file they actually wanted. Common patterns in the wild:
vitest run --reporter=json > vitest-report.jsonthen upload it (e.g. mizchi/flaker.github/workflows/ci.yml, which redirects → uploads → re-imports the report in a later job).vitest --reporter=json | jq '.numFailedTests'.vitest bench --run --reporter=json > benchmark-results.json(e.g. thibmeu/act-ts).A scoped
.vitest/default serves all of these better, with zero config:vitest --reporter=jsonand upload.vitest/json/output.json— no redirect.jq '.numFailedTests' .vitest/json/output.json— and it's re-runnable without re-running the tests, since the artifact persists.So the stdout default isn't a feature these workflows rely on; it's the limitation they work around. Moving to
.vitest/removes the workaround.Goal
When the
jsonandjunitreporters use their default output (nooutputFile), write to a scoped directory under.vitest/:.vitest/ json/ output.json junit/ output.xmlThis mirrors the existing
blobbehavior (blob.ts:88-116): explicitoutputFilealways wins; otherwise fall back tocreateReport(scope)+ a default filename.Desired behavior
outputFileconfigured:jsonwrites.vitest/json/output.jsonjunitwrites.vitest/junit/output.xmloutputFile(string oroutputFile.json/outputFile.junit) is respected and still resolved againstroot, unchanged.Breaking change
The default changes from "print to stdout" to "write to file". This affects:
vitest --reporter=json > out.json(docs/guide/reporters.md:63-67,319,423).Decision: drop stdout entirely; no sentinel opt-in. Every workflow stdout enabled (artifact upload,
| jqpost-processing) is served better by reading.vitest/json/output.jsonafter a plainvitest --reporter=json. Adding anoutputFile: 'stdout'sentinel would just preserve the worse path. ExplicitoutputFile(file path) is unchanged.Scope / notes
.vitestas the report artifact root #10240 (html). Per Make HTML reporter default output use.vitestas the report artifact root #10240, top-level.vitest/index.html+.vitest/ui/are reserved for the html reporter; other reporters keep scoped subdirectories such as.vitest/json/and.vitest/junit/.docs/guide/reporters.md) and the e2e suites (test/e2e/test/reporters/json.test.ts,junit.test.ts, which mostly assert onstdout) will need updating.