mrkeyoor.com_
Sat 08 Aug 20:59 UTC
npmTestingupdated 08 Aug 2026

c8

c8 is a Node.js command-line coverage tool that turns V8's built-in execution coverage into Istanbul-compatible reports. Put it before a test command, and it collects coverage from the Node process and children, remaps generated JavaScript through source maps, prints text summaries or writes formats such as HTML and lcov, and can fail CI when line, branch, function, or statement thresholds are missed. It measures executed JavaScript; it is not a test runner.

Verdict

c8 is the strong default for runner-agnostic Node coverage when native V8 data and Istanbul reports are the right combination. Pin a compatible Node version, enable --all for honest totals, and do not layer it over a runner that already owns coverage without a specific reason.

API stability4/5Core commands and flags such as reporter, all, include, exclude, check-coverage, per-file, and report have remained recognizable across majors, and compatibility with Istanbul output protects downstream tooling. Major 12 has a materially tighter Node engine floor, and experimental Monocart options are explicitly not a fixed contract, so upgrades still require CI and config review.
Docs4/5The README explains config discovery, common defaults, untouched-file coverage, source-map remapping, thresholds, ignore comments, report regeneration, and the optional Monocart path with runnable commands. Its supported-Node section is stale and says Node >=12 while package 12.0.0 requires much newer releases, an important contradiction that can break installation. Full flag detail lives in c8 --help.
Maintenance5/5c8 12.0.0 was published July 14, 2026, and the repository was pushed August 5, 2026. The project tracks current Node and V8 behavior, publishes CI status, and maintains the v8-to-istanbul bridge and Istanbul integrations. The repository currently shows 115 open issues and pull requests combined, which reflects both active use and a meaningful compatibility queue.
Ecosystem5/5c8 recorded 4,136,344 npm downloads from July 31 through August 6, 2026 and outputs standard Istanbul report formats consumed by code-hosting dashboards and coverage services. It can wrap node:test, Mocha, custom scripts, and other Node commands without runner plugins. Established ignore comments and .nycrc discovery also ease migration from older Istanbul tooling.

Use it if

  • You run tests or scripts on Node and want coverage without Babel or Istanbul instrumentation inserted into source files
  • Your test runner has no coverage integration but can be launched as a child command, including node:test, Mocha, or a custom runner
  • You need Istanbul reporters and lcov output for CI services while collecting with native V8 coverage
  • You need coverage across spawned Node subprocesses and can keep them inside c8's inherited environment
Skip it if

Setup reality

Install c8 as a development dependency with npm install --save-dev c8 and invoke it through a package script, npx c8, or your package manager. Version 12.0.0 is a CLI package with bundled types for its programmatic surface, but normal use is the c8 executable. Its npm engine range is ^20.19.0, ^22.12.0, or >=23, which is stricter than the README's stale Node >=12 sentence; CI and local machines must satisfy the package metadata. No native build or credentials are required. c8 creates raw V8 data in a temporary directory and reports in ./coverage by default; both should be ignored by version control. Configuration can live in package.json under c8, in .c8rc, .c8rc.json, .nycrc, or .nycrc.json, and discovery walks upward from the working directory, so a parent config can surprise a nested package. Flags belong before the child command. Quote include and exclude globs so the shell does not expand them first. By default only code loaded by V8 appears. Use all with src and explicit extensions to count untouched files as zero. TypeScript, JSX, and transpiled projects need correct inline or external source maps; exclude-after-remap changes whether filters see generated or original paths. Thresholds do nothing unless check-coverage, check-coverage subcommand, or 100 is enabled. c8 inherits NODE_V8_COVERAGE into Node children, but non-Node workers and detached processes are outside that guarantee. Multiple concurrent runs must use separate temp and report directories. HTML and lcov artifacts are generated files, and stale output can mislead if clean is disabled. The experimental Monocart path also requires a separate monocart-coverage-reports 2.x installation.

Patterns

Cover the built-in Node test runnercover-node-tests

npx c8 node --test

c8 options go before node. Options after node belong to the Node test runner. c8 12 requires Node ^20.19, ^22.12, or >=23.

Add a repeatable coverage scriptadd-package-script

{
  "scripts": {
    "test": "node --test",
    "coverage": "c8 --all --reporter=text --reporter=lcov npm test"
  },
  "devDependencies": {
    "c8": "^12.0.0"
  }
}

Keep the normal test script usable without coverage. The lcov report is written under coverage by default.

Store c8 configuration in package.jsonconfigure-package-json

{
  "c8": {
    "all": true,
    "src": ["src"],
    "include": ["src/**/*.js"],
    "exclude": ["src/**/*.generated.js"],
    "reporter": ["text", "html", "lcov"]
  }
}

Configuration keys omit leading --. c8 also searches parent directories for rc files, so monorepos should make config ownership explicit.

Include completely untested source filescount-unloaded-files

npx c8 --all --src src --extension .js --include 'src/**/*.js' node --test

Without --all, files never loaded by V8 do not appear and cannot lower the total. Quote globs to stop shell expansion.

Fail the test command below coverage thresholdsenforce-thresholds

npx c8 \
  --check-coverage \
  --lines 90 \
  --branches 85 \
  --functions 90 \
  --statements 90 \
  node --test

Numeric thresholds alone do not fail a run. --check-coverage enables enforcement and returns a nonzero exit when a threshold is missed.

Apply thresholds to every fileenforce-per-file

npx c8 --all --check-coverage --per-file --lines 80 --functions 80 node --test

Per-file checks catch one neglected module hidden by a well-tested aggregate, but generated and intentionally thin files should be excluded deliberately.

Print a summary and write HTML and lcovgenerate-multiple-reports

npx c8 \
  --reporter=text-summary \
  --reporter=html \
  --reporter=lcov \
  --reports-dir coverage \
  npm test

Reporters may be repeated. Add coverage and the raw temp directory to .gitignore rather than committing generated reports.

Report TypeScript through source mapsremap-typescript

// tsconfig.json
{
  "compilerOptions": {
    "outDir": "dist",
    "sourceMap": true,
    "inlineSources": true
  }
}

// run compiled tests
npx c8 --all --src src --extension .ts --exclude-after-remap node --test dist/**/*.test.js

Keep .map files beside emitted JavaScript or emit inline maps. Verify report paths after remapping before trusting exclusions and thresholds.

Ignore a platform-only branchignore-unreachable-code

function openConfig() {
  /* c8 ignore next 3 */
  if (process.platform === 'win32') {
    return openWindowsConfig()
  }
  return openPosixConfig()
}

The numeric suffix counts following lines. Use ignore comments sparingly and explain why the branch cannot run in the relevant test matrix.

Regenerate reports from collected raw dataregenerate-report

npx c8 report --reporter=text --reporter=html --temp-directory .coverage-tmp

Raw V8 files must still exist. If the original run used the default clean behavior or CI removed the temp directory, there is nothing to regenerate.

Separate parallel coverage jobsisolate-concurrent-runs

npx c8 \
  --temp-directory .coverage-tmp/unit \
  --reports-dir coverage/unit \
  node --test test/unit

npx c8 \
  --temp-directory .coverage-tmp/integration \
  --reports-dir coverage/integration \
  node --test test/integration

Do not point concurrent c8 processes at the same cleanable temp directory. Merge results only through a deliberate later reporting step.

Try the experimental native V8 reporter pathuse-monocart-reporter

npm install --save-dev monocart-coverage-reports@2
npx c8 \
  --experimental-monocart \
  --reporter=v8 \
  --reporter=console-details \
  node --test

This path is experimental and needs the separate Monocart package. Pin it and validate output before adopting it as a CI contract.

Alternatives

PackageRegistryPick it when
nycnpmUse when an Istanbul instrumentation-first workflow or existing .nycrc setup is already established and V8-native collection is not required
@vitest/coverage-v8npmUse with Vitest when coverage should share the runner's config, watch mode, workspace behavior, and UI
jestnpmUse when Jest is already your runner and its built-in coverage command is preferable to wrapping the entire process separately
monocart-coverage-reportsnpmUse when you want native V8 byte-offset reports or mixed Node and browser coverage with more specialized reporting controls