Launch Week Day 5: Introducing Reachability for PHP.Learn More
Socket
Book a DemoSign in
Socket

c8

Package Overview
Dependencies
Maintainers
1
Versions
90
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

c8 - npm Package Compare versions

Comparing version
1.0.1
to
2.0.0
+63
lib/parse-args.js
const Exclude = require('test-exclude')
const findUp = require('find-up')
const {readFileSync} = require('fs')
const yargs = require('yargs')
const parser = require('yargs-parser')
const configPath = findUp.sync(['.c8rc', '.c8rc.json'])
const config = configPath ? JSON.parse(readFileSync(configPath)) : {}
yargs
.usage('$0 [opts] [script] [opts]')
.option('reporter', {
alias: 'r',
describe: 'coverage reporter(s) to use',
default: 'text'
})
.option('exclude', {
alias: 'x',
default: Exclude.defaultExclude,
describe: 'a list of specific files and directories that should be excluded from coverage, glob patterns are supported.'
})
.option('include', {
alias: 'n',
default: [],
describe: 'a list of specific files that should be covered, glob patterns are supported'
})
.option('coverage-directory', {
default: './coverage',
describe: 'directory to output coverage JSON and reports'
})
.pkgConf('c8')
.config(config)
.demandCommand(1)
.epilog('visit https://git.io/vHysA for list of available reporters')
function hideInstrumenterArgs (yargv) {
var argv = process.argv.slice(1)
argv = argv.slice(argv.indexOf(yargv._[0]))
if (argv[0][0] === '-') {
argv.unshift(process.execPath)
}
return argv
}
function hideInstrumenteeArgs () {
let argv = process.argv.slice(2)
const yargv = parser(argv)
if (!yargv._.length) return argv
// drop all the arguments after the bin being
// instrumented by c8.
argv = argv.slice(0, argv.indexOf(yargv._[0]))
argv.push(yargv._[0])
return argv
}
module.exports = {
yargs,
hideInstrumenterArgs,
hideInstrumenteeArgs
}
const libCoverage = require('istanbul-lib-coverage')
const libReport = require('istanbul-lib-report')
const reports = require('istanbul-reports')
const {readdirSync, readFileSync} = require('fs')
const {resolve} = require('path')
class Report {
constructor ({reporter, coverageDirectory, watermarks}) {
this.reporter = reporter
this.coverageDirectory = coverageDirectory
this.watermarks = watermarks
}
run () {
const map = this._getCoverageMapFromAllCoverageFiles()
var context = libReport.createContext({
dir: './coverage',
watermarks: this.watermarks
})
const tree = libReport.summarizers.pkg(map)
this.reporter.forEach(function (_reporter) {
tree.visit(reports.create(_reporter), context)
})
}
_getCoverageMapFromAllCoverageFiles () {
const map = libCoverage.createCoverageMap({})
this._loadReports().forEach(function (report) {
map.merge(report)
})
return map
}
_loadReports () {
const tmpDirctory = resolve(this.coverageDirectory, './tmp')
const files = readdirSync(tmpDirctory)
return files.map((f) => {
return JSON.parse(readFileSync(
resolve(tmpDirctory, f),
'utf8'
))
})
}
}
module.exports = function (opts) {
const report = new Report(opts)
report.run()
}
const {spawn} = require('child_process')
const debuggerRe = /Debugger listening on ws:\/\/[^:]*:([^/]*)/
module.exports = function (execPath, args = []) {
const info = {
port: -1
}
return new Promise((resolve, reject) => {
const proc = spawn(execPath, args, {
stdio: [process.stdin, process.stdout, 'pipe'],
env: process.env,
cwd: process.cwd()
})
proc.stderr.on('data', (outBuffer) => {
const outString = outBuffer.toString('utf8')
const match = outString.match(debuggerRe)
if (match && !info.url) {
info.port = Number(match[1])
return resolve(info)
} else {
console.error(outString)
}
})
proc.on('close', (code) => {
if (info.port === -1) {
return reject(Error('could not connect to inspector'))
} else {
process.exitCode = code
}
})
})
}
+98
-6
#!/usr/bin/env node
'use strict'
const argv = require('yargs').parse()
const foreground = require('foreground-child')
const sw = require('spawn-wrap')
const CRI = require('chrome-remote-interface')
const Exclude = require('test-exclude')
const {isAbsolute} = require('path')
const mkdirp = require('mkdirp')
const report = require('../lib/report')
const {resolve} = require('path')
const rimraf = require('rimraf')
const spawn = require('../lib/spawn')
const uuid = require('uuid')
const v8ToIstanbul = require('v8-to-istanbul')
const {writeFileSync} = require('fs')
const {
hideInstrumenteeArgs,
hideInstrumenterArgs,
yargs
} = require('../lib/parse-args')
if (argv._.length) {
sw([require.resolve('./wrap')])
foreground(process.argv.slice(2))
const instrumenterArgs = hideInstrumenteeArgs()
const argv = yargs.parse(instrumenterArgs)
const exclude = Exclude({
include: argv.include,
exclude: argv.exclude
})
;(async function executeWithCoverage (instrumenteeArgv) {
try {
const bin = instrumenteeArgv.shift()
const info = await spawn(bin,
[`--inspect-brk=0`].concat(instrumenteeArgv))
const client = await CRI({port: info.port})
const initialPause = new Promise((resolve) => {
client.once('Debugger.paused', resolve)
})
const mainContextInfo = new Promise((resolve) => {
client.once('Runtime.executionContextCreated', (message) => {
resolve(message.context)
})
})
const executionComplete = new Promise((resolve) => {
client.on('Runtime.executionContextDestroyed', async (message) => {
if (message.executionContextId === (await mainContextInfo).id) {
resolve(message)
}
})
})
const {Debugger, Runtime, Profiler} = client
await Promise.all([
Profiler.enable(),
Runtime.enable(),
Debugger.enable(),
Profiler.startPreciseCoverage({callCount: true, detailed: true}),
Runtime.runIfWaitingForDebugger(),
initialPause
])
await Debugger.resume()
await executionComplete
const allV8Coverage = await collectV8Coverage(Profiler)
writeIstanbulFormatCoverage(allV8Coverage)
await client.close()
report({
reporter: Array.isArray(argv.reporter) ? argv.reporter : [argv.reporter],
coverageDirectory: argv.coverageDirectory,
watermarks: argv.watermarks
})
} catch (err) {
console.error(err)
process.exit(1)
}
})(hideInstrumenterArgs(argv))
async function collectV8Coverage (Profiler) {
let {result} = await Profiler.takePreciseCoverage()
result = result.filter(({url}) => {
url = url.replace('file://', '')
return isAbsolute(url) && exclude.shouldInstrument(url)
})
return result
}
function writeIstanbulFormatCoverage (allV8Coverage) {
const tmpDirctory = resolve(argv.coverageDirectory, './tmp')
rimraf.sync(tmpDirctory)
mkdirp.sync(tmpDirctory)
allV8Coverage.forEach((v8) => {
const script = v8ToIstanbul(v8.url)
script.applyCoverage(v8.functions)
writeFileSync(
resolve(tmpDirctory, `./${uuid.v4()}.json`),
JSON.stringify(script.toIstanbul(), null, 2),
'utf8'
)
})
}

@@ -5,2 +5,24 @@ # Change Log

<a name="2.0.0"></a>
# [2.0.0](https://github.com/bcoe/c8/compare/v1.0.1...v2.0.0) (2017-12-17)
### Bug Fixes
* tweak inspector event timing ([#6](https://github.com/bcoe/c8/issues/6)) ([01f654e](https://github.com/bcoe/c8/commit/01f654e))
### Features
* first pass at functional prototype without subprocess support ([#5](https://github.com/bcoe/c8/issues/5)) ([9534f56](https://github.com/bcoe/c8/commit/9534f56))
* implement Istanbul reporting ([#8](https://github.com/bcoe/c8/issues/8)) ([8e430bf](https://github.com/bcoe/c8/commit/8e430bf))
* switch to stderr and default port ([#7](https://github.com/bcoe/c8/issues/7)) ([bb117b7](https://github.com/bcoe/c8/commit/bb117b7))
### BREAKING CHANGES
* dropped subprocess support for the time being, while we march towards an initial implementation.
<a name="1.0.1"></a>

@@ -7,0 +29,0 @@ ## [1.0.1](https://github.com/bcoe/c8/compare/v1.0.0...v1.0.1) (2017-10-26)

+28
-7
{
"name": "c8",
"version": "1.0.1",
"description": "collect test coverage using v8's profiler",
"version": "2.0.0",
"description": "collect test coverage using v8's inspector",
"main": "index.js",
"bin": "./bin/c8.js",
"scripts": {
"test": "nyc mocha test.js",
"test": "./bin/c8.js node ./node_modules/.bin/_mocha ./test/*.js",
"posttest": "standard",
"release": "standard-version"
},
"c8": {
"exclude": [
"test/*.js"
]
},
"standard": {
"ignore": [
"test/fixtures"
]
},
"keywords": [

@@ -26,10 +37,20 @@ "coverage",

"chrome-remote-interface": "^0.25.2",
"foreground-child": "^1.5.6",
"get-port": "^3.2.0",
"spawn-wrap": "=1.3.8",
"yargs": "^10.0.3"
"find-up": "^2.1.0",
"istanbul-lib-coverage": "^1.1.1",
"istanbul-lib-report": "^1.1.2",
"istanbul-reports": "^1.1.3",
"mkdirp": "^0.5.1",
"rimraf": "^2.6.2",
"test-exclude": "^4.1.1",
"uuid": "^3.1.0",
"v8-to-istanbul": "^1.2.0",
"yargs": "^10.0.3",
"yargs-parser": "^8.0.0"
},
"devDependencies": {
"chai": "^4.1.2",
"mocha": "^4.0.1",
"standard": "^10.0.3",
"standard-version": "^4.2.0"
}
}
# c8 - native v8 code-coverage
Code-coverage using [v8's Profiler](https://nodejs.org/dist/latest-v8.x/docs/api/inspector.html)
Code-coverage using [v8's Inspector](https://nodejs.org/dist/latest-v8.x/docs/api/inspector.html)
that's compatible with [Istanbul's reporters](https://istanbul.js.org/docs/advanced/alternative-reporters/).
Like [nyc](https://github.com/istanbuljs/nyc), c8 just magically works, simply:
Like [nyc](https://github.com/istanbuljs/nyc), c8 just magically works:

@@ -13,15 +13,22 @@ ```bash

The above example will collect coverage for `foo.js` using v8's profiler.
The above example will collect coverage for `foo.js` using v8's inspector.
TODO:
## remaining work
- [ ] write logic for converting v8 coverage output to [Istanbul Coverage.json format](https://github.com/gotwarlost/istanbul/blob/master/coverage.json.md).
- [ ] talk to Node.js project about silencing messages:
- [x] write logic for converting v8 coverage output to [Istanbul Coverage.json format](https://github.com/gotwarlost/istanbul/blob/master/coverage.json.md).
* https://github.com/bcoe/v8-to-istanbul
- [ ] talk to node.js project about silencing messages:
> `Debugger listening on ws://127.0.0.1:56399/e850110a-c5df-41d8-8ef2-400f6829617f`.
- [ ] figure out why `detailed` mode does not appear to be working.
- [ ] figure out a better way to determine that all processes in event loop
- [x] figure out why `detailed` mode does not appear to be working.
* this is fixed in v8, as long as you start with `--inspect-brk` you
can collect coverage in detailed mode.
- [x] figure out a better way to determine that all processes in event loop
have terminated (except the inspector session).
- [ ] process.exit() can't perform an async operation; how can we track coverage
- [x] process.exit() can't perform an async operation; how can we track coverage
for scripts that exit?
* we can now listen for the `Runtime.executionContextDestroyed` event.
- [x] figure out why instrumentation of .mjs files does not work:
* see: https://github.com/nodejs/node/issues/17336
const fs = require('fs')
const sw = require('spawn-wrap')
const CRI = require('chrome-remote-interface');
const getPort = require('get-port');
const inspector = require('inspector')
// if there are N or less active handles
// in the event loop, dump coverage and exit.
const EXIT_HANDLE_COUNT = 4
getPort().then(async port => {
// start an inspector session on an unused port.
inspector.open(port, true)
const client = await CRI({port: port})
const {Profiler} = client
await Profiler.enable()
await Profiler.startPreciseCoverage({callCount: true, detailed: true})
// run the original "main" now that we've started the inspector.
sw.runMain()
// wait for everything in event loop to terminate
// except for inspector session.
setInterval(() => {
const handleCount = process._getActiveHandles().length
+ process._getActiveRequests().length
if (handleCount <= EXIT_HANDLE_COUNT) {
outputCoverageAndExit(client, Profiler)
}
}, 100)
}).catch(err => {
throw err
})
async function outputCoverageAndExit (client, Profiler) {
const IGNORED_PATHS = [
/\/bin\/wrap.js/,
/\/node_modules\//,
/node-spawn-wrap/
]
let {result} = await Profiler.takePreciseCoverage()
result = result.filter(coverage => {
for (var ignored, i = 0; (ignored = IGNORED_PATHS[i]) !== undefined; i++) {
if (ignored.test(coverage.url)) return false
}
if (!/^\//.test(coverage.url)) return false
else return true
})
console.log(JSON.stringify(result, null, 2))
client.close()
process.exit(process.exitCode || 0)
}