☔️ @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 --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 | number | CoverageThresholds |
undefined |
● | ● | ● |
| skipFull | boolean |
false |
● | ● | ● |
| skipEmpty | boolean |
false |
● | ● | ● |
| watermarks | Partial<Watermarks> |
[50, 80] per metric |
● | ● | ● |
| hyperlinks | boolean | IDE |
true |
● | ● | ● |
| reportsDirectory | string |
'./coverage' |
● | ● | ● |
| excludeAfterRemap | boolean |
true |
● | ● | – |
| tempDirectory | string |
auto | ● | ● | ● |
| clean | boolean |
auto | ● | ● | ● |
| config | string | false |
undefined |
● | ● | ● |
| requireFlag | boolean |
false |
● | ● | ● |
.nycrc, .c8rc, and bunfig.toml config files are supported and options are automatically remapped.
Note
On Bun, the branches threshold and excludeAfterRemap option are silently ignored.
| Reporter | Node.js | Deno | Bun |
|---|---|---|---|
'text' |
● | ● | ● |
'lcov' |
● | ● | ● |
'lcovonly' |
● | ● | ● |
'text-lcov' |
● | ● | ● |
'text-summary' |
● | ● | ● |
'teamcity' |
● | ● | ● |
'json' |
● | ● | ● |
'json-summary' |
● | ● | ● |
'cobertura' |
● | ● | ● |
'clover' |
● | ● | ● |
'none' |
● | ● | ● |
'v8' |
● | ● | – |
'jsc' |
– | – | ● |
{ "reporter": ["text", "lcov"] }Note
- On Bun,
'v8'falls back to'jsc'. - On Node.js or Deno,
'jsc'falls back to'v8'.
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"] }- Every file Poku passes through its
runnerhook is recorded and dropped from reports. node_modules/and.git/directories are unconditionally banned from coverage output.
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.
number: default threshold applied to every metric.CoverageThresholds: per-metric thresholds, plus the optionalperFiletoggle.undefined: disabled.
{
"checkCoverage": 95
}Above: every metric requires 95.
{
"checkCoverage": {
"lines": 95,
"statements": 95,
"functions": 90,
"branches": 80,
"perFile": false
}
}Above: per-metric granularity. perFile enforces thresholds per file instead of on aggregated totals.
Tip
.nycrc and .c8rc options are automatically remapped into the CoverageThresholds usage.
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.c8rc,.c8rc.jsonbunfig.toml
🚩 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
Using with @pokujs/multi-suite
🧬 Place the coverage plugin at the root level, before multiSuite:
import { coverage } from '@pokujs/coverage';
import { multiSuite } from '@pokujs/multi-suite';
import { defineConfig } from 'poku';
export default defineConfig({
plugins: [
coverage({ include: ['src/**'] }),
multiSuite([
defineConfig({ include: ['test/unit'], concurrency: 8 }),
defineConfig({ include: ['test/e2e'], sequential: true }),
]),
],
});On
teardown, it merges everything into a single report.
@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.
- A richer LCOV built from the JSC data, with function records and real per-line counts.
- Compatibility with
.nycrc/.c8rcconfig files, easing migration from existing coverage setups.
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.
☔️ @pokujs/coverage internally adapts parts of the projects v8-to-istanbul, @jridgewell/trace-mapping, istanbul-reports, and Monocart Coverage 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, that served as a study base and as a reference for comparing results.
🤖 This project is not "vibe-coded". The use of LLMs makes it really easier to perform complex queries, mapping, and filtering data, especially when dealing with massive amounts of raw data from V8 and JSC engines.
In development, this is primarily used as a productivity tool, not as an autopilot. If you'd like to contribute to the project, please keep this in mind: I'll love reading what you, a human, have written 🤝
- You can view detailed queries by runtime and engine in the ./docs.
MIT © wellwelwel and contributors.