Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F100916043
report.js
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Mon, Feb 3, 22:20
Size
10 KB
Mime Type
text/x-c++
Expires
Wed, Feb 5, 22:20 (1 d, 23 h)
Engine
blob
Format
Raw Data
Handle
24055441
Attached To
rOACCT Open Access Compliance Check Tool (OACCT)
report.js
View Options
const Exclude = require('test-exclude')
const libCoverage = require('istanbul-lib-coverage')
const libReport = require('istanbul-lib-report')
const reports = require('istanbul-reports')
const { readdirSync, readFileSync, statSync } = require('fs')
const { isAbsolute, resolve, extname } = require('path')
const { pathToFileURL, fileURLToPath } = require('url')
const getSourceMapFromFile = require('./source-map-from-file')
// TODO: switch back to @c88/v8-coverage once patch is landed.
const v8toIstanbul = require('v8-to-istanbul')
const isCjsEsmBridgeCov = require('./is-cjs-esm-bridge')
const util = require('util')
const debuglog = util.debuglog('c8')
class Report {
constructor ({
exclude,
extension,
excludeAfterRemap,
include,
reporter,
reportsDirectory,
tempDirectory,
watermarks,
omitRelative,
wrapperLength,
resolve: resolvePaths,
all,
src,
allowExternal = false,
skipFull,
excludeNodeModules
}) {
this.reporter = reporter
this.reportsDirectory = reportsDirectory
this.tempDirectory = tempDirectory
this.watermarks = watermarks
this.resolve = resolvePaths
this.exclude = new Exclude({
exclude: exclude,
include: include,
extension: extension,
relativePath: !allowExternal,
excludeNodeModules: excludeNodeModules
})
this.excludeAfterRemap = excludeAfterRemap
this.shouldInstrumentCache = new Map()
this.omitRelative = omitRelative
this.sourceMapCache = {}
this.wrapperLength = wrapperLength
this.all = all
this.src = this._getSrc(src)
this.skipFull = skipFull
}
_getSrc (src) {
if (typeof src === 'string') {
return [src]
} else if (Array.isArray(src)) {
return src
} else {
return [process.cwd()]
}
}
async run () {
const context = libReport.createContext({
dir: this.reportsDirectory,
watermarks: this.watermarks,
coverageMap: await this.getCoverageMapFromAllCoverageFiles()
})
for (const _reporter of this.reporter) {
reports.create(_reporter, {
skipEmpty: false,
skipFull: this.skipFull,
maxCols: process.stdout.columns || 100
}).execute(context)
}
}
async getCoverageMapFromAllCoverageFiles () {
// the merge process can be very expensive, and it's often the case that
// check-coverage is called immediately after a report. We memoize the
// result from getCoverageMapFromAllCoverageFiles() to address this
// use-case.
if (this._allCoverageFiles) return this._allCoverageFiles
const map = libCoverage.createCoverageMap()
const v8ProcessCov = this._getMergedProcessCov()
const resultCountPerPath = new Map()
const possibleCjsEsmBridges = new Map()
for (const v8ScriptCov of v8ProcessCov.result) {
try {
const sources = this._getSourceMap(v8ScriptCov)
const path = resolve(this.resolve, v8ScriptCov.url)
const converter = v8toIstanbul(path, this.wrapperLength, sources, (path) => {
if (this.excludeAfterRemap) {
return !this._shouldInstrument(path)
}
})
await converter.load()
if (resultCountPerPath.has(path)) {
resultCountPerPath.set(path, resultCountPerPath.get(path) + 1)
} else {
resultCountPerPath.set(path, 0)
}
if (isCjsEsmBridgeCov(v8ScriptCov)) {
possibleCjsEsmBridges.set(converter, {
path,
functions: v8ScriptCov.functions
})
} else {
converter.applyCoverage(v8ScriptCov.functions)
map.merge(converter.toIstanbul())
}
} catch (err) {
debuglog(`file: ${v8ScriptCov.url} error: ${err.stack}`)
}
}
for (const [converter, { path, functions }] of possibleCjsEsmBridges) {
if (resultCountPerPath.get(path) <= 1) {
converter.applyCoverage(functions)
map.merge(converter.toIstanbul())
}
}
this._allCoverageFiles = map
return this._allCoverageFiles
}
/**
* Returns source-map and fake source file, if cached during Node.js'
* execution. This is used to support tools like ts-node, which transpile
* using runtime hooks.
*
* Note: requires Node.js 13+
*
* @return {Object} sourceMap and fake source file (created from line #s).
* @private
*/
_getSourceMap (v8ScriptCov) {
const sources = {}
const sourceMapAndLineLengths = this.sourceMapCache[pathToFileURL(v8ScriptCov.url).href]
if (sourceMapAndLineLengths) {
// See: https://github.com/nodejs/node/pull/34305
if (!sourceMapAndLineLengths.data) return
sources.sourceMap = {
sourcemap: sourceMapAndLineLengths.data
}
if (sourceMapAndLineLengths.lineLengths) {
let source = ''
sourceMapAndLineLengths.lineLengths.forEach(length => {
source += `${''.padEnd(length, '.')}\n`
})
sources.source = source
}
}
return sources
}
/**
* Returns the merged V8 process coverage.
*
* The result is computed from the individual process coverages generated
* by Node. It represents the sum of their counts.
*
* @return {ProcessCov} Merged V8 process coverage.
* @private
*/
_getMergedProcessCov () {
const { mergeProcessCovs } = require('@bcoe/v8-coverage')
const v8ProcessCovs = []
const fileIndex = new Set() // Set<string>
for (const v8ProcessCov of this._loadReports()) {
if (this._isCoverageObject(v8ProcessCov)) {
if (v8ProcessCov['source-map-cache']) {
Object.assign(this.sourceMapCache, this._normalizeSourceMapCache(v8ProcessCov['source-map-cache']))
}
v8ProcessCovs.push(this._normalizeProcessCov(v8ProcessCov, fileIndex))
}
}
if (this.all) {
const emptyReports = []
v8ProcessCovs.unshift({
result: emptyReports
})
const workingDirs = this.src
const { extension } = this.exclude
for (const workingDir of workingDirs) {
this.exclude.globSync(workingDir).forEach((f) => {
const fullPath = resolve(workingDir, f)
if (!fileIndex.has(fullPath)) {
const ext = extname(fullPath)
if (extension.includes(ext)) {
const stat = statSync(fullPath)
const sourceMap = getSourceMapFromFile(fullPath)
if (sourceMap) {
this.sourceMapCache[pathToFileURL(fullPath)] = { data: sourceMap }
}
emptyReports.push({
scriptId: 0,
url: resolve(fullPath),
functions: [{
functionName: '(empty-report)',
ranges: [{
startOffset: 0,
endOffset: stat.size,
count: 0
}],
isBlockCoverage: true
}]
})
}
}
})
}
}
return mergeProcessCovs(v8ProcessCovs)
}
/**
* Make sure v8ProcessCov actually contains coverage information.
*
* @return {boolean} does it look like v8ProcessCov?
* @private
*/
_isCoverageObject (maybeV8ProcessCov) {
return maybeV8ProcessCov && Array.isArray(maybeV8ProcessCov.result)
}
/**
* Returns the list of V8 process coverages generated by Node.
*
* @return {ProcessCov[]} Process coverages generated by Node.
* @private
*/
_loadReports () {
const reports = []
for (const file of readdirSync(this.tempDirectory)) {
try {
reports.push(JSON.parse(readFileSync(
resolve(this.tempDirectory, file),
'utf8'
)))
} catch (err) {
debuglog(`${err.stack}`)
}
}
return reports
}
/**
* Normalizes a process coverage.
*
* This function replaces file URLs (`url` property) by their corresponding
* system-dependent path and applies the current inclusion rules to filter out
* the excluded script coverages.
*
* The result is a copy of the input, with script coverages filtered based
* on their `url` and the current inclusion rules.
* There is no deep cloning.
*
* @param v8ProcessCov V8 process coverage to normalize.
* @param fileIndex a Set<string> of paths discovered in coverage
* @return {v8ProcessCov} Normalized V8 process coverage.
* @private
*/
_normalizeProcessCov (v8ProcessCov, fileIndex) {
const result = []
for (const v8ScriptCov of v8ProcessCov.result) {
// https://github.com/nodejs/node/pull/35498 updates Node.js'
// builtin module filenames:
if (/^node:/.test(v8ScriptCov.url)) {
v8ScriptCov.url = `${v8ScriptCov.url.replace(/^node:/, '')}.js`
}
if (/^file:\/\//.test(v8ScriptCov.url)) {
try {
v8ScriptCov.url = fileURLToPath(v8ScriptCov.url)
fileIndex.add(v8ScriptCov.url)
} catch (err) {
debuglog(`${err.stack}`)
continue
}
}
if ((!this.omitRelative || isAbsolute(v8ScriptCov.url))) {
if (this.excludeAfterRemap || this._shouldInstrument(v8ScriptCov.url)) {
result.push(v8ScriptCov)
}
}
}
return { result }
}
/**
* Normalizes a V8 source map cache.
*
* This function normalizes file URLs to a system-independent format.
*
* @param v8SourceMapCache V8 source map cache to normalize.
* @return {v8SourceMapCache} Normalized V8 source map cache.
* @private
*/
_normalizeSourceMapCache (v8SourceMapCache) {
const cache = {}
for (const fileURL of Object.keys(v8SourceMapCache)) {
cache[pathToFileURL(fileURLToPath(fileURL)).href] = v8SourceMapCache[fileURL]
}
return cache
}
/**
* this.exclude.shouldInstrument with cache
*
* @private
* @return {boolean}
*/
_shouldInstrument (filename) {
const cacheResult = this.shouldInstrumentCache.get(filename)
if (cacheResult !== undefined) {
return cacheResult
}
const result = this.exclude.shouldInstrument(filename)
this.shouldInstrumentCache.set(filename, result)
return result
}
}
module.exports = function (opts) {
return new Report(opts)
}
Event Timeline
Log In to Comment