aboutsummaryrefslogtreecommitdiff
path: root/node_modules/v8-to-istanbul/lib
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/v8-to-istanbul/lib')
-rw-r--r--node_modules/v8-to-istanbul/lib/branch.js28
-rw-r--r--node_modules/v8-to-istanbul/lib/function.js29
-rw-r--r--node_modules/v8-to-istanbul/lib/line.js34
-rw-r--r--node_modules/v8-to-istanbul/lib/range.js33
-rw-r--r--node_modules/v8-to-istanbul/lib/source.js245
-rw-r--r--node_modules/v8-to-istanbul/lib/v8-to-istanbul.js318
6 files changed, 687 insertions, 0 deletions
diff --git a/node_modules/v8-to-istanbul/lib/branch.js b/node_modules/v8-to-istanbul/lib/branch.js
new file mode 100644
index 0000000..deffc6c
--- /dev/null
+++ b/node_modules/v8-to-istanbul/lib/branch.js
@@ -0,0 +1,28 @@
+module.exports = class CovBranch {
+ constructor (startLine, startCol, endLine, endCol, count) {
+ this.startLine = startLine
+ this.startCol = startCol
+ this.endLine = endLine
+ this.endCol = endCol
+ this.count = count
+ }
+
+ toIstanbul () {
+ const location = {
+ start: {
+ line: this.startLine,
+ column: this.startCol
+ },
+ end: {
+ line: this.endLine,
+ column: this.endCol
+ }
+ }
+ return {
+ type: 'branch',
+ line: this.startLine,
+ loc: location,
+ locations: [Object.assign({}, location)]
+ }
+ }
+}
diff --git a/node_modules/v8-to-istanbul/lib/function.js b/node_modules/v8-to-istanbul/lib/function.js
new file mode 100644
index 0000000..15f4b73
--- /dev/null
+++ b/node_modules/v8-to-istanbul/lib/function.js
@@ -0,0 +1,29 @@
+module.exports = class CovFunction {
+ constructor (name, startLine, startCol, endLine, endCol, count) {
+ this.name = name
+ this.startLine = startLine
+ this.startCol = startCol
+ this.endLine = endLine
+ this.endCol = endCol
+ this.count = count
+ }
+
+ toIstanbul () {
+ const loc = {
+ start: {
+ line: this.startLine,
+ column: this.startCol
+ },
+ end: {
+ line: this.endLine,
+ column: this.endCol
+ }
+ }
+ return {
+ name: this.name,
+ decl: loc,
+ loc: loc,
+ line: this.startLine
+ }
+ }
+}
diff --git a/node_modules/v8-to-istanbul/lib/line.js b/node_modules/v8-to-istanbul/lib/line.js
new file mode 100644
index 0000000..0fe1a60
--- /dev/null
+++ b/node_modules/v8-to-istanbul/lib/line.js
@@ -0,0 +1,34 @@
+module.exports = class CovLine {
+ constructor (line, startCol, lineStr) {
+ this.line = line
+ // note that startCol and endCol are absolute positions
+ // within a file, not relative to the line.
+ this.startCol = startCol
+
+ // the line length itself does not include the newline characters,
+ // these are however taken into account when enumerating absolute offset.
+ const matchedNewLineChar = lineStr.match(/\r?\n$/u)
+ const newLineLength = matchedNewLineChar ? matchedNewLineChar[0].length : 0
+ this.endCol = startCol + lineStr.length - newLineLength
+
+ // we start with all lines having been executed, and work
+ // backwards zeroing out lines based on V8 output.
+ this.count = 1
+
+ // set by source.js during parsing, if /* c8 ignore next */ is found.
+ this.ignore = false
+ }
+
+ toIstanbul () {
+ return {
+ start: {
+ line: this.line,
+ column: 0
+ },
+ end: {
+ line: this.line,
+ column: this.endCol - this.startCol
+ }
+ }
+ }
+}
diff --git a/node_modules/v8-to-istanbul/lib/range.js b/node_modules/v8-to-istanbul/lib/range.js
new file mode 100644
index 0000000..ef4884e
--- /dev/null
+++ b/node_modules/v8-to-istanbul/lib/range.js
@@ -0,0 +1,33 @@
+/**
+ * ...something resembling a binary search, to find the lowest line within the range.
+ * And then you could break as soon as the line is longer than the range...
+ */
+module.exports.sliceRange = (lines, startCol, endCol, inclusive = false) => {
+ let start = 0
+ let end = lines.length - 1
+
+ /**
+ * I consider this a temporary solution until I find an alternaive way to fix the "off by one issue"
+ */
+ const extStartCol = inclusive ? startCol - 1 : startCol
+
+ while (start < end) {
+ const mid = (start + end) >> 1
+ if (lines[mid].startCol <= startCol && lines[mid].endCol > extStartCol) {
+ start = mid
+ end = start
+ } else if (lines[mid].startCol > startCol) {
+ end = mid - 1
+ } else {
+ start = mid + 1
+ }
+ }
+ if (start === end) {
+ while (end < lines.length && extStartCol < lines[end].endCol && endCol >= lines[end].startCol) {
+ ++end
+ }
+ return lines.slice(start, end)
+ } else {
+ return []
+ }
+}
diff --git a/node_modules/v8-to-istanbul/lib/source.js b/node_modules/v8-to-istanbul/lib/source.js
new file mode 100644
index 0000000..21deb68
--- /dev/null
+++ b/node_modules/v8-to-istanbul/lib/source.js
@@ -0,0 +1,245 @@
+const CovLine = require('./line')
+const { sliceRange } = require('./range')
+const { GREATEST_LOWER_BOUND, LEAST_UPPER_BOUND } = require('source-map').SourceMapConsumer
+
+module.exports = class CovSource {
+ constructor (sourceRaw, wrapperLength) {
+ sourceRaw = sourceRaw ? sourceRaw.trimEnd() : ''
+ this.lines = []
+ this.eof = sourceRaw.length
+ this.shebangLength = getShebangLength(sourceRaw)
+ this.wrapperLength = wrapperLength - this.shebangLength
+ this._buildLines(sourceRaw)
+ }
+
+ _buildLines (source) {
+ let position = 0
+ let ignoreCount = 0
+ let ignoreAll = false
+ for (const [i, lineStr] of source.split(/(?<=\r?\n)/u).entries()) {
+ const line = new CovLine(i + 1, position, lineStr)
+ if (ignoreCount > 0) {
+ line.ignore = true
+ ignoreCount--
+ } else if (ignoreAll) {
+ line.ignore = true
+ }
+ this.lines.push(line)
+ position += lineStr.length
+
+ const ignoreToken = this._parseIgnore(lineStr)
+ if (!ignoreToken) continue
+
+ line.ignore = true
+ if (ignoreToken.count !== undefined) {
+ ignoreCount = ignoreToken.count
+ }
+ if (ignoreToken.start || ignoreToken.stop) {
+ ignoreAll = ignoreToken.start
+ ignoreCount = 0
+ }
+ }
+ }
+
+ /**
+ * Parses for comments:
+ * c8 ignore next
+ * c8 ignore next 3
+ * c8 ignore start
+ * c8 ignore stop
+ * @param {string} lineStr
+ * @return {{count?: number, start?: boolean, stop?: boolean}|undefined}
+ */
+ _parseIgnore (lineStr) {
+ const testIgnoreNextLines = lineStr.match(/^\W*\/\* c8 ignore next (?<count>[0-9]+) *\*\/\W*$/)
+ if (testIgnoreNextLines) {
+ return { count: Number(testIgnoreNextLines.groups.count) }
+ }
+
+ // Check if comment is on its own line.
+ if (lineStr.match(/^\W*\/\* c8 ignore next *\*\/\W*$/)) {
+ return { count: 1 }
+ }
+
+ if (lineStr.match(/\/\* c8 ignore next \*\//)) {
+ // Won't ignore successive lines, but the current line will be ignored.
+ return { count: 0 }
+ }
+
+ const testIgnoreStartStop = lineStr.match(/\/\* c8 ignore (?<mode>start|stop) *\*\//)
+ if (testIgnoreStartStop) {
+ if (testIgnoreStartStop.groups.mode === 'start') return { start: true }
+ if (testIgnoreStartStop.groups.mode === 'stop') return { stop: true }
+ }
+ }
+
+ // given a start column and end column in absolute offsets within
+ // a source file (0 - EOF), returns the relative line column positions.
+ offsetToOriginalRelative (sourceMap, startCol, endCol) {
+ const lines = sliceRange(this.lines, startCol, endCol, true)
+ if (!lines.length) return {}
+
+ const start = originalPositionTryBoth(
+ sourceMap,
+ lines[0].line,
+ Math.max(0, startCol - lines[0].startCol)
+ )
+ if (!(start && start.source)) {
+ return {}
+ }
+
+ let end = originalEndPositionFor(
+ sourceMap,
+ lines[lines.length - 1].line,
+ endCol - lines[lines.length - 1].startCol
+ )
+ if (!(end && end.source)) {
+ return {}
+ }
+
+ if (start.source !== end.source) {
+ return {}
+ }
+
+ if (start.line === end.line && start.column === end.column) {
+ end = sourceMap.originalPositionFor({
+ line: lines[lines.length - 1].line,
+ column: endCol - lines[lines.length - 1].startCol,
+ bias: LEAST_UPPER_BOUND
+ })
+ end.column -= 1
+ }
+
+ return {
+ source: start.source,
+ startLine: start.line,
+ relStartCol: start.column,
+ endLine: end.line,
+ relEndCol: end.column
+ }
+ }
+
+ relativeToOffset (line, relCol) {
+ line = Math.max(line, 1)
+ if (this.lines[line - 1] === undefined) return this.eof
+ return Math.min(this.lines[line - 1].startCol + relCol, this.lines[line - 1].endCol)
+ }
+}
+
+// this implementation is pulled over from istanbul-lib-sourcemap:
+// https://github.com/istanbuljs/istanbuljs/blob/master/packages/istanbul-lib-source-maps/lib/get-mapping.js
+//
+/**
+ * AST ranges are inclusive for start positions and exclusive for end positions.
+ * Source maps are also logically ranges over text, though interacting with
+ * them is generally achieved by working with explicit positions.
+ *
+ * When finding the _end_ location of an AST item, the range behavior is
+ * important because what we're asking for is the _end_ of whatever range
+ * corresponds to the end location we seek.
+ *
+ * This boils down to the following steps, conceptually, though the source-map
+ * library doesn't expose primitives to do this nicely:
+ *
+ * 1. Find the range on the generated file that ends at, or exclusively
+ * contains the end position of the AST node.
+ * 2. Find the range on the original file that corresponds to
+ * that generated range.
+ * 3. Find the _end_ location of that original range.
+ */
+function originalEndPositionFor (sourceMap, line, column) {
+ // Given the generated location, find the original location of the mapping
+ // that corresponds to a range on the generated file that overlaps the
+ // generated file end location. Note however that this position on its
+ // own is not useful because it is the position of the _start_ of the range
+ // on the original file, and we want the _end_ of the range.
+ const beforeEndMapping = originalPositionTryBoth(
+ sourceMap,
+ line,
+ Math.max(column - 1, 1)
+ )
+
+ if (beforeEndMapping.source === null) {
+ return null
+ }
+
+ // Convert that original position back to a generated one, with a bump
+ // to the right, and a rightward bias. Since 'generatedPositionFor' searches
+ // for mappings in the original-order sorted list, this will find the
+ // mapping that corresponds to the one immediately after the
+ // beforeEndMapping mapping.
+ const afterEndMapping = sourceMap.generatedPositionFor({
+ source: beforeEndMapping.source,
+ line: beforeEndMapping.line,
+ column: beforeEndMapping.column + 1,
+ bias: LEAST_UPPER_BOUND
+ })
+ if (
+ // If this is null, it means that we've hit the end of the file,
+ // so we can use Infinity as the end column.
+ afterEndMapping.line === null ||
+ // If these don't match, it means that the call to
+ // 'generatedPositionFor' didn't find any other original mappings on
+ // the line we gave, so consider the binding to extend to infinity.
+ sourceMap.originalPositionFor(afterEndMapping).line !==
+ beforeEndMapping.line
+ ) {
+ return {
+ source: beforeEndMapping.source,
+ line: beforeEndMapping.line,
+ column: Infinity
+ }
+ }
+
+ // Convert the end mapping into the real original position.
+ return sourceMap.originalPositionFor(afterEndMapping)
+}
+
+function originalPositionTryBoth (sourceMap, line, column) {
+ let original = sourceMap.originalPositionFor({
+ line,
+ column,
+ bias: GREATEST_LOWER_BOUND
+ })
+ if (original.line === null) {
+ original = sourceMap.originalPositionFor({
+ line,
+ column,
+ bias: LEAST_UPPER_BOUND
+ })
+ }
+ // The source maps generated by https://github.com/istanbuljs/istanbuljs
+ // (using @babel/core 7.7.5) have behavior, such that a mapping
+ // mid-way through a line maps to an earlier line than a mapping
+ // at position 0. Using the line at positon 0 seems to provide better reports:
+ //
+ // if (true) {
+ // cov_y5divc6zu().b[1][0]++;
+ // cov_y5divc6zu().s[3]++;
+ // console.info('reachable');
+ // } else { ... }
+ // ^ ^
+ // l5 l3
+ const min = sourceMap.originalPositionFor({
+ line,
+ column: 0,
+ bias: GREATEST_LOWER_BOUND
+ })
+ if (min.line > original.line) {
+ original = min
+ }
+ return original
+}
+
+// Not required since Node 12, see: https://github.com/nodejs/node/pull/27375
+const isPreNode12 = /^v1[0-1]\./u.test(process.version)
+function getShebangLength (source) {
+ if (isPreNode12 && source.indexOf('#!') === 0) {
+ const match = source.match(/(?<shebang>#!.*)/)
+ if (match) {
+ return match.groups.shebang.length
+ }
+ } else {
+ return 0
+ }
+}
diff --git a/node_modules/v8-to-istanbul/lib/v8-to-istanbul.js b/node_modules/v8-to-istanbul/lib/v8-to-istanbul.js
new file mode 100644
index 0000000..1b56af1
--- /dev/null
+++ b/node_modules/v8-to-istanbul/lib/v8-to-istanbul.js
@@ -0,0 +1,318 @@
+const assert = require('assert')
+const convertSourceMap = require('convert-source-map')
+const util = require('util')
+const debuglog = util.debuglog('c8')
+const { dirname, isAbsolute, join, resolve } = require('path')
+const { fileURLToPath } = require('url')
+const CovBranch = require('./branch')
+const CovFunction = require('./function')
+const CovSource = require('./source')
+const { sliceRange } = require('./range')
+const compatError = Error(`requires Node.js ${require('../package.json').engines.node}`)
+let readFile = () => { throw compatError }
+try {
+ readFile = require('fs').promises.readFile
+} catch (_err) {
+ // most likely we're on an older version of Node.js.
+}
+const { SourceMapConsumer } = require('source-map')
+const isOlderNode10 = /^v10\.(([0-9]\.)|(1[0-5]\.))/u.test(process.version)
+const isNode8 = /^v8\./.test(process.version)
+
+// Injected when Node.js is loading script into isolate pre Node 10.16.x.
+// see: https://github.com/nodejs/node/pull/21573.
+const cjsWrapperLength = isOlderNode10 ? require('module').wrapper[0].length : 0
+
+module.exports = class V8ToIstanbul {
+ constructor (scriptPath, wrapperLength, sources, excludePath) {
+ assert(typeof scriptPath === 'string', 'scriptPath must be a string')
+ assert(!isNode8, 'This module does not support node 8 or lower, please upgrade to node 10')
+ this.path = parsePath(scriptPath)
+ this.wrapperLength = wrapperLength === undefined ? cjsWrapperLength : wrapperLength
+ this.excludePath = excludePath || (() => false)
+ this.sources = sources || {}
+ this.generatedLines = []
+ this.branches = {}
+ this.functions = {}
+ this.covSources = []
+ this.rawSourceMap = undefined
+ this.sourceMap = undefined
+ this.sourceTranspiled = undefined
+ // Indicate that this report was generated with placeholder data from
+ // running --all:
+ this.all = false
+ }
+
+ async load () {
+ const rawSource = this.sources.source || await readFile(this.path, 'utf8')
+ this.rawSourceMap = this.sources.sourceMap ||
+ // if we find a source-map (either inline, or a .map file) we load
+ // both the transpiled and original source, both of which are used during
+ // the backflips we perform to remap absolute to relative positions.
+ convertSourceMap.fromSource(rawSource) || convertSourceMap.fromMapFileSource(rawSource, dirname(this.path))
+
+ if (this.rawSourceMap) {
+ if (this.rawSourceMap.sourcemap.sources.length > 1) {
+ this.sourceMap = await new SourceMapConsumer(this.rawSourceMap.sourcemap)
+ if (!this.sourceMap.sourcesContent) {
+ this.sourceMap.sourcesContent = await this.sourcesContentFromSources()
+ }
+ this.covSources = this.sourceMap.sourcesContent.map((rawSource, i) => ({ source: new CovSource(rawSource, this.wrapperLength), path: this.sourceMap.sources[i] }))
+ this.sourceTranspiled = new CovSource(rawSource, this.wrapperLength)
+ } else {
+ const candidatePath = this.rawSourceMap.sourcemap.sources.length >= 1 ? this.rawSourceMap.sourcemap.sources[0] : this.rawSourceMap.sourcemap.file
+ this.path = this._resolveSource(this.rawSourceMap, candidatePath || this.path)
+ this.sourceMap = await new SourceMapConsumer(this.rawSourceMap.sourcemap)
+
+ let originalRawSource
+ if (this.sources.sourceMap && this.sources.sourceMap.sourcemap && this.sources.sourceMap.sourcemap.sourcesContent && this.sources.sourceMap.sourcemap.sourcesContent.length === 1) {
+ // If the sourcesContent field has been provided, return it rather than attempting
+ // to load the original source from disk.
+ // TODO: investigate whether there's ever a case where we hit this logic with 1:many sources.
+ originalRawSource = this.sources.sourceMap.sourcemap.sourcesContent[0]
+ } else if (this.sources.originalSource) {
+ // Original source may be populated on the sources object.
+ originalRawSource = this.sources.originalSource
+ } else if (this.sourceMap.sourcesContent && this.sourceMap.sourcesContent[0]) {
+ // perhaps we loaded sourcesContent was populated by an inline source map, or .map file?
+ // TODO: investigate whether there's ever a case where we hit this logic with 1:many sources.
+ originalRawSource = this.sourceMap.sourcesContent[0]
+ } else {
+ // We fallback to reading the original source from disk.
+ originalRawSource = await readFile(this.path, 'utf8')
+ }
+ this.covSources = [{ source: new CovSource(originalRawSource, this.wrapperLength), path: this.path }]
+ this.sourceTranspiled = new CovSource(rawSource, this.wrapperLength)
+ }
+ } else {
+ this.covSources = [{ source: new CovSource(rawSource, this.wrapperLength), path: this.path }]
+ }
+ }
+
+ async sourcesContentFromSources () {
+ const fileList = this.sourceMap.sources.map(relativePath => {
+ const realPath = this._resolveSource(this.rawSourceMap, relativePath)
+ return readFile(realPath, 'utf-8')
+ .then(result => result)
+ .catch(err => {
+ debuglog(`failed to load ${realPath}: ${err.message}`)
+ })
+ })
+ return await Promise.all(fileList)
+ }
+
+ destroy () {
+ if (this.sourceMap) {
+ this.sourceMap.destroy()
+ this.sourceMap = undefined
+ }
+ }
+
+ _resolveSource (rawSourceMap, sourcePath) {
+ if (sourcePath.startsWith('file://')) {
+ return fileURLToPath(sourcePath)
+ }
+ sourcePath = sourcePath.replace(/^webpack:\/\//, '')
+ const sourceRoot = rawSourceMap.sourcemap.sourceRoot ? rawSourceMap.sourcemap.sourceRoot.replace('file://', '') : ''
+ const candidatePath = join(sourceRoot, sourcePath)
+
+ if (isAbsolute(candidatePath)) {
+ return candidatePath
+ } else {
+ return resolve(dirname(this.path), candidatePath)
+ }
+ }
+
+ applyCoverage (blocks) {
+ blocks.forEach(block => {
+ block.ranges.forEach((range, i) => {
+ const { startCol, endCol, path, covSource } = this._maybeRemapStartColEndCol(range)
+ if (this.excludePath(path)) {
+ return
+ }
+ let lines
+ if (block.functionName === '(empty-report)') {
+ // (empty-report), this will result in a report that has all lines zeroed out.
+ lines = covSource.lines.filter((line) => {
+ line.count = 0
+ return true
+ })
+ this.all = lines.length > 0
+ } else {
+ lines = sliceRange(covSource.lines, startCol, endCol)
+ }
+ if (!lines.length) {
+ return
+ }
+
+ const startLineInstance = lines[0]
+ const endLineInstance = lines[lines.length - 1]
+
+ if (block.isBlockCoverage) {
+ this.branches[path] = this.branches[path] || []
+ // record branches.
+ this.branches[path].push(new CovBranch(
+ startLineInstance.line,
+ startCol - startLineInstance.startCol,
+ endLineInstance.line,
+ endCol - endLineInstance.startCol,
+ range.count
+ ))
+
+ // if block-level granularity is enabled, we still create a single
+ // CovFunction tracking object for each set of ranges.
+ if (block.functionName && i === 0) {
+ this.functions[path] = this.functions[path] || []
+ this.functions[path].push(new CovFunction(
+ block.functionName,
+ startLineInstance.line,
+ startCol - startLineInstance.startCol,
+ endLineInstance.line,
+ endCol - endLineInstance.startCol,
+ range.count
+ ))
+ }
+ } else if (block.functionName) {
+ this.functions[path] = this.functions[path] || []
+ // record functions.
+ this.functions[path].push(new CovFunction(
+ block.functionName,
+ startLineInstance.line,
+ startCol - startLineInstance.startCol,
+ endLineInstance.line,
+ endCol - endLineInstance.startCol,
+ range.count
+ ))
+ }
+
+ // record the lines (we record these as statements, such that we're
+ // compatible with Istanbul 2.0).
+ lines.forEach(line => {
+ // make sure branch spans entire line; don't record 'goodbye'
+ // branch in `const foo = true ? 'hello' : 'goodbye'` as a
+ // 0 for line coverage.
+ //
+ // All lines start out with coverage of 1, and are later set to 0
+ // if they are not invoked; line.ignore prevents a line from being
+ // set to 0, and is set if the special comment /* c8 ignore next */
+ // is used.
+
+ if (startCol <= line.startCol && endCol >= line.endCol && !line.ignore) {
+ line.count = range.count
+ }
+ })
+ })
+ })
+ }
+
+ _maybeRemapStartColEndCol (range) {
+ let covSource = this.covSources[0].source
+ let startCol = Math.max(0, range.startOffset - covSource.wrapperLength)
+ let endCol = Math.min(covSource.eof, range.endOffset - covSource.wrapperLength)
+ let path = this.path
+
+ if (this.sourceMap) {
+ startCol = Math.max(0, range.startOffset - this.sourceTranspiled.wrapperLength)
+ endCol = Math.min(this.sourceTranspiled.eof, range.endOffset - this.sourceTranspiled.wrapperLength)
+
+ const { startLine, relStartCol, endLine, relEndCol, source } = this.sourceTranspiled.offsetToOriginalRelative(
+ this.sourceMap,
+ startCol,
+ endCol
+ )
+
+ const matchingSource = this.covSources.find(covSource => covSource.path === source)
+ covSource = matchingSource ? matchingSource.source : this.covSources[0].source
+ path = matchingSource ? matchingSource.path : this.covSources[0].path
+
+ // next we convert these relative positions back to absolute positions
+ // in the original source (which is the format expected in the next step).
+ startCol = covSource.relativeToOffset(startLine, relStartCol)
+ endCol = covSource.relativeToOffset(endLine, relEndCol)
+ }
+
+ return {
+ path,
+ covSource,
+ startCol,
+ endCol
+ }
+ }
+
+ getInnerIstanbul (source, path) {
+ // We apply the "Resolving Sources" logic (as defined in
+ // sourcemaps.info/spec.html) as a final step for 1:many source maps.
+ // for 1:1 source maps, the resolve logic is applied while loading.
+ //
+ // TODO: could we move the resolving logic for 1:1 source maps to the final
+ // step as well? currently this breaks some tests in c8.
+ let resolvedPath = path
+ if (this.rawSourceMap && this.rawSourceMap.sourcemap.sources.length > 1) {
+ resolvedPath = this._resolveSource(this.rawSourceMap, path)
+ }
+
+ if (this.excludePath(resolvedPath)) {
+ return
+ }
+
+ return {
+ [resolvedPath]: {
+ path: resolvedPath,
+ all: this.all,
+ ...this._statementsToIstanbul(source, path),
+ ...this._branchesToIstanbul(source, path),
+ ...this._functionsToIstanbul(source, path)
+ }
+ }
+ }
+
+ toIstanbul () {
+ return this.covSources.reduce((istanbulOuter, { source, path }) => Object.assign(istanbulOuter, this.getInnerIstanbul(source, path)), {})
+ }
+
+ _statementsToIstanbul (source, path) {
+ const statements = {
+ statementMap: {},
+ s: {}
+ }
+ source.lines.forEach((line, index) => {
+ statements.statementMap[`${index}`] = line.toIstanbul()
+ statements.s[`${index}`] = line.count
+ })
+ return statements
+ }
+
+ _branchesToIstanbul (source, path) {
+ const branches = {
+ branchMap: {},
+ b: {}
+ }
+ this.branches[path] = this.branches[path] || []
+ this.branches[path].forEach((branch, index) => {
+ const srcLine = source.lines[branch.startLine - 1]
+ const ignore = srcLine === undefined ? true : srcLine.ignore
+ branches.branchMap[`${index}`] = branch.toIstanbul()
+ branches.b[`${index}`] = [ignore ? 1 : branch.count]
+ })
+ return branches
+ }
+
+ _functionsToIstanbul (source, path) {
+ const functions = {
+ fnMap: {},
+ f: {}
+ }
+ this.functions[path] = this.functions[path] || []
+ this.functions[path].forEach((fn, index) => {
+ const srcLine = source.lines[fn.startLine - 1]
+ const ignore = srcLine === undefined ? true : srcLine.ignore
+ functions.fnMap[`${index}`] = fn.toIstanbul()
+ functions.f[`${index}`] = ignore ? 1 : fn.count
+ })
+ return functions
+ }
+}
+
+function parsePath (scriptPath) {
+ return scriptPath.startsWith('file://') ? fileURLToPath(scriptPath) : scriptPath
+}