monocart-coverage-reports 2.5.5 → 2.5.6

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
@@ -125,7 +125,7 @@ console.log(coverageResults.summary);
125
125
  }
126
126
  ```
127
127
  - istanbul custom reporter
128
- > example: [./test/custom-istanbul-reporter.js](./test/custom-istanbul-reporter.js), see [istanbul built-in reporters' implementation](https://github.com/istanbuljs/istanbuljs/tree/master/packages/istanbul-reports/lib) for reference,
128
+ > example: [./test/custom-istanbul-reporter.js](./test/custom-istanbul-reporter.js), see [istanbul built-in reporters' implementation](https://github.com/istanbuljs/istanbuljs/tree/master/packages/istanbul-reports/lib) for reference.
129
129
  - v8 custom reporter
130
130
  > example: [./test/custom-v8-reporter.js](./test/custom-v8-reporter.js)
131
131
 
@@ -183,13 +183,20 @@ We can use `sourceFilter` to filter the source files. For example, we should rem
183
183
  > src/index.js
184
184
  > src/components/app.js
185
185
  ```
186
- Example:
186
+ For example:
187
187
  ```js
188
188
  const coverageOptions = {
189
189
  entryFilter: (entry) => entry.url.indexOf("main.js") !== -1,
190
190
  sourceFilter: (sourcePath) => sourcePath.search(/src\//) !== -1
191
191
  };
192
192
  ```
193
+ Or `minimatch` pattern:
194
+ ```js
195
+ const coverageOptions = {
196
+ entryFilter: "**/main.js",
197
+ sourceFilter: "**/src/**"
198
+ };
199
+ ```
193
200
 
194
201
  ## onEnd Hook
195
202
  For example, checking thresholds:
@@ -245,19 +252,29 @@ Arguments:
245
252
 
246
253
  Options:
247
254
  -V, --version output the version number
248
- -c, --config <path> config path for options
255
+ -c, --config <path> custom config path
249
256
  -o, --outputDir <dir> output dir for reports
250
257
  -r, --reports <name[,name]> coverage reports to use
251
258
  -n, --name <name> report name for title
259
+ -i, --inputDir <dir> input dir for merging raw files
260
+ --entryFilter <pattern> entry url filter
261
+ --sourceFilter <pattern> source path filter
252
262
  --outputFile <path> output file for v8 report
253
263
  --inline inline html for v8 report
254
264
  --assetsPath <path> assets path if not inline
255
265
  --lcov generate lcov.info file
266
+ --logging <logging> off, error, info, debug
256
267
  -h, --help display help for command
257
268
  ```
258
- - Using configuration file for more [options](#default-options)
269
+ - Supports loading default configuration file
270
+ - `.mcrrc`
271
+ - `mcr.config.json`
272
+ - `mcr.config.mjs`
273
+ - `mcr.config.cjs`
274
+ - `mcr.config.js`
275
+ - Specify custom configuration file
259
276
  ```sh
260
- mcr "node ./test/test-node-env.js" -c test/cli-options.js
277
+ mcr "node ./test.js" -c path-to/my-custom-config.js
261
278
  ```
262
279
 
263
280
  ## Compare Reports
package/lib/cli.js CHANGED
@@ -3,8 +3,7 @@
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
5
  const EC = require('eight-colors');
6
- const { fileURLToPath } = require('url');
7
-
6
+ const { fileURLToPath, pathToFileURL } = require('url');
8
7
  const {
9
8
  foregroundChild,
10
9
  program
@@ -13,21 +12,76 @@ const {
13
12
  const MCR = require('./index.js');
14
13
  const Util = require('./utils/util.js');
15
14
  const defaultOptions = require('./default/options.js');
15
+ const { resolveSourceMap } = require('./converter/collect-source-maps.js');
16
+
16
17
  const version = require('../package.json').version;
17
18
 
18
19
  const cleanDir = (dir) => {
19
20
  Util.rmSync(dir);
20
21
  };
21
22
 
22
- const initOptions = (cliOptions) => {
23
- // merge all cli options
24
- const configPath = cliOptions.config;
23
+ const resolveConfigPath = (customConfigPath) => {
24
+
25
+ const configList = [
26
+ '.mcrrc',
27
+ 'mcr.config.json',
28
+ 'mcr.config.mjs',
29
+ 'mcr.config.cjs',
30
+ 'mcr.config.js'
31
+ ];
32
+
33
+ if (customConfigPath) {
34
+ configList.push(customConfigPath);
35
+ }
36
+
37
+ const existsList = configList.filter((p) => fs.existsSync(p));
38
+ if (!existsList.length) {
39
+ return;
40
+ }
41
+
42
+ // last one
43
+ const configPath = existsList.pop();
44
+ Util.logInfo(`Config: ${EC.cyan(configPath)}`);
45
+
46
+ return configPath;
47
+ };
48
+
49
+ const resolveConfigOptions = async (configPath) => {
50
+
51
+ // json format
52
+ const ext = path.extname(configPath);
53
+ if (ext === '.json' || configPath.slice(-2) === 'rc') {
54
+ return JSON.parse(Util.readFileSync(configPath));
55
+ }
56
+
57
+ let configOptions;
58
+ let err;
59
+ try {
60
+ configOptions = await import(pathToFileURL(configPath));
61
+ } catch (ee) {
62
+ err = ee;
63
+ }
64
+
65
+ if (!configOptions) {
66
+ Util.logError(err.message);
67
+ return;
68
+ }
69
+
70
+ configOptions = configOptions.default || configOptions;
71
+
72
+ return configOptions;
73
+ };
74
+
75
+ const initOptions = async (cliOptions) => {
76
+
77
+ const configPath = resolveConfigPath(cliOptions.config);
25
78
  if (configPath) {
26
- if (fs.existsSync(configPath)) {
27
- const config = require(path.resolve(configPath));
28
- Object.assign(cliOptions, config);
29
- } else {
30
- EC.logRed(`Not found config: ${configPath}`);
79
+ const configOptions = await resolveConfigOptions(configPath);
80
+ if (configOptions) {
81
+ cliOptions = {
82
+ ... configOptions,
83
+ ... cliOptions
84
+ };
31
85
  }
32
86
  }
33
87
 
@@ -46,9 +100,9 @@ const initOptions = (cliOptions) => {
46
100
 
47
101
  const executeCommand = async (command, cliOptions) => {
48
102
 
49
- const options = initOptions(cliOptions);
103
+ const options = await initOptions(cliOptions);
50
104
 
51
- console.log('Execute:', EC.cyan(command));
105
+ Util.logInfo(`Execute: ${EC.cyan(command)}`);
52
106
  // console.log(options);
53
107
 
54
108
  const nodeV8CoverageDir = path.resolve(options.outputDir, '.v8-coverage');
@@ -82,6 +136,9 @@ const executeCommand = async (command, cliOptions) => {
82
136
  const json = JSON.parse(content);
83
137
  let coverageList = json.result;
84
138
 
139
+ // for node sourcemap
140
+ const sourceMapCache = json['source-map-cache'] || {};
141
+
85
142
  // filter node internal files
86
143
  coverageList = coverageList.filter((entry) => entry.url && entry.url.startsWith('file:'));
87
144
 
@@ -90,11 +147,28 @@ const executeCommand = async (command, cliOptions) => {
90
147
  }
91
148
  // attached source content
92
149
  coverageList.forEach((entry) => {
150
+ // source content
93
151
  const filePath = fileURLToPath(entry.url);
94
152
  if (fs.existsSync(filePath)) {
95
153
  entry.source = fs.readFileSync(filePath).toString('utf8');
96
- } else {
97
- EC.logRed('not found file', Util.relativePath(filePath));
154
+ }
155
+
156
+ // sourcemap data
157
+ const sourcemapData = sourceMapCache[entry.url];
158
+ if (sourcemapData) {
159
+ if (sourcemapData.data) {
160
+ entry.sourceMap = resolveSourceMap(sourcemapData.data, entry.url);
161
+ }
162
+ // sourcemapData.lineLengths just for fake source file (can not parse to AST)
163
+ if (!entry.source && sourcemapData.lineLengths) {
164
+ // get runtime code with ts-node
165
+ let source = '';
166
+ sourcemapData.lineLengths.forEach((length) => {
167
+ source += `${''.padEnd(length, '*')}\n`;
168
+ });
169
+ entry.fake = true;
170
+ entry.source = source;
171
+ }
98
172
  }
99
173
  });
100
174
 
@@ -118,18 +192,23 @@ program
118
192
  .description('CLI to generate coverage reports')
119
193
  .version(version)
120
194
  .argument('<command>', 'command to execute')
121
- .option('-c, --config <path>', 'config path for options')
195
+ .option('-c, --config <path>', 'custom config path')
122
196
  .option('-o, --outputDir <dir>', 'output dir for reports')
123
197
  .option('-r, --reports <name[,name]>', 'coverage reports to use')
124
198
  .option('-n, --name <name>', 'report name for title')
125
199
  .option('-i, --inputDir <dir>', 'input dir for merging raw files')
126
200
 
201
+ .option('--entryFilter <pattern>', 'entry url filter')
202
+ .option('--sourceFilter <pattern>', 'source path filter')
203
+
127
204
  .option('--outputFile <path>', 'output file for v8 report')
128
205
  .option('--inline', 'inline html for v8 report')
129
206
  .option('--assetsPath <path>', 'assets path if not inline')
130
207
 
131
208
  .option('--lcov', 'generate lcov.info file')
132
209
 
210
+ .option('--logging <logging>', 'off, error, info, debug')
211
+
133
212
  .action((command, cliOptions) => {
134
213
  executeCommand(command, cliOptions);
135
214
  });
@@ -67,14 +67,14 @@ const getSourceMapUrl = (content, url) => {
67
67
  }
68
68
  };
69
69
 
70
- const resolveSourcesContent = async (sources, url) => {
70
+ const resolveSourcesContent = (sources, url) => {
71
71
  const list = [];
72
72
  for (const file of sources) {
73
73
  const sourceUrl = Util.resolveUrl(file, url);
74
74
  if (sourceUrl) {
75
75
  const sourcePath = sourceUrl.toString();
76
76
  if (sourcePath.startsWith('file:')) {
77
- const content = await Util.readFile(path.resolve(fileURLToPath(sourcePath)));
77
+ const content = Util.readFileSync(path.resolve(fileURLToPath(sourcePath)));
78
78
  list.push(content);
79
79
  continue;
80
80
  }
@@ -86,7 +86,7 @@ const resolveSourcesContent = async (sources, url) => {
86
86
  }
87
87
  };
88
88
 
89
- const resolveSourceMap = async (data, url) => {
89
+ const resolveSourceMap = (data, url) => {
90
90
  if (!data) {
91
91
  return;
92
92
  }
@@ -104,7 +104,7 @@ const resolveSourceMap = async (data, url) => {
104
104
  }
105
105
 
106
106
  // load sources content by sources
107
- const contents = await resolveSourcesContent(sources, url);
107
+ const contents = resolveSourcesContent(sources, url);
108
108
  if (contents) {
109
109
  data.sourcesContent = contents;
110
110
  return data;
@@ -164,7 +164,7 @@ const collectSourceMaps = async (v8list, options) => {
164
164
  // from inline sync
165
165
  const smc = convertSourceMap.fromSource(source);
166
166
  if (smc) {
167
- sourceData.sourceMap = await resolveSourceMap(smc.sourcemap, url);
167
+ sourceData.sourceMap = resolveSourceMap(smc.sourcemap, url);
168
168
  smList.push({
169
169
  url,
170
170
  inline: true
@@ -195,7 +195,7 @@ const collectSourceMaps = async (v8list, options) => {
195
195
  const { url } = sourceData;
196
196
  const data = await loadSourceMap(sourceMapUrl);
197
197
  if (data) {
198
- sourceData.sourceMap = await resolveSourceMap(data, url);
198
+ sourceData.sourceMap = resolveSourceMap(data, url);
199
199
  smList.push({
200
200
  url,
201
201
  sourceMapUrl
@@ -208,4 +208,7 @@ const collectSourceMaps = async (v8list, options) => {
208
208
 
209
209
  };
210
210
 
211
- module.exports = collectSourceMaps;
211
+ module.exports = {
212
+ collectSourceMaps,
213
+ resolveSourceMap
214
+ };
@@ -15,7 +15,7 @@ const {
15
15
  sortRanges, dedupeCountRanges, mergeRangesWith
16
16
  } = require('../utils/dedupe.js');
17
17
  const { getSourceType, initSourceMapSourcePath } = require('../utils/source-path.js');
18
- const { decode } = require('../packages/monocart-coverage-vendor.js');
18
+ const { decode, minimatch } = require('../packages/monocart-coverage-vendor.js');
19
19
 
20
20
  const InfoBranch = require('./info-branch.js');
21
21
  const InfoFunction = require('./info-function.js');
@@ -838,13 +838,25 @@ const getOriginalDecodedMappings = (originalDecodedMap, sourceIndex, locator) =>
838
838
 
839
839
  // ========================================================================================================
840
840
 
841
+ const getSourceFilter = (options) => {
842
+ const sourceFilter = options.sourceFilter;
843
+ if (typeof sourceFilter === 'function') {
844
+ return sourceFilter;
845
+ }
846
+
847
+ if (sourceFilter && typeof sourceFilter === 'string') {
848
+ return (sourcePath) => {
849
+ return minimatch(sourcePath, sourceFilter);
850
+ };
851
+ }
852
+
853
+ return () => true;
854
+ };
855
+
841
856
  const initOriginalList = (state, originalDecodedMap, options) => {
842
857
 
843
858
  // source filter
844
- let sourceFilter = options.sourceFilter;
845
- if (typeof sourceFilter !== 'function') {
846
- sourceFilter = () => true;
847
- }
859
+ const sourceFilter = getSourceFilter(options);
848
860
 
849
861
  // create original content mappings
850
862
  const originalStateMap = new Map();
@@ -855,8 +867,10 @@ const initOriginalList = (state, originalDecodedMap, options) => {
855
867
 
856
868
  // filter
857
869
  if (!sourceFilter(sourcePath)) {
870
+ // console.log('-', sourcePath);
858
871
  return;
859
872
  }
873
+ // console.log(sourcePath);
860
874
 
861
875
  // console.log(`add source: ${k}`);
862
876
  const sourceContent = sourcesContent[sourceIndex];
package/lib/index.d.ts CHANGED
@@ -198,9 +198,14 @@ declare namespace MCR {
198
198
  /** {string|string[]} input raw dir(s) */
199
199
  inputDir?: string | string[];
200
200
 
201
- /** {string} v8 or html for istanbul by default
202
- * {array} multiple reports with options
203
- * v8 report or istanbul supported reports */
201
+ /**
202
+ *
203
+ * {string} 'v8', 'v8,console-details'
204
+ *
205
+ * {array} ['v8'], ['v8', ['console-details', { skipPercent: 80 }]]
206
+ *
207
+ * By default, `v8` for V8 data, `html` for Istanbul data
208
+ */
204
209
  reports?: string | ReportDescription[];
205
210
 
206
211
  /** {string} Report name. Defaults to "Coverage Report". */
@@ -213,11 +218,21 @@ declare namespace MCR {
213
218
  /** (V8 only) {string} Assets path if not inline. Defaults to "./assets" */
214
219
  assetsPath?: string;
215
220
 
216
- /** (V8 only) {function} A filter function to execute for each element in the V8 list. */
217
- entryFilter?: (entry: V8CoverageEntry) => boolean;
218
-
219
- /** (V8 only) {function} A filter function to execute for each element in the sources which unpacked from the source map. */
220
- sourceFilter?: (sourcePath: string) => boolean;
221
+ /** (V8 only)
222
+ *
223
+ * {string} `minimatch` pattern for entry url; multiple patterns: "{pattern1,pattern2}";
224
+ *
225
+ * {function} A filter function for each entry file in the V8 list.
226
+ * */
227
+ entryFilter?: string | ((entry: V8CoverageEntry) => boolean);
228
+
229
+ /** (V8 only)
230
+ *
231
+ * {string} `minimatch` pattern for source path; multiple patterns: "{pattern1,pattern2}";
232
+ *
233
+ * {function} A filter function for each source path when the source is unpacked from the source map.
234
+ * */
235
+ sourceFilter?: string | ((sourcePath: string) => boolean);
221
236
 
222
237
  /** (V8 only) {boolean} Enable/Disable ignoring uncovered codes with the special comments: v8 ignore next/next N/start/stop */
223
238
  v8Ignore?: boolean;