monocart-coverage-reports 2.5.3 → 2.5.5

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.
package/README.md CHANGED
@@ -65,10 +65,10 @@ console.log(coverageResults.summary);
65
65
  ## Available Reports
66
66
  > V8 build-in reports (V8 data only):
67
67
  - `v8`
68
- - Browser: Build with webpack [V8](https://cenfun.github.io/monocart-coverage-reports/v8) and [V8 Minify](https://cenfun.github.io/monocart-coverage-reports/v8-minify); Build with [Rollup](https://cenfun.github.io/monocart-coverage-reports/v8-rollup) and [Esbuild](https://cenfun.github.io/monocart-coverage-reports/v8-esbuild); Collect with [puppeteer](https://cenfun.github.io/monocart-coverage-reports/puppeteer/); [anonymous](https://cenfun.github.io/monocart-coverage-reports/anonymous/) and [css](https://cenfun.github.io/monocart-coverage-reports/css/)
69
- - Node.js: Collect with [env](https://cenfun.github.io/monocart-coverage-reports/v8-node-env), and also V8 [API](https://cenfun.github.io/monocart-coverage-reports/v8-node-api), [Inspector](https://cenfun.github.io/monocart-coverage-reports/v8-node-ins) and [CDP](https://cenfun.github.io/monocart-coverage-reports/v8-node-cdp); Web server example: [koa](https://cenfun.github.io/monocart-coverage-reports/v8-node-koa/)
68
+ - Browser: Build with webpack [V8](https://cenfun.github.io/monocart-coverage-reports/v8) and [Minify](https://cenfun.github.io/monocart-coverage-reports/minify); Build with [Rollup](https://cenfun.github.io/monocart-coverage-reports/rollup) and [Esbuild](https://cenfun.github.io/monocart-coverage-reports/esbuild); Collect with [puppeteer](https://cenfun.github.io/monocart-coverage-reports/puppeteer/); [anonymous](https://cenfun.github.io/monocart-coverage-reports/anonymous/) and [css](https://cenfun.github.io/monocart-coverage-reports/css/)
69
+ - Node.js: Collect with [env](https://cenfun.github.io/monocart-coverage-reports/node-env), and also V8 [API](https://cenfun.github.io/monocart-coverage-reports/node-api), [Inspector](https://cenfun.github.io/monocart-coverage-reports/node-ins) and [CDP](https://cenfun.github.io/monocart-coverage-reports/node-cdp); Web server example: [koa](https://cenfun.github.io/monocart-coverage-reports/node-koa/)
70
70
 
71
- ![](test/v8.gif)
71
+ ![](./assets/v8.gif)
72
72
 
73
73
  - `v8-json`
74
74
  - [V8 coverage-report.json](https://cenfun.github.io/monocart-coverage-reports/v8-and-istanbul/coverage-report.json)
@@ -76,7 +76,7 @@ console.log(coverageResults.summary);
76
76
  - coverage data for [Codecov](https://docs.codecov.com/docs/codecov-custom-coverage-format), see [example](https://app.codecov.io/github/cenfun/monocart-coverage-reports)
77
77
  - `console-details` Show file coverage and uncovered lines in the console. Like `text`, but for V8. For Github actions, we can enforce color with env: `FORCE_COLOR: true`.
78
78
 
79
- ![](test/console-details.png)
79
+ ![](./assets/console-details.png)
80
80
 
81
81
  > Istanbul build-in reports (both V8 and istanbul data):
82
82
  - `clover`
@@ -101,7 +101,7 @@ console.log(coverageResults.summary);
101
101
  > Other reports:
102
102
  - `console-summary` shows coverage summary in the console
103
103
 
104
- ![](test/console-summary.png)
104
+ ![](./assets/console-summary.png)
105
105
 
106
106
  - `raw` only keep all original data, which can be used for other reports input with `inputDir`
107
107
  - see [Merge Coverage Reports](#merge-coverage-reports)
@@ -597,32 +597,12 @@ const a = tf ? 'true' : 'false';
597
597
  ```
598
598
  > `m1` and `m2` are two consecutive mappings, `p` is the position we looking for. However, we can only get the position of the `m1` or `m2` if we don't fix it to `p`. Especially the generated code is different from the original code, such as the code was minified, compressed or converted, it is difficult to find the exact position.
599
599
 
600
- - 2, The coverage of functions and branches is incorrect. V8 only provided coverage at functions and it's blocks. But if a function is uncovered (count = 0), there is no information for it's blocks and sub-level functions.
601
- And also there are some problems about counting the functions and branches:
602
- ```js
603
- // Problem: When the function or parent-level function is uncovered, then its sub-level functions will never be counted.
604
- functions.forEach(block => {
605
- block.ranges.forEach((range, i) => {
606
- if (block.isBlockCoverage) {
607
- // v8-to-istanbul: new CovBranch()
608
- // Problem: not every block is branch, and the first block could be function.
609
- if (block.functionName && i === 0) {
610
- // v8-to-istanbul: new CovFunction()
611
- // Problem: no anonymous function
612
- }
613
- } else if (block.functionName) {
614
- // v8-to-istanbul: new CovFunction()
615
- // Problem: no anonymous function
616
- }
617
- }
618
- });
619
- ```
620
- see source code [v8-to-istanbul.js](https://github.com/istanbuljs/v8-to-istanbul/blob/master/lib/v8-to-istanbul.js)
600
+ - 2, The coverage of functions and branches is incorrect. V8 only provided coverage at functions and it's blocks. But if a function is uncovered (count = 0), there is no information for it's blocks and sub-level functions. And also there are some problems about counting the functions and branches.
621
601
 
622
602
  ### How Monocart Works
623
- We implemented new converter instead of v8-to-istanbul:
603
+ We implemented new converter:
624
604
  - 1, Trying to fix the middle position if not found the exact mapping for the position.
625
- - 2, Finding all functions, statements and branches by parsing the source code [AST](https://github.com/acornjs/acorn), however the V8 cannot provide effective branch coverage information for `AssignmentPattern`.
605
+ - 2, Finding all functions, statements and branches by parsing the source code [AST](https://github.com/acornjs/acorn). However, there's a small issue, which is the V8 cannot provide effective branch coverage information for `AssignmentPattern`.
626
606
 
627
607
  | AST | V8 |
628
608
  | :---------------------| :------------- |
@@ -645,11 +625,11 @@ const coverageOptions = {
645
625
  };
646
626
  ```
647
627
  When `logging` is `debug`, the raw report data will be preserved in `[outputDir]/.cache` or `[outputDir]/raw` if `raw` report is used. And the dist file will be preserved in the V8 list, and by opening the browser's devtool, it makes data verification visualization effortless.
648
- ![](./test/debug-coverage.png)
628
+ ![](./assets/debug-coverage.png)
649
629
 
650
630
  - Check sourcemap with [Source Map Visualization](https://evanw.github.io/source-map-visualization/)
651
631
 
652
- ![](./test/debug-sourcemap.png)
632
+ ![](./assets/debug-sourcemap.png)
653
633
 
654
634
  ## Integration
655
635
  ### [Playwright](https://github.com/microsoft/playwright)
@@ -148,7 +148,9 @@ const getFakeAstInfo = (coverageList) => {
148
148
 
149
149
  const getJsAstInfo = (item, coverageList) => {
150
150
 
151
- const { source, fake } = item;
151
+ const {
152
+ source, fake, empty
153
+ } = item;
152
154
 
153
155
  if (fake) {
154
156
  return getFakeAstInfo(coverageList);
@@ -169,7 +171,11 @@ const getJsAstInfo = (item, coverageList) => {
169
171
  try {
170
172
  ast = acorn.parse(source, options);
171
173
  } catch (e) {
172
- Util.logInfo(`failed to parse source file: ${item.sourcePath} ${e.message}`);
174
+
175
+ // empty = untested file, could be .ts, .vue and so on, that can not be parsed normally
176
+ if (!empty) {
177
+ Util.logInfo(`failed to parse source file: ${item.sourcePath} ${e.message}`);
178
+ }
173
179
  // https://github.com/acornjs/acorn/tree/master/acorn-loose
174
180
  // It is recommended to always try a parse with the regular acorn parser first,
175
181
  // and only fall back to this parser when that one finds syntax errors.
@@ -208,18 +214,20 @@ const addRule = (item, coverageList, coveredRanges, count = 0) => {
208
214
 
209
215
  };
210
216
 
211
- const addAtRule = (item, coverageList, coveredRanges) => {
217
+ const addAtRule = (item, coverageList, coveredRanges, empty) => {
212
218
  const { name, nodes } = item;
213
219
 
220
+ const defaultCount = empty ? 0 : 1;
221
+
214
222
  if (['charset', 'import', 'namespace'].includes(name)) {
215
- addRule(item, coverageList, coveredRanges, 1);
223
+ addRule(item, coverageList, coveredRanges, defaultCount);
216
224
  return;
217
225
  }
218
226
 
219
227
  if (['media', 'supports', 'container', 'layer'].includes(name)) {
220
228
 
221
229
  const childCoverageList = [];
222
- const count = addCssRules(nodes, childCoverageList, coveredRanges);
230
+ const count = addCssRules(nodes, childCoverageList, coveredRanges, empty);
223
231
  if (count) {
224
232
 
225
233
  coverageList.push({
@@ -249,7 +257,7 @@ const addAtRule = (item, coverageList, coveredRanges) => {
249
257
 
250
258
  };
251
259
 
252
- const addCssRules = (list, coverageList, coveredRanges) => {
260
+ const addCssRules = (list, coverageList, coveredRanges, empty) => {
253
261
 
254
262
  let count = 0;
255
263
 
@@ -287,7 +295,7 @@ const addCssRules = (list, coverageList, coveredRanges) => {
287
295
  // });
288
296
 
289
297
  if (type === 'atrule') {
290
- addAtRule(item, coverageList, coveredRanges);
298
+ addAtRule(item, coverageList, coveredRanges, empty);
291
299
  }
292
300
 
293
301
  });
@@ -298,7 +306,7 @@ const addCssRules = (list, coverageList, coveredRanges) => {
298
306
  const getCssAstInfo = (item, coverageList) => {
299
307
 
300
308
  const {
301
- source, sourcePath, ranges
309
+ source, ranges, empty
302
310
  } = item;
303
311
 
304
312
  // sort covered ranges
@@ -312,13 +320,9 @@ const getCssAstInfo = (item, coverageList) => {
312
320
  };
313
321
  });
314
322
 
315
- const time_start_parse = new Date();
316
323
  const ast = parseCss(source);
317
- Util.logTime(`parse css: ${sourcePath}`, time_start_parse);
318
324
 
319
- // if (sourcePath.endsWith('style.css')) {
320
- addCssRules(ast.nodes, coverageList, coveredRanges);
321
- // }
325
+ addCssRules(ast.nodes, coverageList, coveredRanges, empty);
322
326
 
323
327
  return {
324
328
  functions: [],
@@ -1,4 +1,5 @@
1
1
  const fs = require('fs');
2
+ const path = require('path');
2
3
  const EC = require('eight-colors');
3
4
  const { fileURLToPath, pathToFileURL } = require('url');
4
5
  const Concurrency = require('../platform/concurrency.js');
@@ -66,16 +67,49 @@ const getSourceMapUrl = (content, url) => {
66
67
  }
67
68
  };
68
69
 
69
- const resolveSourceMap = (data) => {
70
- if (data) {
71
- const {
72
- sources, sourcesContent, mappings
73
- } = data;
74
- if (!sources || !sourcesContent || !mappings) {
75
- return;
70
+ const resolveSourcesContent = async (sources, url) => {
71
+ const list = [];
72
+ for (const file of sources) {
73
+ const sourceUrl = Util.resolveUrl(file, url);
74
+ if (sourceUrl) {
75
+ const sourcePath = sourceUrl.toString();
76
+ if (sourcePath.startsWith('file:')) {
77
+ const content = await Util.readFile(path.resolve(fileURLToPath(sourcePath)));
78
+ list.push(content);
79
+ continue;
80
+ }
76
81
  }
82
+ Util.logDebug(EC.red(`failed to load source content ${file}`));
83
+ }
84
+ if (list.length === sources.length) {
85
+ return list;
86
+ }
87
+ };
88
+
89
+ const resolveSourceMap = async (data, url) => {
90
+ if (!data) {
91
+ return;
92
+ }
93
+ const {
94
+ sources, sourcesContent, mappings
95
+ } = data;
96
+
97
+ if (!sources || !mappings) {
98
+ return;
99
+ }
100
+
101
+ // exists sources content
102
+ if (sourcesContent && sourcesContent.length === sources.length) {
77
103
  return data;
78
104
  }
105
+
106
+ // load sources content by sources
107
+ const contents = await resolveSourcesContent(sources, url);
108
+ if (contents) {
109
+ data.sourcesContent = contents;
110
+ return data;
111
+ }
112
+
79
113
  };
80
114
 
81
115
  const saveSourceFile = async (filePath, data) => {
@@ -92,7 +126,7 @@ const saveSourceFile = async (filePath, data) => {
92
126
 
93
127
  const collectSourceMaps = async (v8list, options) => {
94
128
 
95
- let count = 0;
129
+ const smList = [];
96
130
  const concurrency = new Concurrency();
97
131
  for (const item of v8list) {
98
132
 
@@ -130,8 +164,11 @@ const collectSourceMaps = async (v8list, options) => {
130
164
  // from inline sync
131
165
  const smc = convertSourceMap.fromSource(source);
132
166
  if (smc) {
133
- sourceData.sourceMap = resolveSourceMap(smc.sourcemap);
134
- count += 1;
167
+ sourceData.sourceMap = await resolveSourceMap(smc.sourcemap, url);
168
+ smList.push({
169
+ url,
170
+ inline: true
171
+ });
135
172
  await saveSourceFile(sourcePath, sourceData);
136
173
  continue;
137
174
  }
@@ -154,16 +191,20 @@ const collectSourceMaps = async (v8list, options) => {
154
191
 
155
192
  // from url concurrency
156
193
  await concurrency.start(async (item) => {
157
- const sourceData = item.sourceData;
158
- const data = await loadSourceMap(item.sourceMapUrl);
194
+ const { sourceMapUrl, sourceData } = item;
195
+ const { url } = sourceData;
196
+ const data = await loadSourceMap(sourceMapUrl);
159
197
  if (data) {
160
- sourceData.sourceMap = resolveSourceMap(data);
161
- count += 1;
198
+ sourceData.sourceMap = await resolveSourceMap(data, url);
199
+ smList.push({
200
+ url,
201
+ sourceMapUrl
202
+ });
162
203
  }
163
204
  await saveSourceFile(item.sourcePath, sourceData);
164
205
  });
165
206
 
166
- return count;
207
+ return smList;
167
208
 
168
209
  };
169
210
 
@@ -1300,6 +1300,7 @@ const convertV8List = (v8list, options) => {
1300
1300
  }
1301
1301
 
1302
1302
  // ============================
1303
+ const time_start_ast = Date.now();
1303
1304
  // move functions and ranges to coverageList
1304
1305
  let coverageList = [];
1305
1306
  let astInfo;
@@ -1318,6 +1319,7 @@ const convertV8List = (v8list, options) => {
1318
1319
  delete item.ranges;
1319
1320
  }
1320
1321
  }
1322
+ Util.logTime(`- parsed ast: ${sourcePath}`, time_start_ast);
1321
1323
 
1322
1324
  // console.log(sourcePath, astInfo.statements.length);
1323
1325
  // ============================
@@ -1345,14 +1347,19 @@ const convertV8List = (v8list, options) => {
1345
1347
  v8Data: item
1346
1348
  };
1347
1349
 
1350
+ const time_start_unpack = Date.now();
1348
1351
  unpackDistFile(item, state, options);
1352
+ Util.logTime(`- unpacked dist: ${sourcePath}`, time_start_unpack);
1349
1353
 
1350
1354
  stateList.push(state);
1351
1355
 
1352
1356
  }
1353
1357
 
1354
- return generateV8DataList(stateList, options);
1358
+ const time_start_convert = Date.now();
1359
+ const dataList = generateV8DataList(stateList, options);
1360
+ Util.logTime('- generated v8 data list', time_start_convert);
1355
1361
 
1362
+ return dataList;
1356
1363
  };
1357
1364
 
1358
1365
  module.exports = {
package/lib/generate.js CHANGED
@@ -247,7 +247,7 @@ const getCoverageResults = async (dataList, sourceCache, options) => {
247
247
  const {
248
248
  v8DataList, coverageData, fileSources
249
249
  } = results;
250
- Util.logTime(`converted v8 data: ${v8DataList.length}`, time_start);
250
+ Util.logTime(`converted v8 list: ${v8DataList.length}`, time_start);
251
251
 
252
252
  return generateV8ListReports(v8DataList, coverageData, fileSources, options);
253
253
  }