☔️ @pokujs/coverage is a Poku plugin that unifies coverage collection across Node.js, Deno, and Bun.
Tip
- @pokujs/coverage supports JSONC, YAML, and TOML config files. You can also use JavaScript and TypeScript by setting the options directly in the plugin.
- The default reporter is designed for easy consumption by LLMs, pinpointing uncovered code with precise locations.
- Move and combine entire test suites between Node.js, Deno, and Bun with zero configuration changes.
- Know exactly what is and isn't tested across each runtime using the same test suite.
- Whether it's CommonJS, ES Modules, TypeScript, or both, just install and use it.
npm i -D @pokujs/coverage{
"scripts": {
"test:bun": "bun poku --coverage",
"test:deno": "deno run -A npm:poku --coverage",
"test:node": "poku --coverage"
}
}- Then run the tests and a coverage summary will be printed after the suite results.
| Option | Type | Default | Node.js | Deno | Bun |
|---|---|---|---|---|---|
| reporter | Reporter | Reporter[] |
'text' |
● | ● | ● |
| include | string[] |
[] |
● | ● | ● |
| exclude | string[] |
list | ● | ● | ● |
| all | boolean |
false |
● | ● | ● |
| src | string | string[] |
[cwd] |
● | ● | ● |
| extension | string | string[] |
list | ● | ● | ● |
| checkCoverage | boolean | number |
undefined |
● | ● | ● |
| statements | number |
0 |
● | ● | ● |
| branches | number |
0 |
● | ● | – |
| functions | number |
0 |
● | ● | ● |
| lines | number |
0 |
● | ● | ● |
| perFile | boolean |
false |
● | ● | ● |
| skipFull | boolean |
false |
● | ● | ● |
| skipEmpty | boolean |
false |
● | ● | ● |
| watermarks | Partial<Watermarks> |
[50, 80] |
● | ● | ● |
| hyperlinks | boolean | IDE |
true |
● | ● | ● |
| reportsDirectory | string |
'./coverage' |
● | ● | ● |
| excludeAfterRemap | boolean |
true |
● | ● | – |
| tempDirectory | string |
auto | ● | ● | ● |
| clean | boolean |
auto | ● | ● | ● |
| config | string | false |
undefined |
● | ● | ● |
Tip
.nycrc and .c8rc config files are supported and are automatically remapped, for example:
"check-coverage": true→"checkCoverage": true"100": true→checkCoverage: 100
Note
On Bun, branches and excludeAfterRemap options are silently ignored.
| Reporter | Node.js | Deno | Bun |
|---|---|---|---|
'text' |
● | ● | ● |
'lcov' |
● | ● | ● |
'lcovonly' |
● | ● | ● |
'text-lcov' |
● | ● | ● |
'text-summary' |
● | ● | ● |
'teamcity' |
● | ● | ● |
'json' |
● | ● | ● |
'json-summary' |
● | ● | ● |
'cobertura' |
● | ● | ● |
'clover' |
● | ● | ● |
'none' |
● | ● | ● |
'v8' |
● | ● | – |
'jsc' |
– | – | ● |
Note
- On Bun,
'v8'falls back to'jsc'. - On Node.js or Deno,
'jsc'falls back to'v8'.
{
"reporter": ["text", "lcov"]
}Glob patterns for files to include. When non-empty, only matching files appear in reports.
{
"include": ["src/**"]
}Glob patterns for files to exclude. Replaces the default list when provided.
{
"exclude": ["test/**", "**/*.spec.ts"]
}Walk the filesystem and report every source file under cwd, including those never touched by tests (reported as zero coverage).
{
"all": true
}Root directories searched when all: true. Overrides cwd as the default walk root. Useful for monorepos. Relative paths are resolved against cwd.
{
"all": true,
"src": ["./packages/core/src", "./packages/api/src"]
}File extensions considered by all: true discovery. Overrides the default list entirely.
{
"all": true,
"extension": [".ts", ".vue"]
}Fail the run when coverage falls below the configured thresholds.
true: enable the check with all thresholds at0(silent no-op unless at least one per-metric threshold is set).number: default threshold applied to every metric. Per-metric root keys override.false/undefined: disabled.
{
"checkCoverage": 95,
"branches": 80
}Above: statements, functions, and lines require 95; branches requires 80.
Minimum statements coverage percentage. Requires checkCoverage to be active.
{
"checkCoverage": true,
"statements": 90
}Minimum branches coverage percentage. Requires checkCoverage to be active.
{
"checkCoverage": true,
"branches": 80
}Minimum functions coverage percentage. Requires checkCoverage to be active.
{
"checkCoverage": true,
"functions": 90
}Minimum lines coverage percentage. Requires checkCoverage to be active.
{
"checkCoverage": true,
"lines": 95
}Enforce thresholds per file instead of on aggregated totals.
{
"checkCoverage": 95,
"perFile": true
}Hide fully-covered files (every non-null metric ≥ 100%) from the text reporter table. Totals are unaffected.
{
"skipFull": true
}Hide files with no executable code from the text reporter table. Totals are unaffected.
{
"skipEmpty": true
}[lowMax, highMin] thresholds for classifying percentages as low / medium / high in the text reporter.
{
"watermarks": {
"lines": [60, 85],
"branches": [60, 85],
"functions": [60, 85],
"statements": [60, 85]
}
}Controls clickable file links in the text reporter.
true: plainfile://links.<ide>: emit IDE-specific URLs.false: disabled.
Available:
'vscode''jetbrains''cursor''windsurf''vscode-insiders'
{
"hyperlinks": "vscode"
}Directory where report files are written. Resolved relative to the Poku working directory.
{
"reportsDirectory": "./coverage"
}When true, globs match original source paths (post source-map remap). When false, globs match transpiled paths (pre-remap, mirrors c8).
{
"excludeAfterRemap": false
}Directory where raw coverage data is written. When omitted, a temp dir is created and cleaned up automatically.
{
"tempDirectory": "./.tmp-coverage"
}Override temp-directory cleanup at teardown.
undefined: auto (clean only if auto-generated).true: always clean.false: never clean.
{
"clean": false
}Path to a config file, or false to disable auto-discovery.
{
"config": ".coveragerc"
}You can also specify the config path via CLI:
poku --coverage --coverageConfig=.coveragerc test/Tip
When no config is specified, the plugin automatically searches for the following files in the working directory (in order):
.coveragerc,.coveragerc.json,.coveragerc.jsonc,.coveragerc.toml,.coveragerc.yaml,.coveragerc.yml.nycrc,.nycrc.json,.nycrc.jsonc,.nycrc.toml,.nycrc.yaml,.nycrc.yml.c8rc,.c8rc.json,.c8rc.jsonc,.c8rc.toml,.c8rc.yaml,.c8rc.yml
When using via plugin, by default, coverage runs whenever the plugin is active. Use requireFlag to only collect coverage when --coverage is passed to the CLI:
// poku.config.js
import { coverage } from '@pokujs/c8';
import { defineConfig } from 'poku';
export default defineConfig({
plugins: [
coverage({
requireFlag: true,
}),
],
});# No coverage (plugin is a no-op)
poku test/
# With coverage
poku --coverage test/Note
Priority order:
- For config file discovery:
--coverageConfig(CLI) >config(plugin option) > auto-discovery - For coverage options: plugin options > config file options
@pokujs/coverage extends Bun's coverage by tapping into the JSC Inspector directly, unlocking:
- The full set of reporters (
html,html-spa,jsc,json,json-summary,cobertura,clover,teamcity,text-summary, etc.). - Consistent options across runtimes (
all,checkCoverage,include,exclude,extension,skipFull,skipEmpty,watermarks, etc.). - Support for
/* jsc ignore */directives (next,start/stop). - Real function names with per-function execution counts.
- Accurate line hit counts derived from basic blocks, instead of binary covered/uncovered.
- A richer LCOV built from the JSC data, with function records and real per-line counts.
Note
Branch coverage is not yet available under Bun. This is a limitation of the JSC Inspector, which Bun depends on.
- The full set of reporters (
html-spa,v8,json,json-summary,cobertura,clover,teamcity,text-summary, etc.). - Consistent options across runtimes (
all,checkCoverage,include,exclude,extension,skipFull,skipEmpty,watermarks, etc.). - Compatibility with
.nycrc/.c8rcconfig files, easing migration from existing coverage setups.
- 🐢 Under Node.js, the plugin sets
NODE_V8_COVERAGEbefore Poku spawns tests. On teardown, the plugin reads the V8 JSON files from<tempDir>and forwards the data. - 🦕 Under Deno, the plugin sets
DENO_COVERAGE_DIRbefore Poku spawns tests. On teardown, the plugin shells out todeno coverage <tempDir>and forwards the data. - 🍞 Under Bun, the plugin attaches to the JSC Inspector over WebSocket and captures basic-block execution counts via
Runtime.getBasicBlocks. On teardown, the plugin reads the JSON files from<tempDir>and forwards the data.
The plugin strips the following files from every report before they are emitted, so the numbers reflect only the source code you actually care about:
- Every file Poku passes through its
runnerhook is recorded and dropped from reports since they are test files. node_modules/and.git/. directories are unconditionally banned from coverage output.
@pokujs/coverage internally adapts parts of the projects v8-to-istanbul, @jridgewell/trace-mapping, and istanbul-reports for multi-runtime support, enabling Istanbul reports for both Node.js, Deno, and Bun.
.js,.css,.png, and.icoassets fromhtmlandhtml-spareporters are copied verbatim from istanbul-reports.
Also, a special thanks to c8 and Monocart Coverage Reports, repositories that served as a study base and as a reference for comparing results.
MIT © wellwelwel and contributors.