c8 7.13.0 → 7.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commands/report.js +2 -1
- package/lib/parse-args.js +6 -0
- package/lib/report.js +109 -32
- package/package.json +1 -1
package/lib/commands/report.js
CHANGED
|
@@ -34,7 +34,8 @@ exports.outputReport = async function (argv) {
|
|
|
34
34
|
allowExternal: argv.allowExternal,
|
|
35
35
|
src: argv.src,
|
|
36
36
|
skipFull: argv.skipFull,
|
|
37
|
-
excludeNodeModules: argv.excludeNodeModules
|
|
37
|
+
excludeNodeModules: argv.excludeNodeModules,
|
|
38
|
+
mergeAsync: argv.mergeAsync
|
|
38
39
|
})
|
|
39
40
|
await report.run()
|
|
40
41
|
if (argv.checkCoverage) await checkCoverages(argv, report)
|
package/lib/parse-args.js
CHANGED
|
@@ -152,6 +152,12 @@ function buildYargs (withCommands = false) {
|
|
|
152
152
|
describe: 'supplying --allowExternal will cause c8 to allow files from outside of your cwd. This applies both to ' +
|
|
153
153
|
'files discovered in coverage temp files and also src files discovered if using the --all flag.'
|
|
154
154
|
})
|
|
155
|
+
.options('merge-async', {
|
|
156
|
+
default: false,
|
|
157
|
+
type: 'boolean',
|
|
158
|
+
describe: 'supplying --merge-async will merge all v8 coverage reports asynchronously and incrementally. ' +
|
|
159
|
+
'This is to avoid OOM issues with Node.js runtime.'
|
|
160
|
+
})
|
|
155
161
|
.pkgConf('c8')
|
|
156
162
|
.demandCommand(1)
|
|
157
163
|
.check((argv) => {
|
package/lib/report.js
CHANGED
|
@@ -2,6 +2,12 @@ const Exclude = require('test-exclude')
|
|
|
2
2
|
const libCoverage = require('istanbul-lib-coverage')
|
|
3
3
|
const libReport = require('istanbul-lib-report')
|
|
4
4
|
const reports = require('istanbul-reports')
|
|
5
|
+
let readFile
|
|
6
|
+
try {
|
|
7
|
+
;({ readFile } = require('fs/promises'))
|
|
8
|
+
} catch (err) {
|
|
9
|
+
;({ readFile } = require('fs').promises)
|
|
10
|
+
}
|
|
5
11
|
const { readdirSync, readFileSync, statSync } = require('fs')
|
|
6
12
|
const { isAbsolute, resolve, extname } = require('path')
|
|
7
13
|
const { pathToFileURL, fileURLToPath } = require('url')
|
|
@@ -30,7 +36,8 @@ class Report {
|
|
|
30
36
|
src,
|
|
31
37
|
allowExternal = false,
|
|
32
38
|
skipFull,
|
|
33
|
-
excludeNodeModules
|
|
39
|
+
excludeNodeModules,
|
|
40
|
+
mergeAsync
|
|
34
41
|
}) {
|
|
35
42
|
this.reporter = reporter
|
|
36
43
|
this.reporterOptions = reporterOptions || {}
|
|
@@ -53,6 +60,7 @@ class Report {
|
|
|
53
60
|
this.all = all
|
|
54
61
|
this.src = this._getSrc(src)
|
|
55
62
|
this.skipFull = skipFull
|
|
63
|
+
this.mergeAsync = mergeAsync
|
|
56
64
|
}
|
|
57
65
|
|
|
58
66
|
_getSrc (src) {
|
|
@@ -90,7 +98,13 @@ class Report {
|
|
|
90
98
|
if (this._allCoverageFiles) return this._allCoverageFiles
|
|
91
99
|
|
|
92
100
|
const map = libCoverage.createCoverageMap()
|
|
93
|
-
|
|
101
|
+
let v8ProcessCov
|
|
102
|
+
|
|
103
|
+
if (this.mergeAsync) {
|
|
104
|
+
v8ProcessCov = await this._getMergedProcessCovAsync()
|
|
105
|
+
} else {
|
|
106
|
+
v8ProcessCov = this._getMergedProcessCov()
|
|
107
|
+
}
|
|
94
108
|
const resultCountPerPath = new Map()
|
|
95
109
|
const possibleCjsEsmBridges = new Map()
|
|
96
110
|
|
|
@@ -188,43 +202,106 @@ class Report {
|
|
|
188
202
|
}
|
|
189
203
|
|
|
190
204
|
if (this.all) {
|
|
191
|
-
const emptyReports =
|
|
205
|
+
const emptyReports = this._includeUncoveredFiles(fileIndex)
|
|
192
206
|
v8ProcessCovs.unshift({
|
|
193
207
|
result: emptyReports
|
|
194
208
|
})
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return mergeProcessCovs(v8ProcessCovs)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Returns the merged V8 process coverage.
|
|
216
|
+
*
|
|
217
|
+
* It asynchronously and incrementally reads and merges individual process coverages
|
|
218
|
+
* generated by Node. This can be used via the `--merge-async` CLI arg. It's intended
|
|
219
|
+
* to be used across a large multi-process test run.
|
|
220
|
+
*
|
|
221
|
+
* @return {ProcessCov} Merged V8 process coverage.
|
|
222
|
+
* @private
|
|
223
|
+
*/
|
|
224
|
+
async _getMergedProcessCovAsync () {
|
|
225
|
+
const { mergeProcessCovs } = require('@bcoe/v8-coverage')
|
|
226
|
+
const fileIndex = new Set() // Set<string>
|
|
227
|
+
let mergedCov = null
|
|
228
|
+
for (const file of readdirSync(this.tempDirectory)) {
|
|
229
|
+
try {
|
|
230
|
+
const rawFile = await readFile(
|
|
231
|
+
resolve(this.tempDirectory, file),
|
|
232
|
+
'utf8'
|
|
233
|
+
)
|
|
234
|
+
let report = JSON.parse(rawFile)
|
|
235
|
+
|
|
236
|
+
if (this._isCoverageObject(report)) {
|
|
237
|
+
if (report['source-map-cache']) {
|
|
238
|
+
Object.assign(this.sourceMapCache, this._normalizeSourceMapCache(report['source-map-cache']))
|
|
222
239
|
}
|
|
223
|
-
|
|
240
|
+
report = this._normalizeProcessCov(report, fileIndex)
|
|
241
|
+
if (mergedCov) {
|
|
242
|
+
mergedCov = mergeProcessCovs([mergedCov, report])
|
|
243
|
+
} else {
|
|
244
|
+
mergedCov = mergeProcessCovs([report])
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
} catch (err) {
|
|
248
|
+
debuglog(`${err.stack}`)
|
|
224
249
|
}
|
|
225
250
|
}
|
|
226
251
|
|
|
227
|
-
|
|
252
|
+
if (this.all) {
|
|
253
|
+
const emptyReports = this._includeUncoveredFiles(fileIndex)
|
|
254
|
+
const emptyReport = {
|
|
255
|
+
result: emptyReports
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
mergedCov = mergeProcessCovs([emptyReport, mergedCov])
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return mergedCov
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Adds empty coverage reports to account for uncovered/untested code.
|
|
266
|
+
* This is only done when the `--all` flag is present.
|
|
267
|
+
*
|
|
268
|
+
* @param {Set} fileIndex list of files that have coverage
|
|
269
|
+
* @returns {Array} list of empty coverage reports
|
|
270
|
+
*/
|
|
271
|
+
_includeUncoveredFiles (fileIndex) {
|
|
272
|
+
const emptyReports = []
|
|
273
|
+
const workingDirs = this.src
|
|
274
|
+
const { extension } = this.exclude
|
|
275
|
+
for (const workingDir of workingDirs) {
|
|
276
|
+
this.exclude.globSync(workingDir).forEach((f) => {
|
|
277
|
+
const fullPath = resolve(workingDir, f)
|
|
278
|
+
if (!fileIndex.has(fullPath)) {
|
|
279
|
+
const ext = extname(fullPath)
|
|
280
|
+
if (extension.includes(ext)) {
|
|
281
|
+
const stat = statSync(fullPath)
|
|
282
|
+
const sourceMap = getSourceMapFromFile(fullPath)
|
|
283
|
+
if (sourceMap) {
|
|
284
|
+
this.sourceMapCache[pathToFileURL(fullPath)] = { data: sourceMap }
|
|
285
|
+
}
|
|
286
|
+
emptyReports.push({
|
|
287
|
+
scriptId: 0,
|
|
288
|
+
url: resolve(fullPath),
|
|
289
|
+
functions: [{
|
|
290
|
+
functionName: '(empty-report)',
|
|
291
|
+
ranges: [{
|
|
292
|
+
startOffset: 0,
|
|
293
|
+
endOffset: stat.size,
|
|
294
|
+
count: 0
|
|
295
|
+
}],
|
|
296
|
+
isBlockCoverage: true
|
|
297
|
+
}]
|
|
298
|
+
})
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
})
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return emptyReports
|
|
228
305
|
}
|
|
229
306
|
|
|
230
307
|
/**
|