Skip to content

Commit 99dbbb3

Browse files
authored
feat: refactored config to fix precedence of config vs. args (#388)
* feat: refactored config to fix precedence of config vs. args see #379 * fix: address standard nits * fix: remove cruft from package.json * fix: address @mourner's code review
1 parent 9ebaea8 commit 99dbbb3

File tree

7 files changed

+357
-388
lines changed

7 files changed

+357
-388
lines changed

bin/nyc.js

+25-209
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#!/usr/bin/env node
2-
var arrify = require('arrify')
2+
var configUtil = require('../lib/config-util')
33
var foreground = require('foreground-child')
44
var NYC
55
try {
@@ -10,68 +10,44 @@ try {
1010
var processArgs = require('../lib/process-args')
1111

1212
var sw = require('spawn-wrap')
13-
var testExclude = require('test-exclude')
1413
var wrapper = require.resolve('./wrap.js')
15-
var Yargs = require('yargs/yargs')
1614

17-
var yargs = decorateYargs(buildYargs())
18-
var argv = yargs.parse(processArgs.hideInstrumenteeArgs())
15+
// parse configuration and command-line arguments;
16+
// we keep these values in a few different forms,
17+
// used in the various execution contexts of nyc:
18+
// reporting, instrumenting subprocesses, etc.
19+
var yargs = configUtil.decorateYargs(configUtil.buildYargs())
20+
var instrumenterArgs = processArgs.hideInstrumenteeArgs()
21+
var argv = yargs.parse(instrumenterArgs)
22+
var config = configUtil.loadConfig(instrumenterArgs)
1923

2024
if (argv._[0] === 'report') {
2125
// run a report.
2226
process.env.NYC_CWD = process.cwd()
2327

24-
report(argv)
28+
report(config)
2529
} else if (argv._[0] === 'check-coverage') {
26-
checkCoverage(argv)
30+
checkCoverage(config)
2731
} else if (argv._[0] === 'instrument') {
2832
// look in lib/commands/instrument.js for logic.
2933
} else if (argv._.length) {
30-
// wrap subprocesses and execute argv[1]
31-
argv.require = arrify(argv.require)
32-
argv.extension = arrify(argv.extension)
33-
argv.exclude = arrify(argv.exclude)
34-
argv.include = arrify(argv.include)
35-
3634
// if instrument is set to false,
3735
// enable a noop instrumenter.
38-
if (!argv.instrument) argv.instrumenter = './lib/instrumenters/noop'
39-
else argv.instrumenter = './lib/instrumenters/istanbul'
36+
if (!config.instrument) config.instrumenter = './lib/instrumenters/noop'
37+
else config.instrumenter = './lib/instrumenters/istanbul'
4038

41-
var nyc = (new NYC({
42-
require: argv.require,
43-
include: argv.include,
44-
exclude: argv.exclude,
45-
sourceMap: !!argv.sourceMap,
46-
instrumenter: argv.instrumenter,
47-
hookRunInContext: argv.hookRunInContext,
48-
showProcessTree: argv.showProcessTree
49-
}))
39+
var nyc = (new NYC(config))
5040
nyc.reset()
5141

52-
if (argv.all) nyc.addAllFiles()
42+
if (config.all) nyc.addAllFiles()
5343

5444
var env = {
55-
NYC_CWD: process.cwd(),
5645
NYC_CACHE: argv.cache ? 'enable' : 'disable',
57-
NYC_SOURCE_MAP: argv.sourceMap ? 'enable' : 'disable',
58-
NYC_INSTRUMENTER: argv.instrumenter,
59-
NYC_HOOK_RUN_IN_CONTEXT: argv.hookRunInContext ? 'enable' : 'disable',
60-
NYC_SHOW_PROCESS_TREE: argv.showProcessTree ? 'enable' : 'disable',
46+
NYC_CONFIG: JSON.stringify(config),
47+
NYC_CWD: process.cwd(),
6148
NYC_ROOT_ID: nyc.rootId,
62-
BABEL_DISABLE_CACHE: 1
63-
}
64-
if (argv.require.length) {
65-
env.NYC_REQUIRE = argv.require.join(',')
66-
}
67-
if (argv.extension.length) {
68-
env.NYC_EXTENSION = argv.extension.join(',')
69-
}
70-
if (argv.exclude.length) {
71-
env.NYC_EXCLUDE = argv.exclude.join(':')
72-
}
73-
if (argv.include.length) {
74-
env.NYC_INCLUDE = argv.include.join(':')
49+
BABEL_DISABLE_CACHE: 1,
50+
NYC_INSTRUMENTER: config.instrumenter
7551
}
7652
sw([wrapper], env)
7753

@@ -82,17 +58,17 @@ if (argv._[0] === 'report') {
8258
foreground(processArgs.hideInstrumenterArgs(
8359
// use the same argv descrption, but don't exit
8460
// for flags like --help.
85-
buildYargs().parse(process.argv.slice(2))
61+
configUtil.buildYargs().parse(process.argv.slice(2))
8662
), function (done) {
8763
var mainChildExitCode = process.exitCode
8864

89-
if (argv.checkCoverage) {
90-
checkCoverage(argv)
65+
if (config.checkCoverage) {
66+
checkCoverage(config)
9167
process.exitCode = process.exitCode || mainChildExitCode
92-
if (!argv.silent) report(argv)
68+
if (!config.silent) report(config)
9369
return done()
9470
} else {
95-
if (!argv.silent) report(argv)
71+
if (!config.silent) report(config)
9672
return done()
9773
}
9874
})
@@ -104,12 +80,7 @@ if (argv._[0] === 'report') {
10480
function report (argv) {
10581
process.env.NYC_CWD = process.cwd()
10682

107-
var nyc = new NYC({
108-
reporter: argv.reporter,
109-
reportDir: argv.reportDir,
110-
tempDirectory: argv.tempDirectory,
111-
showProcessTree: argv.showProcessTree
112-
})
83+
var nyc = new NYC(argv)
11384
nyc.report()
11485
}
11586

@@ -123,158 +94,3 @@ function checkCoverage (argv, cb) {
12394
statements: argv.statements
12495
})
12596
}
126-
127-
function buildYargs () {
128-
return Yargs([])
129-
.usage('$0 [command] [options]\n\nrun your tests with the nyc bin to instrument them with coverage')
130-
.command('report', 'run coverage report for .nyc_output', function (yargs) {
131-
return yargs
132-
.usage('$0 report [options]')
133-
.option('reporter', {
134-
alias: 'r',
135-
describe: 'coverage reporter(s) to use',
136-
default: 'text'
137-
})
138-
.option('report-dir', {
139-
describe: 'directory to output coverage reports in',
140-
default: 'coverage'
141-
})
142-
.option('temp-directory', {
143-
describe: 'directory from which coverage JSON files are read',
144-
default: './.nyc_output'
145-
})
146-
.option('show-process-tree', {
147-
describe: 'display the tree of spawned processes',
148-
default: false,
149-
type: 'boolean'
150-
})
151-
.example('$0 report --reporter=lcov', 'output an HTML lcov report to ./coverage')
152-
})
153-
.command('check-coverage', 'check whether coverage is within thresholds provided', function (yargs) {
154-
return yargs
155-
.usage('$0 check-coverage [options]')
156-
.option('branches', {
157-
default: 0,
158-
description: 'what % of branches must be covered?'
159-
})
160-
.option('functions', {
161-
default: 0,
162-
description: 'what % of functions must be covered?'
163-
})
164-
.option('lines', {
165-
default: 90,
166-
description: 'what % of lines must be covered?'
167-
})
168-
.option('statements', {
169-
default: 0,
170-
description: 'what % of statements must be covered?'
171-
})
172-
.example('$0 check-coverage --lines 95', "check whether the JSON in nyc's output folder meets the thresholds provided")
173-
})
174-
.option('reporter', {
175-
alias: 'r',
176-
describe: 'coverage reporter(s) to use',
177-
default: 'text'
178-
})
179-
.option('report-dir', {
180-
describe: 'directory to output coverage reports in',
181-
default: 'coverage'
182-
})
183-
.option('silent', {
184-
alias: 's',
185-
default: false,
186-
type: 'boolean',
187-
describe: "don't output a report after tests finish running"
188-
})
189-
.option('all', {
190-
alias: 'a',
191-
default: false,
192-
type: 'boolean',
193-
describe: 'whether or not to instrument all files of the project (not just the ones touched by your test suite)'
194-
})
195-
.option('exclude', {
196-
alias: 'x',
197-
default: testExclude.defaultExclude,
198-
describe: 'a list of specific files and directories that should be excluded from coverage, glob patterns are supported, node_modules is always excluded'
199-
})
200-
.option('include', {
201-
alias: 'n',
202-
default: [],
203-
describe: 'a list of specific files that should be covered, glob patterns are supported'
204-
})
205-
.option('require', {
206-
alias: 'i',
207-
default: [],
208-
describe: 'a list of additional modules that nyc should attempt to require in its subprocess, e.g., babel-register, babel-polyfill.'
209-
})
210-
.option('cache', {
211-
alias: 'c',
212-
default: false,
213-
type: 'boolean',
214-
describe: 'cache instrumentation results for improved performance'
215-
})
216-
.option('extension', {
217-
alias: 'e',
218-
default: [],
219-
describe: 'a list of extensions that nyc should handle in addition to .js'
220-
})
221-
.option('check-coverage', {
222-
type: 'boolean',
223-
default: false,
224-
describe: 'check whether coverage is within thresholds provided'
225-
})
226-
.option('branches', {
227-
default: 0,
228-
description: 'what % of branches must be covered?'
229-
})
230-
.option('functions', {
231-
default: 0,
232-
description: 'what % of functions must be covered?'
233-
})
234-
.option('lines', {
235-
default: 90,
236-
description: 'what % of lines must be covered?'
237-
})
238-
.option('statements', {
239-
default: 0,
240-
description: 'what % of statements must be covered?'
241-
})
242-
.option('source-map', {
243-
default: true,
244-
type: 'boolean',
245-
description: 'should nyc detect and handle source maps?'
246-
})
247-
.option('instrument', {
248-
default: true,
249-
type: 'boolean',
250-
description: 'should nyc handle instrumentation?'
251-
})
252-
.option('hook-run-in-context', {
253-
default: true,
254-
type: 'boolean',
255-
description: 'should nyc wrap vm.runInThisContext?'
256-
})
257-
.option('show-process-tree', {
258-
describe: 'display the tree of spawned processes',
259-
default: false,
260-
type: 'boolean'
261-
})
262-
.pkgConf('nyc', process.cwd())
263-
.example('$0 npm test', 'instrument your tests with coverage')
264-
.example('$0 --require babel-core/register npm test', 'instrument your tests with coverage and babel')
265-
.example('$0 report --reporter=text-lcov', 'output lcov report after running your tests')
266-
.epilog('visit https://git.io/voHar for list of available reporters')
267-
.boolean('help')
268-
.boolean('h')
269-
.boolean('version')
270-
}
271-
272-
// decorate yargs with all the actions
273-
// that would make it exit: help, version, command.
274-
function decorateYargs (yargs) {
275-
return yargs
276-
.help('h')
277-
.alias('h', 'help')
278-
.version()
279-
.command(require('../lib/commands/instrument'))
280-
}

bin/wrap.js

+9-15
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,14 @@ try {
99
var parentPid = process.env.NYC_PARENT_PID || '0'
1010
process.env.NYC_PARENT_PID = process.pid
1111

12-
;(new NYC({
13-
require: process.env.NYC_REQUIRE ? process.env.NYC_REQUIRE.split(',') : [],
14-
extension: process.env.NYC_EXTENSION ? process.env.NYC_EXTENSION.split(',') : [],
15-
exclude: process.env.NYC_EXCLUDE ? process.env.NYC_EXCLUDE.split(':') : [],
16-
include: process.env.NYC_INCLUDE ? process.env.NYC_INCLUDE.split(':') : [],
17-
enableCache: process.env.NYC_CACHE === 'enable',
18-
sourceMap: process.env.NYC_SOURCE_MAP === 'enable',
19-
instrumenter: process.env.NYC_INSTRUMENTER,
20-
hookRunInContext: process.env.NYC_HOOK_RUN_IN_CONTEXT === 'enable',
21-
showProcessTree: process.env.NYC_SHOW_PROCESS_TREE === 'enable',
22-
_processInfo: {
23-
ppid: parentPid,
24-
root: process.env.NYC_ROOT_ID
25-
}
26-
})).wrap()
12+
var config = {}
13+
if (process.env.NYC_CONFIG) config = JSON.parse(process.env.NYC_CONFIG)
14+
config.enableCache = process.env.NYC_CACHE === 'enable'
15+
config._processInfo = {
16+
ppid: parentPid,
17+
root: process.env.NYC_ROOT_ID
18+
}
19+
20+
;(new NYC(config)).wrap()
2721

2822
sw.runMain()

index.js

+8-27
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/* global __coverage__ */
2+
var arrify = require('arrify')
23
var fs = require('fs')
34
var glob = require('glob')
45
var libCoverage = require('istanbul-lib-coverage')
@@ -13,14 +14,11 @@ var path = require('path')
1314
var rimraf = require('rimraf')
1415
var onExit = require('signal-exit')
1516
var resolveFrom = require('resolve-from')
16-
var arrify = require('arrify')
1717
var convertSourceMap = require('convert-source-map')
1818
var md5hex = require('md5-hex')
1919
var findCacheDir = require('find-cache-dir')
2020
var js = require('default-require-extensions/js')
21-
var pkgUp = require('pkg-up')
2221
var testExclude = require('test-exclude')
23-
var yargs = require('yargs')
2422

2523
var ProcessInfo
2624
try {
@@ -34,17 +32,16 @@ if (/index\.covered\.js$/.test(__filename)) {
3432
require('./lib/self-coverage-helper')
3533
}
3634

37-
function NYC (opts) {
38-
var config = this._loadConfig(opts || {})
35+
function NYC (config) {
36+
config = config || {}
3937

40-
this._istanbul = config.istanbul
4138
this.subprocessBin = config.subprocessBin || path.resolve(__dirname, './bin/nyc.js')
4239
this._tempDirectory = config.tempDirectory || './.nyc_output'
4340
this._instrumenterLib = require(config.instrumenter || './lib/instrumenters/istanbul')
44-
this._reportDir = config.reportDir
45-
this._sourceMap = config.sourceMap
46-
this._showProcessTree = config.showProcessTree
47-
this.cwd = config.cwd
41+
this._reportDir = config.reportDir || 'coverage'
42+
this._sourceMap = typeof config.sourceMap === 'boolean' ? config.sourceMap : true
43+
this._showProcessTree = config.showProcessTree || false
44+
this.cwd = config.cwd || process.cwd()
4845

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

@@ -80,26 +77,10 @@ function NYC (opts) {
8077
this.loadedMaps = null
8178
this.fakeRequire = null
8279

83-
this.processInfo = new ProcessInfo(opts && opts._processInfo)
80+
this.processInfo = new ProcessInfo(config && config._processInfo)
8481
this.rootId = this.processInfo.root || this.generateUniqueID()
8582
}
8683

87-
NYC.prototype._loadConfig = function (opts) {
88-
var cwd = opts.cwd || process.env.NYC_CWD || process.cwd()
89-
var pkgPath = pkgUp.sync(cwd)
90-
91-
if (pkgPath) {
92-
cwd = path.dirname(pkgPath)
93-
}
94-
95-
opts.cwd = cwd
96-
97-
return yargs([])
98-
.pkgConf('nyc', cwd)
99-
.default(opts)
100-
.argv
101-
}
102-
10384
NYC.prototype._createTransform = function (ext) {
10485
var _this = this
10586
return cachingTransform({

0 commit comments

Comments
 (0)