Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: support source map with multiple sources #102

Merged
merged 8 commits into from
Aug 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ coverage
.nyc_output
node_modules
.idea
.vscode
2 changes: 1 addition & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ declare class V8ToIstanbul {
toIstanbul(): CoverageMapData
}

declare function v8ToIstanbul(scriptPath: string, wrapperLength?: number, sources?: Sources): V8ToIstanbul
declare function v8ToIstanbul(scriptPath: string, wrapperLength?: number, sources?: Sources, excludePath?: (path) => boolean): V8ToIstanbul

export = v8ToIstanbul
4 changes: 2 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const V8ToIstanbul = require('./lib/v8-to-istanbul')

module.exports = function (path, wrapperLength, sources) {
return new V8ToIstanbul(path, wrapperLength, sources)
module.exports = function (path, wrapperLength, sources, excludePath) {
return new V8ToIstanbul(path, wrapperLength, sources, excludePath)
}
1 change: 1 addition & 0 deletions lib/source.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ module.exports = class CovSource {
}

return {
source: start.source,
startLine: start.line,
relStartCol: start.column,
endLine: end.line,
Expand Down
100 changes: 62 additions & 38 deletions lib/v8-to-istanbul.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,18 @@ const isNode8 = /^v8\./.test(process.version)
const cjsWrapperLength = isOlderNode10 ? require('module').wrapper[0].length : 0

module.exports = class V8ToIstanbul {
constructor (scriptPath, wrapperLength, sources) {
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.branches = {}
this.functions = {}
this.covSources = []
this.sourceMap = undefined
this.source = undefined
this.sourceTranspiled = undefined
}

Expand All @@ -44,8 +45,9 @@ module.exports = class V8ToIstanbul {

if (rawSourceMap) {
if (rawSourceMap.sourcemap.sources.length > 1) {
console.warn('v8-to-istanbul: source-mappings from one to many files not yet supported')
this.source = new CovSource(rawSource, this.wrapperLength)
this.sourceMap = await new SourceMapConsumer(rawSourceMap.sourcemap)
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 {
this._rewritePath(rawSourceMap)
this.sourceMap = await new SourceMapConsumer(rawSourceMap.sourcemap)
Expand All @@ -63,11 +65,11 @@ module.exports = class V8ToIstanbul {
originalRawSource = await readFile(this.path, 'utf8')
}

this.source = new CovSource(originalRawSource, this.wrapperLength)
this.covSources = [{ source: new CovSource(originalRawSource, this.wrapperLength), path: this.sourceMap.sources[0] }]
this.sourceTranspiled = new CovSource(rawSource, this.wrapperLength)
}
} else {
this.source = new CovSource(rawSource, this.wrapperLength)
this.covSources = [{ source: new CovSource(rawSource, this.wrapperLength), path: this.path }]
}
}

Expand All @@ -86,26 +88,28 @@ module.exports = class V8ToIstanbul {
applyCoverage (blocks) {
blocks.forEach(block => {
block.ranges.forEach((range, i) => {
const {
startCol,
endCol
} = this._maybeRemapStartColEndCol(range)
const lines = this.source.lines.filter(line => {
const { startCol, endCol, path, covSource } = this._maybeRemapStartColEndCol(range)
if (this.excludePath(path)) {
return
}
const lines = covSource.lines.filter(line => {
// Upstream tooling can provide a block with the functionName
// (empty-report), this will result in a report that has all
// lines zeroed out.
if (block.functionName === '(empty-report)') {
line.count = 0
return true
}

return startCol <= line.endCol && endCol >= line.startCol
})
const startLineInstance = lines[0]
const endLineInstance = lines[lines.length - 1]

if (block.isBlockCoverage && lines.length) {
this.branches[path] = this.branches[path] || []
// record branches.
this.branches.push(new CovBranch(
this.branches[path].push(new CovBranch(
startLineInstance.line,
startCol - startLineInstance.startCol,
endLineInstance.line,
Expand All @@ -116,7 +120,8 @@ module.exports = class V8ToIstanbul {
// if block-level granularity is enabled, we we still create a single
// CovFunction tracking object for each set of ranges.
if (block.functionName && i === 0) {
this.functions.push(new CovFunction(
this.functions[path] = this.functions[path] || []
this.functions[path].push(new CovFunction(
block.functionName,
startLineInstance.line,
startCol - startLineInstance.startCol,
Expand All @@ -126,8 +131,9 @@ module.exports = class V8ToIstanbul {
))
}
} else if (block.functionName && lines.length) {
this.functions[path] = this.functions[path] || []
// record functions.
this.functions.push(new CovFunction(
this.functions[path].push(new CovFunction(
block.functionName,
startLineInstance.line,
startCol - startLineInstance.startCol,
Expand All @@ -148,6 +154,7 @@ module.exports = class V8ToIstanbul {
// 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
}
Expand All @@ -157,75 +164,92 @@ module.exports = class V8ToIstanbul {
}

_maybeRemapStartColEndCol (range) {
let startCol = Math.max(0, range.startOffset - this.source.wrapperLength)
let endCol = Math.min(this.source.eof, range.endOffset - this.source.wrapperLength)
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 } = this.sourceTranspiled.offsetToOriginalRelative(
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 = this.source.relativeToOffset(startLine, relStartCol)
endCol = this.source.relativeToOffset(endLine, relEndCol)
startCol = covSource.relativeToOffset(startLine, relStartCol)
endCol = covSource.relativeToOffset(endLine, relEndCol)
}

return {
path,
covSource,
startCol,
endCol
}
}

getInnerIstanbul (source, path) {
if (this.excludePath(path)) {
return
}

return {
[path]: {
path,
...this._statementsToIstanbul(source, path),
...this._branchesToIstanbul(source, path),
...this._functionsToIstanbul(source, path)
}
}
}

toIstanbul () {
const istanbulInner = Object.assign(
{ path: this.path },
this._statementsToIstanbul(),
this._branchesToIstanbul(),
this._functionsToIstanbul()
)
const istanbulOuter = {}
istanbulOuter[this.path] = istanbulInner
return istanbulOuter
return this.covSources.reduce((istanbulOuter, { source, path }) => Object.assign(istanbulOuter, this.getInnerIstanbul(source, path)), {})
}

_statementsToIstanbul () {
_statementsToIstanbul (source, path) {
const statements = {
statementMap: {},
s: {}
}
this.source.lines.forEach((line, index) => {
source.lines.forEach((line, index) => {
statements.statementMap[`${index}`] = line.toIstanbul()
statements.s[`${index}`] = line.count
})
return statements
}

_branchesToIstanbul () {
_branchesToIstanbul (source, path) {
const branches = {
branchMap: {},
b: {}
}
this.branches.forEach((branch, index) => {
const ignore = this.source.lines[branch.startLine - 1].ignore
this.branches[path] = this.branches[path] || []
this.branches[path].forEach((branch, index) => {
const ignore = source.lines[branch.startLine - 1].ignore
branches.branchMap[`${index}`] = branch.toIstanbul()
branches.b[`${index}`] = [ignore ? 1 : branch.count]
})
return branches
}

_functionsToIstanbul () {
_functionsToIstanbul (source, path) {
const functions = {
fnMap: {},
f: {}
}
this.functions.forEach((fn, index) => {
const ignore = this.source.lines[fn.startLine - 1].ignore
this.functions[path] = this.functions[path] || []
this.functions[path].forEach((fn, index) => {
const ignore = source.lines[fn.startLine - 1].ignore
functions.fnMap[`${index}`] = fn.toIstanbul()
functions.f[`${index}`] = ignore ? 1 : fn.count
})
Expand Down
Loading