@@ -3,8 +3,8 @@ const istanbul = require('istanbul-lib-coverage')
3
3
const { join, resolve, isAbsolute } = require ( 'path' )
4
4
const { existsSync, mkdirSync, readFileSync, writeFileSync } = require ( 'fs' )
5
5
const execa = require ( 'execa' )
6
- const fs = require ( 'fs ' )
7
- const { fixSourcePathes } = require ( './utils' )
6
+ const path = require ( 'path ' )
7
+ const { fixSourcePathes, showNycInfo } = require ( './utils' )
8
8
const NYC = require ( 'nyc' )
9
9
10
10
const debug = require ( 'debug' ) ( 'code-coverage' )
@@ -20,8 +20,8 @@ const nycFilename = join(coverageFolder, 'out.json')
20
20
// potentially there might be "nyc" options in other configuration files
21
21
// it allows, but for now ignore those options
22
22
const pkgFilename = join ( processWorkingDirectory , 'package.json' )
23
- const pkg = fs . existsSync ( pkgFilename )
24
- ? JSON . parse ( fs . readFileSync ( pkgFilename , 'utf8' ) )
23
+ const pkg = existsSync ( pkgFilename )
24
+ ? JSON . parse ( readFileSync ( pkgFilename , 'utf8' ) )
25
25
: { }
26
26
const nycOptions = pkg . nyc || { }
27
27
const scripts = pkg . scripts || { }
@@ -38,38 +38,121 @@ function saveCoverage(coverage) {
38
38
}
39
39
40
40
/**
41
- * A small debug utility to inspect paths saved in NYC output JSON file
41
+ * @param {string[] } filepaths
42
+ * @returns {string | undefined } common prefix that corresponds to current folder
42
43
*/
43
- function showNycInfo ( nycFilename ) {
44
+ function findCommonRoot ( filepaths ) {
45
+ if ( ! filepaths . length ) {
46
+ debug ( 'cannot find common root without any files' )
47
+ return
48
+ }
49
+
50
+ // assuming / as file separator
51
+ const splitParts = filepaths . map ( name => name . split ( '/' ) )
52
+ const lengths = splitParts . map ( arr => arr . length )
53
+ const shortestLength = Math . min . apply ( null , lengths )
54
+ debug ( 'shorted file path has %d parts' , shortestLength )
55
+
56
+ const cwd = process . cwd ( )
57
+ let commonPrefix = [ ]
58
+ let foundCurrentFolder
59
+
60
+ for ( let k = 0 ; k < shortestLength ; k += 1 ) {
61
+ const part = splitParts [ 0 ] [ k ]
62
+ const prefix = commonPrefix . concat ( part ) . join ( '/' )
63
+ debug ( 'testing prefix %o' , prefix )
64
+ const allFilesStart = filepaths . every ( name => name . startsWith ( prefix ) )
65
+ if ( ! allFilesStart ) {
66
+ debug ( 'stopped at non-common prefix %s' , prefix )
67
+ break
68
+ }
69
+
70
+ commonPrefix . push ( part )
71
+
72
+ const removedPrefixNames = filepaths . map ( filepath =>
73
+ filepath . slice ( prefix . length )
74
+ )
75
+ debug ( 'removedPrefix %o' , removedPrefixNames )
76
+ const foundAllPaths = removedPrefixNames . every ( filepath =>
77
+ existsSync ( path . join ( cwd , filepath ) )
78
+ )
79
+ debug ( 'all files found at %s? %o' , prefix , foundAllPaths )
80
+ if ( foundAllPaths ) {
81
+ debug ( 'found prefix that matches current folder: %s' , prefix )
82
+ foundCurrentFolder = prefix
83
+ break
84
+ }
85
+ }
86
+
87
+ return foundCurrentFolder
88
+ }
89
+
90
+ function tryFindingLocalFiles ( nycFilename ) {
91
+ const nycCoverage = JSON . parse ( readFileSync ( nycFilename , 'utf8' ) )
92
+ const coverageKeys = Object . keys ( nycCoverage )
93
+ const filenames = coverageKeys . map ( key => nycCoverage [ key ] . path )
94
+ const commonFolder = findCommonRoot ( filenames )
95
+ if ( ! commonFolder ) {
96
+ debug ( 'could not find common folder %s' , commonFolder )
97
+ return
98
+ }
99
+ const cwd = process . cwd ( )
100
+ debug (
101
+ 'found common folder %s that matches current working directory %s' ,
102
+ commonFolder ,
103
+ cwd
104
+ )
105
+ const length = commonFolder . length
106
+ let changed
107
+
108
+ coverageKeys . forEach ( key => {
109
+ const from = nycCoverage [ key ] . path
110
+ if ( from . startsWith ( commonFolder ) ) {
111
+ const to = path . join ( cwd , from . slice ( length ) )
112
+ nycCoverage [ key ] . path = to
113
+ debug ( 'replaced %s -> %s' , from , to )
114
+ changed = true
115
+ }
116
+ } )
117
+
118
+ if ( changed ) {
119
+ debug ( 'saving updated file %s' , nycFilename )
120
+ writeFileSync (
121
+ nycFilename ,
122
+ JSON . stringify ( nycCoverage , null , 2 ) + '\n' ,
123
+ 'utf8'
124
+ )
125
+ }
126
+ }
127
+
128
+ function checkAllPathsNotFound ( nycFilename ) {
44
129
const nycCoverage = JSON . parse ( readFileSync ( nycFilename , 'utf8' ) )
45
130
46
131
const coverageKeys = Object . keys ( nycCoverage )
47
132
if ( ! coverageKeys . length ) {
48
133
console . error ( '⚠️ file %s has no coverage information' , nycFilename )
49
134
return
50
135
}
51
- debug ( 'NYC file %s has %d key(s)' , nycFilename , coverageKeys . length )
52
136
53
- const maxPrintKeys = 3
54
- const showKeys = coverageKeys . slice ( 0 , maxPrintKeys )
55
-
56
- showKeys . forEach ( ( key , k ) => {
137
+ const allFilesAreMissing = coverageKeys . every ( ( key , k ) => {
57
138
const coverage = nycCoverage [ key ]
58
-
59
- // printing a few found keys and file paths from the coverage file
60
- // will make debugging any problems much much easier
61
- if ( k < maxPrintKeys ) {
62
- debug ( '%d key %s file path %s' , k + 1 , key , coverage . path )
63
- }
139
+ return ! existsSync ( coverage . path )
64
140
} )
141
+
142
+ debug (
143
+ 'in file %s all files are not found? %o' ,
144
+ nycFilename ,
145
+ allFilesAreMissing
146
+ )
147
+ return allFilesAreMissing
65
148
}
66
149
67
150
/**
68
151
* Looks at all coverage objects in the given JSON coverage file
69
152
* and if the file is relative, and exists, changes its path to
70
153
* be absolute.
71
154
*/
72
- function resolvePaths ( nycFilename ) {
155
+ function resolveRelativePaths ( nycFilename ) {
73
156
const nycCoverage = JSON . parse ( readFileSync ( nycFilename , 'utf8' ) )
74
157
75
158
const coverageKeys = Object . keys ( nycCoverage )
@@ -84,12 +167,23 @@ function resolvePaths(nycFilename) {
84
167
coverageKeys . forEach ( ( key , k ) => {
85
168
const coverage = nycCoverage [ key ]
86
169
87
- if ( coverage . path && ! isAbsolute ( coverage . path ) ) {
170
+ if ( ! coverage . path ) {
171
+ debug ( 'key %s does not have path' , key )
172
+ return
173
+ }
174
+
175
+ if ( ! isAbsolute ( coverage . path ) ) {
88
176
if ( existsSync ( coverage . path ) ) {
89
177
debug ( 'resolving path %s' , coverage . path )
90
178
coverage . path = resolve ( coverage . path )
91
179
changed = true
92
180
}
181
+ return
182
+ }
183
+
184
+ // path is absolute, let's check if it exists
185
+ if ( ! existsSync ( coverage . path ) ) {
186
+ debug ( '⚠️ cannot find file %s with hash %s' , coverage . path , coverage . hash )
93
187
}
94
188
} )
95
189
@@ -164,7 +258,12 @@ const tasks = {
164
258
}
165
259
166
260
showNycInfo ( nycFilename )
167
- resolvePaths ( nycFilename )
261
+ const allSourceFilesMissing = checkAllPathsNotFound ( nycFilename )
262
+ if ( allSourceFilesMissing ) {
263
+ tryFindingLocalFiles ( nycFilename )
264
+ }
265
+
266
+ resolveRelativePaths ( nycFilename )
168
267
169
268
if ( customNycReportScript ) {
170
269
debug (
0 commit comments