Skip to content

Commit

Permalink
feat: make __coverage__ the default approach we advocate for ES2015…
Browse files Browse the repository at this point in the history
… coverage (#268)

* feat: make nyc behave better when used with babel-plugin-__coverage__ (see #266)

* feat: abstract test include/exclude logic into its own module

* fix: add text-exclude dependency

* fix: should have instrumenters in files list

* fix: fix remapping issue with source-maps

* fix: typo was causing argument not to propagate

* docs: add section introducing __coverage__ for ES6/ES7

* docs: a couple small edits
  • Loading branch information
bcoe committed Jun 13, 2016
1 parent 8164b5e commit 9fd2295
Show file tree
Hide file tree
Showing 12 changed files with 213 additions and 156 deletions.
38 changes: 36 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,42 @@ of the pre-transpiled code. You'll have to configure your custom require hook
to inline the source map in the transpiled code. For Babel that means setting
the `sourceMaps` option to `inline`.

## Use babel-plugin__coverage__ for Better ES6/ES7 Support

[`babel-plugin-__coverage__`](https://github.com/dtinth/babel-plugin-__coverage__) can be used to enable better first-class ES6 support.

1. enable the `__coverage__` plugin:

```json
{
"babel": {
"presets": ["es2015"],
"plugins": ["__coverage__"]
}
}
```

2. disable nyc's instrumentation and source-maps:

```json
{
"nyc": {
"include": [
"src/*.js"
],
"require": [
"babel-register"
],
"sourceMap": false,
"instrumenter": "./lib/instrumenters/noop"
}
}
```

That's all there is to it, better ES6 syntax highlighting awaits:

<img width="500" src="screen2.png">

## Support For Custom File Extensions (.jsx, .es6)

Supporting file extensions can be configured through either the configuration arguments or with the `nyc` config section in `package.json`.
Expand Down Expand Up @@ -249,8 +285,6 @@ That's all there is to it!
[codecov](https://codecov.io/) is a great tool for adding
coverage reports to your GitHub project, even viewing them inline on GitHub with a browser extension:

![browser extension](https://d234q63orb21db.cloudfront.net/ad63907877249140772dff929ad1c340e424962a/media/images/next/extension.png)

Here's how to get `nyc` integrated with codecov and travis-ci.org:

1. add the codecov and nyc dependencies to your module:
Expand Down
17 changes: 15 additions & 2 deletions bin/nyc.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,16 @@ var yargs = require('yargs/yargs')(process.argv.slice(2))
description: 'what % of statements must be covered?',
global: true
})
.option('source-map', {
default: true,
type: 'boolean',
description: 'should nyc detect and handle source maps?'
})
.option('instrumenter', {
default: './lib/instrumenters/istanbul',
type: 'string',
description: 'what library should be used to instrument coverage?'
})
.help('h')
.alias('h', 'help')
.version()
Expand Down Expand Up @@ -125,15 +135,18 @@ if (argv._[0] === 'report') {
var nyc = (new NYC({
require: argv.require,
include: argv.include,
exclude: argv.exclude
exclude: argv.exclude,
sourceMap: !!argv.sourceMap
}))
nyc.reset()

if (argv.all) nyc.addAllFiles()

var env = {
NYC_CWD: process.cwd(),
NYC_CACHE: argv.cache ? 'enable' : 'disable'
NYC_CACHE: argv.cache ? 'enable' : 'disable',
NYC_SOURCE_MAP: argv.sourceMap ? 'enable' : 'disable',
NYC_INSTRUMENTER: argv.instrumenter
}
if (argv.require.length) {
env.NYC_REQUIRE = argv.require.join(',')
Expand Down
4 changes: 3 additions & 1 deletion bin/wrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ try {
extension: process.env.NYC_EXTENSION ? process.env.NYC_EXTENSION.split(',') : [],
exclude: process.env.NYC_EXCLUDE ? process.env.NYC_EXCLUDE.split(',') : [],
include: process.env.NYC_INCLUDE ? process.env.NYC_INCLUDE.split(',') : [],
enableCache: process.env.NYC_CACHE === 'enable'
enableCache: process.env.NYC_CACHE === 'enable',
sourceMap: process.env.NYC_SOURCE_MAP === 'enable',
instrumenter: process.env.NYC_INSTRUMENTER
})).wrap()

sw.runMain()
98 changes: 26 additions & 72 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/* global __coverage__ */
var fs = require('fs')
var glob = require('glob')
var micromatch = require('micromatch')
var mkdirp = require('mkdirp')
var Module = require('module')
var appendTransform = require('append-transform')
Expand All @@ -17,7 +16,8 @@ var md5hex = require('md5-hex')
var findCacheDir = require('find-cache-dir')
var js = require('default-require-extensions/js')
var pkgUp = require('pkg-up')
var yargs = require('yargs/yargs')
var testExclude = require('test-exclude')
var yargs = require('yargs')

/* istanbul ignore next */
if (/index\.covered\.js$/.test(__filename)) {
Expand All @@ -30,29 +30,23 @@ function NYC (opts) {
this._istanbul = config.istanbul
this.subprocessBin = config.subprocessBin || path.resolve(__dirname, './bin/nyc.js')
this._tempDirectory = config.tempDirectory || './.nyc_output'
this._instrumenterLib = require(config.instrumenter || './lib/instrumenters/istanbul')
this._reportDir = config.reportDir
this._sourceMap = config.sourceMap
this.cwd = config.cwd

this.reporter = arrify(config.reporter || 'text')

// load exclude stanza from config.
this.include = false
if (config.include && config.include.length > 0) {
this.include = this._prepGlobPatterns(arrify(config.include))
}

this.exclude = this._prepGlobPatterns(
['**/node_modules/**'].concat(arrify(
config.exclude && config.exclude.length > 0
? config.exclude
: ['test/**', 'test{,-*}.js', '**/*.test.js', '**/__tests__/**']
))
)

this.cacheDirectory = findCacheDir({name: 'nyc', cwd: this.cwd})

this.enableCache = Boolean(this.cacheDirectory && (config.enableCache === true || process.env.NYC_CACHE === 'enable'))

this.exclude = testExclude({
cwd: this.cwd,
include: config.include,
exclude: config.exclude
})

// require extensions can be provided as config in package.json.
this.require = arrify(config.require)

Expand Down Expand Up @@ -129,43 +123,7 @@ NYC.prototype.instrumenter = function () {
}

NYC.prototype._createInstrumenter = function () {
var configFile = path.resolve(this.cwd, './.istanbul.yml')

if (!fs.existsSync(configFile)) configFile = undefined

var istanbul = this.istanbul()

var instrumenterConfig = istanbul.config.loadFile(configFile).instrumentation.config

return new istanbul.Instrumenter({
coverageVariable: '__coverage__',
embedSource: instrumenterConfig['embed-source'],
noCompact: !instrumenterConfig.compact,
preserveComments: instrumenterConfig['preserve-comments']
})
}

NYC.prototype._prepGlobPatterns = function (patterns) {
if (!patterns) return patterns

var result = []

function add (pattern) {
if (result.indexOf(pattern) === -1) {
result.push(pattern)
}
}

patterns.forEach(function (pattern) {
// Allow gitignore style of directory exclusion
if (!/\/\*\*$/.test(pattern)) {
add(pattern.replace(/\/$/, '') + '/**')
}

add(pattern)
})

return result
return this._instrumenterLib(this.cwd)
}

NYC.prototype.addFile = function (filename) {
Expand All @@ -190,14 +148,6 @@ NYC.prototype._readTranspiledSource = function (path) {
return source
}

NYC.prototype.shouldInstrumentFile = function (filename, relFile) {
// Don't instrument files that are outside of the current working directory.
if (/^\.\./.test(path.relative(this.cwd, filename))) return false

relFile = relFile.replace(/^\.[\\\/]/, '') // remove leading './' or '.\'.
return (!this.include || micromatch.any(relFile, this.include)) && !micromatch.any(relFile, this.exclude)
}

NYC.prototype.addAllFiles = function () {
var _this = this

Expand All @@ -210,7 +160,7 @@ NYC.prototype.addAllFiles = function () {
pattern = '**/*{' + this.extensions.join() + '}'
}

glob.sync(pattern, {cwd: this.cwd, nodir: true, ignore: this.exclude}).forEach(function (filename) {
glob.sync(pattern, {cwd: this.cwd, nodir: true, ignore: this.exclude.exclude}).forEach(function (filename) {
var obj = _this.addFile(path.join(_this.cwd, filename))
if (obj.instrument) {
module._compile(
Expand All @@ -224,7 +174,7 @@ NYC.prototype.addAllFiles = function () {
}

NYC.prototype._maybeInstrumentSource = function (code, filename, relFile) {
var instrument = this.shouldInstrumentFile(filename, relFile)
var instrument = this.exclude.shouldInstrument(filename, relFile)

if (!instrument) {
return null
Expand All @@ -248,20 +198,24 @@ NYC.prototype._transformFactory = function (cacheDir) {
return function (code, metadata, hash) {
var filename = metadata.filename

var sourceMap = convertSourceMap.fromSource(code) || convertSourceMap.fromMapFileSource(code, path.dirname(filename))
if (sourceMap) {
if (hash) {
var mapPath = path.join(cacheDir, hash + '.map')
fs.writeFileSync(mapPath, sourceMap.toJSON())
} else {
_this.sourceMapCache.addMap(filename, sourceMap.toJSON())
}
}
if (_this._sourceMap) _this._handleSourceMap(cacheDir, code, hash, filename)

return instrumenter.instrumentSync(code, filename)
}
}

NYC.prototype._handleSourceMap = function (cacheDir, code, hash, filename) {
var sourceMap = convertSourceMap.fromSource(code) || convertSourceMap.fromMapFileSource(code, path.dirname(filename))
if (sourceMap) {
if (hash) {
var mapPath = path.join(cacheDir, hash + '.map')
fs.writeFileSync(mapPath, sourceMap.toJSON())
} else {
this.sourceMapCache.addMap(filename, sourceMap.toJSON())
}
}
}

NYC.prototype._handleJs = function (code, filename) {
var relFile = path.relative(this.cwd, filename)
return this._maybeInstrumentSource(code, filename, relFile) || code
Expand Down
23 changes: 23 additions & 0 deletions lib/instrumenters/istanbul.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
var fs = require('fs')
var path = require('path')

function InstrumenterIstanbul (cwd) {
var configFile = path.resolve(cwd, './.istanbul.yml')

if (!fs.existsSync(configFile)) configFile = undefined
var istanbul = InstrumenterIstanbul.istanbul()
var instrumenterConfig = istanbul.config.loadFile(configFile).instrumentation.config

return new istanbul.Instrumenter({
coverageVariable: '__coverage__',
embedSource: instrumenterConfig['embed-source'],
noCompact: !instrumenterConfig.compact,
preserveComments: instrumenterConfig['preserve-comments']
})
}

InstrumenterIstanbul.istanbul = function () {
return InstrumenterIstanbul._istanbul || (InstrumenterIstanbul._istanbul = require('istanbul'))
}

module.exports = InstrumenterIstanbul
9 changes: 9 additions & 0 deletions lib/instrumenters/noop.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
function NOOP () {
return {
instrumentSync: function (code) {
return code
}
}
}

module.exports = NOOP
5 changes: 5 additions & 0 deletions lib/source-map-cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,11 @@ SourceMapCache.prototype._rewriteBranches = function (fileReport, sourceMap) {

if (locations.length > 0) {
b[index + ''] = fileReport.b[k]

/* istanbul ignore next: hard to test for edge-case,
counts for more statements than exist post remapping */
while (b[index + ''].length > locations.length) b[index + ''].pop()

branchMap[index + ''] = {
line: locations[0].start.line,
type: item.type,
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"index.js",
"bin/*.js",
"lib/*.js",
"lib/instrumenters/*.js",
"!**/*covered.js"
],
"nyc": {
Expand Down Expand Up @@ -85,6 +86,7 @@
"signal-exit": "^2.1.1",
"source-map": "^0.5.3",
"spawn-wrap": "^1.2.2",
"test-exclude": "^1.1.0",
"yargs": "^4.7.0"
},
"devDependencies": {
Expand Down Expand Up @@ -131,6 +133,7 @@
"signal-exit",
"source-map",
"spawn-wrap",
"test-exclude",
"yargs"
]
}
Binary file added screen2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions test/fixtures/cli/env.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log(JSON.stringify(process.env))
42 changes: 42 additions & 0 deletions test/src/nyc-bin.js
Original file line number Diff line number Diff line change
Expand Up @@ -197,4 +197,46 @@ describe('the nyc cli', function () {
})
})
})

it('passes configuration via environment variables', function (done) {
var args = [
bin,
'--silent',
'--require=mkdirp',
'--include=env.js',
'--exclude=batman.js',
'--extension=.js',
'--cache=true',
'--source-map=true',
'--instrumenter=./lib/instrumenters/istanbul.js',
process.execPath,
'./env.js'
]
var expected = {
NYC_REQUIRE: 'mkdirp',
NYC_INCLUDE: 'env.js',
NYC_EXCLUDE: 'batman.js',
NYC_EXTENSION: '.js',
NYC_CACHE: 'enable',
NYC_SOURCE_MAP: 'enable',
NYC_INSTRUMENTER: './lib/instrumenters/istanbul.js'
}

var proc = spawn(process.execPath, args, {
cwd: fixturesCLI,
env: env
})

var stdout = ''
proc.stdout.on('data', function (chunk) {
stdout += chunk
})

proc.on('close', function (code) {
code.should.equal(0)
var env = JSON.parse(stdout)
env.should.include(expected)
done()
})
})
})
Loading

0 comments on commit 9fd2295

Please sign in to comment.