SupercovBlog

Guide

Vitest coverage exclude: files, folders and lines

The patterns and comments that work in Vitest 5.1, and five that look right but are not, each one measured.

Not imported? Not in the report. White file cards standing in a brushed-aluminum rack, with one violet card left outside it.

Put the pattern in test.coverage.exclude, write it from the project root and end folders with /**: src/generated/**. To ignore lines inside a file, use a /* v8 ignore */ comment.

Exclude patterns at a glance

To excludeWrite
A foldercoverage.exclude: ['src/generated/**']
A folder name anywherecoverage.exclude: ['**/generated/**']
One filecoverage.exclude: ['src/legacy/old-pricing.ts']
Files by namecoverage.exclude: ['**/*.config.ts']
From the command line--coverage.exclude='src/generated/**'
The next statement/* v8 ignore next */
An if branch/* v8 ignore if */
A block of lines/* v8 ignore start */ … /* v8 ignore stop */

Test files never need excluding. Vitest leaves them out of the report, even a test file inside src that your include pattern matches.

We ran every setup here with Vitest 5.1.0 and Node.js 24. The test project has four source files. One of them, src/legacy/old-pricing.ts, no test imports.

Exclude files and folders

import { defineConfig } from 'vitest/config'

export default defineConfig({
  test: {
    coverage: {
      include: ['src/**/*.ts'],
      exclude: ['src/generated/**', 'src/legacy/**'],
    },
  },
})

The report then listed two files, 9 of 9 lines. Patterns are globs matched against each file’s path from the project root.

include matters as much as exclude. By default Vitest reports only the files your tests import. Our unimported legacy file was not in the report until we added include: ['src/**/*.ts']; then it showed at 0 of 1 lines. So a file you never import is already out of the report, and it appears only when include brings it in.

A diagram. Files the tests import are always in the report; files matched by include are added, even at 0%. coverage.exclude removes files by globs from the project root, and test files are never included. In our run 3 imported files plus 1 from include made 4; excluding src/generated/** and src/legacy/** left 2 files, 9 of 9 lines.
Which files reach Vitest's coverage report, and where coverage.exclude applies.

Ignore lines with comments

function format(cents: number): string {
  /* v8 ignore if */
  if (cents < 0) {
    return `-${format(-cents)}`
  }
  return `${Math.floor(cents / 100)}.${String(cents % 100).padStart(2, '0')}`
}

Our receipt.ts has this hint and one /* v8 ignore next */. Without the two comments it had 3 of 5 lines and 1 of 2 branches; with them, 3 of 3 lines and no branches left to cover. next skips the next statement, and start / stop skip everything between them.

The docs suggest -- @preserve, as in /* v8 ignore if -- @preserve */, because older builds stripped comments from TypeScript. In Vitest 5.1.0 the hints worked both with and without it.

On the default V8 provider, /* istanbul ignore if */ worked too. On the Istanbul provider only istanbul hints work: the v8 ignore comments were ignored and the file went back to 3 of 5 lines.

Why your Vitest coverage exclude isn’t working

What we triedWhat happened
exclude: ['generated']Nothing excluded: the pattern is matched from the root
exclude: ['src/generated'] or 'src/generated/'The imported file stayed in the report
exclude: ['src/legacy'], a folder no test importsExcluded
test.exclude instead of test.coverage.excludeNo change: it picks which test files run
v8 ignore comments with provider: 'istanbul'Ignored
A grid of six setups against four files. Without include, legacy is missing, 10 of 11 lines. include adds it, 10 of 12. Excluding src/generated/** leaves pricing and receipt, 9 of 9. Excluding generated keeps all four. Excluding src/generated without /** keeps the imported generated file and drops only legacy. test.exclude keeps all four.
What each setup left in the report, measured with Vitest 5.1.0.

The second and third rows disagree. The docs say a pattern without a wildcard counts as a folder, and that held only for files Vitest found through include. Files your tests import need the /**. Write it every time.

You may have seen ...coverageConfigDefaults.exclude spread into exclude. In Vitest 5.1.0 it is an empty list, so it changes nothing.

What Supercov adds

npx supercov -- npx vitest run
npx supercov runs latest gaps

Supercov runs the same command and records which test covered each line, with branches and MC/DC, without Vitest’s coverage provider. It decides what counts from your source directories, not from coverage.exclude. See Vitest coverage: V8 vs Istanbul for how the two providers compare, and per-test coverage in Vitest.