/
coverage.ts
118 lines (99 loc) · 3.53 KB
/
coverage.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import { existsSync, promises as fs } from 'fs'
import { takeCoverage } from 'v8'
import { createRequire } from 'module'
import { pathToFileURL } from 'url'
import type { Profiler } from 'inspector'
import { resolve } from 'pathe'
import type { RawSourceMap } from 'vite-node'
import type { Vitest } from '../node'
import { toArray } from '../utils'
import type { C8Options, ResolvedC8Options } from '../types'
const defaultExcludes = [
'coverage/**',
'packages/*/test{,s}/**',
'**/*.d.ts',
'test{,s}/**',
'test{,-*}.{js,cjs,mjs,ts,tsx,jsx}',
'**/*{.,-}test.{js,cjs,mjs,ts,tsx,jsx}',
'**/__tests__/**',
'**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc}.config.{js,cjs,mjs,ts}',
'**/.{eslint,mocha}rc.{js,cjs}',
]
export function resolveC8Options(options: C8Options, root: string): ResolvedC8Options {
const resolved: ResolvedC8Options = {
enabled: false,
clean: true,
cleanOnRerun: false,
reportsDirectory: './coverage',
excludeNodeModules: true,
exclude: defaultExcludes,
reporter: ['text', 'html'],
allowExternal: false,
// default extensions used by c8, plus '.vue' and '.svelte'
// see https://github.com/istanbuljs/schema/blob/master/default-extension.js
extension: ['.js', '.cjs', '.mjs', '.ts', '.tsx', '.jsx', '.vue', 'svelte'],
...options as any,
}
resolved.reporter = toArray(resolved.reporter)
resolved.reportsDirectory = resolve(root, resolved.reportsDirectory)
resolved.tempDirectory = process.env.NODE_V8_COVERAGE || resolve(resolved.reportsDirectory, 'tmp')
return resolved as ResolvedC8Options
}
export async function cleanCoverage(options: ResolvedC8Options, clean = true) {
if (clean && existsSync(options.reportsDirectory))
await fs.rm(options.reportsDirectory, { recursive: true, force: true })
if (!existsSync(options.tempDirectory))
await fs.mkdir(options.tempDirectory, { recursive: true })
}
const require = createRequire(import.meta.url)
export async function reportCoverage(ctx: Vitest) {
// Flush coverage to disk
takeCoverage()
// eslint-disable-next-line @typescript-eslint/no-var-requires
const createReport = require('c8/lib/report')
const report = createReport(ctx.config.coverage)
// add source maps
const sourceMapMata: Record<string, { map: RawSourceMap; source: string | undefined }> = {}
await Promise.all(Array
.from(ctx.vitenode.fetchCache.entries())
.filter(i => !i[0].includes('/node_modules/'))
.map(async([file, { result }]) => {
const map = result.map
if (!map)
return
const url = pathToFileURL(file).href
let code: string | undefined
try {
code = (await fs.readFile(file)).toString()
}
catch {}
const sources = map.sources.length
? map.sources.map(i => pathToFileURL(i).href)
: [url]
sourceMapMata[url] = {
source: result.code,
map: {
sourcesContent: code ? [code] : undefined,
...map,
sources,
},
}
}))
// This is a magic number. It corresponds to the amount of code
// that we add in packages/vite-node/src/client.ts:114 (vm.runInThisContext)
// TODO: Include our transformations in soucemaps
const offset = 190
report._getSourceMap = (coverage: Profiler.ScriptCoverage) => {
const path = pathToFileURL(coverage.url).href
const data = sourceMapMata[path]
if (!data)
return {}
return {
sourceMap: {
sourcemap: data.map,
},
source: Array(offset).fill('.').join('') + data.source,
}
}
await report.run()
}