monocart-coverage-reports 2.2.1 → 2.3.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.
@@ -33,17 +33,25 @@ module.exports = class InfoBranch {
33
33
  const line = groupLoc.start.line;
34
34
 
35
35
  // remove ignored
36
- const locations = this.locations.filter((it) => !it.ignored);
36
+ // do NOT change previous number type to object, need used for sourcemap
37
+ const newLocations = this.locations.filter((it) => !it.ignored).map((it) => {
38
+ const item = {
39
+ start: it.start,
40
+ end: it.end,
41
+ none: it.none,
42
+ count: it.count
43
+ };
37
44
 
38
- // [ { start:{line,column}, end:{line,column}, count }, ...]
39
- locations.forEach((item) => {
45
+ // [ { start:{line,column}, end:{line,column}, count }, ...]
40
46
  Util.updateOffsetToLocation(locator, item);
47
+
48
+ return item;
41
49
  });
42
50
 
43
51
  const map = {
44
52
  loc: groupLoc,
45
53
  type: this.type,
46
- locations: locations.map((item) => {
54
+ locations: newLocations.map((item) => {
47
55
  const {
48
56
  start, end, none
49
57
  } = item;
@@ -63,7 +71,7 @@ module.exports = class InfoBranch {
63
71
  line: line
64
72
  };
65
73
 
66
- const counts = locations.map((item) => item.count);
74
+ const counts = newLocations.map((item) => item.count);
67
75
 
68
76
  return {
69
77
  map,
@@ -6,6 +6,9 @@ module.exports = {
6
6
  // {string} output dir
7
7
  outputDir: './coverage-reports',
8
8
 
9
+ // {string|string[]} input raw dir(s)
10
+ inputDir: null,
11
+
9
12
  // {string} v8 or html for istanbul by default
10
13
  // {array} multiple reports with options
11
14
  // v8 report or istanbul supported reports
package/lib/generate.js CHANGED
@@ -44,84 +44,83 @@ const getReportGroup = (reports, lcov, dataType) => {
44
44
  reportMap.lcovonly = {};
45
45
  }
46
46
 
47
- const defaultReport = dataType === 'v8' ? 'v8' : 'html';
48
-
49
- if (Util.isList(reports)) {
50
- reports.forEach((it) => {
51
- if (Util.isList(it)) {
52
- // ["v8"]
53
- reportMap[it[0]] = {
47
+ const reportList = Util.toList(reports, ',');
48
+ reportList.forEach((it) => {
49
+ if (Util.isList(it)) {
50
+ // ["v8"], ["v8", {}]
51
+ const id = it[0];
52
+ if (typeof id === 'string' && id) {
53
+ reportMap[id] = {
54
54
  ... it[1]
55
55
  };
56
- return;
57
- }
58
- if (typeof it === 'string') {
59
- reportMap[it] = {};
60
- return;
61
56
  }
62
- reportMap[defaultReport] = {};
63
- });
64
- } else if (typeof reports === 'string' && reports) {
65
- reportMap[reports] = {};
66
- } else {
57
+ return;
58
+ }
59
+ if (typeof it === 'string' && it) {
60
+ reportMap[it] = {};
61
+ }
62
+ });
63
+
64
+ // using default report if no reports
65
+ if (!Object.keys(reportMap).length) {
66
+ const defaultReport = dataType === 'v8' ? 'v8' : 'html';
67
67
  reportMap[defaultReport] = {};
68
68
  }
69
69
 
70
- const allSupportedReports = [
71
-
70
+ const allBuildInReports = {
72
71
  // v8
73
- 'v8',
74
- 'v8-json',
75
-
76
- // both
77
- 'console-summary',
72
+ 'v8': 'v8',
73
+ 'v8-json': 'v8',
74
+ 'codecov': 'v8',
78
75
 
79
76
  // istanbul
80
- 'clover',
81
- 'cobertura',
82
- 'html',
83
- 'html-spa',
84
- 'json',
85
- 'json-summary',
86
- 'lcov',
87
- 'lcovonly',
88
- 'none',
89
- 'teamcity',
90
- 'text',
91
- 'text-lcov',
92
- 'text-summary'
93
- ];
77
+ 'clover': 'istanbul',
78
+ 'cobertura': 'istanbul',
79
+ 'html': 'istanbul',
80
+ 'html-spa': 'istanbul',
81
+ 'json': 'istanbul',
82
+ 'json-summary': 'istanbul',
83
+ 'lcov': 'istanbul',
84
+ 'lcovonly': 'istanbul',
85
+ 'none': 'istanbul',
86
+ 'teamcity': 'istanbul',
87
+ 'text': 'istanbul',
88
+ 'text-lcov': 'istanbul',
89
+ 'text-summary': 'istanbul',
90
+
91
+ // both
92
+ 'console-summary': 'both',
93
+ 'raw': 'both'
94
+ };
94
95
 
95
96
  // group v8 and istanbul
96
97
  const reportGroup = {};
97
98
  Object.keys(reportMap).forEach((k) => {
98
- if (!allSupportedReports.includes(k)) {
99
- return;
100
- }
99
+ const options = reportMap[k];
101
100
 
102
- // v8
103
- if (k.startsWith('v8')) {
104
- // v8
105
- if (!reportGroup.v8) {
106
- reportGroup.v8 = {};
107
- }
108
- reportGroup.v8[k] = reportMap[k];
109
- return;
101
+ let type = allBuildInReports[k];
102
+ if (!type) {
103
+ // for custom reporter
104
+ type = options.type || 'v8';
110
105
  }
111
106
 
112
- // both
113
- if (k === 'console-summary') {
114
- reportGroup[k] = reportMap[k];
115
- return;
107
+ let group = reportGroup[type];
108
+ if (!group) {
109
+ group = {};
110
+ reportGroup[type] = group;
116
111
  }
117
112
 
118
- // istanbul
119
- if (!reportGroup.istanbul) {
120
- reportGroup.istanbul = {};
121
- }
122
- reportGroup.istanbul[k] = reportMap[k];
113
+ group[k] = options;
114
+
123
115
  });
124
116
 
117
+ // requires a default istanbul report if data is istanbul
118
+ if (dataType === 'istanbul' && !reportGroup.istanbul) {
119
+ reportGroup.istanbul = {
120
+ html: {}
121
+ };
122
+ }
123
+
125
124
  return reportGroup;
126
125
  };
127
126
 
@@ -154,19 +153,14 @@ const nFormatter = (v) => {
154
153
  return v;
155
154
  };
156
155
 
157
- const showConsoleSummary = (coverageResults, options) => {
156
+ const showConsoleSummary = (reportData, reportOptions, options) => {
158
157
 
159
- const reportGroup = options.reportGroup;
160
- const consoleOptions = reportGroup['console-summary'];
161
- if (!consoleOptions) {
162
- return;
163
- }
164
-
165
- const { metrics } = consoleOptions;
158
+ const { metrics } = reportOptions;
166
159
 
167
160
  const {
168
161
  summary, name, type
169
- } = coverageResults;
162
+ } = reportData;
163
+
170
164
  if (name) {
171
165
  EC.logCyan(name);
172
166
  }
@@ -222,10 +216,36 @@ const showConsoleSummary = (coverageResults, options) => {
222
216
  });
223
217
  };
224
218
 
219
+ const saveRawReport = (reportData, reportOptions, options) => {
220
+ const rawOptions = {
221
+ outputDir: 'raw',
222
+ ... reportOptions
223
+ };
224
+
225
+ const cacheDir = options.cacheDir;
226
+ const rawDir = path.resolve(options.outputDir, rawOptions.outputDir);
227
+ // console.log(rawDir, cacheDir);
228
+ if (fs.existsSync(rawDir)) {
229
+ Util.logError(`Failed to save raw report because the dir already exists: ${Util.relativePath(rawDir)}`);
230
+ return;
231
+ }
232
+
233
+ const rawParent = path.dirname(rawDir);
234
+ if (!fs.existsSync(rawParent)) {
235
+ fs.mkdirSync(rawParent, {
236
+ recursive: true
237
+ });
238
+ }
239
+
240
+ // just rename the cache folder name
241
+ fs.renameSync(cacheDir, rawDir);
242
+
243
+ };
244
+
225
245
 
226
246
  // ========================================================================================================
227
247
 
228
- const getCoverageResults = async (dataList, options) => {
248
+ const getCoverageResults = async (dataList, sourceCache, options) => {
229
249
  // get first and check v8list or istanbul data
230
250
  const firstData = dataList[0];
231
251
  const dataType = firstData.type;
@@ -238,27 +258,50 @@ const getCoverageResults = async (dataList, options) => {
238
258
  // v8list
239
259
  if (dataType === 'v8') {
240
260
  // merge v8list first
241
- const v8list = await mergeV8Coverage(dataList, options);
261
+ const v8list = await mergeV8Coverage(dataList, sourceCache, options);
242
262
  // console.log('after merge', v8list.map((it) => it.url));
243
263
 
244
- const { coverageData, fileSources } = await convertV8List(v8list, options);
245
- return generateV8ListReports(v8list, coverageData, fileSources, options);
264
+ // only debug level
265
+ const time_start = Date.now();
266
+ const results = convertV8List(v8list, options);
267
+ const {
268
+ v8DataList, coverageData, fileSources
269
+ } = results;
270
+ Util.logTime(`converted v8 data: ${v8DataList.length}`, time_start);
246
271
 
272
+ return generateV8ListReports(v8DataList, coverageData, fileSources, options);
247
273
  }
248
274
 
249
275
  // istanbul data
250
- const istanbulData = mergeIstanbulCoverage(dataList, options);
276
+ const istanbulData = mergeIstanbulCoverage(dataList);
251
277
  const { coverageData, fileSources } = initIstanbulData(istanbulData, options);
252
278
  return saveIstanbulReports(coverageData, fileSources, options);
253
279
  };
254
280
 
255
- const generateCoverageReports = async (dataList, options) => {
256
- const coverageResults = await getCoverageResults(dataList, options);
281
+ const generateCoverageReports = async (dataList, sourceCache, options) => {
282
+ const coverageResults = await getCoverageResults(dataList, sourceCache, options);
257
283
 
258
284
  // [ 'type', 'reportPath', 'name', 'watermarks', 'summary', 'files' ]
259
285
  // console.log(Object.keys(coverageResults));
260
286
 
261
- showConsoleSummary(coverageResults, options);
287
+ const buildInBothReports = {
288
+ 'console-summary': showConsoleSummary,
289
+ 'raw': saveRawReport
290
+ };
291
+
292
+ const bothGroup = options.reportGroup.both;
293
+ if (bothGroup) {
294
+ const bothReports = Object.keys(bothGroup);
295
+ for (const reportName of bothReports) {
296
+ const reportOptions = bothGroup[reportName];
297
+ const buildInHandler = buildInBothReports[reportName];
298
+ if (buildInHandler) {
299
+ await buildInHandler(coverageResults, reportOptions, options);
300
+ } else {
301
+ await Util.runCustomReporter(reportName, coverageResults, reportOptions, options);
302
+ }
303
+ }
304
+ }
262
305
 
263
306
  return coverageResults;
264
307
  };
@@ -266,26 +309,45 @@ const generateCoverageReports = async (dataList, options) => {
266
309
  // ========================================================================================================
267
310
 
268
311
 
269
- const getCoverageDataList = async (cacheDir) => {
270
- const files = fs.readdirSync(cacheDir).filter((f) => f.startsWith('coverage-'));
271
- if (!files.length) {
272
- return;
273
- }
274
-
312
+ const getInputData = async (inputList) => {
275
313
  const dataList = [];
276
- for (const item of files) {
277
- const content = await Util.readFile(path.resolve(cacheDir, item));
278
- if (content) {
279
- dataList.push(JSON.parse(content));
314
+ const sourceCache = new Map();
315
+
316
+ const addJsonData = async (dir, filename) => {
317
+ const isCoverage = filename.startsWith('coverage-');
318
+ const isSource = filename.startsWith('source-');
319
+ if (isCoverage || isSource) {
320
+ const content = await Util.readFile(path.resolve(dir, filename));
321
+ if (content) {
322
+ const json = JSON.parse(content);
323
+ if (isCoverage) {
324
+ dataList.push(json);
325
+ } else {
326
+ sourceCache.set(json.id, json);
327
+ }
328
+ }
280
329
  }
281
- }
330
+ };
282
331
 
283
- if (dataList.length) {
284
- return dataList;
332
+ for (const dir of inputList) {
333
+ const allFiles = fs.readdirSync(dir);
334
+ if (!allFiles.length) {
335
+ continue;
336
+ }
337
+ for (const filename of allFiles) {
338
+ // only json file
339
+ if (filename.endsWith('.json')) {
340
+ await addJsonData(dir, filename);
341
+ }
342
+ }
285
343
  }
344
+ return {
345
+ dataList,
346
+ sourceCache
347
+ };
286
348
  };
287
349
 
288
350
  module.exports = {
289
- getCoverageDataList,
351
+ getInputData,
290
352
  generateCoverageReports
291
353
  };
package/lib/index.d.ts CHANGED
@@ -34,8 +34,8 @@ declare namespace MCR {
34
34
  ['v8-json'] | ["v8-json", {
35
35
  outputFile?: string;
36
36
  }] |
37
- ['console-summary'] | ['console-summary', {
38
- metrics?: Array<"bytes" | "functions" | "branches" | "lines" | "statements">;
37
+ ['codecov'] | ["codecov", {
38
+ outputFile?: string;
39
39
  }] |
40
40
  ['clover'] | ['clover', {
41
41
  file?: string;
@@ -88,6 +88,16 @@ declare namespace MCR {
88
88
  }] |
89
89
  ['text-summary'] | ['text-summary', {
90
90
  file?: string;
91
+ }] |
92
+ ['console-summary'] | ['console-summary', {
93
+ metrics?: Array<"bytes" | "functions" | "branches" | "lines" | "statements">;
94
+ }] |
95
+ ['raw'] | ['raw', {
96
+ outputDir?: string;
97
+ }] |
98
+ [string] | [string, {
99
+ type?: "v8" | "istanbul" | "both";
100
+ [key: string]: any;
91
101
  }];
92
102
 
93
103
  export type AddedResults = {
@@ -169,6 +179,9 @@ declare namespace MCR {
169
179
  /** {string} output dir */
170
180
  outputDir?: string;
171
181
 
182
+ /** {string|string[]} input raw dir(s) */
183
+ inputDir?: string | string[];
184
+
172
185
  /** {string} v8 or html for istanbul by default
173
186
  * {array} multiple reports with options
174
187
  * v8 report or istanbul supported reports */
package/lib/index.js CHANGED
@@ -4,7 +4,7 @@ const defaultOptions = require('./default/options.js');
4
4
  const Util = require('./utils/util.js');
5
5
 
6
6
  const { initV8ListAndSourcemap } = require('./v8/v8.js');
7
- const { getCoverageDataList, generateCoverageReports } = require('./generate.js');
7
+ const { getInputData, generateCoverageReports } = require('./generate.js');
8
8
 
9
9
  class CoverageReport {
10
10
 
@@ -41,10 +41,12 @@ class CoverageReport {
41
41
  // add coverage data: {array} V8 format, {object} Istanbul format
42
42
  async add(data) {
43
43
 
44
+ const time_start = Date.now();
45
+
44
46
  this.initOptions();
45
47
 
46
48
  if (!Util.checkCoverageData(data)) {
47
- Util.logError(`The coverage data must be Array(V8) or Object(Istanbul): ${this.options.name}`);
49
+ Util.logError(`${this.options.name}: The added coverage data must be Array(V8) or Object(Istanbul)`);
48
50
  return;
49
51
  }
50
52
 
@@ -69,40 +71,68 @@ class CoverageReport {
69
71
  const dataContent = JSON.stringify(results);
70
72
  await Util.writeFile(dataPath, dataContent);
71
73
 
74
+ Util.logTime(`added coverage data: ${results.type}`, time_start);
75
+
72
76
  return results;
73
77
  }
74
78
 
75
79
  // generate report
76
80
  async generate() {
77
81
 
78
- if (!this.hasCache()) {
79
- Util.logError(`Not found coverage cache: ${this.options.cacheDir}`);
82
+ const time_start = Date.now();
83
+
84
+ this.initOptions();
85
+
86
+ const { inputDir, cacheDir } = this.options;
87
+ const inputDirs = Util.toList(inputDir, ',');
88
+ if (this.hasCache()) {
89
+ inputDirs.push(cacheDir);
90
+ }
91
+
92
+ const inputList = inputDirs.filter((dir) => {
93
+ const hasDir = fs.existsSync(dir);
94
+ if (!hasDir) {
95
+ Util.logError(`Not found coverage data dir: ${Util.relativePath(dir)}`);
96
+ }
97
+ return hasDir;
98
+ });
99
+
100
+ if (!inputList.length) {
80
101
  return;
81
102
  }
82
103
 
83
- const cacheDir = this.options.cacheDir;
84
- const dataList = await getCoverageDataList(cacheDir);
85
- if (!dataList) {
86
- Util.logError(`Not found coverage data in cache dir: ${cacheDir}`);
104
+ const { dataList, sourceCache } = await getInputData(inputList);
105
+ if (!dataList.length) {
106
+ const dirs = inputList.map((dir) => Util.relativePath(dir));
107
+ Util.logError(`Not found coverage data in dir(s): ${dirs.join(', ')}`);
87
108
  return;
88
109
  }
89
110
 
90
111
  // empty output dir except cache dir before generate reports
91
112
  const outputDir = this.options.outputDir;
113
+
114
+ if (fs.existsSync(outputDir)) {
92
115
  // if assets dir is out of output dir will be ignore
93
- fs.readdirSync(outputDir).forEach((itemName) => {
94
- if (itemName === this.cacheDirName) {
95
- return;
96
- }
97
- Util.rmSync(path.resolve(outputDir, itemName));
98
- });
116
+ fs.readdirSync(outputDir).forEach((itemName) => {
117
+ if (itemName === this.cacheDirName) {
118
+ return;
119
+ }
120
+ Util.rmSync(path.resolve(outputDir, itemName));
121
+ });
122
+ } else {
123
+ fs.mkdirSync(outputDir, {
124
+ recursive: true
125
+ });
126
+ }
99
127
 
100
- const coverageResults = await generateCoverageReports(dataList, this.options);
128
+ const coverageResults = await generateCoverageReports(dataList, sourceCache, this.options);
101
129
 
102
130
  if (this.logging !== 'debug') {
103
131
  Util.rmSync(cacheDir);
104
132
  }
105
133
 
134
+ Util.logTime(`generated coverage reports: ${coverageResults.reportPath}`, time_start);
135
+
106
136
  const onEnd = this.options.onEnd;
107
137
  if (typeof onEnd === 'function') {
108
138
  await onEnd(coverageResults);
@@ -52,7 +52,7 @@ const saveIstanbulReports = (coverageData, fileSources, options) => {
52
52
  coverageMap,
53
53
  // The summarizer to default to (may be overridden by some reports)
54
54
  // values can be nested/flat/pkg. Defaults to 'pkg'
55
- defaultSummarizer: options.defaultSummarizer || 'nested',
55
+ defaultSummarizer: options.defaultSummarizer || 'pkg',
56
56
 
57
57
  dir: options.outputDir,
58
58
  watermarks,
@@ -87,9 +87,10 @@ const saveIstanbulReports = (coverageData, fileSources, options) => {
87
87
  // create a context for report generation
88
88
  const context = istanbulLibReport.createContext(contextOptions);
89
89
 
90
- const istanbulMap = options.reportGroup.istanbul;
91
- Object.keys(istanbulMap).forEach((reportName) => {
92
- const report = istanbulReports.create(reportName, istanbulMap[reportName]);
90
+ const istanbulGroup = options.reportGroup.istanbul;
91
+ Object.keys(istanbulGroup).forEach((reportName) => {
92
+ const reportOptions = istanbulGroup[reportName];
93
+ const report = istanbulReports.create(reportName, reportOptions);
93
94
  report.execute(context);
94
95
  });
95
96
 
@@ -115,14 +116,13 @@ const saveIstanbulReports = (coverageData, fileSources, options) => {
115
116
  return coverageResults;
116
117
  };
117
118
 
118
- const mergeIstanbulCoverage = (dataList, options) => {
119
+ const mergeIstanbulCoverage = (dataList) => {
120
+ const istanbulCoverageList = dataList.map((it) => it.data);
119
121
  const coverageMap = istanbulLibCoverage.createCoverageMap();
120
- dataList.forEach((d) => {
121
- coverageMap.merge(d.data);
122
+ istanbulCoverageList.forEach((coverage) => {
123
+ coverageMap.merge(coverage);
122
124
  });
123
-
124
125
  const istanbulData = coverageMap.toJSON();
125
-
126
126
  return istanbulData;
127
127
  };
128
128