monocart-coverage-reports 2.7.5 → 2.7.7

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/lib/cli.js CHANGED
@@ -1,6 +1,5 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const fs = require('fs');
4
3
  const path = require('path');
5
4
  const EC = require('eight-colors');
6
5
 
@@ -22,25 +21,12 @@ const getRegisterPath = (filename) => {
22
21
  return `./${rel}`;
23
22
  };
24
23
 
25
- const isFeatureSupported = (nv) => {
26
- // module register added in Node.js: v20.6.0
27
- const [major, minor] = nv.split('.').map((s) => parseInt(s));
28
- if (major < 20) {
29
- return false;
30
- }
31
- if (major > 20) {
32
- return true;
33
- }
34
- if (minor < 6) {
35
- return false;
36
- }
37
- return true;
38
- };
39
-
40
24
  const initNodeOptions = async (cliOptions) => {
25
+
26
+ // module register added in Node.js: v20.6.0
41
27
  const nv = process.versions.node;
42
- if (!isFeatureSupported(nv)) {
43
- Util.logInfo(EC.yellow(`The current Node.js version "${nv}" does NOT support "module.register", it requires "20.6.0" or higher.`));
28
+ if (Util.cmpVersion(nv, '20.6.0') < 0) {
29
+ Util.logInfo(`The current Node.js version "${nv}" does NOT support "module.register", it requires "20.6.0" or higher.`);
44
30
  return;
45
31
  }
46
32
 
@@ -133,11 +119,6 @@ const executeCommand = async (command, cliOptions) => {
133
119
  }
134
120
  // =========================================
135
121
 
136
- if (!fs.existsSync(nodeV8CoverageDir)) {
137
- EC.logRed(`Invalid V8 coverage dir: ${Util.relativePath(nodeV8CoverageDir)}`);
138
- return 1;
139
- }
140
-
141
122
  await coverageReport.addFromDir(nodeV8CoverageDir);
142
123
  await coverageReport.generate();
143
124
 
@@ -155,14 +136,29 @@ process.on('uncaughtException', function(err) {
155
136
  Util.logError(`Process uncaughtException: ${err.message || err}`);
156
137
  });
157
138
 
139
+ // the -- separator
140
+ const argv = [];
141
+ const subArgv = [];
142
+ let separator = false;
143
+ process.argv.forEach((it) => {
144
+ if (!separator && it === '--') {
145
+ separator = true;
146
+ }
147
+ if (separator) {
148
+ subArgv.push(it);
149
+ } else {
150
+ argv.push(it);
151
+ }
152
+ });
153
+
158
154
  program
159
155
  .name('mcr')
160
156
  .description('CLI to generate coverage reports')
161
157
  .version(version)
162
- .argument('<command>', 'command to execute')
158
+ .argument('[command]', 'command to execute')
163
159
  .allowUnknownOption()
164
160
  .option('-c, --config <path>', 'custom config file path')
165
- .option('--logging <logging>', 'off, error, info, debug')
161
+ .option('-l, --logging <logging>', 'off, error, info, debug')
166
162
 
167
163
  .option('-n, --name <name>', 'report name for title')
168
164
  .option('-r, --reports <name[,name]>', 'coverage reports to use')
@@ -183,8 +179,17 @@ program
183
179
  .option('--require <module>', 'preload module at startup')
184
180
 
185
181
  .action((_command, cliOptions) => {
186
- const command = program.args.join(' ');
182
+ const args = [].concat(program.args).concat(subArgv);
183
+ if (args[0] === '--') {
184
+ args.shift();
185
+ }
186
+ const command = args.join(' ').trim();
187
+ if (!command) {
188
+ program.outputHelp();
189
+ return;
190
+ }
191
+
187
192
  executeCommand(command, cliOptions);
188
193
  });
189
194
 
190
- program.parse();
195
+ program.parse(argv);
@@ -4,7 +4,7 @@ const EC = require('eight-colors');
4
4
  const { WebSocket } = require('../packages/monocart-coverage-vendor.js');
5
5
  const Util = require('../utils/util.js');
6
6
 
7
- const CDPSession = require('./cdp-session.js');
7
+ const WSSession = require('./ws-session.js');
8
8
  const CoverageClient = require('./coverage-client.js');
9
9
 
10
10
  const getDebuggerUrl = async (options) => {
@@ -85,7 +85,7 @@ const getCDPSession = async (options) => {
85
85
  ws.once('open', () => {
86
86
  clearTimeout(timeoutId);
87
87
  Util.logDebug(`${EC.green('Connected')} ${url}`);
88
- const session = new CDPSession(ws);
88
+ const session = new WSSession(ws);
89
89
  resolve([null, session]);
90
90
  });
91
91
 
@@ -131,19 +131,22 @@ class CoverageClient {
131
131
  unbindEvents(this.session, this.jsEvents);
132
132
  this.jsEvents = null;
133
133
 
134
- const jsCoverage = profileResponse.result;
135
- // console.log('coverageList', coverageList);
134
+ const jsCoverage = [];
136
135
 
137
- jsCoverage.forEach((entry) => {
138
- // anonymous url
139
- entry.url = entry.url || '';
140
- // add source
141
- const source = this.scriptSources.get(entry.scriptId);
142
- if (!source) {
143
- Util.logDebug(`Not found js source: ${entry.url}`);
144
- }
145
- entry.source = source || '';
146
- });
136
+ // console.log('coverageList', coverageList);
137
+ if (profileResponse && profileResponse.result) {
138
+ profileResponse.result.forEach((entry) => {
139
+ // anonymous url
140
+ entry.url = entry.url || '';
141
+ // add source
142
+ const source = this.scriptSources.get(entry.scriptId);
143
+ if (!source) {
144
+ Util.logDebug(`Not found js source: ${entry.url}`);
145
+ }
146
+ entry.source = source || '';
147
+ jsCoverage.push(entry);
148
+ });
149
+ }
147
150
 
148
151
  this.scriptSources.clear();
149
152
  this.enabledJS = false;
@@ -239,22 +242,39 @@ class CoverageClient {
239
242
  }
240
243
 
241
244
  async stopCoverage() {
245
+ if (!this.session) {
246
+ return;
247
+ }
242
248
  const [jsCoverage, cssCoverage] = await Promise.all([
243
249
  this.stopJSCoverage(),
244
250
  this.stopCSSCoverage()
245
251
  ]);
246
- return [... jsCoverage, ... cssCoverage];
252
+ // could be undefined
253
+ let coverageList = [];
254
+ if (jsCoverage) {
255
+ coverageList = coverageList.concat(jsCoverage);
256
+ }
257
+ if (cssCoverage) {
258
+ coverageList = coverageList.concat(cssCoverage);
259
+ }
260
+ return coverageList;
247
261
  }
248
262
 
249
263
  // =================================================================================================
250
264
  // write the coverage started by NODE_V8_COVERAGE to disk on demand
251
265
  async writeCoverage() {
252
- // // enable and start v8 coverage
266
+ if (!this.session) {
267
+ return;
268
+ }
269
+
253
270
  await this.session.send('Runtime.enable');
254
271
 
255
- // write the coverage started by NODE_V8_COVERAGE to disk on demand
272
+ // write the coverage started by NODE_V8_COVERAGE to disk on demand
256
273
  const res = await this.session.send('Runtime.evaluate', {
257
- expression: 'new Promise(resolve=>{ require("v8").takeCoverage(); resolve(process.env.NODE_V8_COVERAGE); })',
274
+ expression: `new Promise((resolve) => {
275
+ require("v8").takeCoverage();
276
+ resolve(process.env.NODE_V8_COVERAGE);
277
+ })`,
258
278
  includeCommandLineAPI: true,
259
279
  returnByValue: true,
260
280
  awaitPromise: true
@@ -262,7 +282,31 @@ class CoverageClient {
262
282
 
263
283
  await this.session.send('Runtime.disable');
264
284
 
265
- return res.result.value;
285
+ return res && res.result && res.result.value;
286
+ }
287
+
288
+ // get istanbul coverage data
289
+ async getIstanbulCoverage(coverageKey = '__coverage__') {
290
+ if (!this.session) {
291
+ return;
292
+ }
293
+
294
+ await this.session.send('Runtime.enable');
295
+
296
+ // both browser and Node.js
297
+ const res = await this.session.send('Runtime.evaluate', {
298
+ expression: `new Promise((resolve) => {
299
+ const globalTarget = typeof window !== 'undefined' ? window : global;
300
+ resolve(globalTarget['${coverageKey}']);
301
+ })`,
302
+ includeCommandLineAPI: true,
303
+ returnByValue: true,
304
+ awaitPromise: true
305
+ });
306
+
307
+ await this.session.send('Runtime.disable');
308
+
309
+ return res && res.result && res.result.value;
266
310
  }
267
311
 
268
312
  // =================================================================================================
@@ -270,10 +314,8 @@ class CoverageClient {
270
314
  if (!this.session) {
271
315
  return;
272
316
  }
273
-
274
317
  await this.session.detach();
275
318
  this.session = null;
276
-
277
319
  }
278
320
  }
279
321
 
@@ -1,6 +1,6 @@
1
1
  const { EventEmitter } = require('events');
2
2
 
3
- class CDPSession extends EventEmitter {
3
+ class WSSession extends EventEmitter {
4
4
  constructor(ws) {
5
5
  super();
6
6
  this.ws = ws;
@@ -60,4 +60,4 @@ class CDPSession extends EventEmitter {
60
60
  }
61
61
  }
62
62
 
63
- module.exports = CDPSession;
63
+ module.exports = WSSession;
@@ -3,10 +3,12 @@
3
3
  * @copyright https://github.com/cenfun/monocart-coverage-reports
4
4
  * @author cenfun@gmail.com
5
5
  */
6
- const Util = require('../utils/util.js');
7
6
 
8
- // position mapping for conversion between offset and line/column
7
+ const EC = require('eight-colors');
9
8
  const { Locator } = require('monocart-formatter');
9
+
10
+ const Util = require('../utils/util.js');
11
+
10
12
  const findOriginalRange = require('./find-original-range.js');
11
13
  const { getJsAstInfo, getCssAstInfo } = require('./ast.js');
12
14
  const { getIgnoredRanges } = require('./ignore.js');
@@ -1459,7 +1461,7 @@ const convertV8List = (v8list, options) => {
1459
1461
  delete item.ranges;
1460
1462
  }
1461
1463
  }
1462
- Util.logTime(`- parsed ast: ${sourcePath}`, time_start_ast);
1464
+ Util.logTime(`- parsed ast: ${sourcePath} (${EC.cyan(Util.BSF(maxContentLength))})`, time_start_ast);
1463
1465
 
1464
1466
  // console.log(sourcePath, astInfo.statements.length);
1465
1467
  // ============================
@@ -1476,6 +1478,7 @@ const convertV8List = (v8list, options) => {
1476
1478
  locator,
1477
1479
  maxContentLength,
1478
1480
  decodedMappings: [],
1481
+ cacheMap: new Map(),
1479
1482
  // coverage info
1480
1483
  bytes: [],
1481
1484
  functions: [],
@@ -1491,7 +1494,8 @@ const convertV8List = (v8list, options) => {
1491
1494
 
1492
1495
  const time_start_unpack = Date.now();
1493
1496
  unpackDistFile(item, state, options);
1494
- Util.logTime(`- unpacked dist: ${sourcePath}`, time_start_unpack);
1497
+ const unpackedFiles = EC.cyan(`${state.originalList.length} files`);
1498
+ Util.logTime(`- unpacked dist: ${sourcePath} (${unpackedFiles})`, time_start_unpack);
1495
1499
 
1496
1500
  stateList.push(state);
1497
1501
 
@@ -1499,7 +1503,8 @@ const convertV8List = (v8list, options) => {
1499
1503
 
1500
1504
  const time_start_convert = Date.now();
1501
1505
  const dataList = generateV8DataList(stateList, options);
1502
- Util.logTime('- generated v8 data list', time_start_convert);
1506
+ const dataFiles = EC.cyan(`${dataList.v8DataList.length} files`);
1507
+ Util.logTime(`- generated data list (${dataFiles})`, time_start_convert);
1503
1508
 
1504
1509
  return dataList;
1505
1510
  };
@@ -820,16 +820,26 @@ const getMappingInfo = (start, end, state) => {
820
820
 
821
821
  const findOriginalRange = (start, end, state, originalMap) => {
822
822
 
823
- const { sourcePath } = state;
823
+ const { sourcePath, cacheMap } = state;
824
+
825
+ const key = `${start}_${end}`;
826
+ if (cacheMap.has(key)) {
827
+ return cacheMap.get(key);
828
+ }
824
829
 
825
830
  const createMappingError = (errors) => {
826
- return {
831
+ const res = {
827
832
  error: true,
828
833
  start,
829
834
  end,
830
835
  sourcePath,
831
836
  errors
832
837
  };
838
+
839
+ // cache error response
840
+ cacheMap.set(key, res);
841
+
842
+ return res;
833
843
  };
834
844
 
835
845
  const cache = getOriginalState(start, end, state, originalMap);
@@ -852,12 +862,23 @@ const findOriginalRange = (start, end, state, originalMap) => {
852
862
 
853
863
  const { originalState } = cache;
854
864
 
855
- return {
865
+ const inComment = originalState.locator.lineParser.commentParser.isComment(originalStart, originalEnd);
866
+ if (inComment) {
867
+ return createMappingError(['the range in a original comment']);
868
+ }
869
+
870
+
871
+ const res = {
856
872
  start: originalStart,
857
873
  end: originalEnd,
858
874
  originalState
859
875
  };
860
876
 
877
+ // cache response
878
+ cacheMap.set(key, res);
879
+
880
+ return res;
881
+
861
882
  };
862
883
 
863
884
  module.exports = findOriginalRange;
package/lib/generate.js CHANGED
@@ -21,7 +21,7 @@ const { getGroupedRows } = require('./utils/snapshot.js');
21
21
 
22
22
  // maybe v8 or to istanbul reports
23
23
  const generateV8ListReports = async (v8list, coverageData, fileSources, options) => {
24
-
24
+ const time_start = Date.now();
25
25
  let istanbulReportPath;
26
26
  // v8 to istanbul reports
27
27
  if (options.reportGroup.istanbul) {
@@ -32,7 +32,7 @@ const generateV8ListReports = async (v8list, coverageData, fileSources, options)
32
32
  // v8 reports and v8 coverage results
33
33
  // could be no v8 or v8-json, but requires v8 coverage results
34
34
  const v8CoverageResults = await saveV8Report(v8list, options, istanbulReportPath);
35
-
35
+ Util.logTime('- saved coverage reports', time_start);
36
36
  return v8CoverageResults;
37
37
  };
38
38
 
@@ -436,15 +436,10 @@ const getCoverageResults = async (dataList, sourceCache, options) => {
436
436
  // merge v8list first
437
437
  const v8list = await mergeV8Coverage(dataList, sourceCache, options);
438
438
  // console.log('after merge', v8list.map((it) => it.url));
439
-
440
- // only debug level
441
- const time_start = Date.now();
442
439
  const results = convertV8List(v8list, options);
443
440
  const {
444
441
  v8DataList, coverageData, fileSources
445
442
  } = results;
446
- Util.logTime(`converted v8 list: ${v8DataList.length}`, time_start);
447
-
448
443
  return generateV8ListReports(v8DataList, coverageData, fileSources, options);
449
444
  }
450
445
 
@@ -806,7 +801,7 @@ const readCoverageData = (filePath, entryFilter, sourceCache) => {
806
801
  const readFromDir = async (dir, entryFilter, addHandler) => {
807
802
 
808
803
  if (!dir || !fs.existsSync(dir)) {
809
- Util.logError(`Invalid V8 coverage dir: ${dir}`);
804
+ Util.logInfo(`Not found V8 coverage dir: ${dir}`);
810
805
  return;
811
806
  }
812
807
 
package/lib/index.d.ts CHANGED
@@ -119,7 +119,7 @@ declare namespace MCR {
119
119
  outputDir?: string;
120
120
  }] |
121
121
  [string] | [string, {
122
- type?: "v8" | "istanbul" | "both";
122
+ type?: "v8" | "istanbul" | "both" | string;
123
123
  [key: string]: any;
124
124
  }];
125
125
 
@@ -192,7 +192,7 @@ declare namespace MCR {
192
192
  ignores?: IgnoredRange[];
193
193
  /** codecov json format */
194
194
  lines: {
195
- [key: string]: number | string
195
+ [key: string]: number | string;
196
196
  };
197
197
  }
198
198
  }
@@ -287,7 +287,7 @@ declare namespace MCR {
287
287
  */
288
288
  filter?: string | {
289
289
  [pattern: string]: "js" | "css" | boolean;
290
- } | ((filePath: string) => "js" | "css" | boolean)
290
+ } | ((filePath: string) => "js" | "css" | boolean);
291
291
  };
292
292
 
293
293
  /** (V8 only) {boolean} Enable/Disable ignoring uncovered codes with the special comments: v8 ignore next/next N/start/stop */
@@ -320,6 +320,7 @@ declare namespace MCR {
320
320
  /** {function} onEnd hook */
321
321
  onEnd?: (coverageResults: CoverageResults) => Promise<void>;
322
322
 
323
+ [key: string]: any;
323
324
  }
324
325
 
325
326
  export interface McrCliOptions extends CoverageReportOptions {
@@ -441,15 +442,27 @@ declare namespace MCR {
441
442
  stopCoverage: () => Promise<V8CoverageEntry[]>;
442
443
 
443
444
  /** write the coverage started by NODE_V8_COVERAGE to disk on demand, returns v8 coverage dir */
444
- writeCoverage: () => Promise<string>
445
+ writeCoverage: () => Promise<string>;
445
446
 
447
+ /** get istanbul coverage data
448
+ * @param coverageKey defaults to `__coverage__`
449
+ */
450
+ getIstanbulCoverage: (coverageKey?: string) => Promise<any>;
446
451
  }
447
452
 
448
453
  /** Adapt to the CDPSession of Playwright or Puppeteer */
449
454
  export interface CDPSession {
450
455
  send: (method: string, params?: any) => Promise<any>;
451
456
  on: (type: string, handler: (e: any) => void) => void;
452
- detach: () => Promise<void>
457
+ detach: () => Promise<void>;
458
+ }
459
+
460
+ /** custom websocket CDPSession */
461
+ export class WSSession implements CDPSession {
462
+ constructor(ws: any);
463
+ send: (method: string, params?: any) => Promise<any>;
464
+ on: (type: string, handler: (e: any) => void) => void;
465
+ detach: () => Promise<void>;
453
466
  }
454
467
 
455
468
  export interface CDPOptions {
@@ -464,7 +477,7 @@ declare namespace MCR {
464
477
  /** enable https, defaults to false (http) */
465
478
  secure?: boolean;
466
479
  /** target filter, defaults to first page */
467
- target?: (targets) => any;
480
+ target?: (targets: any) => any;
468
481
  /** websocket options */
469
482
  ws?: any;
470
483
  /** timeout, defaults to 10 * 1000 */
package/lib/index.js CHANGED
@@ -11,6 +11,8 @@ const {
11
11
  getInputData, readFromDir, generateCoverageReports
12
12
  } = require('./generate.js');
13
13
  const { getSnapshot, diffSnapshot } = require('./utils/snapshot.js');
14
+
15
+ const WSSession = require('./client/ws-session.js');
14
16
  const CDPClient = require('./client/cdp-client.js');
15
17
 
16
18
  const resolveConfigOptions = async (configPath) => {
@@ -181,7 +183,7 @@ class CoverageReport {
181
183
  Util.rmSync(cacheDir);
182
184
  }
183
185
 
184
- Util.logTime(`generated coverage reports: ${coverageResults.reportPath}`, time_start);
186
+ Util.logTime(`generated coverage reports: ${EC.cyan(coverageResults.reportPath)}`, time_start);
185
187
 
186
188
  const onEnd = this.options.onEnd;
187
189
  if (typeof onEnd === 'function') {
@@ -286,6 +288,7 @@ const MCR = function(options) {
286
288
  MCR.CoverageReport = CoverageReport;
287
289
  MCR.getSnapshot = getSnapshot;
288
290
  MCR.diffSnapshot = diffSnapshot;
291
+ MCR.WSSession = WSSession;
289
292
  MCR.CDPClient = CDPClient;
290
293
 
291
294
  module.exports = MCR;
package/lib/index.mjs CHANGED
@@ -3,4 +3,6 @@ export default MCR;
3
3
  export const CoverageReport = MCR.CoverageReport;
4
4
  export const getSnapshot = MCR.getSnapshot;
5
5
  export const diffSnapshot = MCR.diffSnapshot;
6
+
7
+ export const WSSession = MCR.WSSession;
6
8
  export const CDPClient = MCR.CDPClient;