The Node.js coverage provider module implements the CoverageProviderModule interface for Node.js environments. It manages the V8 profiler lifecycle using the Node.js Inspector API to collect coverage data without source code instrumentation.
import coverageModule from '@vitest/coverage-v8';The default export implements the CoverageProviderModule interface with lifecycle methods for coverage collection:
interface CoverageProviderModule {
/**
* Initialize V8 coverage collection using Node.js Inspector API
* @param options - Configuration options
* @param options.isolate - If false and coverage is already enabled, skip initialization
*/
startCoverage(options: { isolate?: boolean }): Promise<void>;
/**
* Take a snapshot of current V8 coverage data
* @param options - Configuration options
* @param options.moduleExecutionInfo - Map of file paths to execution metadata including startOffset
* @returns Object containing array of script coverage results with offset information
*/
takeCoverage(options?: {
moduleExecutionInfo?: Map<string, { startOffset: number }>;
}): Promise<{ result: ScriptCoverageWithOffset[] }>;
/**
* Stop coverage collection and disconnect from V8 Inspector
* @param options - Configuration options
* @param options.isolate - If false, skip stopping (leave coverage enabled)
*/
stopCoverage(options: { isolate?: boolean }): Promise<void>;
/**
* Factory method to get the V8CoverageProvider instance
* @returns Promise resolving to V8CoverageProvider instance
*/
getProvider(): Promise<V8CoverageProvider>;
}Initializes V8 coverage collection by connecting to the Node.js Inspector and enabling the profiler.
/**
* Initialize V8 coverage collection using Node.js Inspector API.
* Creates an inspector session, enables the Profiler, and starts precise coverage collection.
*
* @param options - Configuration options
* @param options.isolate - If false and coverage is already enabled, skip initialization.
* This allows multiple test files to share the same coverage session.
* @returns Promise that resolves when coverage collection is started
*
* @example
* // Start coverage for a single test file
* await coverageModule.startCoverage({ isolate: true });
*
* @example
* // Start coverage in shared mode (won't restart if already enabled)
* await coverageModule.startCoverage({ isolate: false });
*/
function startCoverage(options: { isolate?: boolean }): Promise<void>;Behavior:
node:inspector/promises Session if not already createdProfiler.enableProfiler.startPreciseCoverage with options:
callCount: true - Collect execution countsdetailed: true - Collect detailed coverage informationisolate is false and coverage is already enabled, does nothing (allows session reuse)Inspector API Used:
inspector.Session from node:inspector/promisesProfiler.enable - Enables the profilerProfiler.startPreciseCoverage - Starts detailed coverage collectionCaptures a snapshot of the current coverage data from V8 and performs initial filtering.
/**
* Take a snapshot of current V8 coverage data.
* Retrieves coverage from the V8 profiler and filters out irrelevant files
* (non-file URLs, node_modules). Also adds startOffset information for module wrappers.
*
* @param options - Configuration options
* @param options.moduleExecutionInfo - Map of normalized file paths to execution metadata.
* The startOffset from this map is added to each coverage entry
* to account for wrapper code added during module execution.
* @returns Promise resolving to object containing array of script coverage results
*
* @example
* const { result } = await coverageModule.takeCoverage({
* moduleExecutionInfo: new Map([
* ['/path/to/file.js', { startOffset: 0 }],
* ['/path/to/module.js', { startOffset: 42 }]
* ])
* });
*
* console.log(result); // Array of ScriptCoverageWithOffset objects
*/
function takeCoverage(options?: {
moduleExecutionInfo?: Map<string, { startOffset: number }>;
}): Promise<{ result: ScriptCoverageWithOffset[] }>;Behavior:
Profiler.takePreciseCoverage to get coverage snapshot from V8file:// protocolnode_modules/startOffset to each entry from moduleExecutionInfo map (defaults to 0 if not found)startOffset accounts for wrapper code that module systems add around module codeReturns: Object with shape:
{
result: ScriptCoverageWithOffset[] // Array of coverage entries
}Filtering Logic:
file:// (excludes eval, data URLs, etc.)/node_modules/ (excludes dependencies)Stops coverage collection and cleans up the Inspector session.
/**
* Stop coverage collection and disconnect from V8 Inspector.
* Stops the profiler and disconnects the inspector session.
*
* @param options - Configuration options
* @param options.isolate - If false, skip stopping. This allows the coverage session
* to remain active for subsequent test files.
* @returns Promise that resolves when coverage collection is stopped
*
* @example
* // Stop coverage and clean up
* await coverageModule.stopCoverage({ isolate: true });
*
* @example
* // Keep coverage session active (no-op)
* await coverageModule.stopCoverage({ isolate: false });
*/
function stopCoverage(options: { isolate?: boolean }): Promise<void>;Behavior:
isolate is false, does nothing (allows session to continue for next test)Profiler.stopPreciseCoverageProfiler.disableInspector API Used:
Profiler.stopPreciseCoverage - Stops coverage collectionProfiler.disable - Disables the profilersession.disconnect() - Disconnects the inspectorFactory method that returns an instance of the V8CoverageProvider class.
/**
* Factory method to get the V8CoverageProvider instance.
* The provider is dynamically imported to prevent bundling.
*
* @returns Promise resolving to new V8CoverageProvider instance
*
* @example
* const provider = await coverageModule.getProvider();
* provider.initialize(vitestContext);
* const coverageMap = await provider.generateCoverage({ allTestsRun: true });
*/
function getProvider(): Promise<V8CoverageProvider>;Behavior:
./provider.js to avoid bundling the providerV8CoverageProviderExtended script coverage type that includes offset information for module wrapper code:
interface ScriptCoverageWithOffset extends Profiler.ScriptCoverage {
/**
* Offset for wrapper code added during module execution.
* Module systems (CommonJS, ESM) often wrap module code with additional code.
* This offset adjusts coverage ranges to account for that wrapper code.
*/
startOffset: number;
}V8 script coverage data from Node.js Inspector API:
interface ScriptCoverage {
/** Unique script identifier assigned by V8 */
scriptId: string;
/** URL of the script (e.g., "file:///path/to/file.js") */
url: string;
/** Coverage entries for functions in the script */
functions: FunctionCoverage[];
}
interface FunctionCoverage {
/** Name of the function (empty string for anonymous functions) */
functionName: string;
/** Coverage ranges within the function showing which parts were executed */
ranges: Range[];
/**
* Indicates whether this is block-level coverage (true) or function-level only (false).
* When true, ranges contain detailed execution counts for code blocks.
*/
isBlockCoverage: boolean;
}
interface Range {
/** Start offset in the script source code (in characters) */
startOffset: number;
/** End offset in the script source code (in characters) */
endOffset: number;
/** Number of times this range was executed */
count: number;
}Complete usage example showing the lifecycle:
import coverageModule from '@vitest/coverage-v8';
// 1. Start coverage collection
await coverageModule.startCoverage({ isolate: true });
// 2. Run your test code
// ... test execution happens here ...
// 3. Take coverage snapshot after tests
const { result } = await coverageModule.takeCoverage({
moduleExecutionInfo: new Map([
['/path/to/file.js', { startOffset: 0 }]
])
});
console.log(`Collected coverage for ${result.length} files`);
// 4. Stop coverage collection
await coverageModule.stopCoverage({ isolate: true });
// 5. Get provider for processing coverage data
const provider = await coverageModule.getProvider();The module maintains a single inspector session:
startCoverage calltakeCoverage callsstopCoverage is called with isolate: trueThe isolate parameter controls session lifecycle:
isolate: true - Each test file gets its own coverage session (start/stop for each file)isolate: false - All test files share one coverage session (cumulative coverage)Vitest uses isolate: false by default to accumulate coverage across all test files.
On StackBlitz platform, V8 coverage is not supported:
takeCoverage returns empty resultsCoverage is filtered to include only relevant files:
file:// protocol (excludes internal scripts, eval code)node_modules/ (excludes dependency code)This filtering reduces the amount of data sent over RPC and improves performance.