Repository navigation
Description
Describe the bug
When one source file is loaded by a node project and a browser project, coverage-v8 maps one statement to two different end columns: the node copy ends at a column number, the browser copy ends at null. Istanbul keys statements by the whole range, so the report holds the statement twice.
With sharding this produces false uncovered statements. A shard where the file is loaded but the statement never runs reports 3:20-3:29 with 0 hits. A browser shard reports 3:20-3:null with 1 hit. --merge-reports keeps both, so the merged report shows an uncovered statement while lines stay at 100%. We hit this in CI on 196 statements across 15 files.
It needs a destructuring declaration whose initializer is a call (const { label } = useThing();). Plain declarations map to null in both projects and merge correctly.
Reproduction
https://github.com/sumanthratna/vitest-coverage-browser-end-column (pnpm install && pnpm exec playwright install chromium && pnpm test; the README prints the statement ranges)
src/ctx.ts
export function useThing() { return { label: 'world' }; }src/plain.ts
import { useThing } from './ctx';
export function greet() {
const { label } = useThing();
return label;
}src/plain.test.ts (node project)
import { expect, test } from 'vitest';
import { greet } from './plain';
test('loads', () => expect(typeof greet).toBe('function'));src/plain.test.tsx (browser project)
import { expect, test } from 'vitest';
import { greet } from './plain';
test('p', () => expect(greet()).toBe('world'));vitest.config.ts
import { playwright } from '@vitest/browser-playwright';
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
projects: [
{ extends: true, test: { name: 'node', include: ['src/**/*.test.ts'] } },
{ extends: true, test: { name: 'browser', include: ['src/**/*.test.tsx'],
browser: { enabled: true, headless: true, provider: playwright(), instances: [{ browser: 'chromium' }] } } },
],
coverage: { provider: 'v8', include: ['src/plain.ts'], reporter: ['json'] },
},
});Run vitest run --coverage. coverage/coverage-final.json for src/plain.ts has three statements for a two-statement file:
statementMap: 3:20-3:29, 4:2-4:null, 3:20-3:null
Sharded (--shard=1/2 and --shard=2/2 with --reporter=blob, then --merge-reports --coverage), the merged report has 3:20-3:29 hits=0 beside 3:20-3:null hits=1.
Expected: one statement at 3:20 with the same end column from both projects, so hits add up.
Same result on 4.1.12 and 5.1.0. A node-only config with the same files maps both shards to 3:20-3:29 and merges correctly.
System Info
vitest 5.1.0 and 4.1.12, @vitest/coverage-v8 (same versions), @vitest/browser-playwright (same versions), playwright 1.64.1, Node 22.23.2, macOS (also seen on GitHub Actions ubuntu-latest).
Used Package Manager
pnpm
Activity
AriPerkkio commentedon Nov 9, 2026
This is same as #9366 but happens without any third party plugins. We'll need something like vitest-dev/istanbuljs#25 or source map ending adjustment on oxc side for the fix.