@@ -13,7 +13,8 @@ import remapping from '@ampproject/remapping'
1313import { normalize , resolve } from 'pathe'
1414import c from 'picocolors'
1515import { provider } from 'std-env'
16- import type { EncodedSourceMap } from 'vite-node'
16+ import { cleanUrl } from 'vite-node/utils'
17+ import type { EncodedSourceMap , FetchResult } from 'vite-node'
1718import { coverageConfigDefaults , defaultExclude , defaultInclude } from 'vitest/config'
1819import { BaseCoverageProvider } from 'vitest/coverage'
1920import type { AfterSuiteRunMeta , CoverageProvider , CoverageV8Options , ReportContext , ResolvedCoverageOptions } from 'vitest'
@@ -36,6 +37,7 @@ interface TestExclude {
3637}
3738
3839type Options = ResolvedCoverageOptions < 'v8' >
40+ type TransformResults = Map < string , FetchResult >
3941
4042// TODO: vite-node should export this
4143const WRAPPER_LENGTH = 185
@@ -99,18 +101,19 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage
99101 if ( provider === 'stackblitz' )
100102 this . ctx . logger . log ( c . blue ( ' % ' ) + c . yellow ( '@vitest/coverage-v8 does not work on Stackblitz. Report will be empty.' ) )
101103
104+ const transformResults = normalizeTransformResults ( this . ctx . projects . map ( project => project . vitenode . fetchCache ) )
102105 const merged = mergeProcessCovs ( this . coverages )
103106 const scriptCoverages = merged . result . filter ( result => this . testExclude . shouldInstrument ( fileURLToPath ( result . url ) ) )
104107
105108 if ( this . options . all && allTestsRun ) {
106109 const coveredFiles = Array . from ( scriptCoverages . map ( r => r . url ) )
107- const untestedFiles = await this . getUntestedFiles ( coveredFiles )
110+ const untestedFiles = await this . getUntestedFiles ( coveredFiles , transformResults )
108111
109112 scriptCoverages . push ( ...untestedFiles )
110113 }
111114
112115 const converted = await Promise . all ( scriptCoverages . map ( async ( { url, functions } ) => {
113- const sources = await this . getSources ( url , functions )
116+ const sources = await this . getSources ( url , transformResults , functions )
114117
115118 // If no source map was found from vite-node we can assume this file was not run in the wrapper
116119 const wrapperLength = sources . sourceMap ? WRAPPER_LENGTH : 0
@@ -177,14 +180,14 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage
177180 }
178181 }
179182
180- private async getUntestedFiles ( testedFiles : string [ ] ) : Promise < Profiler . ScriptCoverage [ ] > {
183+ private async getUntestedFiles ( testedFiles : string [ ] , transformResults : TransformResults ) : Promise < Profiler . ScriptCoverage [ ] > {
181184 const includedFiles = await this . testExclude . glob ( this . ctx . config . root )
182185 const uncoveredFiles = includedFiles
183186 . map ( file => pathToFileURL ( resolve ( this . ctx . config . root , file ) ) )
184187 . filter ( file => ! testedFiles . includes ( file . href ) )
185188
186189 return await Promise . all ( uncoveredFiles . map ( async ( uncoveredFile ) => {
187- const { source } = await this . getSources ( uncoveredFile . href )
190+ const { source } = await this . getSources ( uncoveredFile . href , transformResults )
188191
189192 return {
190193 url : uncoveredFile . href ,
@@ -204,16 +207,14 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage
204207 } ) )
205208 }
206209
207- private async getSources ( url : string , functions : Profiler . FunctionCoverage [ ] = [ ] ) : Promise < {
210+ private async getSources ( url : string , transformResults : TransformResults , functions : Profiler . FunctionCoverage [ ] = [ ] ) : Promise < {
208211 source : string
209212 originalSource ?: string
210213 sourceMap ?: { sourcemap : EncodedSourceMap }
211214 } > {
212215 const filePath = normalize ( fileURLToPath ( url ) )
213- const transformResult = this . ctx . projects
214- . map ( project => project . vitenode . fetchCache . get ( filePath ) ?. result )
215- . filter ( Boolean )
216- . shift ( )
216+
217+ const transformResult = transformResults . get ( filePath )
217218
218219 const map = transformResult ?. map
219220 const code = transformResult ?. code
@@ -277,3 +278,18 @@ function findLongestFunctionLength(functions: Profiler.FunctionCoverage[]) {
277278 return Math . max ( previous , maxEndOffset )
278279 } , 0 )
279280}
281+
282+ function normalizeTransformResults ( fetchCaches : Map < string , { result : FetchResult } > [ ] ) {
283+ const normalized : TransformResults = new Map ( )
284+
285+ for ( const fetchCache of fetchCaches ) {
286+ for ( const [ key , value ] of fetchCache . entries ( ) ) {
287+ const cleanEntry = cleanUrl ( key )
288+
289+ if ( ! normalized . has ( cleanEntry ) )
290+ normalized . set ( cleanEntry , value . result )
291+ }
292+ }
293+
294+ return normalized
295+ }
0 commit comments