nyc
Advanced tools
Comparing version 14.0.0 to 14.1.0
@@ -70,2 +70,3 @@ #!/usr/bin/env node | ||
nyc.maybePurgeSourceMapCache() | ||
if (argv.checkCoverage) { | ||
@@ -89,3 +90,4 @@ nyc.checkCoverage({ | ||
// I don't have a clue what you're doing. | ||
process.exitCode = 1 | ||
yargs.showHelp() | ||
} |
@@ -19,4 +19,10 @@ var sw = require('spawn-wrap') | ||
if (process.env.NYC_CONFIG_OVERRIDE) { | ||
var override = JSON.parse(process.env.NYC_CONFIG_OVERRIDE) | ||
config = Object.assign(config, override) | ||
process.env.NYC_CONFIG = JSON.stringify(config) | ||
} | ||
;(new NYC(config)).wrap() | ||
sw.runMain() |
@@ -5,2 +5,19 @@ # Change Log | ||
# [14.1.0](https://github.com/istanbuljs/nyc/compare/v14.0.0...v14.1.0) (2019-04-24) | ||
### Bug Fixes | ||
* Do not crash when nyc is run inside itself. ([#1068](https://github.com/istanbuljs/nyc/issues/1068)) ([c4fcf5e](https://github.com/istanbuljs/nyc/commit/c4fcf5e)), closes [#1067](https://github.com/istanbuljs/nyc/issues/1067) | ||
* Exit with code 1 when nyc doesn't know what to do. ([#1070](https://github.com/istanbuljs/nyc/issues/1070)) ([21fb2c8](https://github.com/istanbuljs/nyc/commit/21fb2c8)) | ||
* Purge source-map cache before reporting if cache is disabled. ([#1080](https://github.com/istanbuljs/nyc/issues/1080)) ([3d9eaa4](https://github.com/istanbuljs/nyc/commit/3d9eaa4)) | ||
* Use correct config property for parser plugins ([#1082](https://github.com/istanbuljs/nyc/issues/1082)) ([a7bc7ae](https://github.com/istanbuljs/nyc/commit/a7bc7ae)) | ||
### Features | ||
* add support for yaml configuration file ([#1054](https://github.com/istanbuljs/nyc/issues/1054)) ([ca37ffa](https://github.com/istanbuljs/nyc/commit/ca37ffa)) | ||
# [14.0.0](https://github.com/istanbuljs/nyc/compare/v13.3.0...v14.0.0) (2019-04-15) | ||
@@ -7,0 +24,0 @@ |
906
index.js
@@ -35,562 +35,568 @@ 'use strict' | ||
function NYC (config) { | ||
config = config || {} | ||
this.config = config | ||
function coverageFinder () { | ||
var coverage = global.__coverage__ | ||
if (typeof __coverage__ === 'object') coverage = __coverage__ | ||
if (!coverage) coverage = global['__coverage__'] = {} | ||
return coverage | ||
} | ||
this.subprocessBin = config.subprocessBin || path.resolve(__dirname, './bin/nyc.js') | ||
this._tempDirectory = config.tempDirectory || config.tempDir || './.nyc_output' | ||
this._instrumenterLib = require(config.instrumenter || './lib/instrumenters/istanbul') | ||
this._reportDir = config.reportDir || 'coverage' | ||
this._sourceMap = typeof config.sourceMap === 'boolean' ? config.sourceMap : true | ||
this._showProcessTree = config.showProcessTree || false | ||
this._eagerInstantiation = config.eager || false | ||
this.cwd = config.cwd || process.cwd() | ||
this.reporter = [].concat(config.reporter || 'text') | ||
class NYC { | ||
constructor (config) { | ||
config = config || {} | ||
this.config = config | ||
this.cacheDirectory = (config.cacheDir && path.resolve(config.cacheDir)) || findCacheDir({ name: 'nyc', cwd: this.cwd }) | ||
this.cache = Boolean(this.cacheDirectory && config.cache) | ||
this.subprocessBin = config.subprocessBin || path.resolve(__dirname, './bin/nyc.js') | ||
this._tempDirectory = config.tempDirectory || config.tempDir || './.nyc_output' | ||
this._instrumenterLib = require(config.instrumenter || './lib/instrumenters/istanbul') | ||
this._reportDir = config.reportDir || 'coverage' | ||
this._sourceMap = typeof config.sourceMap === 'boolean' ? config.sourceMap : true | ||
this._showProcessTree = config.showProcessTree || false | ||
this._eagerInstantiation = config.eager || false | ||
this.cwd = config.cwd || process.cwd() | ||
this.reporter = [].concat(config.reporter || 'text') | ||
this.extensions = [].concat(config.extension || []) | ||
.concat('.js') | ||
.map(ext => ext.toLowerCase()) | ||
.filter((item, pos, arr) => arr.indexOf(item) === pos) | ||
this.cacheDirectory = (config.cacheDir && path.resolve(config.cacheDir)) || findCacheDir({ name: 'nyc', cwd: this.cwd }) | ||
this.cache = Boolean(this.cacheDirectory && config.cache) | ||
this.exclude = testExclude({ | ||
cwd: this.cwd, | ||
include: config.include, | ||
exclude: config.exclude, | ||
excludeNodeModules: config.excludeNodeModules !== false, | ||
extension: this.extensions | ||
}) | ||
this.extensions = [].concat(config.extension || []) | ||
.concat('.js') | ||
.map(ext => ext.toLowerCase()) | ||
.filter((item, pos, arr) => arr.indexOf(item) === pos) | ||
this.sourceMaps = new SourceMaps({ | ||
cache: this.cache, | ||
cacheDirectory: this.cacheDirectory | ||
}) | ||
this.exclude = testExclude({ | ||
cwd: this.cwd, | ||
include: config.include, | ||
exclude: config.exclude, | ||
excludeNodeModules: config.excludeNodeModules !== false, | ||
extension: this.extensions | ||
}) | ||
// require extensions can be provided as config in package.json. | ||
this.require = [].concat(config.require || []) | ||
this.sourceMaps = new SourceMaps({ | ||
cache: this.cache, | ||
cacheDirectory: this.cacheDirectory | ||
}) | ||
this.transforms = this.extensions.reduce((transforms, ext) => { | ||
transforms[ext] = this._createTransform(ext) | ||
return transforms | ||
}, {}) | ||
// require extensions can be provided as config in package.json. | ||
this.require = [].concat(config.require || []) | ||
this.hookRequire = config.hookRequire | ||
this.hookRunInContext = config.hookRunInContext | ||
this.hookRunInThisContext = config.hookRunInThisContext | ||
this.fakeRequire = null | ||
this.transforms = this.extensions.reduce((transforms, ext) => { | ||
transforms[ext] = this._createTransform(ext) | ||
return transforms | ||
}, {}) | ||
this.processInfo = new ProcessInfo(config && config._processInfo) | ||
this.rootId = this.processInfo.root || this.generateUniqueID() | ||
this.hookRequire = config.hookRequire | ||
this.hookRunInContext = config.hookRunInContext | ||
this.hookRunInThisContext = config.hookRunInThisContext | ||
this.fakeRequire = null | ||
this.hashCache = {} | ||
} | ||
this.processInfo = new ProcessInfo(config && config._processInfo) | ||
this.rootId = this.processInfo.root || uuid() | ||
NYC.prototype._createTransform = function (ext) { | ||
var opts = { | ||
salt: Hash.salt(this.config), | ||
hashData: (input, metadata) => [metadata.filename], | ||
onHash: (input, metadata, hash) => { | ||
this.hashCache[metadata.filename] = hash | ||
}, | ||
cacheDir: this.cacheDirectory, | ||
// when running --all we should not load source-file from | ||
// cache, we want to instead return the fake source. | ||
disableCache: this._disableCachingTransform(), | ||
ext: ext | ||
this.hashCache = {} | ||
} | ||
if (this._eagerInstantiation) { | ||
opts.transform = this._transformFactory(this.cacheDirectory) | ||
} else { | ||
opts.factory = this._transformFactory.bind(this) | ||
_createTransform (ext) { | ||
var opts = { | ||
salt: Hash.salt(this.config), | ||
hashData: (input, metadata) => [metadata.filename], | ||
onHash: (input, metadata, hash) => { | ||
this.hashCache[metadata.filename] = hash | ||
}, | ||
cacheDir: this.cacheDirectory, | ||
// when running --all we should not load source-file from | ||
// cache, we want to instead return the fake source. | ||
disableCache: this._disableCachingTransform(), | ||
ext: ext | ||
} | ||
if (this._eagerInstantiation) { | ||
opts.transform = this._transformFactory(this.cacheDirectory) | ||
} else { | ||
opts.factory = this._transformFactory.bind(this) | ||
} | ||
return cachingTransform(opts) | ||
} | ||
return cachingTransform(opts) | ||
} | ||
NYC.prototype._disableCachingTransform = function () { | ||
return !(this.cache && this.config.isChildProcess) | ||
} | ||
_disableCachingTransform () { | ||
return !(this.cache && this.config.isChildProcess) | ||
} | ||
NYC.prototype._loadAdditionalModules = function () { | ||
this.require.forEach(requireModule => { | ||
// Attempt to require the module relative to the directory being instrumented. | ||
// Then try other locations, e.g. the nyc node_modules folder. | ||
require(resolveFrom.silent(this.cwd, requireModule) || requireModule) | ||
}) | ||
} | ||
_loadAdditionalModules () { | ||
this.require.forEach(requireModule => { | ||
// Attempt to require the module relative to the directory being instrumented. | ||
// Then try other locations, e.g. the nyc node_modules folder. | ||
require(resolveFrom.silent(this.cwd, requireModule) || requireModule) | ||
}) | ||
} | ||
NYC.prototype.instrumenter = function () { | ||
return this._instrumenter || (this._instrumenter = this._createInstrumenter()) | ||
} | ||
instrumenter () { | ||
return this._instrumenter || (this._instrumenter = this._createInstrumenter()) | ||
} | ||
NYC.prototype._createInstrumenter = function () { | ||
return this._instrumenterLib({ | ||
ignoreClassMethods: [].concat(this.config.ignoreClassMethod).filter(a => a), | ||
produceSourceMap: this.config.produceSourceMap, | ||
compact: this.config.compact, | ||
preserveComments: this.config.preserveComments, | ||
esModules: this.config.esModules, | ||
plugins: this.config.parserPlugins | ||
}) | ||
} | ||
_createInstrumenter () { | ||
return this._instrumenterLib({ | ||
ignoreClassMethods: [].concat(this.config.ignoreClassMethod).filter(a => a), | ||
produceSourceMap: this.config.produceSourceMap, | ||
compact: this.config.compact, | ||
preserveComments: this.config.preserveComments, | ||
esModules: this.config.esModules, | ||
plugins: this.config.parserPlugins | ||
}) | ||
} | ||
NYC.prototype.addFile = function (filename) { | ||
const source = this._readTranspiledSource(filename) | ||
this._maybeInstrumentSource(source, filename) | ||
} | ||
addFile (filename) { | ||
const source = this._readTranspiledSource(filename) | ||
this._maybeInstrumentSource(source, filename) | ||
} | ||
NYC.prototype._readTranspiledSource = function (filePath) { | ||
var source = null | ||
var ext = path.extname(filePath) | ||
if (typeof Module._extensions[ext] === 'undefined') { | ||
ext = '.js' | ||
_readTranspiledSource (filePath) { | ||
var source = null | ||
var ext = path.extname(filePath) | ||
if (typeof Module._extensions[ext] === 'undefined') { | ||
ext = '.js' | ||
} | ||
Module._extensions[ext]({ | ||
_compile: function (content, filename) { | ||
source = content | ||
} | ||
}, filePath) | ||
return source | ||
} | ||
Module._extensions[ext]({ | ||
_compile: function (content, filename) { | ||
source = content | ||
} | ||
}, filePath) | ||
return source | ||
} | ||
NYC.prototype.addAllFiles = function () { | ||
this._loadAdditionalModules() | ||
addAllFiles () { | ||
this._loadAdditionalModules() | ||
this.fakeRequire = true | ||
this.exclude.globSync(this.cwd).forEach(relFile => { | ||
const filename = path.resolve(this.cwd, relFile) | ||
this.addFile(filename) | ||
const coverage = coverageFinder() | ||
const lastCoverage = this.instrumenter().lastFileCoverage() | ||
if (lastCoverage) { | ||
coverage[lastCoverage.path] = lastCoverage | ||
} | ||
}) | ||
this.fakeRequire = false | ||
this.fakeRequire = true | ||
this.exclude.globSync(this.cwd).forEach(relFile => { | ||
const filename = path.resolve(this.cwd, relFile) | ||
this.addFile(filename) | ||
const coverage = coverageFinder() | ||
const lastCoverage = this.instrumenter().lastFileCoverage() | ||
if (lastCoverage) { | ||
coverage[lastCoverage.path] = lastCoverage | ||
} | ||
}) | ||
this.fakeRequire = false | ||
this.writeCoverageFile() | ||
} | ||
this.writeCoverageFile() | ||
} | ||
NYC.prototype.instrumentAllFiles = function (input, output, cb) { | ||
let inputDir = '.' + path.sep | ||
const visitor = relFile => { | ||
const inFile = path.resolve(inputDir, relFile) | ||
const inCode = fs.readFileSync(inFile, 'utf-8') | ||
const outCode = this._transform(inCode, inFile) || inCode | ||
instrumentAllFiles (input, output, cb) { | ||
let inputDir = '.' + path.sep | ||
const visitor = relFile => { | ||
const inFile = path.resolve(inputDir, relFile) | ||
const inCode = fs.readFileSync(inFile, 'utf-8') | ||
const outCode = this._transform(inCode, inFile) || inCode | ||
if (output) { | ||
const mode = fs.statSync(inFile).mode | ||
const outFile = path.resolve(output, relFile) | ||
mkdirp.sync(path.dirname(outFile)) | ||
fs.writeFileSync(outFile, outCode) | ||
fs.chmodSync(outFile, mode) | ||
} else { | ||
console.log(outCode) | ||
if (output) { | ||
const mode = fs.statSync(inFile).mode | ||
const outFile = path.resolve(output, relFile) | ||
mkdirp.sync(path.dirname(outFile)) | ||
fs.writeFileSync(outFile, outCode) | ||
fs.chmodSync(outFile, mode) | ||
} else { | ||
console.log(outCode) | ||
} | ||
} | ||
} | ||
this._loadAdditionalModules() | ||
this._loadAdditionalModules() | ||
try { | ||
const stats = fs.lstatSync(input) | ||
if (stats.isDirectory()) { | ||
inputDir = input | ||
try { | ||
const stats = fs.lstatSync(input) | ||
if (stats.isDirectory()) { | ||
inputDir = input | ||
const filesToInstrument = this.exclude.globSync(input) | ||
const filesToInstrument = this.exclude.globSync(input) | ||
if (this.config.completeCopy && output) { | ||
const globOptions = { dot: true, nodir: true, ignore: ['**/.git', '**/.git/**', path.join(output, '**')] } | ||
glob.sync(path.resolve(input, '**'), globOptions) | ||
.forEach(src => cpFile.sync(src, path.join(output, path.relative(input, src)))) | ||
if (this.config.completeCopy && output) { | ||
const globOptions = { dot: true, nodir: true, ignore: ['**/.git', '**/.git/**', path.join(output, '**')] } | ||
glob.sync(path.resolve(input, '**'), globOptions) | ||
.forEach(src => cpFile.sync(src, path.join(output, path.relative(input, src)))) | ||
} | ||
filesToInstrument.forEach(visitor) | ||
} else { | ||
visitor(input) | ||
} | ||
filesToInstrument.forEach(visitor) | ||
} else { | ||
visitor(input) | ||
} catch (err) { | ||
return cb(err) | ||
} | ||
} catch (err) { | ||
return cb(err) | ||
cb() | ||
} | ||
cb() | ||
} | ||
NYC.prototype._transform = function (code, filename) { | ||
const extname = path.extname(filename).toLowerCase() | ||
const transform = this.transforms[extname] || (() => null) | ||
_transform (code, filename) { | ||
const extname = path.extname(filename).toLowerCase() | ||
const transform = this.transforms[extname] || (() => null) | ||
return transform(code, { filename }) | ||
} | ||
return transform(code, { filename }) | ||
} | ||
NYC.prototype._maybeInstrumentSource = function (code, filename) { | ||
if (!this.exclude.shouldInstrument(filename)) { | ||
return null | ||
_maybeInstrumentSource (code, filename) { | ||
if (!this.exclude.shouldInstrument(filename)) { | ||
return null | ||
} | ||
return this._transform(code, filename) | ||
} | ||
return this._transform(code, filename) | ||
} | ||
maybePurgeSourceMapCache () { | ||
if (!this.cache) { | ||
this.sourceMaps.purgeCache() | ||
} | ||
} | ||
NYC.prototype._transformFactory = function (cacheDir) { | ||
const instrumenter = this.instrumenter() | ||
let instrumented | ||
_transformFactory (cacheDir) { | ||
const instrumenter = this.instrumenter() | ||
let instrumented | ||
return (code, metadata, hash) => { | ||
const filename = metadata.filename | ||
let sourceMap = null | ||
return (code, metadata, hash) => { | ||
const filename = metadata.filename | ||
let sourceMap = null | ||
if (this._sourceMap) sourceMap = this.sourceMaps.extractAndRegister(code, filename, hash) | ||
if (this._sourceMap) sourceMap = this.sourceMaps.extractAndRegister(code, filename, hash) | ||
try { | ||
instrumented = instrumenter.instrumentSync(code, filename, sourceMap) | ||
} catch (e) { | ||
debugLog('failed to instrument ' + filename + ' with error: ' + e.stack) | ||
if (this.config.exitOnError) { | ||
console.error('Failed to instrument ' + filename) | ||
process.exit(1) | ||
try { | ||
instrumented = instrumenter.instrumentSync(code, filename, sourceMap) | ||
} catch (e) { | ||
debugLog('failed to instrument ' + filename + ' with error: ' + e.stack) | ||
if (this.config.exitOnError) { | ||
console.error('Failed to instrument ' + filename) | ||
process.exit(1) | ||
} else { | ||
instrumented = code | ||
} | ||
} | ||
if (this.fakeRequire) { | ||
return 'function x () {}' | ||
} else { | ||
instrumented = code | ||
return instrumented | ||
} | ||
} | ||
} | ||
if (this.fakeRequire) { | ||
return 'function x () {}' | ||
} else { | ||
return instrumented | ||
} | ||
_handleJs (code, options) { | ||
// ensure the path has correct casing (see istanbuljs/nyc#269 and nodejs/node#6624) | ||
const filename = path.resolve(this.cwd, options.filename) | ||
return this._maybeInstrumentSource(code, filename) || code | ||
} | ||
} | ||
NYC.prototype._handleJs = function (code, options) { | ||
// ensure the path has correct casing (see istanbuljs/nyc#269 and nodejs/node#6624) | ||
const filename = path.resolve(this.cwd, options.filename) | ||
return this._maybeInstrumentSource(code, filename) || code | ||
} | ||
_addHook (type) { | ||
const handleJs = this._handleJs.bind(this) | ||
const dummyMatcher = () => true // we do all processing in transformer | ||
libHook['hook' + type](dummyMatcher, handleJs, { extensions: this.extensions }) | ||
} | ||
NYC.prototype._addHook = function (type) { | ||
const handleJs = this._handleJs.bind(this) | ||
const dummyMatcher = () => true // we do all processing in transformer | ||
libHook['hook' + type](dummyMatcher, handleJs, { extensions: this.extensions }) | ||
} | ||
_addRequireHooks () { | ||
if (this.hookRequire) { | ||
this._addHook('Require') | ||
} | ||
if (this.hookRunInContext) { | ||
this._addHook('RunInContext') | ||
} | ||
if (this.hookRunInThisContext) { | ||
this._addHook('RunInThisContext') | ||
} | ||
} | ||
NYC.prototype._addRequireHooks = function () { | ||
if (this.hookRequire) { | ||
this._addHook('Require') | ||
cleanup () { | ||
if (!process.env.NYC_CWD) rimraf.sync(this.tempDirectory()) | ||
} | ||
if (this.hookRunInContext) { | ||
this._addHook('RunInContext') | ||
clearCache () { | ||
if (this.cache) { | ||
rimraf.sync(this.cacheDirectory) | ||
} | ||
} | ||
if (this.hookRunInThisContext) { | ||
this._addHook('RunInThisContext') | ||
} | ||
} | ||
NYC.prototype.cleanup = function () { | ||
if (!process.env.NYC_CWD) rimraf.sync(this.tempDirectory()) | ||
} | ||
createTempDirectory () { | ||
mkdirp.sync(this.tempDirectory()) | ||
if (this.cache) mkdirp.sync(this.cacheDirectory) | ||
NYC.prototype.clearCache = function () { | ||
if (this.cache) { | ||
rimraf.sync(this.cacheDirectory) | ||
mkdirp.sync(this.processInfoDirectory()) | ||
} | ||
} | ||
NYC.prototype.createTempDirectory = function () { | ||
mkdirp.sync(this.tempDirectory()) | ||
if (this.cache) mkdirp.sync(this.cacheDirectory) | ||
reset () { | ||
this.cleanup() | ||
this.createTempDirectory() | ||
} | ||
mkdirp.sync(this.processInfoDirectory()) | ||
} | ||
_wrapExit () { | ||
// we always want to write coverage | ||
// regardless of how the process exits. | ||
onExit(() => { | ||
this.writeCoverageFile() | ||
}, { alwaysLast: true }) | ||
} | ||
NYC.prototype.reset = function () { | ||
this.cleanup() | ||
this.createTempDirectory() | ||
} | ||
wrap (bin) { | ||
process.env.NYC_PROCESS_ID = this.processInfo.uuid | ||
this._addRequireHooks() | ||
this._wrapExit() | ||
this._loadAdditionalModules() | ||
return this | ||
} | ||
NYC.prototype._wrapExit = function () { | ||
// we always want to write coverage | ||
// regardless of how the process exits. | ||
onExit(() => { | ||
this.writeCoverageFile() | ||
}, { alwaysLast: true }) | ||
} | ||
writeCoverageFile () { | ||
var coverage = coverageFinder() | ||
if (!coverage) return | ||
NYC.prototype.wrap = function (bin) { | ||
process.env.NYC_PROCESS_ID = this.processInfo.uuid | ||
this._addRequireHooks() | ||
this._wrapExit() | ||
this._loadAdditionalModules() | ||
return this | ||
} | ||
NYC.prototype.generateUniqueID = uuid | ||
NYC.prototype.writeCoverageFile = function () { | ||
var coverage = coverageFinder() | ||
if (!coverage) return | ||
// Remove any files that should be excluded but snuck into the coverage | ||
Object.keys(coverage).forEach(function (absFile) { | ||
if (!this.exclude.shouldInstrument(absFile)) { | ||
delete coverage[absFile] | ||
} | ||
}, this) | ||
if (this.cache) { | ||
// Remove any files that should be excluded but snuck into the coverage | ||
Object.keys(coverage).forEach(function (absFile) { | ||
if (this.hashCache[absFile] && coverage[absFile]) { | ||
coverage[absFile].contentHash = this.hashCache[absFile] | ||
if (!this.exclude.shouldInstrument(absFile)) { | ||
delete coverage[absFile] | ||
} | ||
}, this) | ||
} else { | ||
coverage = this.sourceMaps.remapCoverage(coverage) | ||
} | ||
var id = this.processInfo.uuid | ||
var coverageFilename = path.resolve(this.tempDirectory(), id + '.json') | ||
if (this.cache) { | ||
Object.keys(coverage).forEach(function (absFile) { | ||
if (this.hashCache[absFile] && coverage[absFile]) { | ||
coverage[absFile].contentHash = this.hashCache[absFile] | ||
} | ||
}, this) | ||
} else { | ||
coverage = this.sourceMaps.remapCoverage(coverage) | ||
} | ||
fs.writeFileSync( | ||
coverageFilename, | ||
JSON.stringify(coverage), | ||
'utf-8' | ||
) | ||
var id = this.processInfo.uuid | ||
var coverageFilename = path.resolve(this.tempDirectory(), id + '.json') | ||
this.processInfo.coverageFilename = coverageFilename | ||
this.processInfo.files = Object.keys(coverage) | ||
fs.writeFileSync( | ||
coverageFilename, | ||
JSON.stringify(coverage), | ||
'utf-8' | ||
) | ||
fs.writeFileSync( | ||
path.resolve(this.processInfoDirectory(), id + '.json'), | ||
JSON.stringify(this.processInfo), | ||
'utf-8' | ||
) | ||
} | ||
this.processInfo.coverageFilename = coverageFilename | ||
this.processInfo.files = Object.keys(coverage) | ||
function coverageFinder () { | ||
var coverage = global.__coverage__ | ||
if (typeof __coverage__ === 'object') coverage = __coverage__ | ||
if (!coverage) coverage = global['__coverage__'] = {} | ||
return coverage | ||
} | ||
fs.writeFileSync( | ||
path.resolve(this.processInfoDirectory(), id + '.json'), | ||
JSON.stringify(this.processInfo), | ||
'utf-8' | ||
) | ||
} | ||
NYC.prototype.getCoverageMapFromAllCoverageFiles = function (baseDirectory) { | ||
const map = libCoverage.createCoverageMap({}) | ||
getCoverageMapFromAllCoverageFiles (baseDirectory) { | ||
const map = libCoverage.createCoverageMap({}) | ||
this.eachReport(undefined, (report) => { | ||
map.merge(report) | ||
}, baseDirectory) | ||
this.eachReport(undefined, (report) => { | ||
map.merge(report) | ||
}, baseDirectory) | ||
map.data = this.sourceMaps.remapCoverage(map.data) | ||
map.data = this.sourceMaps.remapCoverage(map.data) | ||
// depending on whether source-code is pre-instrumented | ||
// or instrumented using a JIT plugin like @babel/require | ||
// you may opt to exclude files after applying | ||
// source-map remapping logic. | ||
if (this.config.excludeAfterRemap) { | ||
map.filter(filename => this.exclude.shouldInstrument(filename)) | ||
// depending on whether source-code is pre-instrumented | ||
// or instrumented using a JIT plugin like @babel/require | ||
// you may opt to exclude files after applying | ||
// source-map remapping logic. | ||
if (this.config.excludeAfterRemap) { | ||
map.filter(filename => this.exclude.shouldInstrument(filename)) | ||
} | ||
return map | ||
} | ||
return map | ||
} | ||
report () { | ||
var tree | ||
var map = this.getCoverageMapFromAllCoverageFiles() | ||
var context = libReport.createContext({ | ||
dir: this.reportDirectory(), | ||
watermarks: this.config.watermarks | ||
}) | ||
NYC.prototype.report = function () { | ||
var tree | ||
var map = this.getCoverageMapFromAllCoverageFiles() | ||
var context = libReport.createContext({ | ||
dir: this.reportDirectory(), | ||
watermarks: this.config.watermarks | ||
}) | ||
tree = libReport.summarizers.pkg(map) | ||
tree = libReport.summarizers.pkg(map) | ||
this.reporter.forEach((_reporter) => { | ||
tree.visit(reports.create(_reporter, { | ||
skipEmpty: this.config.skipEmpty, | ||
skipFull: this.config.skipFull | ||
}), context) | ||
}) | ||
this.reporter.forEach((_reporter) => { | ||
tree.visit(reports.create(_reporter, { | ||
skipEmpty: this.config.skipEmpty, | ||
skipFull: this.config.skipFull | ||
}), context) | ||
}) | ||
if (this._showProcessTree) { | ||
this.showProcessTree() | ||
if (this._showProcessTree) { | ||
this.showProcessTree() | ||
} | ||
} | ||
} | ||
// XXX(@isaacs) Index generation should move to istanbul-lib-processinfo | ||
NYC.prototype.writeProcessIndex = function () { | ||
const dir = this.processInfoDirectory() | ||
const pidToUid = new Map() | ||
const infoByUid = new Map() | ||
const eidToUid = new Map() | ||
const infos = fs.readdirSync(dir).filter(f => f !== 'index.json').map(f => { | ||
try { | ||
const info = JSON.parse(fs.readFileSync(path.resolve(dir, f), 'utf-8')) | ||
info.children = [] | ||
pidToUid.set(info.uuid, info.pid) | ||
pidToUid.set(info.pid, info.uuid) | ||
infoByUid.set(info.uuid, info) | ||
if (info.externalId) { | ||
eidToUid.set(info.externalId, info.uuid) | ||
// XXX(@isaacs) Index generation should move to istanbul-lib-processinfo | ||
writeProcessIndex () { | ||
const dir = this.processInfoDirectory() | ||
const pidToUid = new Map() | ||
const infoByUid = new Map() | ||
const eidToUid = new Map() | ||
const infos = fs.readdirSync(dir).filter(f => f !== 'index.json').map(f => { | ||
try { | ||
const info = JSON.parse(fs.readFileSync(path.resolve(dir, f), 'utf-8')) | ||
info.children = [] | ||
pidToUid.set(info.uuid, info.pid) | ||
pidToUid.set(info.pid, info.uuid) | ||
infoByUid.set(info.uuid, info) | ||
if (info.externalId) { | ||
eidToUid.set(info.externalId, info.uuid) | ||
} | ||
return info | ||
} catch (er) { | ||
return null | ||
} | ||
return info | ||
} catch (er) { | ||
return null | ||
} | ||
}).filter(Boolean) | ||
}).filter(Boolean) | ||
// create all the parent-child links and write back the updated info | ||
infos.forEach(info => { | ||
if (info.parent) { | ||
const parentInfo = infoByUid.get(info.parent) | ||
if (parentInfo.children.indexOf(info.uuid) === -1) { | ||
parentInfo.children.push(info.uuid) | ||
// create all the parent-child links and write back the updated info | ||
infos.forEach(info => { | ||
if (info.parent) { | ||
const parentInfo = infoByUid.get(info.parent) | ||
if (parentInfo && !parentInfo.children.includes(info.uuid)) { | ||
parentInfo.children.push(info.uuid) | ||
} | ||
} | ||
} | ||
}) | ||
// figure out which files were touched by each process. | ||
const files = infos.reduce((files, info) => { | ||
info.files.forEach(f => { | ||
files[f] = files[f] || [] | ||
files[f].push(info.uuid) | ||
}) | ||
return files | ||
}, {}) | ||
// build the actual index! | ||
const index = infos.reduce((index, info) => { | ||
index.processes[info.uuid] = {} | ||
index.processes[info.uuid].parent = info.parent | ||
if (info.externalId) { | ||
if (index.externalIds[info.externalId]) { | ||
throw new Error(`External ID ${info.externalId} used by multiple processes`) | ||
// figure out which files were touched by each process. | ||
const files = infos.reduce((files, info) => { | ||
info.files.forEach(f => { | ||
files[f] = files[f] || [] | ||
files[f].push(info.uuid) | ||
}) | ||
return files | ||
}, {}) | ||
// build the actual index! | ||
const index = infos.reduce((index, info) => { | ||
index.processes[info.uuid] = {} | ||
index.processes[info.uuid].parent = info.parent | ||
if (info.externalId) { | ||
if (index.externalIds[info.externalId]) { | ||
throw new Error(`External ID ${info.externalId} used by multiple processes`) | ||
} | ||
index.processes[info.uuid].externalId = info.externalId | ||
index.externalIds[info.externalId] = { | ||
root: info.uuid, | ||
children: info.children | ||
} | ||
} | ||
index.processes[info.uuid].externalId = info.externalId | ||
index.externalIds[info.externalId] = { | ||
root: info.uuid, | ||
children: info.children | ||
} | ||
} | ||
index.processes[info.uuid].children = Array.from(info.children) | ||
return index | ||
}, { processes: {}, files: files, externalIds: {} }) | ||
index.processes[info.uuid].children = Array.from(info.children) | ||
return index | ||
}, { processes: {}, files: files, externalIds: {} }) | ||
// flatten the descendant sets of all the externalId procs | ||
Object.keys(index.externalIds).forEach(eid => { | ||
const { children } = index.externalIds[eid] | ||
// push the next generation onto the list so we accumulate them all | ||
for (let i = 0; i < children.length; i++) { | ||
const nextGen = index.processes[children[i]].children | ||
if (nextGen && nextGen.length) { | ||
children.push(...nextGen.filter(uuid => children.indexOf(uuid) === -1)) | ||
// flatten the descendant sets of all the externalId procs | ||
Object.keys(index.externalIds).forEach(eid => { | ||
const { children } = index.externalIds[eid] | ||
// push the next generation onto the list so we accumulate them all | ||
for (let i = 0; i < children.length; i++) { | ||
const nextGen = index.processes[children[i]].children | ||
if (nextGen && nextGen.length) { | ||
children.push(...nextGen.filter(uuid => children.indexOf(uuid) === -1)) | ||
} | ||
} | ||
} | ||
}) | ||
}) | ||
fs.writeFileSync(path.resolve(dir, 'index.json'), JSON.stringify(index)) | ||
} | ||
fs.writeFileSync(path.resolve(dir, 'index.json'), JSON.stringify(index)) | ||
} | ||
NYC.prototype.showProcessTree = function () { | ||
var processTree = ProcessInfo.buildProcessTree(this._loadProcessInfos()) | ||
showProcessTree () { | ||
var processTree = ProcessInfo.buildProcessTree(this._loadProcessInfos()) | ||
console.log(processTree.render(this)) | ||
} | ||
console.log(processTree.render(this)) | ||
} | ||
NYC.prototype.checkCoverage = function (thresholds, perFile) { | ||
var map = this.getCoverageMapFromAllCoverageFiles() | ||
var nyc = this | ||
checkCoverage (thresholds, perFile) { | ||
var map = this.getCoverageMapFromAllCoverageFiles() | ||
var nyc = this | ||
if (perFile) { | ||
map.files().forEach(function (file) { | ||
// ERROR: Coverage for lines (90.12%) does not meet threshold (120%) for index.js | ||
nyc._checkCoverage(map.fileCoverageFor(file).toSummary(), thresholds, file) | ||
if (perFile) { | ||
map.files().forEach(function (file) { | ||
// ERROR: Coverage for lines (90.12%) does not meet threshold (120%) for index.js | ||
nyc._checkCoverage(map.fileCoverageFor(file).toSummary(), thresholds, file) | ||
}) | ||
} else { | ||
// ERROR: Coverage for lines (90.12%) does not meet global threshold (120%) | ||
nyc._checkCoverage(map.getCoverageSummary(), thresholds) | ||
} | ||
} | ||
_checkCoverage (summary, thresholds, file) { | ||
Object.keys(thresholds).forEach(function (key) { | ||
var coverage = summary[key].pct | ||
if (coverage < thresholds[key]) { | ||
process.exitCode = 1 | ||
if (file) { | ||
console.error('ERROR: Coverage for ' + key + ' (' + coverage + '%) does not meet threshold (' + thresholds[key] + '%) for ' + file) | ||
} else { | ||
console.error('ERROR: Coverage for ' + key + ' (' + coverage + '%) does not meet global threshold (' + thresholds[key] + '%)') | ||
} | ||
} | ||
}) | ||
} else { | ||
// ERROR: Coverage for lines (90.12%) does not meet global threshold (120%) | ||
nyc._checkCoverage(map.getCoverageSummary(), thresholds) | ||
} | ||
} | ||
NYC.prototype._checkCoverage = function (summary, thresholds, file) { | ||
Object.keys(thresholds).forEach(function (key) { | ||
var coverage = summary[key].pct | ||
if (coverage < thresholds[key]) { | ||
process.exitCode = 1 | ||
if (file) { | ||
console.error('ERROR: Coverage for ' + key + ' (' + coverage + '%) does not meet threshold (' + thresholds[key] + '%) for ' + file) | ||
} else { | ||
console.error('ERROR: Coverage for ' + key + ' (' + coverage + '%) does not meet global threshold (' + thresholds[key] + '%)') | ||
_loadProcessInfos () { | ||
return fs.readdirSync(this.processInfoDirectory()).map(f => { | ||
let data | ||
try { | ||
data = JSON.parse(fs.readFileSync( | ||
path.resolve(this.processInfoDirectory(), f), | ||
'utf-8' | ||
)) | ||
} catch (e) { // handle corrupt JSON output. | ||
return null | ||
} | ||
} | ||
}) | ||
} | ||
if (f !== 'index.json') { | ||
data.nodes = [] | ||
data = new ProcessInfo(data) | ||
} | ||
return { file: path.basename(f, '.json'), data: data } | ||
}).filter(Boolean).reduce((infos, info) => { | ||
infos[info.file] = info.data | ||
return infos | ||
}, {}) | ||
} | ||
NYC.prototype._loadProcessInfos = function () { | ||
return fs.readdirSync(this.processInfoDirectory()).map(f => { | ||
let data | ||
try { | ||
data = JSON.parse(fs.readFileSync( | ||
path.resolve(this.processInfoDirectory(), f), | ||
'utf-8' | ||
)) | ||
} catch (e) { // handle corrupt JSON output. | ||
return null | ||
eachReport (filenames, iterator, baseDirectory) { | ||
baseDirectory = baseDirectory || this.tempDirectory() | ||
if (typeof filenames === 'function') { | ||
iterator = filenames | ||
filenames = undefined | ||
} | ||
if (f !== 'index.json') { | ||
data.nodes = [] | ||
data = new ProcessInfo(data) | ||
} | ||
return { file: path.basename(f, '.json'), data: data } | ||
}).filter(Boolean).reduce((infos, info) => { | ||
infos[info.file] = info.data | ||
return infos | ||
}, {}) | ||
} | ||
NYC.prototype.eachReport = function (filenames, iterator, baseDirectory) { | ||
baseDirectory = baseDirectory || this.tempDirectory() | ||
var _this = this | ||
var files = filenames || fs.readdirSync(baseDirectory) | ||
if (typeof filenames === 'function') { | ||
iterator = filenames | ||
filenames = undefined | ||
} | ||
files.forEach(function (f) { | ||
var report | ||
try { | ||
report = JSON.parse(fs.readFileSync( | ||
path.resolve(baseDirectory, f), | ||
'utf-8' | ||
)) | ||
var _this = this | ||
var files = filenames || fs.readdirSync(baseDirectory) | ||
_this.sourceMaps.reloadCachedSourceMaps(report) | ||
} catch (e) { // handle corrupt JSON output. | ||
report = {} | ||
} | ||
files.forEach(function (f) { | ||
var report | ||
try { | ||
report = JSON.parse(fs.readFileSync( | ||
path.resolve(baseDirectory, f), | ||
'utf-8' | ||
)) | ||
iterator(report) | ||
}) | ||
} | ||
_this.sourceMaps.reloadCachedSourceMaps(report) | ||
} catch (e) { // handle corrupt JSON output. | ||
report = {} | ||
} | ||
loadReports (filenames) { | ||
var reports = [] | ||
iterator(report) | ||
}) | ||
} | ||
this.eachReport(filenames, (report) => { | ||
reports.push(report) | ||
}) | ||
NYC.prototype.loadReports = function (filenames) { | ||
var reports = [] | ||
return reports | ||
} | ||
this.eachReport(filenames, (report) => { | ||
reports.push(report) | ||
}) | ||
tempDirectory () { | ||
return path.resolve(this.cwd, this._tempDirectory) | ||
} | ||
return reports | ||
} | ||
reportDirectory () { | ||
return path.resolve(this.cwd, this._reportDir) | ||
} | ||
NYC.prototype.tempDirectory = function () { | ||
return path.resolve(this.cwd, this._tempDirectory) | ||
processInfoDirectory () { | ||
return path.resolve(this.tempDirectory(), 'processinfo') | ||
} | ||
} | ||
NYC.prototype.reportDirectory = function () { | ||
return path.resolve(this.cwd, this._reportDir) | ||
} | ||
NYC.prototype.processInfoDirectory = function () { | ||
return path.resolve(this.tempDirectory(), 'processinfo') | ||
} | ||
module.exports = NYC |
@@ -24,2 +24,4 @@ 'use strict' | ||
'.nycrc.json', | ||
'.nycrc.yml', | ||
'.nycrc.yaml', | ||
'nyc.config.js' | ||
@@ -31,4 +33,9 @@ ] | ||
if (rcPath) { | ||
if (rcPath.toLowerCase().endsWith('.js')) { | ||
const rcExt = path.extname(rcPath.toLowerCase()) | ||
if (rcExt === '.js') { | ||
config = require(rcPath) | ||
} else if (rcExt === '.yml' || rcExt === '.yaml') { | ||
config = require('js-yaml').load( | ||
fs.readFileSync(rcPath, 'utf8') | ||
) | ||
} else { | ||
@@ -35,0 +42,0 @@ config = JSON.parse( |
@@ -8,3 +8,3 @@ 'use strict' | ||
function InstrumenterIstanbul (options) { | ||
const plugins = options.parserPlugins | ||
const { plugins } = options | ||
const configPlugins = plugins ? { plugins } : {} | ||
@@ -11,0 +11,0 @@ |
@@ -7,3 +7,2 @@ const convertSourceMap = require('convert-source-map') | ||
const sourceMapCache = libSourceMaps.createSourceMapStore() | ||
function SourceMaps (opts) { | ||
@@ -13,5 +12,10 @@ this.cache = opts.cache | ||
this.loadedMaps = {} | ||
this._sourceMapCache = sourceMapCache | ||
this._sourceMapCache = libSourceMaps.createSourceMapStore() | ||
} | ||
SourceMaps.prototype.purgeCache = function () { | ||
this._sourceMapCache = libSourceMaps.createSourceMapStore() | ||
this.loadedMaps = {} | ||
} | ||
SourceMaps.prototype.extractAndRegister = function (code, filename, hash) { | ||
@@ -18,0 +22,0 @@ var sourceMap = convertSourceMap.fromSource(code) || convertSourceMap.fromMapFileSource(code, path.dirname(filename)) |
{ | ||
"name": "nyc", | ||
"version": "14.0.0", | ||
"version": "14.1.0", | ||
"description": "the Istanbul command line interface", | ||
"main": "index.js", | ||
"scripts": { | ||
"pretest": "npm run clean && npm run instrument", | ||
"test": "tap -t360 --no-cov -b ./test/*.js && npm run report", | ||
"posttest": "standard", | ||
"lint": "standard", | ||
"pretest": "npm run lint && npm run clean && npm run instrument", | ||
"test": "tap -t360 --no-cov -b test/*.js", | ||
"snap": "cross-env TAP_SNAPSHOT=1 npm test", | ||
"posttest": "npm run report", | ||
"clean": "rimraf ./.nyc_output ./node_modules/.cache ./.self_coverage ./test/fixtures/.nyc_output ./test/fixtures/node_modules/.cache ./self-coverage", | ||
"instrument": "node ./build-self-coverage.js", | ||
"report": "node ./self-coverage/bin/nyc report --temp-dir ./.self_coverage/ -r text -r lcov", | ||
"report": "node ./bin/nyc report --temp-dir ./.self_coverage/ -r text -r lcov", | ||
"release": "standard-version" | ||
@@ -77,8 +79,9 @@ }, | ||
"glob": "^7.1.3", | ||
"istanbul-lib-coverage": "^2.0.4", | ||
"istanbul-lib-hook": "^2.0.6", | ||
"istanbul-lib-instrument": "^3.2.0", | ||
"istanbul-lib-report": "^2.0.7", | ||
"istanbul-lib-source-maps": "^3.0.5", | ||
"istanbul-reports": "^2.2.2", | ||
"istanbul-lib-coverage": "^2.0.5", | ||
"istanbul-lib-hook": "^2.0.7", | ||
"istanbul-lib-instrument": "^3.3.0", | ||
"istanbul-lib-report": "^2.0.8", | ||
"istanbul-lib-source-maps": "^3.0.6", | ||
"istanbul-reports": "^2.2.4", | ||
"js-yaml": "^3.13.1", | ||
"make-dir": "^2.1.0", | ||
@@ -90,3 +93,3 @@ "merge-source-map": "^1.1.0", | ||
"spawn-wrap": "^1.4.2", | ||
"test-exclude": "^5.2.2", | ||
"test-exclude": "^5.2.3", | ||
"uuid": "^3.3.2", | ||
@@ -100,5 +103,7 @@ "yargs": "^13.2.2", | ||
"coveralls": "^3.0.3", | ||
"cross-env": "^5.2.0", | ||
"is-windows": "^1.0.2", | ||
"lodash": "^4.17.11", | ||
"newline-regex": "^0.2.1", | ||
"pify": "^4.0.1", | ||
"requirejs": "^2.3.6", | ||
@@ -110,3 +115,3 @@ "sanitize-filename": "^1.6.1", | ||
"strip-indent": "^2.0.0", | ||
"tap": "^12.6.1", | ||
"tap": "^12.6.5", | ||
"which": "^1.3.1", | ||
@@ -113,0 +118,0 @@ "zero-fill": "^2.2.3" |
@@ -198,21 +198,4 @@ # nyc | ||
## Producing instrumented source | ||
## [Producing instrumented source](./docs/instrument.md) | ||
The `nyc instrument` command can produce a set of instrumented source files. | ||
These files are suitable for client side deployment in end to end testing. | ||
You can create an instrumented version of your source code by running: | ||
```bash | ||
nyc instrument <input> [output] | ||
``` | ||
`<input>` can be any file or directory within the project root directory. | ||
The `[output]` directory is optional and can be located anywhere, if it is not set the instrumented code will be sent to `stdout`. | ||
For example, `nyc instrument . ./output` will produce instrumented versions of any source files it finds in `.` and store them in `./output`. | ||
Any existing output can be removed by specifying the `--delete` option. | ||
Run `nyc instrument --help` to display a full list of available command options. | ||
**Note:** `nyc instrument` will not copy the contents of a `.git` folder to the output directory. | ||
## Setting the project root directory | ||
@@ -345,4 +328,12 @@ | ||
Any configuration options that can be set via the command line can also be specified in the `nyc` stanza of your package.json, or within a `.nycrc`, `.nycrc.json`, or `nyc.config.js` file: | ||
Any configuration options that can be set via the command line can also be specified in the `nyc` stanza of your package.json, or within a seperate configuration file - a variety of flavors are available: | ||
| File name | File Association | | ||
|-----------------|------------------| | ||
| `.nycrc` | JSON | | ||
| `.nycrc.json` | JSON | | ||
| `.nycrc.yaml` | YAML | | ||
| `.nycrc.yml` | YAML | | ||
| `nyc.config.js` | CommonJS export | | ||
**package.json:** | ||
@@ -349,0 +340,0 @@ |
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
111359
1473
25
17
475
28
+ Addedjs-yaml@^3.13.1
+ Addedargparse@1.0.10(transitive)
+ Addedesprima@4.0.1(transitive)
+ Addedjs-yaml@3.14.1(transitive)
+ Addedsprintf-js@1.0.3(transitive)
Updatedistanbul-lib-coverage@^2.0.5
Updatedistanbul-lib-hook@^2.0.7
Updatedistanbul-lib-report@^2.0.8
Updatedistanbul-reports@^2.2.4
Updatedtest-exclude@^5.2.3