Skip to content

Commit

Permalink
feat!: use Node.js' source-map cache, to support tools like ts-node (b…
Browse files Browse the repository at this point in the history
…coe#152)

BREAKING CHANGE: Node.js' source-map and lineLength cache is now used to remap coverage output (this allows tools like ts-node to be supported, which transpile at runtime).
  • Loading branch information
bcoe authored Oct 24, 2019
1 parent a107093 commit 53bba15
Show file tree
Hide file tree
Showing 9 changed files with 164 additions and 9 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ os:
- osx
- windows
node_js:
- "12"
- "13"
after_success: npm run coverage
35 changes: 34 additions & 1 deletion lib/report.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class Report {
include: include
})
this.omitRelative = omitRelative
this.sourceMapCache = {}
this.wrapperLength = wrapperLength
}

Expand Down Expand Up @@ -63,8 +64,9 @@ class Report {

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)
const converter = v8toIstanbul(path, this.wrapperLength, sources)
await converter.load()

if (resultCountPerPath.has(path)) {
Expand Down Expand Up @@ -98,6 +100,34 @@ class Report {
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 = {}
if (this.sourceMapCache[`file://${v8ScriptCov.url}`]) {
const sourceMapAndLineLengths = this.sourceMapCache[`file://${v8ScriptCov.url}`]
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.
*
Expand All @@ -111,6 +141,9 @@ class Report {
const v8ProcessCovs = []
for (const v8ProcessCov of this._loadReports()) {
if (this._isCoverageObject(v8ProcessCov)) {
if (v8ProcessCov['source-map-cache']) {
Object.assign(this.sourceMapCache, v8ProcessCov['source-map-cache'])
}
v8ProcessCovs.push(this._normalizeProcessCov(v8ProcessCov))
}
}
Expand Down
61 changes: 58 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"istanbul-reports": "^2.2.6",
"rimraf": "^3.0.0",
"test-exclude": "^5.2.3",
"v8-to-istanbul": "^3.2.3",
"v8-to-istanbul": "^3.2.6",
"yargs": "^14.0.0",
"yargs-parser": "^14.0.0"
},
Expand All @@ -51,7 +51,9 @@
"coveralls": "^3.0.6",
"mocha": "^6.2.0",
"standard": "^14.1.0",
"standard-version": "^7.0.0"
"standard-version": "^7.0.0",
"ts-node": "^8.4.1",
"typescript": "^3.6.4"
},
"engines": {
"node": ">=10.12.0"
Expand Down
2 changes: 1 addition & 1 deletion test/fixtures/source-maps/branches/branches.uglify.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion test/fixtures/source-maps/classes/classes.uglify.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 34 additions & 0 deletions test/fixtures/ts-node-basic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
export interface FooOptions {
x: number;
}

class Foo {
x: number;
constructor (options: FooOptions) {
this.x = options.x ? options.x : 99
if (this.x) {
console.info('covered')
} else {
console.info('uncovered')
}
this.methodC()
}
methodA (): number {
console.info('covered')
return 33
}
/* c8 ignore next 3 */
methodB () {
console.info('uncovered')
}
private methodC () {
console.info('covered')
}
methodD () {
console.info('uncovered')
}
}

const a = new Foo({x: 0})
const b = new Foo({x: 33})
a.methodA()
16 changes: 16 additions & 0 deletions test/integration.js
Original file line number Diff line number Diff line change
Expand Up @@ -311,4 +311,20 @@ describe('c8', () => {
})
})
})

describe('ts-node', () => {
beforeEach(cb => rimraf('tmp/source-map', cb))

it('reads source-map from cache, and applies to coverage', () => {
const { output } = spawnSync(nodePath, [
c8Path,
'--exclude="test/*.js"',
'--temp-directory=tmp/source-map',
'--clean=true',
'./node_modules/.bin/ts-node',
require.resolve('./fixtures/ts-node-basic.ts')
])
output.toString('utf8').should.matchSnapshot()
})
})
})
15 changes: 15 additions & 0 deletions test/integration.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -219,3 +219,18 @@ All files | 83.33 | 85.71 | 60 | 83.33 | |
-----------|----------|----------|----------|----------|-------------------|
,"
`;

exports[`c8 ts-node reads source-map from cache, and applies to coverage 1`] = `
",covered
covered
covered
covered
covered
------------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
------------------|----------|----------|----------|----------|-------------------|
All files | 88.24 | 87.5 | 80 | 88.24 | |
ts-node-basic.ts | 88.24 | 87.5 | 80 | 88.24 | 12,13,28,29 |
------------------|----------|----------|----------|----------|-------------------|
,"
`;

0 comments on commit 53bba15

Please sign in to comment.