Skip to content

Commit e45b51b

Browse files
authored
feat: adds instrument command line option (#298)
* feat: adds instrument command line option * fix: load instrumented version of NYC; fix option description based on @kentcdodds' code review * docs: update README.md * docs: gave README.md a once over, and made a few edits
1 parent f67bff7 commit e45b51b

File tree

6 files changed

+268
-39
lines changed

6 files changed

+268
-39
lines changed

README.md

+21-12
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@
66
[![Windows Tests](https://img.shields.io/appveyor/ci/bcoe/nyc-ilw23/master.svg?label=Windows%20Tests)](https://ci.appveyor.com/project/bcoe/nyc-ilw23)
77
[![Standard Version](https://img.shields.io/badge/release-standard%20version-brightgreen.svg)](https://github.com/conventional-changelog/standard-version)
88

9-
Istanbul's high-tech command line interface, with support for:
9+
Istanbul's state of the art command line interface, with support for:
1010

1111
* applications that spawn subprocesses.
1212
* ES2015 transforms, via [babel-plugin-istanbul](https://github.com/istanbuljs/babel-plugin-istanbul), or source-maps.
1313

14-
## Instrumenting Your Code
14+
## Instrumenting your code
1515

1616
You can install nyc as a development dependency and add it to the test stanza
1717
in your package.json.
@@ -50,10 +50,10 @@ and a `text-lcov` coverage report.
5050
nyc --reporter=lcov --reporter=text-lcov npm test
5151
```
5252

53-
## Support For Custom Require Hooks (Babel! ES2015!)
53+
## Support for custom require hooks (babel, webpack, etc.)
5454

5555
nyc supports custom require hooks like
56-
[`babel-register`](http://babeljs.io/docs/usage/require/). If necessary nyc can
56+
[`babel-register`](http://babeljs.io/docs/usage/require/). nyc can
5757
load the hooks for you, [using the `--require`
5858
flag](#require-additional-modules).
5959

@@ -62,7 +62,7 @@ of the pre-transpiled code. You'll have to configure your custom require hook
6262
to inline the source map in the transpiled code. For Babel that means setting
6363
the `sourceMaps` option to `inline`.
6464

65-
## Use with babel-plugin-istanbul for Better ES6/ES7 Support
65+
## Use with babel-plugin-istanbul for ES6/ES7/ES2015 Support
6666

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

@@ -107,7 +107,7 @@ That's all there is to it, better ES6 syntax highlighting awaits:
107107

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

110-
## Support For Custom File Extensions (.jsx, .es6)
110+
## Support for alternate file extensions (.jsx, .es6)
111111

112112
Supporting file extensions can be configured through either the configuration arguments or with the `nyc` config section in `package.json`.
113113

@@ -126,7 +126,7 @@ nyc --extension .jsx --extension .es6 npm test
126126
}
127127
```
128128

129-
## Checking Coverage
129+
## Checking coverage
130130

131131
nyc can fail tests if coverage falls below a threshold.
132132
After running your tests with nyc, simply run:
@@ -144,7 +144,7 @@ nyc --check-coverage --lines 100 npm test
144144

145145
The above check fails if coverage falls below 100%.
146146

147-
## Running Reports
147+
## Running reports
148148

149149
Once you've run your tests with nyc, simply run:
150150

@@ -162,7 +162,7 @@ you can use any reporters that are supported by istanbul:
162162
nyc report --reporter=lcov
163163
```
164164

165-
## Excluding Files
165+
## Excluding files
166166

167167
You can tell nyc to exclude specific files and directories by adding
168168
an `nyc.exclude` array to your `package.json`. Each element of
@@ -192,7 +192,7 @@ directory:
192192
which would exclude `test`/`__tests__` directories as well as `test.js`, `*.test.js`,
193193
and `test-*.js` files. Specifying your own exclude property overrides these defaults.
194194

195-
## Including Files
195+
## Including files
196196

197197
As an alternative to providing a list of files to `exclude`, you can provide
198198
an `include` key to specify specific files that should be covered:
@@ -207,7 +207,7 @@ an `include` key to specify specific files that should be covered:
207207

208208
> Note: include defaults to `['**']`
209209
210-
## Include Reports For Files That Are Not Required
210+
## Include reports for files that are not required
211211

212212
By default nyc does not collect coverage for files that have not
213213
been required, run nyc with the flag `--all` to enable this.
@@ -240,7 +240,16 @@ can also be specified in the `nyc` stanza of your package.json:
240240
}
241241
```
242242

243-
## Integrating With Coveralls
243+
## Instrumenting source files
244+
245+
nyc's `instrument` command can be used to instrument
246+
source files outside of the context of your unit-tests:
247+
248+
__instrument the entire ./lib folder:__
249+
250+
`nyc instrument ./lib ./output`
251+
252+
## Integrating with coveralls
244253

245254
[coveralls.io](https://coveralls.io) is a great tool for adding
246255
coverage reports to your GitHub project. Here's how to get nyc

bin/nyc.js

+37-15
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,47 @@ var yargs = require('yargs/yargs')(process.argv.slice(2))
1616
.command('report', 'run coverage report for .nyc_output', function (yargs) {
1717
return yargs
1818
.usage('$0 report [options]')
19+
.option('reporter', {
20+
alias: 'r',
21+
describe: 'coverage reporter(s) to use',
22+
default: 'text'
23+
})
24+
.option('report-dir', {
25+
describe: 'directory to output coverage reports in',
26+
default: 'coverage'
27+
})
1928
.example('$0 report --reporter=lcov', 'output an HTML lcov report to ./coverage')
2029
})
2130
.command('check-coverage', 'check whether coverage is within thresholds provided', function (yargs) {
2231
return yargs
2332
.usage('$0 check-coverage [options]')
33+
.option('branches', {
34+
default: 0,
35+
description: 'what % of branches must be covered?'
36+
})
37+
.option('functions', {
38+
default: 0,
39+
description: 'what % of functions must be covered?'
40+
})
41+
.option('lines', {
42+
default: 90,
43+
description: 'what % of lines must be covered?'
44+
})
45+
.option('statements', {
46+
default: 0,
47+
description: 'what % of statements must be covered?'
48+
})
2449
.example('$0 check-coverage --lines 95', "check whether the JSON in nyc's output folder meets the thresholds provided")
2550
})
51+
.command(require('../lib/commands/instrument'))
2652
.option('reporter', {
2753
alias: 'r',
2854
describe: 'coverage reporter(s) to use',
29-
default: 'text',
30-
global: true
55+
default: 'text'
3156
})
3257
.option('report-dir', {
33-
describe: 'default directory to output coverage reports in',
34-
default: 'coverage',
35-
global: true
58+
describe: 'directory to output coverage reports in',
59+
default: 'coverage'
3660
})
3761
.option('silent', {
3862
alias: 's',
@@ -67,7 +91,7 @@ var yargs = require('yargs/yargs')(process.argv.slice(2))
6791
type: 'boolean',
6892
describe: 'cache instrumentation results for improved performance'
6993
})
70-
.options('extension', {
94+
.option('extension', {
7195
alias: 'e',
7296
default: [],
7397
describe: 'a list of extensions that nyc should handle in addition to .js'
@@ -79,23 +103,19 @@ var yargs = require('yargs/yargs')(process.argv.slice(2))
79103
})
80104
.option('branches', {
81105
default: 0,
82-
description: 'what % of branches must be covered?',
83-
global: true
106+
description: 'what % of branches must be covered?'
84107
})
85108
.option('functions', {
86109
default: 0,
87-
description: 'what % of functions must be covered?',
88-
global: true
110+
description: 'what % of functions must be covered?'
89111
})
90112
.option('lines', {
91113
default: 90,
92-
description: 'what % of lines must be covered?',
93-
global: true
114+
description: 'what % of lines must be covered?'
94115
})
95116
.option('statements', {
96117
default: 0,
97-
description: 'what % of statements must be covered?',
98-
global: true
118+
description: 'what % of statements must be covered?'
99119
})
100120
.option('source-map', {
101121
default: true,
@@ -112,7 +132,7 @@ var yargs = require('yargs/yargs')(process.argv.slice(2))
112132
.version()
113133
.pkgConf('nyc', process.cwd())
114134
.example('$0 npm test', 'instrument your tests with coverage')
115-
.example('$0 --require --require babel-core/register npm test', 'instrument your tests with coverage and babel')
135+
.example('$0 --require babel-core/register npm test', 'instrument your tests with coverage and babel')
116136
.example('$0 report --reporter=text-lcov', 'output lcov report after running your tests')
117137
.epilog('visit https://git.io/voHar for list of available reporters')
118138

@@ -125,6 +145,8 @@ if (argv._[0] === 'report') {
125145
report(argv)
126146
} else if (argv._[0] === 'check-coverage') {
127147
checkCoverage(argv)
148+
} else if (argv._[0] === 'instrument') {
149+
// noop, let the command handler do its thing.
128150
} else if (argv._.length) {
129151
// wrap subprocesses and execute argv[1]
130152
argv.require = arrify(argv.require)

index.js

+58-9
Original file line numberDiff line numberDiff line change
@@ -157,18 +157,10 @@ NYC.prototype.addAllFiles = function () {
157157

158158
this._loadAdditionalModules()
159159

160-
var pattern = null
161-
if (this.extensions.length === 1) {
162-
pattern = '**/*' + this.extensions[0]
163-
} else {
164-
pattern = '**/*{' + this.extensions.join() + '}'
165-
}
166-
167160
this.fakeRequire = true
168-
glob.sync(pattern, {cwd: this.cwd, nodir: true, ignore: this.exclude.exclude}).forEach(function (filename) {
161+
this.walkAllFiles(this.cwd, function (filename) {
169162
filename = path.resolve(_this.cwd, filename)
170163
_this.addFile(filename)
171-
172164
var coverage = coverageFinder()
173165
var lastCoverage = _this.instrumenter().lastFileCoverage()
174166
if (lastCoverage && _this.exclude.shouldInstrument(filename)) {
@@ -180,6 +172,63 @@ NYC.prototype.addAllFiles = function () {
180172
this.writeCoverageFile()
181173
}
182174

175+
NYC.prototype.instrumentAllFiles = function (input, output, cb) {
176+
var _this = this
177+
var inputDir = '.' + path.sep
178+
var visitor = function (filename) {
179+
var ext
180+
var transform
181+
var inFile = path.relative(_this.cwd, path.resolve(inputDir, filename))
182+
var code = fs.readFileSync(inFile, 'utf-8')
183+
184+
for (ext in _this.transforms) {
185+
if (filename.toLowerCase().substr(-ext.length) === ext) {
186+
transform = _this.transforms[ext]
187+
break
188+
}
189+
}
190+
191+
if (transform) {
192+
code = transform(code, {filename: filename, relFile: inFile})
193+
}
194+
195+
if (!output) {
196+
console.log(code)
197+
} else {
198+
var outFile = path.relative(_this.cwd, path.resolve(output, filename))
199+
mkdirp.sync(path.dirname(outFile))
200+
fs.writeFileSync(outFile, code, 'utf-8')
201+
}
202+
}
203+
204+
this._loadAdditionalModules()
205+
206+
try {
207+
var stats = fs.lstatSync(input)
208+
if (stats.isDirectory()) {
209+
inputDir = input
210+
this.walkAllFiles(input, visitor)
211+
} else {
212+
visitor(input)
213+
}
214+
} catch (err) {
215+
return cb(err)
216+
}
217+
}
218+
219+
NYC.prototype.walkAllFiles = function (dir, visitor) {
220+
var pattern = null
221+
if (this.extensions.length === 1) {
222+
pattern = '**/*' + this.extensions[0]
223+
} else {
224+
pattern = '**/*{' + this.extensions.join() + '}'
225+
}
226+
227+
glob.sync(pattern, {cwd: dir, nodir: true, ignore: this.exclude.exclude}).forEach(function (filename) {
228+
visitor(filename)
229+
})
230+
}
231+
183232
NYC.prototype._maybeInstrumentSource = function (code, filename, relFile) {
184233
var instrument = this.exclude.shouldInstrument(filename, relFile)
185234
if (!instrument) {

lib/commands/instrument.js

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
var NYC
2+
try {
3+
NYC = require('../../index.covered.js')
4+
} catch (e) {
5+
NYC = require('../../index.js')
6+
}
7+
8+
exports.command = 'instrument <input> [output]'
9+
10+
exports.describe = 'instruments a file or a directory tree and writes the instrumented code to the desired output location'
11+
12+
exports.builder = function (yargs) {
13+
return yargs
14+
.usage('$0 instrument <input> [output]')
15+
.option('require', {
16+
alias: 'i',
17+
default: [],
18+
describe: 'a list of additional modules that nyc should attempt to require in its subprocess, e.g., babel-register, babel-polyfill.'
19+
})
20+
.option('extension', {
21+
alias: 'e',
22+
default: [],
23+
describe: 'a list of extensions that nyc should handle in addition to .js'
24+
})
25+
.option('source-map', {
26+
default: true,
27+
type: 'boolean',
28+
description: 'should nyc detect and handle source maps?'
29+
})
30+
.option('instrument', {
31+
default: true,
32+
type: 'boolean',
33+
description: 'should nyc handle instrumentation?'
34+
})
35+
.example('$0 instrument ./lib ./output', 'instrument all .js files in ./lib with coverage and output in ./output')
36+
}
37+
38+
exports.handler = function (argv) {
39+
// if instrument is set to false,
40+
// enable a noop instrumenter.
41+
if (!argv.instrument) argv.instrumenter = './lib/instrumenters/noop'
42+
else argv.instrumenter = './lib/instrumenters/istanbul'
43+
44+
var nyc = new NYC({
45+
instrumenter: argv.instrumenter,
46+
sourceMap: argv.sourceMap,
47+
extension: argv.extension,
48+
require: argv.require
49+
})
50+
51+
nyc.instrumentAllFiles(argv.input, argv.output, function (err) {
52+
if (err) console.error(err.message)
53+
process.exit(1)
54+
})
55+
}

package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -84,13 +84,13 @@
8484
"istanbul-lib-instrument": "^1.1.0-alpha.1",
8585
"istanbul-lib-report": "^1.0.0-alpha.3",
8686
"istanbul-lib-source-maps": "^1.0.0-alpha.10",
87-
"istanbul-reports": "^1.0.0-alpha.6",
87+
"istanbul-reports": "^1.0.0-alpha.7",
8888
"md5-hex": "^1.2.0",
8989
"micromatch": "^2.3.7",
9090
"mkdirp": "^0.5.0",
9191
"pkg-up": "^1.0.0",
9292
"resolve-from": "^2.0.0",
93-
"rimraf": "^2.5.0",
93+
"rimraf": "^2.5.3",
9494
"signal-exit": "^3.0.0",
9595
"spawn-wrap": "^1.2.2",
9696
"test-exclude": "^1.1.0",
@@ -147,4 +147,4 @@
147147
"test-exclude",
148148
"yargs"
149149
]
150-
}
150+
}

0 commit comments

Comments
 (0)