1
1
import { existsSync , promises as fs , writeFileSync } from 'node:fs'
2
2
import type { Profiler } from 'node:inspector'
3
3
import { fileURLToPath , pathToFileURL } from 'node:url'
4
+ import { basename , dirname } from 'node:path'
4
5
import v8ToIstanbul from 'v8-to-istanbul'
5
6
import { mergeProcessCovs } from '@bcoe/v8-coverage'
6
7
import libReport from 'istanbul-lib-report'
@@ -193,6 +194,18 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage
193
194
coverageMap . merge ( await transformCoverage ( converted ) )
194
195
}
195
196
197
+ await this . generateReports ( coverageMap , allTestsRun )
198
+
199
+ // In watch mode we need to preserve the previous results if cleanOnRerun is disabled
200
+ const keepResults = ! this . options . cleanOnRerun && this . ctx . config . watch
201
+
202
+ if ( ! keepResults ) {
203
+ this . coverageFiles = new Map ( )
204
+ await fs . rm ( this . coverageFilesDirectory , { recursive : true } )
205
+ }
206
+ }
207
+
208
+ async generateReports ( coverageMap : CoverageMap , allTestsRun : boolean | undefined ) {
196
209
const context = libReport . createContext ( {
197
210
dir : this . options . reportsDirectory ,
198
211
coverageMap,
@@ -203,6 +216,11 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage
203
216
this . ctx . logger . log ( c . blue ( ' % ' ) + c . dim ( 'Coverage report from ' ) + c . yellow ( this . name ) )
204
217
205
218
for ( const reporter of this . options . reporter ) {
219
+ if ( reporter [ 0 ] === 'blob' ) {
220
+ await this . createBlobReport ( coverageMap , reporter [ 1 ] )
221
+ continue
222
+ }
223
+
206
224
// Type assertion required for custom reporters
207
225
reports . create ( reporter [ 0 ] as Parameters < typeof reports . create > [ 0 ] , {
208
226
skipFull : this . options . skipFull ,
@@ -239,14 +257,37 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage
239
257
} )
240
258
}
241
259
}
260
+ }
261
+
262
+ async mergeReports ( path : string ) {
263
+ const directory = resolve ( path , 'coverage' )
264
+ const files = await fs . readdir ( directory )
265
+ const coverageMap = libCoverage . createCoverageMap ( { } )
266
+
267
+ for ( const file of files ) {
268
+ const report = await fs . readFile ( resolve ( directory , file ) , 'utf8' )
269
+ coverageMap . merge ( JSON . parse ( report ) )
270
+ }
242
271
243
- // In watch mode we need to preserve the previous results if cleanOnRerun is disabled
244
- const keepResults = ! this . options . cleanOnRerun && this . ctx . config . watch
272
+ await this . generateReports ( coverageMap , true )
273
+ }
245
274
246
- if ( ! keepResults ) {
247
- this . coverageFiles = new Map ( )
248
- await fs . rm ( this . coverageFilesDirectory , { recursive : true } )
275
+ async createBlobReport ( coverageMap : CoverageMap , options : Options [ 'reporter' ] [ number ] [ 1 ] ) {
276
+ const outputFile = 'outputFile' in options && options . outputFile as string
277
+ const dir = outputFile ? dirname ( outputFile ) : '.vitest-reports/coverage'
278
+ let file = outputFile && basename ( outputFile )
279
+
280
+ if ( ! file ) {
281
+ const shard = this . ctx . config . shard
282
+ file = shard
283
+ ? `.coverage-blob-${ shard . index } -${ shard . count } .json`
284
+ : '.coverage-blob.json'
249
285
}
286
+
287
+ const context = libReport . createContext ( { dir, coverageMap } )
288
+ reports . create ( 'json' , { file } ) . execute ( context )
289
+
290
+ this . ctx . logger . log ( `coverage blob report written to ${ resolve ( dir , file ) } ` )
250
291
}
251
292
252
293
private async getUntestedFiles ( testedFiles : string [ ] ) : Promise < RawCoverage > {
0 commit comments