From 0f745ca7f1e48c6989bf87c02f35a979a1c2043a Mon Sep 17 00:00:00 2001 From: Corey Farrell Date: Wed, 17 Apr 2019 14:14:32 -0400 Subject: [PATCH] chore: Use class to declare NYC (#1069) --- index.js | 926 +++++++++++++++++++++++++++---------------------------- 1 file changed, 463 insertions(+), 463 deletions(-) diff --git a/index.js b/index.js index e0d67c3b3..8c9d8e9a8 100755 --- a/index.js +++ b/index.js @@ -32,564 +32,564 @@ if (/self-coverage/.test(__dirname)) { require('../self-coverage-helper') } -function NYC (config) { - config = config || {} - this.config = config - - 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.cacheDirectory = (config.cacheDir && path.resolve(config.cacheDir)) || findCacheDir({ name: 'nyc', cwd: this.cwd }) - this.cache = Boolean(this.cacheDirectory && config.cache) - - this.extensions = [].concat(config.extension || []) - .concat('.js') - .map(ext => ext.toLowerCase()) - .filter((item, pos, arr) => arr.indexOf(item) === pos) - - this.exclude = testExclude({ - cwd: this.cwd, - include: config.include, - exclude: config.exclude, - excludeNodeModules: config.excludeNodeModules !== false, - extension: this.extensions - }) - - this.sourceMaps = new SourceMaps({ - cache: this.cache, - cacheDirectory: this.cacheDirectory - }) - - // require extensions can be provided as config in package.json. - this.require = [].concat(config.require || []) - - this.transforms = this.extensions.reduce((transforms, ext) => { - transforms[ext] = this._createTransform(ext) - return transforms - }, {}) - - this.hookRequire = config.hookRequire - this.hookRunInContext = config.hookRunInContext - this.hookRunInThisContext = config.hookRunInThisContext - this.fakeRequire = null - - this.processInfo = new ProcessInfo(config && config._processInfo) - this.rootId = this.processInfo.root || this.generateUniqueID() - - this.hashCache = {} +function coverageFinder () { + var coverage = global.__coverage__ + if (typeof __coverage__ === 'object') coverage = __coverage__ + if (!coverage) coverage = global['__coverage__'] = {} + return coverage } -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 - } - if (this._eagerInstantiation) { - opts.transform = this._transformFactory(this.cacheDirectory) - } else { - opts.factory = this._transformFactory.bind(this) - } - return cachingTransform(opts) -} +class NYC { + constructor (config) { + config = config || {} + this.config = config + + 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.cacheDirectory = (config.cacheDir && path.resolve(config.cacheDir)) || findCacheDir({ name: 'nyc', cwd: this.cwd }) + this.cache = Boolean(this.cacheDirectory && config.cache) + + this.extensions = [].concat(config.extension || []) + .concat('.js') + .map(ext => ext.toLowerCase()) + .filter((item, pos, arr) => arr.indexOf(item) === pos) + + this.exclude = testExclude({ + cwd: this.cwd, + include: config.include, + exclude: config.exclude, + excludeNodeModules: config.excludeNodeModules !== false, + extension: this.extensions + }) -NYC.prototype._disableCachingTransform = function () { - return !(this.cache && this.config.isChildProcess) -} + this.sourceMaps = new SourceMaps({ + cache: this.cache, + cacheDirectory: this.cacheDirectory + }) -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) - }) -} + // require extensions can be provided as config in package.json. + this.require = [].concat(config.require || []) -NYC.prototype.instrumenter = function () { - return this._instrumenter || (this._instrumenter = this._createInstrumenter()) -} + this.transforms = this.extensions.reduce((transforms, ext) => { + transforms[ext] = this._createTransform(ext) + return transforms + }, {}) -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 - }) -} + this.hookRequire = config.hookRequire + this.hookRunInContext = config.hookRunInContext + this.hookRunInThisContext = config.hookRunInThisContext + this.fakeRequire = null -NYC.prototype.addFile = function (filename) { - const source = this._readTranspiledSource(filename) - this._maybeInstrumentSource(source, filename) -} + this.processInfo = new ProcessInfo(config && config._processInfo) + this.rootId = this.processInfo.root || uuid() -NYC.prototype._readTranspiledSource = function (filePath) { - var source = null - var ext = path.extname(filePath) - if (typeof Module._extensions[ext] === 'undefined') { - ext = '.js' + this.hashCache = {} } - Module._extensions[ext]({ - _compile: function (content, filename) { - source = content - } - }, filePath) - return source -} -NYC.prototype.addAllFiles = function () { - 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 + _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 } - }) - this.fakeRequire = false - - 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 - - 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) + if (this._eagerInstantiation) { + opts.transform = this._transformFactory(this.cacheDirectory) } else { - console.log(outCode) + opts.factory = this._transformFactory.bind(this) } + return cachingTransform(opts) } - this._loadAdditionalModules() - - try { - const stats = fs.lstatSync(input) - if (stats.isDirectory()) { - inputDir = input + _disableCachingTransform () { + return !(this.cache && this.config.isChildProcess) + } - const filesToInstrument = this.exclude.globSync(input) + _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) + }) + } - 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) - } - } catch (err) { - return cb(err) + instrumenter () { + return this._instrumenter || (this._instrumenter = this._createInstrumenter()) } - cb() -} -NYC.prototype._transform = function (code, filename) { - const extname = path.extname(filename).toLowerCase() - const transform = this.transforms[extname] || (() => null) + _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 + }) + } - return transform(code, { filename }) -} + addFile (filename) { + const source = this._readTranspiledSource(filename) + this._maybeInstrumentSource(source, filename) + } -NYC.prototype._maybeInstrumentSource = function (code, filename) { - if (!this.exclude.shouldInstrument(filename)) { - return null + _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 } - return this._transform(code, filename) -} + 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 -NYC.prototype._transformFactory = function (cacheDir) { - const instrumenter = this.instrumenter() - let instrumented + this.writeCoverageFile() + } - return (code, metadata, hash) => { - const filename = metadata.filename - let sourceMap = null + 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 (this._sourceMap) sourceMap = this.sourceMaps.extractAndRegister(code, filename, hash) + this._loadAdditionalModules() 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) + const stats = fs.lstatSync(input) + if (stats.isDirectory()) { + inputDir = 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)))) + } + filesToInstrument.forEach(visitor) } else { - instrumented = code + visitor(input) } + } catch (err) { + return cb(err) } - - if (this.fakeRequire) { - return 'function x () {}' - } else { - return instrumented - } + cb() } -} -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 -} + _transform (code, filename) { + const extname = path.extname(filename).toLowerCase() + const transform = this.transforms[extname] || (() => null) -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 }) -} + return transform(code, { filename }) + } + + _maybeInstrumentSource (code, filename) { + if (!this.exclude.shouldInstrument(filename)) { + return null + } -NYC.prototype._addRequireHooks = function () { - if (this.hookRequire) { - this._addHook('Require') + return this._transform(code, filename) } - if (this.hookRunInContext) { - this._addHook('RunInContext') + + _transformFactory (cacheDir) { + const instrumenter = this.instrumenter() + let instrumented + + return (code, metadata, hash) => { + const filename = metadata.filename + let sourceMap = null + + 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) + } else { + instrumented = code + } + } + + if (this.fakeRequire) { + return 'function x () {}' + } else { + return instrumented + } + } } - if (this.hookRunInThisContext) { - this._addHook('RunInThisContext') + + _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.cleanup = function () { - if (!process.env.NYC_CWD) rimraf.sync(this.tempDirectory()) -} + _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.clearCache = function () { - if (this.cache) { - rimraf.sync(this.cacheDirectory) + _addRequireHooks () { + if (this.hookRequire) { + this._addHook('Require') + } + if (this.hookRunInContext) { + this._addHook('RunInContext') + } + if (this.hookRunInThisContext) { + this._addHook('RunInThisContext') + } } -} -NYC.prototype.createTempDirectory = function () { - mkdirp.sync(this.tempDirectory()) - if (this.cache) mkdirp.sync(this.cacheDirectory) + cleanup () { + if (!process.env.NYC_CWD) rimraf.sync(this.tempDirectory()) + } - mkdirp.sync(this.processInfoDirectory()) -} + clearCache () { + if (this.cache) { + rimraf.sync(this.cacheDirectory) + } + } -NYC.prototype.reset = function () { - this.cleanup() - this.createTempDirectory() -} + createTempDirectory () { + mkdirp.sync(this.tempDirectory()) + if (this.cache) mkdirp.sync(this.cacheDirectory) -NYC.prototype._wrapExit = function () { - // we always want to write coverage - // regardless of how the process exits. - onExit(() => { - this.writeCoverageFile() - }, { alwaysLast: true }) -} + mkdirp.sync(this.processInfoDirectory()) + } -NYC.prototype.wrap = function (bin) { - process.env.NYC_PROCESS_ID = this.processInfo.uuid - this._addRequireHooks() - this._wrapExit() - this._loadAdditionalModules() - return this -} + reset () { + this.cleanup() + this.createTempDirectory() + } -NYC.prototype.generateUniqueID = uuid + _wrapExit () { + // we always want to write coverage + // regardless of how the process exits. + onExit(() => { + this.writeCoverageFile() + }, { alwaysLast: true }) + } -NYC.prototype.writeCoverageFile = function () { - var coverage = coverageFinder() - if (!coverage) return + wrap (bin) { + process.env.NYC_PROCESS_ID = this.processInfo.uuid + this._addRequireHooks() + this._wrapExit() + this._loadAdditionalModules() + return this + } - // 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) + writeCoverageFile () { + var coverage = coverageFinder() + if (!coverage) return - 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 + } -NYC.prototype.report = function () { - var tree - var map = this.getCoverageMapFromAllCoverageFiles() - var context = libReport.createContext({ - dir: this.reportDirectory(), - watermarks: this.config.watermarks - }) + report () { + 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) - - // 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) + }).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 && !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`) - } - index.processes[info.uuid].externalId = info.externalId - index.externalIds[info.externalId] = { - root: info.uuid, - children: info.children + + // 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].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)) + 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)) + } } - } - }) + }) - 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 - } - if (f !== 'index.json') { - data.nodes = [] - data = new ProcessInfo(data) + eachReport (filenames, iterator, baseDirectory) { + baseDirectory = baseDirectory || this.tempDirectory() + + if (typeof filenames === 'function') { + iterator = filenames + filenames = undefined } - 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) - - files.forEach(function (f) { - var report - try { - report = JSON.parse(fs.readFileSync( - path.resolve(baseDirectory, f), - 'utf-8' - )) - - _this.sourceMaps.reloadCachedSourceMaps(report) - } catch (e) { // handle corrupt JSON output. - report = {} - } + _this.sourceMaps.reloadCachedSourceMaps(report) + } catch (e) { // handle corrupt JSON output. + report = {} + } - iterator(report) - }) -} + iterator(report) + }) + } -NYC.prototype.loadReports = function (filenames) { - var reports = [] + loadReports (filenames) { + var reports = [] - this.eachReport(filenames, (report) => { - reports.push(report) - }) + this.eachReport(filenames, (report) => { + reports.push(report) + }) - return reports -} + return reports + } -NYC.prototype.tempDirectory = function () { - return path.resolve(this.cwd, this._tempDirectory) -} + tempDirectory () { + return path.resolve(this.cwd, this._tempDirectory) + } -NYC.prototype.reportDirectory = function () { - return path.resolve(this.cwd, this._reportDir) -} + reportDirectory () { + return path.resolve(this.cwd, this._reportDir) + } -NYC.prototype.processInfoDirectory = function () { - return path.resolve(this.tempDirectory(), 'processinfo') + processInfoDirectory () { + return path.resolve(this.tempDirectory(), 'processinfo') + } } module.exports = NYC