Skip to content

Commit 2b64cf8

Browse files
gotwarlostbcoe
authored andcommitted
feat: use istanbul-lib-hook to wrap require and support vm hooks (#308)
1 parent 60843a4 commit 2b64cf8

File tree

10 files changed

+104
-13
lines changed

10 files changed

+104
-13
lines changed

bin/nyc.js

+14-2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ var yargs = require('yargs/yargs')(process.argv.slice(2))
2525
describe: 'directory to output coverage reports in',
2626
default: 'coverage'
2727
})
28+
.option('temp-directory', {
29+
describe: 'directory from which coverage JSON files are read',
30+
default: './.nyc_output'
31+
})
2832
.example('$0 report --reporter=lcov', 'output an HTML lcov report to ./coverage')
2933
})
3034
.command('check-coverage', 'check whether coverage is within thresholds provided', function (yargs) {
@@ -127,6 +131,11 @@ var yargs = require('yargs/yargs')(process.argv.slice(2))
127131
type: 'boolean',
128132
description: 'should nyc handle instrumentation?'
129133
})
134+
.option('hook-run-in-context', {
135+
default: true,
136+
type: 'boolean',
137+
description: 'should nyc wrap vm.runInThisContext?'
138+
})
130139
.help('h')
131140
.alias('h', 'help')
132141
.version()
@@ -164,7 +173,8 @@ if (argv._[0] === 'report') {
164173
include: argv.include,
165174
exclude: argv.exclude,
166175
sourceMap: !!argv.sourceMap,
167-
instrumenter: argv.instrumenter
176+
instrumenter: argv.instrumenter,
177+
hookRunInContext: argv.hookRunInContext
168178
}))
169179
nyc.reset()
170180

@@ -175,6 +185,7 @@ if (argv._[0] === 'report') {
175185
NYC_CACHE: argv.cache ? 'enable' : 'disable',
176186
NYC_SOURCE_MAP: argv.sourceMap ? 'enable' : 'disable',
177187
NYC_INSTRUMENTER: argv.instrumenter,
188+
NYC_HOOK_RUN_IN_CONTEXT: argv.hookRunInContext ? 'enable' : 'disable',
178189
BABEL_DISABLE_CACHE: 1
179190
}
180191
if (argv.require.length) {
@@ -218,7 +229,8 @@ function report (argv) {
218229

219230
;(new NYC({
220231
reporter: argv.reporter,
221-
reportDir: argv.reportDir
232+
reportDir: argv.reportDir,
233+
tempDirectory: argv.tempDirectory
222234
})).report()
223235
}
224236

bin/wrap.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ try {
1313
include: process.env.NYC_INCLUDE ? process.env.NYC_INCLUDE.split(',') : [],
1414
enableCache: process.env.NYC_CACHE === 'enable',
1515
sourceMap: process.env.NYC_SOURCE_MAP === 'enable',
16-
instrumenter: process.env.NYC_INSTRUMENTER
16+
instrumenter: process.env.NYC_INSTRUMENTER,
17+
hookRunInContext: process.env.NYC_HOOK_RUN_IN_CONTEXT === 'enable'
1718
})).wrap()
1819

1920
sw.runMain()

build-self-coverage.js

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
var istanbul = require('istanbul')
1+
var istanbul = require('istanbul-lib-instrument')
22
var fs = require('fs')
33
var path = require('path')
44

@@ -8,10 +8,9 @@ var path = require('path')
88
var indexPath = path.join(__dirname, name)
99
var source = fs.readFileSync(indexPath, 'utf8')
1010

11-
var instrumentor = new istanbul.Instrumenter({
11+
var instrumentor = istanbul.createInstrumenter({
1212
coverageVariable: '___NYC_SELF_COVERAGE___',
13-
esModules: true,
14-
noAutoWrap: true
13+
esModules: true
1514
})
1615

1716
var instrumentedSource = instrumentor.instrumentSync(source, indexPath)

index.js

+15-3
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
var fs = require('fs')
33
var glob = require('glob')
44
var libCoverage = require('istanbul-lib-coverage')
5+
var libHook = require('istanbul-lib-hook')
56
var libReport = require('istanbul-lib-report')
67
var libSourceMaps = require('istanbul-lib-source-maps')
78
var reports = require('istanbul-reports')
89
var mkdirp = require('mkdirp')
910
var Module = require('module')
10-
var appendTransform = require('append-transform')
1111
var cachingTransform = require('caching-transform')
1212
var path = require('path')
1313
var rimraf = require('rimraf')
@@ -67,6 +67,7 @@ function NYC (opts) {
6767

6868
this.sourceMapCache = libSourceMaps.createSourceMapStore()
6969

70+
this.hookRunInContext = config.hookRunInContext
7071
this.hashCache = {}
7172
this.loadedMaps = null
7273
this.fakeRequire = null
@@ -289,13 +290,23 @@ NYC.prototype._handleJs = function (code, filename) {
289290
return this._maybeInstrumentSource(code, filename, relFile) || code
290291
}
291292

292-
NYC.prototype._wrapRequire = function () {
293+
NYC.prototype._addHook = function (type) {
293294
var handleJs = this._handleJs.bind(this)
295+
var dummyMatcher = function () { return true } // we do all processing in transformer
296+
libHook['hook' + type](dummyMatcher, handleJs, { extensions: this.extensions })
297+
}
294298

299+
NYC.prototype._wrapRequire = function () {
295300
this.extensions.forEach(function (ext) {
296301
require.extensions[ext] = js
297-
appendTransform(handleJs, ext)
298302
})
303+
this._addHook('Require')
304+
}
305+
306+
NYC.prototype._addOtherHooks = function () {
307+
if (this.hookRunInContext) {
308+
this._addHook('RunInThisContext')
309+
}
299310
}
300311

301312
NYC.prototype.cleanup = function () {
@@ -329,6 +340,7 @@ NYC.prototype._wrapExit = function () {
329340

330341
NYC.prototype.wrap = function (bin) {
331342
this._wrapRequire()
343+
this._addOtherHooks()
332344
this._wrapExit()
333345
this._loadAdditionalModules()
334346
return this

package.json

+4-3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"build": "node ./build-tests",
1212
"instrument": "node ./build-self-coverage.js",
1313
"run-tests": "tap -t120 --no-cov -b ./test/build/*.js ./test/src/nyc-bin.js",
14-
"report": "istanbul report --include=./.self_coverage/*.json lcov text",
14+
"report": "node ./bin/nyc --temp-directory ./.self_coverage/ -r text -r lcov report",
1515
"cover": "npm run clean && npm run build && npm run instrument && npm run run-tests && npm run report",
1616
"dev": "npm run clean && npm run build && npm run run-tests",
1717
"version": "standard-version"
@@ -72,7 +72,6 @@
7272
"author": "Ben Coe <[email protected]>",
7373
"license": "ISC",
7474
"dependencies": {
75-
"append-transform": "^0.4.0",
7675
"arrify": "^1.0.1",
7776
"caching-transform": "^1.0.0",
7877
"convert-source-map": "^1.1.2",
@@ -82,6 +81,7 @@
8281
"foreground-child": "^1.5.3",
8382
"glob": "^7.0.3",
8483
"istanbul-lib-coverage": "^1.0.0-alpha.4",
84+
"istanbul-lib-hook": "^1.0.0-alpha.4",
8585
"istanbul-lib-instrument": "^1.1.0-alpha.1",
8686
"istanbul-lib-report": "^1.0.0-alpha.3",
8787
"istanbul-lib-source-maps": "^1.0.0-alpha.10",
@@ -105,9 +105,9 @@
105105
"exists-sync": "0.0.3",
106106
"forking-tap": "^0.1.1",
107107
"is-windows": "^0.2.0",
108-
"istanbul": "^0.4.4",
109108
"lodash": "^4.12.0",
110109
"newline-regex": "^0.2.1",
110+
"requirejs": "^2.2.0",
111111
"sanitize-filename": "^1.5.3",
112112
"sinon": "^1.15.3",
113113
"source-map-support": "^0.4.1",
@@ -134,6 +134,7 @@
134134
"glob",
135135
"istanbul-lib-coverage",
136136
"istanbul-lib-instrument",
137+
"istanbul-lib-hook",
137138
"istanbul-lib-report",
138139
"istanbul-lib-source-maps",
139140
"istanbul-reports",

test/fixtures/hooks/index.js

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// RequireJS uses `vm.runInThisContext`
2+
// make sure we add hooks for it as well
3+
4+
var rjs = require('requirejs'),
5+
assert = require('assert');
6+
7+
rjs.config({
8+
baseUrl : __dirname,
9+
nodeRequire : require
10+
});
11+
12+
rjs(['./lib/lorem'], function(lorem){
13+
var result = lorem(1, 2, 3);
14+
assert.equal(9, result);
15+
});
16+

test/fixtures/hooks/lib/ipsum.js

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
define(function(){
2+
3+
function sum(a, b) {
4+
return a + b;
5+
}
6+
7+
return {
8+
sum : sum
9+
};
10+
11+
});

test/fixtures/hooks/lib/lorem.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
define(['./ipsum'], function (ipsum) {
2+
3+
return function exec(a, b, c){
4+
return ipsum.sum(a, b) * c;
5+
};
6+
7+
});

test/fixtures/hooks/package.json

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"name": "hooktest",
3+
"version": "1.1.1",
4+
"description": "AMD/ requirejs test project",
5+
"main": "index.js",
6+
"dependencies": {
7+
"requirejs": "^2.2.0"
8+
},
9+
"private": true
10+
}

test/src/nyc-bin.js

+22
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ var fs = require('fs')
55
var spawn = require('child_process').spawn
66
var isWindows = require('is-windows')()
77
var fixturesCLI = path.resolve(__dirname, '../fixtures/cli')
8+
var fixturesHooks = path.resolve(__dirname, '../fixtures/hooks')
89
var fakebin = path.resolve(fixturesCLI, 'fakebin')
910
var bin = path.resolve(__dirname, '../../bin/nyc')
1011
var rimraf = require('rimraf')
@@ -388,4 +389,25 @@ describe('the nyc cli', function () {
388389
})
389390
})
390391
})
392+
393+
describe('hooks', function () {
394+
it('provides coverage for requireJS and AMD modules', function (done) {
395+
var args = [bin, process.execPath, './index.js']
396+
397+
var proc = spawn(process.execPath, args, {
398+
cwd: fixturesHooks,
399+
env: process.env
400+
})
401+
var stdout = ''
402+
proc.stdout.on('data', function (chunk) {
403+
stdout += chunk
404+
})
405+
proc.on('close', function (code) {
406+
code.should.equal(0)
407+
stdout.should.match(/ipsum\.js/)
408+
stdout.should.match(/lorem\.js/)
409+
done()
410+
})
411+
})
412+
})
391413
})

0 commit comments

Comments
 (0)