or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

coverage-provider-browser.mdcoverage-provider-node.mdindex.mdv8-coverage-provider.md
tile.json

v8-coverage-provider.mddocs/

V8 Coverage Provider

The V8CoverageProvider class is the core implementation of the coverage workflow. It processes raw V8 coverage data, converts it to Istanbul format using AST-based remapping, handles source maps, generates coverage reports, and validates coverage thresholds.

Core Import

import { V8CoverageProvider } from '@vitest/coverage-v8/provider';

Note: The provider is typically instantiated via the getProvider() method of the coverage module, not imported directly.

Capabilities

Class Definition

class V8CoverageProvider extends BaseCoverageProvider<ResolvedCoverageOptions<'v8'>> {
  /** Provider identifier */
  readonly name: 'v8';

  /** Version of the coverage provider package */
  version: string;

  /**
   * Initialize the provider with Vitest context
   * @param ctx - Vitest instance
   */
  initialize(ctx: Vitest): void;

  /**
   * Create an empty Istanbul coverage map
   * @returns Empty CoverageMap instance
   */
  createCoverageMap(): CoverageMap;

  /**
   * Generate coverage map from collected V8 coverage data
   * @param options - Report context with metadata
   * @returns Promise resolving to Istanbul CoverageMap
   */
  generateCoverage(options: ReportContext): Promise<CoverageMap>;

  /**
   * Generate coverage reports in configured formats
   * @param coverageMap - Istanbul coverage map with coverage data
   * @param allTestsRun - Whether all tests were run
   * @returns Promise that resolves when reports are generated
   */
  generateReports(coverageMap: CoverageMap, allTestsRun?: boolean): Promise<void>;

  /**
   * Parse a configuration module file using magicast
   * @param configFilePath - Path to configuration file
   * @returns Promise resolving to proxified module
   */
  parseConfigModule(configFilePath: string): Promise<ProxifiedModule<any>>;
}

Initialize

Initializes the provider with the Vitest context and configuration.

/**
 * Initialize the provider with Vitest context.
 * This method is called by Vitest before tests run. It stores the context
 * and performs version compatibility checks.
 *
 * @param ctx - Vitest instance containing configuration and logger
 *
 * @example
 * import { V8CoverageProvider } from '@vitest/coverage-v8/provider';
 *
 * const provider = new V8CoverageProvider();
 * provider.initialize(vitestContext);
 */
function initialize(ctx: Vitest): void;

Behavior:

  • Stores Vitest context for later use
  • Performs version compatibility check (warns if vitest and coverage versions mismatch)
  • Inherited _initialize method sets up:
    • Coverage options from context
    • Coverage files directory
    • Project roots
    • Include/exclude patterns

Version Check: Warns if running mixed versions of vitest and @vitest/coverage-v8, which may cause bugs.

Create Coverage Map

Creates an empty Istanbul coverage map instance.

/**
 * Create an empty Istanbul coverage map.
 * Coverage maps store file coverage data in Istanbul format and provide
 * methods for merging, filtering, and querying coverage.
 *
 * @returns Empty CoverageMap instance from istanbul-lib-coverage
 *
 * @example
 * const coverageMap = provider.createCoverageMap();
 * coverageMap.merge(otherCoverageMap);
 * const files = coverageMap.files(); // Get list of covered files
 */
function createCoverageMap(): CoverageMap;

Returns: Empty Istanbul CoverageMap instance that can be populated with coverage data.

CoverageMap Methods:

  • files(): Get array of covered file paths
  • fileCoverageFor(filename): Get coverage for specific file
  • merge(otherMap): Merge another coverage map into this one
  • filter(predicate): Filter files based on predicate function

Generate Coverage

Generates a complete coverage map from collected V8 coverage data.

/**
 * Generate coverage map from collected V8 coverage data.
 * This is the main coverage processing method that:
 * 1. Reads coverage files from disk
 * 2. Merges coverage data from multiple test runs
 * 3. Converts V8 coverage to Istanbul format
 * 4. Applies source map transformations
 * 5. Includes untested files if configured
 * 6. Filters results based on include/exclude patterns
 *
 * @param options - Report context
 * @param options.allTestsRun - Indicates whether all tests were run.
 *                              If false, only specific tests were run (e.g., --changed).
 *                              Affects whether untested files are included.
 * @returns Promise resolving to Istanbul CoverageMap with complete coverage data
 *
 * @example
 * const coverageMap = await provider.generateCoverage({
 *   allTestsRun: true
 * });
 *
 * console.log(`Covered ${coverageMap.files().length} files`);
 */
function generateCoverage(options: ReportContext): Promise<CoverageMap>;

Parameters:

  • options.allTestsRun: Boolean indicating if all tests ran (vs. subset of tests)

Behavior:

  1. Read Coverage Files: Reads raw V8 coverage files saved during test execution
  2. Merge Coverage: Uses @bcoe/v8-coverage to merge coverage from multiple test runs
  3. Preserve Start Offset: Ensures startOffset is not lost during merging
  4. Convert Coverage: Converts V8 coverage to Istanbul format per project/environment
  5. Transform Coverage: Applies source map transformations via istanbul-lib-source-maps
  6. Include Untested Files: Optionally includes files not executed during tests (if include is configured and either all tests ran or cleanOnRerun: false)
  7. Filter Results: Filters coverage map to only existing files
  8. Apply Exclusions: Optionally applies exclusions after remapping (if excludeAfterRemap: true)

Processing Flow:

Raw V8 Coverage Files
  ↓
Merge (handle multiple test runs)
  ↓
Convert (V8 → Istanbul per project)
  ↓
Source Map Transform
  ↓
Merge into CoverageMap
  ↓
Add Untested Files
  ↓
Filter & Apply Exclusions
  ↓
Final CoverageMap

Performance:

  • Processes coverage files concurrently based on processingConcurrency option
  • Logs timing information when debug is enabled
  • Default concurrency: Math.min(20, os.availableParallelism?.() ?? os.cpus().length)

Returns: Istanbul CoverageMap containing coverage for all files.

Generate Reports

Generates coverage reports in all configured formats.

/**
 * Generate coverage reports in configured formats.
 * Uses Istanbul reporters to generate coverage reports (text, html, json, lcov, etc.).
 * Also validates coverage thresholds if configured.
 *
 * @param coverageMap - Istanbul coverage map with coverage data
 * @param allTestsRun - Optional boolean indicating if all tests were run
 * @returns Promise that resolves when all reports are generated
 *
 * @example
 * const coverageMap = await provider.generateCoverage({ allTestsRun: true });
 * await provider.generateReports(coverageMap, true);
 * // Coverage reports now available in configured directory
 *
 * @example
 * // With custom reporters in vitest config:
 * // coverage: {
 * //   reporter: [['text'], ['html'], ['json', { file: 'coverage.json' }]]
 * // }
 * await provider.generateReports(coverageMap);
 */
function generateReports(
  coverageMap: CoverageMap,
  allTestsRun?: boolean
): Promise<void>;

Parameters:

  • coverageMap: Istanbul coverage map with coverage data
  • allTestsRun: Optional boolean for threshold validation

Behavior:

  1. StackBlitz Warning: Logs warning if running on StackBlitz (V8 coverage not supported)
  2. Create Report Context: Creates Istanbul report context with:
    • Report directory path
    • Coverage map
    • Watermarks for coverage levels
  3. Log Header: Logs coverage report header to console (if terminal reporter is configured)
  4. Generate Reports: For each configured reporter:
    • Creates reporter instance via istanbul-reports
    • Passes reporter options (skipFull, projectRoot, custom options)
    • Executes reporter to generate output files
  5. Check Thresholds: Validates coverage thresholds if configured

Reporter Types:

  • text: Console output with coverage summary
  • html: HTML coverage report with drill-down
  • json: Machine-readable JSON coverage data
  • lcov: LCOV format for CI/CD tools
  • clover: Clover XML format
  • Many more (see Istanbul documentation)

Reporter Options:

  • skipFull: Skip files with 100% coverage
  • projectRoot: Root directory for relative paths
  • Custom reporter options via array syntax: ['html', { subdir: 'html-report' }]

Threshold Validation: If options.thresholds is configured, validates that coverage meets minimum requirements:

  • Can specify global thresholds
  • Can specify per-file thresholds using glob patterns
  • Can enforce per-file threshold checking
  • Can auto-update thresholds when coverage improves

Parse Config Module

Parses a configuration file using magicast for programmatic manipulation.

/**
 * Parse a configuration module file using magicast.
 * This method is used internally for configuration analysis and manipulation.
 *
 * @param configFilePath - Absolute path to configuration file
 * @returns Promise resolving to proxified module that can be programmatically manipulated
 *
 * @example
 * const configModule = await provider.parseConfigModule('/path/to/vitest.config.ts');
 * // Can now analyze or modify the config programmatically
 */
function parseConfigModule(configFilePath: string): Promise<ProxifiedModule<any>>;

Behavior:

  • Reads file contents using fs.readFile
  • Parses using magicast.parseModule
  • Returns proxified module for manipulation

Use Case: Used by Vitest's configuration system for analyzing and potentially modifying configuration files programmatically (e.g., for threshold auto-update).

Coverage Conversion Details

AST-Based Remapping

The provider uses ast-v8-to-istanbul for accurate V8 to Istanbul conversion:

Process:

  1. Parse source code into AST using vitest/node.parseAstAsync
  2. Map V8 function ranges to AST nodes
  3. Convert to Istanbul statement/branch/function coverage
  4. Apply source maps to remap to original source

Advantages:

  • Accurate source map handling
  • Proper handling of transpiled code
  • Ignores framework-specific transformations

Ignored AST Patterns

The provider ignores specific code patterns to avoid coverage noise:

Vite SSR Transformations:

  • __vite_ssr_import_* variables
  • __vite_ssr_exports__ assignments
  • __vite_ssr_export_default__ variables
  • __vite_ssr_import_meta__ assignments

CJS Imports:

  • __vite__cjsImport* ternary expressions

In-Source Tests:

  • if (import.meta.vitest) blocks (SSR and web modes)

Browser Mode:

  • import.meta.env assignments

SWC Decorators:

  • _ts_decorate call expressions

These patterns are excluded because they're added by the build tool, not user code.

Source Map Handling

The provider handles complex source map scenarios:

Vue Single File Components:

  • Ensures map.sources array is populated
  • Converts relative source paths to absolute URLs
  • Handles Vue's multi-file source maps

Missing Source Maps:

  • Falls back to reading original source file
  • Generates dummy source for deleted/dynamic files

Vite Transformations:

  • Uses Vite's transform pipeline to get transformed code
  • Retrieves associated source maps
  • Handles inline source map comments

Untested File Coverage

When coverage.include is configured, the provider can include untested files:

Process:

  1. Get list of tested files from coverage map
  2. Use getUntestedFiles to find files matching include but not tested
  3. For each untested file:
    • Transform via Vite pipeline
    • Parse into AST
    • Generate "zero coverage" Istanbul data
    • Apply source maps
  4. Merge untested file coverage into main coverage map

Result: Untested files show 0% coverage, making them visible in reports.

Types

ReportContext

interface ReportContext {
  /**
   * Indicates whether all tests were run.
   * False when only specific tests were run (e.g., --changed, single file).
   * Affects whether untested files are included in the report.
   */
  allTestsRun?: boolean;
}

CoverageMap

interface CoverageMap {
  /**
   * Get list of all covered file paths
   * @returns Array of absolute file paths
   */
  files(): string[];

  /**
   * Get coverage data for a specific file
   * @param filename - Absolute file path
   * @returns FileCoverage object with detailed coverage data
   */
  fileCoverageFor(filename: string): FileCoverage;

  /**
   * Merge another coverage map into this one
   * @param other - Coverage map to merge
   */
  merge(other: CoverageMap): void;

  /**
   * Filter coverage map based on predicate
   * @param predicate - Function that returns true to keep file
   */
  filter(predicate: (filename: string) => boolean): void;
}

interface FileCoverage {
  /** File path */
  path: string;

  /** Statement coverage map */
  statementMap: Record<string, Range>;

  /** Function coverage map */
  fnMap: Record<string, FunctionMapping>;

  /** Branch coverage map */
  branchMap: Record<string, BranchMapping>;

  /** Statement execution counts */
  s: Record<string, number>;

  /** Function execution counts */
  f: Record<string, number>;

  /** Branch execution counts */
  b: Record<string, number[]>;
}

ProxifiedModule

/**
 * Proxified module from magicast that allows programmatic manipulation
 * of JavaScript/TypeScript configuration files
 */
type ProxifiedModule<T = any> = {
  exports: T;
  // Additional magicast proxy methods
};

Vitest

interface Vitest {
  /** Vitest version */
  version: string;

  /** Logger instance */
  logger: {
    log: (message: string) => void;
    warn: (message: string) => void;
    error: (message: string, error?: unknown) => void;
  };

  /** Vitest configuration */
  config: {
    root: string;
    // ... other config options
  };

  /** Coverage options (resolved) */
  _coverageOptions: ResolvedCoverageOptions<'v8'>;

  /** Get root project */
  getRootProject(): TestProject;
}

ResolvedCoverageOptions

interface ResolvedCoverageOptions<T extends 'v8' | 'istanbul'> {
  /** Coverage provider type */
  provider: T;

  /** Enable coverage collection */
  enabled: boolean;

  /** Glob patterns for files to include */
  include?: string[];

  /** Glob patterns for files to exclude */
  exclude: string[];

  /** Directory for coverage reports */
  reportsDirectory: string;

  /** Reporter configurations */
  reporter: CoverageReporterWithOptions[];

  /** Clean coverage before running tests */
  clean: boolean;

  /** Clean coverage on watch mode rerun */
  cleanOnRerun: boolean;

  /** Skip files with 100% coverage */
  skipFull?: boolean;

  /** Coverage thresholds */
  thresholds?: Record<string, any>;

  /** Watermarks for coverage levels */
  watermarks?: {
    statements?: [number, number];
    functions?: [number, number];
    branches?: [number, number];
    lines?: [number, number];
  };

  /** Generate report even when tests fail */
  reportOnFailure: boolean;

  /** Allow coverage of external files */
  allowExternal: boolean;

  /** Apply exclusions after source map remapping */
  excludeAfterRemap?: boolean;

  /** Concurrency limit for processing */
  processingConcurrency: number;

  /** Class method names to ignore */
  ignoreClassMethods?: string[];
}

Performance Considerations

Concurrent Processing

The provider processes coverage files concurrently:

  • Splits files into chunks based on processingConcurrency
  • Processes each chunk in parallel using Promise.all
  • Default concurrency: Math.min(20, os.cpus().length)

Debug Logging

When debug is enabled (via DEBUG=vitest:coverage environment variable):

  • Logs timing for each file conversion
  • Warns about slow files (>3 seconds)
  • Logs total coverage generation time
  • Highlights slow operations in red

Memory Management

The provider manages memory carefully:

  • Processes files in chunks to avoid memory spikes
  • Cleans up temporary data after processing
  • Merges coverage incrementally per project/environment

Optimization Techniques

  1. Early Filtering: Filters coverage files before conversion
  2. Chunked Processing: Processes large file sets in manageable chunks
  3. Incremental Merging: Merges coverage data progressively
  4. Source Map Caching: Caches transformed code and source maps
  5. AST Reuse: Reuses parsed ASTs where possible

Error Handling

Parse Errors

If AST parsing fails for a file:

  • Logs error with file path
  • Excludes file from coverage
  • Continues processing other files
  • Returns empty coverage for failed file

Missing Files

If source file doesn't exist:

  • For tested files: Generates dummy source based on function lengths
  • For untested files: Skips the file
  • Logs warning in debug mode

Source Map Errors

If source map processing fails:

  • Falls back to original source code
  • Logs warning in debug mode
  • Coverage is still reported (may be inaccurate)

Integration with Vitest

The provider integrates deeply with Vitest:

Lifecycle Hooks:

  1. initialize - Called when provider is loaded
  2. clean - Called before test run to clean old coverage
  3. onAfterSuiteRun - Called after each test file
  4. generateCoverage - Called after all tests complete
  5. reportCoverage - Called to generate reports
  6. onTestFailure - Called when tests fail (for reportOnFailure option)

Vitest APIs Used:

  • parseAstAsync - Parse source code into AST
  • BaseCoverageProvider - Base class with common functionality
  • Project system - Multi-project workspace support
  • Vite integration - Transform pipeline and source maps

Usage Example

Complete example showing provider usage:

import { V8CoverageProvider } from '@vitest/coverage-v8/provider';

// 1. Create and initialize provider
const provider = new V8CoverageProvider();
provider.initialize(vitestContext);

// 2. Generate coverage map from collected data
const coverageMap = await provider.generateCoverage({
  allTestsRun: true
});

// 3. Generate reports
await provider.generateReports(coverageMap, true);

// Reports are now available in the configured directory
// Default: ./coverage/

Typically, you don't use the provider directly. Vitest handles the lifecycle automatically via the coverage module's getProvider() method.