Skip to content

Commit 3c1b92b

Browse files
authored
feat: switch to using Node's built in coverage (#22)
BREAKING CHANGE: switches to using NODE_V8_COVERAGE rather than inspector directly
1 parent f14508e commit 3c1b92b

15 files changed

Lines changed: 2585 additions & 4069 deletions

README.md

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# c8 - native v8 code-coverage
1+
# c8 - native V8 code-coverage
22

3-
Code-coverage using [v8's Inspector](https://nodejs.org/dist/latest-v8.x/docs/api/inspector.html)
3+
Code-coverage using [V8's Inspector](https://nodejs.org/dist/latest-v8.x/docs/api/inspector.html)
44
that's compatible with [Istanbul's reporters](https://istanbul.js.org/docs/advanced/alternative-reporters/).
55

66
Like [nyc](https://github.com/istanbuljs/nyc), c8 just magically works:
@@ -10,20 +10,14 @@ npm i c8 -g
1010
c8 node foo.js
1111
```
1212

13-
The above example will collect coverage for `foo.js` using v8's inspector.
13+
The above example will collect coverage for `foo.js` using V8's inspector.
1414

15-
## Disclaimer
16-
17-
c8 uses bleeding edge v8 features (_it's an ongoing experiment, testing
18-
what will eventually be possible in the realm of test coverage in Node.js_).
15+
## c8 report
1916

20-
For the best experience, try running with [a canary build of Node.js](https://github.com/v8/node).
17+
run `c8 report` to regenerate reports after `c8` has already been run.
2118

22-
## How it Works
23-
24-
Before running your application c8 creates [an inspector session](https://nodejs.org/api/inspector.html) in v8 and enables v8's
25-
[built in coverage reporting](https://v8project.blogspot.com/2017/12/javascript-code-coverage.html).
19+
## Disclaimer
2620

27-
Just before your application exits, c8 fetches the coverage information from
28-
v8 and writes it to disk in a format compatible with
29-
[Istanbul's reporters](https://istanbul.js.org/).
21+
c8 uses
22+
[bleeding edge Node.js features](https://github.com/nodejs/node/pull/22527),
23+
make sure you're running Node.js `>= 10.10.0`.

bin/c8.js

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,36 @@
44
const foreground = require('foreground-child')
55
const mkdirp = require('mkdirp')
66
const report = require('../lib/report')
7-
const {resolve} = require('path')
87
const rimraf = require('rimraf')
9-
const sw = require('spawn-wrap')
108
const {
119
hideInstrumenteeArgs,
1210
hideInstrumenterArgs,
1311
yargs
1412
} = require('../lib/parse-args')
1513

1614
const instrumenterArgs = hideInstrumenteeArgs()
15+
let argv = yargs.parse(instrumenterArgs)
1716

18-
const argv = yargs.parse(instrumenterArgs)
17+
if (argv._[0] === 'report') {
18+
argv = yargs.parse(process.argv) // support flag arguments after "report".
19+
outputReport()
20+
} else {
21+
rimraf.sync(argv.tempDirectory)
22+
mkdirp.sync(argv.tempDirectory)
23+
process.env.NODE_V8_COVERAGE = argv.tempDirectory
1924

20-
const tmpDirctory = resolve(argv.coverageDirectory, './tmp')
21-
rimraf.sync(tmpDirctory)
22-
mkdirp.sync(tmpDirctory)
23-
24-
sw([require.resolve('../lib/wrap')], {
25-
C8_ARGV: JSON.stringify(argv)
26-
})
25+
foreground(hideInstrumenterArgs(argv), () => {
26+
outputReport()
27+
})
28+
}
2729

28-
foreground(hideInstrumenterArgs(argv), (out) => {
30+
function outputReport () {
2931
report({
32+
include: argv.include,
33+
exclude: argv.exclude,
3034
reporter: Array.isArray(argv.reporter) ? argv.reporter : [argv.reporter],
31-
coverageDirectory: argv.coverageDirectory,
32-
watermarks: argv.watermarks
35+
tempDirectory: argv.tempDirectory,
36+
watermarks: argv.watermarks,
37+
resolve: argv.resolve
3338
})
34-
})
39+
}

lib/parse-args.js

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
const Exclude = require('test-exclude')
22
const findUp = require('find-up')
3-
const {readFileSync} = require('fs')
3+
const { readFileSync } = require('fs')
44
const yargs = require('yargs')
55
const parser = require('yargs-parser')
66

77
const configPath = findUp.sync(['.c8rc', '.c8rc.json'])
88
const config = configPath ? JSON.parse(readFileSync(configPath)) : {}
99

10-
yargs
10+
yargs()
1111
.usage('$0 [opts] [script] [opts]')
1212
.option('reporter', {
1313
alias: 'r',
@@ -17,17 +17,26 @@ yargs
1717
.option('exclude', {
1818
alias: 'x',
1919
default: Exclude.defaultExclude,
20-
describe: 'a list of specific files and directories that should be excluded from coverage, glob patterns are supported.'
20+
describe: 'a list of specific files and directories that should be excluded from coverage (glob patterns are supported)'
2121
})
2222
.option('include', {
2323
alias: 'n',
2424
default: [],
25-
describe: 'a list of specific files that should be covered, glob patterns are supported'
25+
describe: 'a list of specific files that should be covered (glob patterns are supported)'
2626
})
2727
.option('coverage-directory', {
2828
default: './coverage',
2929
describe: 'directory to output coverage JSON and reports'
3030
})
31+
.option('temp-directory', {
32+
default: './coverage/tmp',
33+
describe: 'directory V8 coverage data is written to and read from'
34+
})
35+
.option('resolve', {
36+
default: '',
37+
describe: 'resolve paths to alternate base directory'
38+
})
39+
.command('report', 'read V8 coverage data from temp and output report')
3140
.pkgConf('c8')
3241
.config(config)
3342
.demandCommand(1)

lib/report.js

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,29 @@
1+
const Exclude = require('test-exclude')
12
const libCoverage = require('istanbul-lib-coverage')
23
const libReport = require('istanbul-lib-report')
34
const reports = require('istanbul-reports')
4-
const {readdirSync, readFileSync} = require('fs')
5-
const {resolve} = require('path')
5+
const { readdirSync, readFileSync } = require('fs')
6+
const { resolve, isAbsolute } = require('path')
7+
const v8CoverageMerge = require('v8-coverage-merge')
8+
const v8toIstanbul = require('v8-to-istanbul')
69

710
class Report {
8-
constructor ({reporter, coverageDirectory, watermarks}) {
11+
constructor ({
12+
exclude,
13+
include,
14+
reporter,
15+
tempDirectory,
16+
watermarks,
17+
resolve
18+
}) {
919
this.reporter = reporter
10-
this.coverageDirectory = coverageDirectory
20+
this.tempDirectory = tempDirectory
1121
this.watermarks = watermarks
22+
this.resolve = resolve
23+
this.exclude = Exclude({
24+
exclude: exclude,
25+
include: include
26+
})
1227
}
1328
run () {
1429
const map = this._getCoverageMapFromAllCoverageFiles()
@@ -25,20 +40,39 @@ class Report {
2540
}
2641
_getCoverageMapFromAllCoverageFiles () {
2742
const map = libCoverage.createCoverageMap({})
43+
const mergedResults = {}
44+
this._loadReports().forEach((report) => {
45+
report.result.forEach((result) => {
46+
if (this.exclude.shouldInstrument(result.url) &&
47+
isAbsolute(result.url)) {
48+
if (mergedResults[result.url]) {
49+
mergedResults[result.url] = v8CoverageMerge(
50+
mergedResults[result.url],
51+
result
52+
)
53+
} else {
54+
mergedResults[result.url] = result
55+
}
56+
}
57+
})
58+
})
2859

29-
this._loadReports().forEach(function (report) {
30-
map.merge(report)
60+
Object.keys(mergedResults).forEach((url) => {
61+
const result = mergedResults[url]
62+
const path = resolve(this.resolve, result.url)
63+
const script = v8toIstanbul(path)
64+
script.applyCoverage(result.functions)
65+
map.merge(script.toIstanbul())
3166
})
3267

3368
return map
3469
}
3570
_loadReports () {
36-
const tmpDirctory = resolve(this.coverageDirectory, './tmp')
37-
const files = readdirSync(tmpDirctory)
71+
const files = readdirSync(this.tempDirectory)
3872

3973
return files.map((f) => {
4074
return JSON.parse(readFileSync(
41-
resolve(tmpDirctory, f),
75+
resolve(this.tempDirectory, f),
4276
'utf8'
4377
))
4478
})

lib/wrap.js

Lines changed: 0 additions & 81 deletions
This file was deleted.

0 commit comments

Comments
 (0)