Skip to content

Commit cdfdff3

Browse files
authored
feat: add --exclude-after-remap option for users who pre-instrument their codebase (#697)
1 parent a413f6a commit cdfdff3

File tree

11 files changed

+200
-23
lines changed

11 files changed

+200
-23
lines changed

README.md

+33-8
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,14 @@ and a `text-lcov` coverage report.
5353
nyc --reporter=lcov --reporter=text-lcov npm test
5454
```
5555

56-
### Accurate stack traces using source maps
56+
### Accurate stack traces using source-maps
5757

5858
When `produce-source-map` is set to true, then the instrumented source files will
5959
include inline source maps for the instrumenter transform. When combined with
6060
[source-map-support](https://github.com/evanw/node-source-map-support),
6161
stack traces for instrumented code will reflect their original lines.
6262

63-
### Support for custom require hooks (babel, webpack, etc.)
63+
### Support for custom require hooks (babel, typescript, etc.)
6464

6565
nyc supports custom require hooks like
6666
[`babel-register`](http://babeljs.io/docs/usage/require/). nyc can
@@ -69,9 +69,20 @@ flag](#require-additional-modules).
6969

7070
Source maps are used to map coverage information back to the appropriate lines
7171
of the pre-transpiled code. You'll have to configure your custom require hook
72-
to inline the source map in the transpiled code. For Babel that means setting
72+
to inline the source-map in the transpiled code. For Babel that means setting
7373
the `sourceMaps` option to `inline`.
7474

75+
### Source-Map support for pre-instrumented codebases
76+
77+
If you opt to pre-instrument your source-code (rather than using a just-in-time
78+
transpiler like [`babel-register`](http://babeljs.io/docs/usage/require/))
79+
nyc supports both inline source-maps and `.map` files.
80+
81+
_Important: If you are using nyc with a project that pre-instruments its code,
82+
run nyc with the configuration option `--exclude-after-remap` set to `false`.
83+
Otherwise nyc's reports will exclude any files that source-maps remap to folders
84+
covered under exclude rules._
85+
7586
## Use with `babel-plugin-istanbul` for Babel Support
7687

7788
We recommend using [`babel-plugin-istanbul`](https://github.com/istanbuljs/babel-plugin-istanbul) if your
@@ -118,20 +129,20 @@ That's all there is to it, better ES2015+ syntax highlighting awaits:
118129

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

121-
## Support for alternate file extensions (.jsx, .es6)
132+
## Support for alternate file extensions (.jsx, .mjs)
122133

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

125136
```shell
126-
nyc --extension .jsx --extension .es6 npm test
137+
nyc --extension .jsx --extension .mjs npm test
127138
```
128139

129140
```json
130141
{
131142
"nyc": {
132143
"extension": [
133144
".jsx",
134-
".es6"
145+
".mjs"
135146
]
136147
}
137148
}
@@ -320,9 +331,18 @@ You can specify custom high and low watermarks in nyc's configuration:
320331
}
321332
```
322333

323-
## Other advanced features
334+
## Parsing Hints (Ignoring Lines)
324335

325-
Take a look at http://istanbul.js.org/docs/advanced/ and please feel free to [contribute documentation](https://github.com/istanbuljs/istanbuljs.github.io/tree/development/content).
336+
There may be some sections of your codebase that you wish to purposefully
337+
exclude from coverage tracking, to do so you can use the following parsing
338+
hints:
339+
340+
* `/* istanbul ignore if */`: ignore the next if statement.
341+
* `/* istanbul ignore else */`: ignore the else portion of an if statement.
342+
* `/* istanbul ignore next */`: ignore the next _thing_ in the source-code (
343+
functions, if statements, classes, you name it).
344+
* `/* istanbul ignore file */`: ignore an entire source-file (this should be
345+
placed at the top of the file).
326346

327347
## Integrating with coveralls
328348

@@ -396,8 +416,13 @@ Here's how to get `nyc` integrated with codecov and travis-ci.org:
396416
That's all there is to it!
397417

398418
## Integrating with TAP formatters
419+
399420
Many testing frameworks (Mocha, Tape, Tap, etc.) can produce [TAP](https://en.wikipedia.org/wiki/Test_Anything_Protocol) output. [tap-nyc](https://github.com/MegaArman/tap-nyc) is a TAP formatter designed to look nice with nyc.
400421

401422
## More tutorials
402423

403424
You can find more tutorials at http://istanbul.js.org/docs/tutorials
425+
426+
## Other advanced features
427+
428+
Take a look at http://istanbul.js.org/docs/advanced/ and please feel free to [contribute documentation](https://github.com/istanbuljs/istanbuljs.github.io/tree/development/content).

index.js

+9-3
Original file line numberDiff line numberDiff line change
@@ -420,9 +420,15 @@ NYC.prototype._getCoverageMapFromAllCoverageFiles = function () {
420420
this.loadReports().forEach(function (report) {
421421
map.merge(report)
422422
})
423-
map.filter(function (filename) {
424-
return _this.exclude.shouldInstrument(filename)
425-
})
423+
// depending on whether source-code is pre-instrumented
424+
// or instrumented using a JIT plugin like babel-require
425+
// you may opt to exclude files after applying
426+
// source-map remapping logic.
427+
if (this.config.excludeAfterRemap) {
428+
map.filter(function (filename) {
429+
return _this.exclude.shouldInstrument(filename)
430+
})
431+
}
426432
map.data = this.sourceMaps.remapCoverage(map.data)
427433
return map
428434
}

lib/config-util.js

+6
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,12 @@ Config.buildYargs = function (cwd) {
7575
describe: 'a list of specific files and directories that should be excluded from coverage, glob patterns are supported, node_modules is always excluded',
7676
global: false
7777
})
78+
.option('exclude-after-remap', {
79+
default: true,
80+
type: 'boolean',
81+
description: 'should exclude logic be performed after the source-map remaps filenames?',
82+
global: false
83+
})
7884
.option('include', {
7985
alias: 'n',
8086
default: [],

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@
9696
"resolve-from": "^2.0.0",
9797
"rimraf": "^2.5.4",
9898
"signal-exit": "^3.0.1",
99-
"spawn-wrap": "^1.4.0",
99+
"spawn-wrap": "1.3.8",
100100
"test-exclude": "^4.1.1",
101101
"yargs": "^10.0.3",
102102
"yargs-parser": "^8.0.0"
@@ -119,6 +119,7 @@
119119
"split-lines": "^1.0.0",
120120
"standard": "^9.0.2",
121121
"standard-version": "^4.0.0",
122+
"strip-indent": "^2.0.0",
122123
"tap": "^10.0.0",
123124
"which": "^1.2.11",
124125
"zero-fill": "^2.2.3"

test/fixtures/source-maps/instrumented/s1.min.js

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/fixtures/source-maps/instrumented/s1.min.js.map

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/fixtures/source-maps/instrumented/s2.min.js

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
var apple = 99
2+
var banana = 200
3+
function add (item1, item2) {
4+
return item1 + item2
5+
}
6+
function multiply (item1, item2) {
7+
return item1 * item2
8+
}
9+
add(apple, banana)
+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
var strawberry = 99
2+
var pineapple = 200
3+
function add (item1, item2) {
4+
return item1 + item2
5+
}
6+
add(strawberry, pineapple)
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}

test/nyc-bin.js

+129-11
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
/* global describe, it */
22

3-
var _ = require('lodash')
4-
var path = require('path')
5-
var bin = path.resolve(__dirname, '../bin/nyc')
6-
var fixturesCLI = path.resolve(__dirname, './fixtures/cli')
7-
var fakebin = path.resolve(fixturesCLI, 'fakebin')
8-
var fixturesHooks = path.resolve(__dirname, './fixtures/hooks')
9-
var fs = require('fs')
10-
var glob = require('glob')
11-
var isWindows = require('is-windows')()
12-
var rimraf = require('rimraf')
13-
var spawn = require('child_process').spawn
3+
const _ = require('lodash')
4+
const path = require('path')
5+
const bin = path.resolve(__dirname, '../bin/nyc')
6+
const fixturesCLI = path.resolve(__dirname, './fixtures/cli')
7+
const fixturesHooks = path.resolve(__dirname, './fixtures/hooks')
8+
const fixturesSourceMaps = path.resolve(__dirname, './fixtures/source-maps')
9+
const fakebin = path.resolve(fixturesCLI, 'fakebin')
10+
const fs = require('fs')
11+
const glob = require('glob')
12+
const isWindows = require('is-windows')()
13+
const rimraf = require('rimraf')
14+
const spawn = require('child_process').spawn
15+
const si = require('strip-indent')
1416

1517
require('chai').should()
1618

@@ -762,4 +764,120 @@ describe('the nyc cli', function () {
762764
done()
763765
})
764766
})
767+
768+
// the following tests exercise nyc's behavior around source-maps
769+
// that have been included with pre-instrumented files. Perhaps, as an
770+
// example, unit tests are being run against minified JavaScript.
771+
// --exclude-after-remap will likely need to be set to false when
772+
// using nyc with this type of configuration.
773+
describe('source-maps', () => {
774+
describe('--all', () => {
775+
it('includes files with both .map files and inline source-maps', (done) => {
776+
const args = [
777+
bin,
778+
'--all',
779+
'--cache', 'false',
780+
'--exclude-after-remap', 'false',
781+
'--exclude', 'original',
782+
process.execPath, './instrumented/s1.min.js'
783+
]
784+
785+
const proc = spawn(process.execPath, args, {
786+
cwd: fixturesSourceMaps,
787+
env: env
788+
})
789+
790+
var stdout = ''
791+
proc.stdout.on('data', function (chunk) {
792+
stdout += chunk
793+
})
794+
795+
proc.on('close', function (code) {
796+
code.should.equal(0)
797+
stdoutShouldEqual(stdout, `
798+
----------|----------|----------|----------|----------|----------------|
799+
File | % Stmts | % Branch | % Funcs | % Lines |Uncovered Lines |
800+
----------|----------|----------|----------|----------|----------------|
801+
All files | 44.44 | 100 | 33.33 | 44.44 | |
802+
s1.js | 80 | 100 | 50 | 80 | 7 |
803+
s2.js | 0 | 100 | 0 | 0 | 1,2,4,6 |
804+
----------|----------|----------|----------|----------|----------------|`
805+
)
806+
done()
807+
})
808+
})
809+
})
810+
811+
describe('.map file', () => {
812+
it('appropriately instruments file with corresponding .map file', (done) => {
813+
const args = [
814+
bin,
815+
'--cache', 'false',
816+
'--exclude-after-remap', 'false',
817+
'--exclude', 'original',
818+
process.execPath, './instrumented/s1.min.js'
819+
]
820+
821+
const proc = spawn(process.execPath, args, {
822+
cwd: fixturesSourceMaps,
823+
env: env
824+
})
825+
826+
var stdout = ''
827+
proc.stdout.on('data', function (chunk) {
828+
stdout += chunk
829+
})
830+
831+
proc.on('close', function (code) {
832+
code.should.equal(0)
833+
stdoutShouldEqual(stdout, `
834+
----------|----------|----------|----------|----------|----------------|
835+
File | % Stmts | % Branch | % Funcs | % Lines |Uncovered Lines |
836+
----------|----------|----------|----------|----------|----------------|
837+
All files | 80 | 100 | 50 | 80 | |
838+
s1.js | 80 | 100 | 50 | 80 | 7 |
839+
----------|----------|----------|----------|----------|----------------|`)
840+
done()
841+
})
842+
})
843+
})
844+
845+
describe('inline', () => {
846+
it('appropriately instruments a file with an inline source-map', (done) => {
847+
const args = [
848+
bin,
849+
'--cache', 'false',
850+
'--exclude-after-remap', 'false',
851+
'--exclude', 'original',
852+
process.execPath, './instrumented/s2.min.js'
853+
]
854+
855+
const proc = spawn(process.execPath, args, {
856+
cwd: fixturesSourceMaps,
857+
env: env
858+
})
859+
860+
var stdout = ''
861+
proc.stdout.on('data', function (chunk) {
862+
stdout += chunk
863+
})
864+
865+
proc.on('close', function (code) {
866+
code.should.equal(0)
867+
stdoutShouldEqual(stdout, `
868+
----------|----------|----------|----------|----------|----------------|
869+
File | % Stmts | % Branch | % Funcs | % Lines |Uncovered Lines |
870+
----------|----------|----------|----------|----------|----------------|
871+
All files | 100 | 100 | 100 | 100 | |
872+
s2.js | 100 | 100 | 100 | 100 | |
873+
----------|----------|----------|----------|----------|----------------|`)
874+
done()
875+
})
876+
})
877+
})
878+
})
765879
})
880+
881+
function stdoutShouldEqual (stdout, expected) {
882+
`\n${stdout}`.should.equal(`${si(expected)}\n`)
883+
}

0 commit comments

Comments
 (0)