monocart-coverage-reports 2.5.9 → 2.6.1
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/LICENSE +21 -21
- package/README.md +791 -778
- package/lib/cli.js +250 -215
- package/lib/converter/ast-visitor.js +675 -666
- package/lib/converter/ast.js +353 -353
- package/lib/converter/collect-source-maps.js +201 -201
- package/lib/converter/converter.js +1487 -1429
- package/lib/converter/find-original-range.js +737 -737
- package/lib/converter/ignore.js +163 -163
- package/lib/converter/info-branch.js +93 -93
- package/lib/converter/info-function.js +63 -63
- package/lib/converter/info-statement.js +43 -43
- package/lib/default/options.js +84 -84
- package/lib/default/template.html +47 -47
- package/lib/generate.js +720 -532
- package/lib/index.d.ts +360 -324
- package/lib/index.js +187 -180
- package/lib/index.mjs +5 -3
- package/lib/istanbul/istanbul-summary.js +70 -63
- package/lib/istanbul/istanbul.js +146 -146
- package/lib/packages/monocart-coverage-v8.js +1 -1
- package/lib/platform/concurrency.js +74 -74
- package/lib/platform/share.js +484 -484
- package/lib/utils/dedupe.js +118 -118
- package/lib/utils/request.js +62 -62
- package/lib/utils/snapshot.js +381 -0
- package/lib/utils/source-path.js +179 -179
- package/lib/utils/util.js +526 -526
- package/lib/v8/v8-summary.js +138 -138
- package/lib/v8/v8.js +400 -630
- package/package.json +81 -80
package/lib/utils/util.js
CHANGED
|
@@ -1,526 +1,526 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const { writeFile, readFile } = require('fs/promises');
|
|
3
|
-
const path = require('path');
|
|
4
|
-
const { pathToFileURL } = require('url');
|
|
5
|
-
const os = require('os');
|
|
6
|
-
const crypto = require('crypto');
|
|
7
|
-
const EC = require('eight-colors');
|
|
8
|
-
const CG = require('console-grid');
|
|
9
|
-
const Share = require('../platform/share.js');
|
|
10
|
-
const request = require('./request.js');
|
|
11
|
-
const { deflateSync } = require('lz-utils');
|
|
12
|
-
|
|
13
|
-
const { acornWalk, supportsColor } = require('../packages/monocart-coverage-vendor.js');
|
|
14
|
-
|
|
15
|
-
// https://github.com/chalk/supports-color
|
|
16
|
-
// disabled color if Terminal stdout does not support color
|
|
17
|
-
if (!supportsColor.stdout) {
|
|
18
|
-
EC.disabled = true;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const Util = {
|
|
22
|
-
... Share,
|
|
23
|
-
|
|
24
|
-
EC,
|
|
25
|
-
CG,
|
|
26
|
-
|
|
27
|
-
root: process.cwd(),
|
|
28
|
-
|
|
29
|
-
request,
|
|
30
|
-
|
|
31
|
-
relativePath: function(p, root) {
|
|
32
|
-
p = `${p}`;
|
|
33
|
-
root = `${root || Util.root}`;
|
|
34
|
-
let rp = path.relative(root, p);
|
|
35
|
-
rp = Util.formatPath(rp);
|
|
36
|
-
return rp;
|
|
37
|
-
},
|
|
38
|
-
|
|
39
|
-
replace: function(str, obj, defaultValue) {
|
|
40
|
-
str = `${str}`;
|
|
41
|
-
if (!obj) {
|
|
42
|
-
return str;
|
|
43
|
-
}
|
|
44
|
-
str = str.replace(/\{([^}{]+)\}/g, function(match, key) {
|
|
45
|
-
if (!Util.hasOwn(obj, key)) {
|
|
46
|
-
if (typeof (defaultValue) !== 'undefined') {
|
|
47
|
-
return defaultValue;
|
|
48
|
-
}
|
|
49
|
-
return match;
|
|
50
|
-
}
|
|
51
|
-
let val = obj[key];
|
|
52
|
-
if (typeof (val) === 'function') {
|
|
53
|
-
val = val(obj, key);
|
|
54
|
-
}
|
|
55
|
-
if (typeof (val) === 'undefined') {
|
|
56
|
-
val = '';
|
|
57
|
-
}
|
|
58
|
-
return val;
|
|
59
|
-
});
|
|
60
|
-
return str;
|
|
61
|
-
},
|
|
62
|
-
|
|
63
|
-
calculateSha1: (buffer) => {
|
|
64
|
-
const hash = crypto.createHash('sha1');
|
|
65
|
-
hash.update(buffer);
|
|
66
|
-
return hash.digest('hex');
|
|
67
|
-
},
|
|
68
|
-
|
|
69
|
-
checkCoverageData: (data) => {
|
|
70
|
-
if (!data) {
|
|
71
|
-
return false;
|
|
72
|
-
}
|
|
73
|
-
if (Array.isArray(data) && data.length) {
|
|
74
|
-
return true;
|
|
75
|
-
}
|
|
76
|
-
if (Object.keys(data).length) {
|
|
77
|
-
return true;
|
|
78
|
-
}
|
|
79
|
-
return false;
|
|
80
|
-
},
|
|
81
|
-
|
|
82
|
-
resolveUrl: (input, base) => {
|
|
83
|
-
let url;
|
|
84
|
-
try {
|
|
85
|
-
url = new URL(input, base);
|
|
86
|
-
} catch (e) {
|
|
87
|
-
// console.error('error url', input);
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
return url;
|
|
91
|
-
},
|
|
92
|
-
|
|
93
|
-
resolveCacheSourcePath: (cacheDir, id) => {
|
|
94
|
-
const filename = `source-${id}.json`;
|
|
95
|
-
const sourcePath = path.resolve(cacheDir, filename);
|
|
96
|
-
return sourcePath;
|
|
97
|
-
},
|
|
98
|
-
|
|
99
|
-
resolveWatermarks: (defaultWatermarks, watermarks) => {
|
|
100
|
-
if (watermarks) {
|
|
101
|
-
if (Array.isArray(watermarks)) {
|
|
102
|
-
Object.keys(defaultWatermarks).forEach((k) => {
|
|
103
|
-
defaultWatermarks[k] = watermarks;
|
|
104
|
-
});
|
|
105
|
-
} else {
|
|
106
|
-
Object.assign(defaultWatermarks, watermarks);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
return defaultWatermarks;
|
|
110
|
-
},
|
|
111
|
-
|
|
112
|
-
resolveReportPath: (options, htmlPathHandler) => {
|
|
113
|
-
|
|
114
|
-
let reportPath = options.reportPath;
|
|
115
|
-
if (typeof reportPath === 'function') {
|
|
116
|
-
reportPath = reportPath();
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (typeof reportPath === 'string') {
|
|
120
|
-
if (reportPath) {
|
|
121
|
-
const p = path.resolve(options.outputDir, reportPath);
|
|
122
|
-
return Util.relativePath(p);
|
|
123
|
-
}
|
|
124
|
-
// empty
|
|
125
|
-
return reportPath;
|
|
126
|
-
}
|
|
127
|
-
// using default html path as report path
|
|
128
|
-
return htmlPathHandler();
|
|
129
|
-
},
|
|
130
|
-
|
|
131
|
-
resolveNodeModule: (p) => {
|
|
132
|
-
|
|
133
|
-
// root
|
|
134
|
-
const cwd = path.resolve('node_modules', p);
|
|
135
|
-
if (fs.existsSync(cwd)) {
|
|
136
|
-
return cwd;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// sub node modules
|
|
140
|
-
const sub = path.resolve(__dirname, '../../node_modules', p);
|
|
141
|
-
if (fs.existsSync(sub)) {
|
|
142
|
-
return sub;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// same level dep
|
|
146
|
-
const dep = path.resolve(__dirname, '../../../', p);
|
|
147
|
-
if (fs.existsSync(dep)) {
|
|
148
|
-
return dep;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
Util.logError(`Not found module: ${p}`);
|
|
152
|
-
|
|
153
|
-
return cwd;
|
|
154
|
-
},
|
|
155
|
-
|
|
156
|
-
resolvePackage: (p) => {
|
|
157
|
-
return path.resolve(__dirname, '../packages', p);
|
|
158
|
-
},
|
|
159
|
-
|
|
160
|
-
saveHtmlReport: async (options) => {
|
|
161
|
-
|
|
162
|
-
const {
|
|
163
|
-
inline,
|
|
164
|
-
reportData,
|
|
165
|
-
jsFiles,
|
|
166
|
-
assetsPath,
|
|
167
|
-
outputDir,
|
|
168
|
-
htmlFile,
|
|
169
|
-
|
|
170
|
-
reportDataFile
|
|
171
|
-
} = options;
|
|
172
|
-
|
|
173
|
-
// report data
|
|
174
|
-
const reportDataCompressed = deflateSync(JSON.stringify(reportData));
|
|
175
|
-
const reportDataStr = `window.reportData = '${reportDataCompressed}';`;
|
|
176
|
-
|
|
177
|
-
// js libs
|
|
178
|
-
const jsList = [];
|
|
179
|
-
for (const jsFile of jsFiles) {
|
|
180
|
-
const jsStr = await Util.readFile(jsFile);
|
|
181
|
-
jsList.push({
|
|
182
|
-
filename: path.basename(jsFile),
|
|
183
|
-
str: jsStr
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// html content
|
|
188
|
-
let htmlStr = '';
|
|
189
|
-
const EOL = Util.getEOL();
|
|
190
|
-
if (inline) {
|
|
191
|
-
htmlStr = [
|
|
192
|
-
'<script>',
|
|
193
|
-
reportDataStr,
|
|
194
|
-
... jsList.map((it) => it.str),
|
|
195
|
-
'</script>'
|
|
196
|
-
].join(EOL);
|
|
197
|
-
} else {
|
|
198
|
-
|
|
199
|
-
await Util.writeFile(path.resolve(outputDir, reportDataFile), reportDataStr);
|
|
200
|
-
|
|
201
|
-
const assetsDir = path.resolve(outputDir, assetsPath);
|
|
202
|
-
const relAssetsDir = Util.relativePath(assetsDir, outputDir);
|
|
203
|
-
|
|
204
|
-
for (const item of jsList) {
|
|
205
|
-
const filePath = path.resolve(assetsDir, item.filename);
|
|
206
|
-
if (!fs.existsSync(filePath)) {
|
|
207
|
-
await Util.writeFile(filePath, item.str);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
htmlStr = [
|
|
212
|
-
`<script src="${reportDataFile}"></script>`,
|
|
213
|
-
... jsList.map((it) => `<script src="${relAssetsDir}/${it.filename}"></script>`)
|
|
214
|
-
].join(EOL);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// html
|
|
218
|
-
const htmlPath = path.resolve(outputDir, htmlFile);
|
|
219
|
-
const template = Util.getTemplate(path.resolve(__dirname, '../default/template.html'));
|
|
220
|
-
const html = Util.replace(template, {
|
|
221
|
-
title: reportData.title,
|
|
222
|
-
content: htmlStr
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
await Util.writeFile(htmlPath, html);
|
|
226
|
-
|
|
227
|
-
return Util.relativePath(htmlPath);
|
|
228
|
-
},
|
|
229
|
-
|
|
230
|
-
runCustomReporter: async (reportName, reportData, reportOptions, globalOptions) => {
|
|
231
|
-
let CustomReporter;
|
|
232
|
-
let err;
|
|
233
|
-
try {
|
|
234
|
-
CustomReporter = await import(reportName);
|
|
235
|
-
} catch (e) {
|
|
236
|
-
err = e;
|
|
237
|
-
try {
|
|
238
|
-
CustomReporter = await import(pathToFileURL(reportName));
|
|
239
|
-
} catch (ee) {
|
|
240
|
-
err = ee;
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
if (!CustomReporter) {
|
|
245
|
-
Util.logError(err.message);
|
|
246
|
-
return;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
CustomReporter = CustomReporter.default || CustomReporter;
|
|
250
|
-
|
|
251
|
-
const reporter = new CustomReporter(reportOptions, globalOptions);
|
|
252
|
-
|
|
253
|
-
const results = await reporter.generate(reportData);
|
|
254
|
-
|
|
255
|
-
return results;
|
|
256
|
-
},
|
|
257
|
-
|
|
258
|
-
getEOL: function(content) {
|
|
259
|
-
if (!content) {
|
|
260
|
-
return os.EOL;
|
|
261
|
-
}
|
|
262
|
-
const nIndex = content.lastIndexOf('\n');
|
|
263
|
-
if (nIndex === -1) {
|
|
264
|
-
return os.EOL;
|
|
265
|
-
}
|
|
266
|
-
if (content.substr(nIndex - 1, 1) === '\r') {
|
|
267
|
-
return '\r\n';
|
|
268
|
-
}
|
|
269
|
-
return '\n';
|
|
270
|
-
},
|
|
271
|
-
|
|
272
|
-
visitAst: (rootNode, visitors) => {
|
|
273
|
-
const baseVisitor = acornWalk.base;
|
|
274
|
-
const parents = [rootNode];
|
|
275
|
-
const visitor = (node, st, override) => {
|
|
276
|
-
const type = override || node.type;
|
|
277
|
-
// console.log('visit', node.type, override, parents.length);
|
|
278
|
-
const handler = visitors[type];
|
|
279
|
-
if (handler) {
|
|
280
|
-
const res = handler(node, parents);
|
|
281
|
-
if (res === 'break') {
|
|
282
|
-
return;
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
const isNew = node !== parents[parents.length - 1];
|
|
286
|
-
if (isNew) {
|
|
287
|
-
parents.push(node);
|
|
288
|
-
}
|
|
289
|
-
baseVisitor[type](node, st, visitor);
|
|
290
|
-
if (isNew) {
|
|
291
|
-
parents.pop();
|
|
292
|
-
}
|
|
293
|
-
};
|
|
294
|
-
visitor(rootNode);
|
|
295
|
-
},
|
|
296
|
-
|
|
297
|
-
updateOffsetToLocation: (locator, loc) => {
|
|
298
|
-
const sLoc = locator.offsetToLocation(loc.start);
|
|
299
|
-
loc.start = {
|
|
300
|
-
line: sLoc.line,
|
|
301
|
-
column: sLoc.column
|
|
302
|
-
};
|
|
303
|
-
const eLoc = locator.offsetToLocation(loc.end);
|
|
304
|
-
loc.end = {
|
|
305
|
-
line: eLoc.line,
|
|
306
|
-
column: eLoc.column
|
|
307
|
-
};
|
|
308
|
-
},
|
|
309
|
-
|
|
310
|
-
forEachFile: function(dir, extList, callback) {
|
|
311
|
-
if (!fs.existsSync(dir)) {
|
|
312
|
-
return;
|
|
313
|
-
}
|
|
314
|
-
// all exts, full name, ext
|
|
315
|
-
const isMatched = (name) => !Util.isList(extList) || extList.includes(name) || extList.includes(path.extname(name));
|
|
316
|
-
const subDirs = [];
|
|
317
|
-
const subDirHandler = () => {
|
|
318
|
-
if (!subDirs.length) {
|
|
319
|
-
return;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
for (const subDir of subDirs) {
|
|
323
|
-
const res = Util.forEachFile(subDir, extList, callback);
|
|
324
|
-
if (res === 'break') {
|
|
325
|
-
return;
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
};
|
|
329
|
-
|
|
330
|
-
const list = fs.readdirSync(dir);
|
|
331
|
-
for (const name of list) {
|
|
332
|
-
const abs = path.resolve(dir, name);
|
|
333
|
-
const info = fs.statSync(abs);
|
|
334
|
-
if (info.isDirectory()) {
|
|
335
|
-
subDirs.push(abs);
|
|
336
|
-
continue;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
if (info.isFile() && isMatched(name)) {
|
|
340
|
-
const res = callback(name, dir);
|
|
341
|
-
if (res === 'break') {
|
|
342
|
-
return;
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
subDirHandler();
|
|
349
|
-
|
|
350
|
-
},
|
|
351
|
-
|
|
352
|
-
readFileSync: function(filePath) {
|
|
353
|
-
if (fs.existsSync(filePath)) {
|
|
354
|
-
// Returns: <string> | <Buffer>
|
|
355
|
-
const buf = fs.readFileSync(filePath);
|
|
356
|
-
if (Buffer.isBuffer(buf)) {
|
|
357
|
-
return buf.toString('utf8');
|
|
358
|
-
}
|
|
359
|
-
return buf;
|
|
360
|
-
}
|
|
361
|
-
},
|
|
362
|
-
|
|
363
|
-
readFile: async (filePath) => {
|
|
364
|
-
if (fs.existsSync(filePath)) {
|
|
365
|
-
const buf = await readFile(filePath).catch((e) => {
|
|
366
|
-
Util.logError(`read file: ${filePath} ${e.message || e}`);
|
|
367
|
-
});
|
|
368
|
-
if (Buffer.isBuffer(buf)) {
|
|
369
|
-
return buf.toString('utf8');
|
|
370
|
-
}
|
|
371
|
-
return buf;
|
|
372
|
-
}
|
|
373
|
-
},
|
|
374
|
-
|
|
375
|
-
writeFileSync: function(filePath, content) {
|
|
376
|
-
if (!fs.existsSync(filePath)) {
|
|
377
|
-
const p = path.dirname(filePath);
|
|
378
|
-
if (!fs.existsSync(p)) {
|
|
379
|
-
fs.mkdirSync(p, {
|
|
380
|
-
recursive: true
|
|
381
|
-
});
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
fs.writeFileSync(filePath, content);
|
|
385
|
-
},
|
|
386
|
-
|
|
387
|
-
writeFile: async (filePath, content) => {
|
|
388
|
-
if (!fs.existsSync(filePath)) {
|
|
389
|
-
const p = path.dirname(filePath);
|
|
390
|
-
if (!fs.existsSync(p)) {
|
|
391
|
-
fs.mkdirSync(p, {
|
|
392
|
-
recursive: true
|
|
393
|
-
});
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
await writeFile(filePath, content).catch((e) => {
|
|
397
|
-
Util.logError(`write file: ${filePath} ${e.message || e}`);
|
|
398
|
-
});
|
|
399
|
-
},
|
|
400
|
-
|
|
401
|
-
rmSync: (p) => {
|
|
402
|
-
if (!fs.existsSync(p)) {
|
|
403
|
-
return;
|
|
404
|
-
}
|
|
405
|
-
try {
|
|
406
|
-
fs.rmSync(p, {
|
|
407
|
-
recursive: true,
|
|
408
|
-
force: true,
|
|
409
|
-
maxRetries: 10
|
|
410
|
-
});
|
|
411
|
-
} catch (err) {
|
|
412
|
-
console.log(err.message);
|
|
413
|
-
}
|
|
414
|
-
},
|
|
415
|
-
|
|
416
|
-
getTemplate: function(templatePath) {
|
|
417
|
-
if (!Util.templateCache) {
|
|
418
|
-
Util.templateCache = {};
|
|
419
|
-
}
|
|
420
|
-
let template = Util.templateCache[templatePath];
|
|
421
|
-
if (!template) {
|
|
422
|
-
template = Util.readFileSync(templatePath);
|
|
423
|
-
if (template) {
|
|
424
|
-
Util.templateCache[templatePath] = template;
|
|
425
|
-
} else {
|
|
426
|
-
Util.logError(`not found template: ${templatePath}`);
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
return template;
|
|
430
|
-
},
|
|
431
|
-
|
|
432
|
-
getColorStrByStatus: (str, status) => {
|
|
433
|
-
if (status === 'low') {
|
|
434
|
-
return EC.red(str);
|
|
435
|
-
}
|
|
436
|
-
if (status === 'medium') {
|
|
437
|
-
return EC.yellow(str);
|
|
438
|
-
}
|
|
439
|
-
if (status === 'high') {
|
|
440
|
-
return EC.green(str);
|
|
441
|
-
}
|
|
442
|
-
return str;
|
|
443
|
-
},
|
|
444
|
-
|
|
445
|
-
// ==========================================================================================
|
|
446
|
-
|
|
447
|
-
loggingLevels: {
|
|
448
|
-
off: 0,
|
|
449
|
-
error: 10,
|
|
450
|
-
info: 20,
|
|
451
|
-
debug: 30
|
|
452
|
-
},
|
|
453
|
-
|
|
454
|
-
isDebug: () => {
|
|
455
|
-
return Util.loggingType === 'debug';
|
|
456
|
-
},
|
|
457
|
-
|
|
458
|
-
initLoggingLevel: (level) => {
|
|
459
|
-
|
|
460
|
-
const types = {
|
|
461
|
-
off: 'off',
|
|
462
|
-
error: 'error',
|
|
463
|
-
info: 'info',
|
|
464
|
-
debug: 'debug'
|
|
465
|
-
};
|
|
466
|
-
const type = types[level] || types.info;
|
|
467
|
-
Util.loggingType = type;
|
|
468
|
-
Util.loggingLevel = Util.loggingLevels[type];
|
|
469
|
-
|
|
470
|
-
// console.log('=========================================');
|
|
471
|
-
// console.log(from, Util.loggingType, Util.loggingLevel);
|
|
472
|
-
|
|
473
|
-
return type;
|
|
474
|
-
},
|
|
475
|
-
|
|
476
|
-
logError: (message) => {
|
|
477
|
-
if (Util.loggingLevel < Util.loggingLevels.error) {
|
|
478
|
-
return;
|
|
479
|
-
}
|
|
480
|
-
EC.logRed(`[MCR] ${message}`);
|
|
481
|
-
},
|
|
482
|
-
|
|
483
|
-
logInfo: (message) => {
|
|
484
|
-
if (Util.loggingLevel < Util.loggingLevels.info) {
|
|
485
|
-
return;
|
|
486
|
-
}
|
|
487
|
-
console.log(`[MCR] ${message}`);
|
|
488
|
-
},
|
|
489
|
-
|
|
490
|
-
// grid is info level
|
|
491
|
-
logGrid: (gridData) => {
|
|
492
|
-
if (Util.loggingLevel < Util.loggingLevels.info) {
|
|
493
|
-
return;
|
|
494
|
-
}
|
|
495
|
-
CG(gridData);
|
|
496
|
-
},
|
|
497
|
-
|
|
498
|
-
logDebug: (message) => {
|
|
499
|
-
if (Util.loggingLevel < Util.loggingLevels.debug) {
|
|
500
|
-
return;
|
|
501
|
-
}
|
|
502
|
-
console.log(`[MCR] ${message}`);
|
|
503
|
-
},
|
|
504
|
-
|
|
505
|
-
// time is debug level, fix the log info, for checking snapshot
|
|
506
|
-
logTime: (message, time_start) => {
|
|
507
|
-
if (Util.loggingLevel < Util.loggingLevels.debug) {
|
|
508
|
-
return;
|
|
509
|
-
}
|
|
510
|
-
const duration = Date.now() - time_start;
|
|
511
|
-
const durationH = Util.TSF(duration);
|
|
512
|
-
const ls = [`[MCR] ${message}`, ' ('];
|
|
513
|
-
if (duration <= 100) {
|
|
514
|
-
ls.push(EC.green(durationH));
|
|
515
|
-
} else if (duration < 500) {
|
|
516
|
-
ls.push(EC.yellow(durationH));
|
|
517
|
-
} else {
|
|
518
|
-
ls.push(EC.red(durationH));
|
|
519
|
-
}
|
|
520
|
-
ls.push(')');
|
|
521
|
-
console.log(ls.join(''));
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
};
|
|
525
|
-
|
|
526
|
-
module.exports = Util;
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const { writeFile, readFile } = require('fs/promises');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const { pathToFileURL } = require('url');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
const crypto = require('crypto');
|
|
7
|
+
const EC = require('eight-colors');
|
|
8
|
+
const CG = require('console-grid');
|
|
9
|
+
const Share = require('../platform/share.js');
|
|
10
|
+
const request = require('./request.js');
|
|
11
|
+
const { deflateSync } = require('lz-utils');
|
|
12
|
+
|
|
13
|
+
const { acornWalk, supportsColor } = require('../packages/monocart-coverage-vendor.js');
|
|
14
|
+
|
|
15
|
+
// https://github.com/chalk/supports-color
|
|
16
|
+
// disabled color if Terminal stdout does not support color
|
|
17
|
+
if (!supportsColor.stdout) {
|
|
18
|
+
EC.disabled = true;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const Util = {
|
|
22
|
+
... Share,
|
|
23
|
+
|
|
24
|
+
EC,
|
|
25
|
+
CG,
|
|
26
|
+
|
|
27
|
+
root: process.cwd(),
|
|
28
|
+
|
|
29
|
+
request,
|
|
30
|
+
|
|
31
|
+
relativePath: function(p, root) {
|
|
32
|
+
p = `${p}`;
|
|
33
|
+
root = `${root || Util.root}`;
|
|
34
|
+
let rp = path.relative(root, p);
|
|
35
|
+
rp = Util.formatPath(rp);
|
|
36
|
+
return rp;
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
replace: function(str, obj, defaultValue) {
|
|
40
|
+
str = `${str}`;
|
|
41
|
+
if (!obj) {
|
|
42
|
+
return str;
|
|
43
|
+
}
|
|
44
|
+
str = str.replace(/\{([^}{]+)\}/g, function(match, key) {
|
|
45
|
+
if (!Util.hasOwn(obj, key)) {
|
|
46
|
+
if (typeof (defaultValue) !== 'undefined') {
|
|
47
|
+
return defaultValue;
|
|
48
|
+
}
|
|
49
|
+
return match;
|
|
50
|
+
}
|
|
51
|
+
let val = obj[key];
|
|
52
|
+
if (typeof (val) === 'function') {
|
|
53
|
+
val = val(obj, key);
|
|
54
|
+
}
|
|
55
|
+
if (typeof (val) === 'undefined') {
|
|
56
|
+
val = '';
|
|
57
|
+
}
|
|
58
|
+
return val;
|
|
59
|
+
});
|
|
60
|
+
return str;
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
calculateSha1: (buffer) => {
|
|
64
|
+
const hash = crypto.createHash('sha1');
|
|
65
|
+
hash.update(buffer);
|
|
66
|
+
return hash.digest('hex');
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
checkCoverageData: (data) => {
|
|
70
|
+
if (!data) {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
if (Array.isArray(data) && data.length) {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
if (Object.keys(data).length) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
return false;
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
resolveUrl: (input, base) => {
|
|
83
|
+
let url;
|
|
84
|
+
try {
|
|
85
|
+
url = new URL(input, base);
|
|
86
|
+
} catch (e) {
|
|
87
|
+
// console.error('error url', input);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
return url;
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
resolveCacheSourcePath: (cacheDir, id) => {
|
|
94
|
+
const filename = `source-${id}.json`;
|
|
95
|
+
const sourcePath = path.resolve(cacheDir, filename);
|
|
96
|
+
return sourcePath;
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
resolveWatermarks: (defaultWatermarks, watermarks) => {
|
|
100
|
+
if (watermarks) {
|
|
101
|
+
if (Array.isArray(watermarks)) {
|
|
102
|
+
Object.keys(defaultWatermarks).forEach((k) => {
|
|
103
|
+
defaultWatermarks[k] = watermarks;
|
|
104
|
+
});
|
|
105
|
+
} else {
|
|
106
|
+
Object.assign(defaultWatermarks, watermarks);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return defaultWatermarks;
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
resolveReportPath: (options, htmlPathHandler) => {
|
|
113
|
+
|
|
114
|
+
let reportPath = options.reportPath;
|
|
115
|
+
if (typeof reportPath === 'function') {
|
|
116
|
+
reportPath = reportPath();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (typeof reportPath === 'string') {
|
|
120
|
+
if (reportPath) {
|
|
121
|
+
const p = path.resolve(options.outputDir, reportPath);
|
|
122
|
+
return Util.relativePath(p);
|
|
123
|
+
}
|
|
124
|
+
// empty
|
|
125
|
+
return reportPath;
|
|
126
|
+
}
|
|
127
|
+
// using default html path as report path
|
|
128
|
+
return htmlPathHandler();
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
resolveNodeModule: (p) => {
|
|
132
|
+
|
|
133
|
+
// root
|
|
134
|
+
const cwd = path.resolve('node_modules', p);
|
|
135
|
+
if (fs.existsSync(cwd)) {
|
|
136
|
+
return cwd;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// sub node modules
|
|
140
|
+
const sub = path.resolve(__dirname, '../../node_modules', p);
|
|
141
|
+
if (fs.existsSync(sub)) {
|
|
142
|
+
return sub;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// same level dep
|
|
146
|
+
const dep = path.resolve(__dirname, '../../../', p);
|
|
147
|
+
if (fs.existsSync(dep)) {
|
|
148
|
+
return dep;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
Util.logError(`Not found module: ${p}`);
|
|
152
|
+
|
|
153
|
+
return cwd;
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
resolvePackage: (p) => {
|
|
157
|
+
return path.resolve(__dirname, '../packages', p);
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
saveHtmlReport: async (options) => {
|
|
161
|
+
|
|
162
|
+
const {
|
|
163
|
+
inline,
|
|
164
|
+
reportData,
|
|
165
|
+
jsFiles,
|
|
166
|
+
assetsPath,
|
|
167
|
+
outputDir,
|
|
168
|
+
htmlFile,
|
|
169
|
+
|
|
170
|
+
reportDataFile
|
|
171
|
+
} = options;
|
|
172
|
+
|
|
173
|
+
// report data
|
|
174
|
+
const reportDataCompressed = deflateSync(JSON.stringify(reportData));
|
|
175
|
+
const reportDataStr = `window.reportData = '${reportDataCompressed}';`;
|
|
176
|
+
|
|
177
|
+
// js libs
|
|
178
|
+
const jsList = [];
|
|
179
|
+
for (const jsFile of jsFiles) {
|
|
180
|
+
const jsStr = await Util.readFile(jsFile);
|
|
181
|
+
jsList.push({
|
|
182
|
+
filename: path.basename(jsFile),
|
|
183
|
+
str: jsStr
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// html content
|
|
188
|
+
let htmlStr = '';
|
|
189
|
+
const EOL = Util.getEOL();
|
|
190
|
+
if (inline) {
|
|
191
|
+
htmlStr = [
|
|
192
|
+
'<script>',
|
|
193
|
+
reportDataStr,
|
|
194
|
+
... jsList.map((it) => it.str),
|
|
195
|
+
'</script>'
|
|
196
|
+
].join(EOL);
|
|
197
|
+
} else {
|
|
198
|
+
|
|
199
|
+
await Util.writeFile(path.resolve(outputDir, reportDataFile), reportDataStr);
|
|
200
|
+
|
|
201
|
+
const assetsDir = path.resolve(outputDir, assetsPath);
|
|
202
|
+
const relAssetsDir = Util.relativePath(assetsDir, outputDir);
|
|
203
|
+
|
|
204
|
+
for (const item of jsList) {
|
|
205
|
+
const filePath = path.resolve(assetsDir, item.filename);
|
|
206
|
+
if (!fs.existsSync(filePath)) {
|
|
207
|
+
await Util.writeFile(filePath, item.str);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
htmlStr = [
|
|
212
|
+
`<script src="${reportDataFile}"></script>`,
|
|
213
|
+
... jsList.map((it) => `<script src="${relAssetsDir}/${it.filename}"></script>`)
|
|
214
|
+
].join(EOL);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// html
|
|
218
|
+
const htmlPath = path.resolve(outputDir, htmlFile);
|
|
219
|
+
const template = Util.getTemplate(path.resolve(__dirname, '../default/template.html'));
|
|
220
|
+
const html = Util.replace(template, {
|
|
221
|
+
title: reportData.title,
|
|
222
|
+
content: htmlStr
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
await Util.writeFile(htmlPath, html);
|
|
226
|
+
|
|
227
|
+
return Util.relativePath(htmlPath);
|
|
228
|
+
},
|
|
229
|
+
|
|
230
|
+
runCustomReporter: async (reportName, reportData, reportOptions, globalOptions) => {
|
|
231
|
+
let CustomReporter;
|
|
232
|
+
let err;
|
|
233
|
+
try {
|
|
234
|
+
CustomReporter = await import(reportName);
|
|
235
|
+
} catch (e) {
|
|
236
|
+
err = e;
|
|
237
|
+
try {
|
|
238
|
+
CustomReporter = await import(pathToFileURL(reportName));
|
|
239
|
+
} catch (ee) {
|
|
240
|
+
err = ee;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (!CustomReporter) {
|
|
245
|
+
Util.logError(err.message);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
CustomReporter = CustomReporter.default || CustomReporter;
|
|
250
|
+
|
|
251
|
+
const reporter = new CustomReporter(reportOptions, globalOptions);
|
|
252
|
+
|
|
253
|
+
const results = await reporter.generate(reportData);
|
|
254
|
+
|
|
255
|
+
return results;
|
|
256
|
+
},
|
|
257
|
+
|
|
258
|
+
getEOL: function(content) {
|
|
259
|
+
if (!content) {
|
|
260
|
+
return os.EOL;
|
|
261
|
+
}
|
|
262
|
+
const nIndex = content.lastIndexOf('\n');
|
|
263
|
+
if (nIndex === -1) {
|
|
264
|
+
return os.EOL;
|
|
265
|
+
}
|
|
266
|
+
if (content.substr(nIndex - 1, 1) === '\r') {
|
|
267
|
+
return '\r\n';
|
|
268
|
+
}
|
|
269
|
+
return '\n';
|
|
270
|
+
},
|
|
271
|
+
|
|
272
|
+
visitAst: (rootNode, visitors) => {
|
|
273
|
+
const baseVisitor = acornWalk.base;
|
|
274
|
+
const parents = [rootNode];
|
|
275
|
+
const visitor = (node, st, override) => {
|
|
276
|
+
const type = override || node.type;
|
|
277
|
+
// console.log('visit', node.type, override, parents.length);
|
|
278
|
+
const handler = visitors[type];
|
|
279
|
+
if (handler) {
|
|
280
|
+
const res = handler(node, parents);
|
|
281
|
+
if (res === 'break') {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
const isNew = node !== parents[parents.length - 1];
|
|
286
|
+
if (isNew) {
|
|
287
|
+
parents.push(node);
|
|
288
|
+
}
|
|
289
|
+
baseVisitor[type](node, st, visitor);
|
|
290
|
+
if (isNew) {
|
|
291
|
+
parents.pop();
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
visitor(rootNode);
|
|
295
|
+
},
|
|
296
|
+
|
|
297
|
+
updateOffsetToLocation: (locator, loc) => {
|
|
298
|
+
const sLoc = locator.offsetToLocation(loc.start);
|
|
299
|
+
loc.start = {
|
|
300
|
+
line: sLoc.line,
|
|
301
|
+
column: sLoc.column
|
|
302
|
+
};
|
|
303
|
+
const eLoc = locator.offsetToLocation(loc.end);
|
|
304
|
+
loc.end = {
|
|
305
|
+
line: eLoc.line,
|
|
306
|
+
column: eLoc.column
|
|
307
|
+
};
|
|
308
|
+
},
|
|
309
|
+
|
|
310
|
+
forEachFile: function(dir, extList, callback) {
|
|
311
|
+
if (!fs.existsSync(dir)) {
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
// all exts, full name, ext
|
|
315
|
+
const isMatched = (name) => !Util.isList(extList) || extList.includes(name) || extList.includes(path.extname(name));
|
|
316
|
+
const subDirs = [];
|
|
317
|
+
const subDirHandler = () => {
|
|
318
|
+
if (!subDirs.length) {
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
for (const subDir of subDirs) {
|
|
323
|
+
const res = Util.forEachFile(subDir, extList, callback);
|
|
324
|
+
if (res === 'break') {
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
const list = fs.readdirSync(dir);
|
|
331
|
+
for (const name of list) {
|
|
332
|
+
const abs = path.resolve(dir, name);
|
|
333
|
+
const info = fs.statSync(abs);
|
|
334
|
+
if (info.isDirectory()) {
|
|
335
|
+
subDirs.push(abs);
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (info.isFile() && isMatched(name)) {
|
|
340
|
+
const res = callback(name, dir);
|
|
341
|
+
if (res === 'break') {
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
subDirHandler();
|
|
349
|
+
|
|
350
|
+
},
|
|
351
|
+
|
|
352
|
+
readFileSync: function(filePath) {
|
|
353
|
+
if (fs.existsSync(filePath)) {
|
|
354
|
+
// Returns: <string> | <Buffer>
|
|
355
|
+
const buf = fs.readFileSync(filePath);
|
|
356
|
+
if (Buffer.isBuffer(buf)) {
|
|
357
|
+
return buf.toString('utf8');
|
|
358
|
+
}
|
|
359
|
+
return buf;
|
|
360
|
+
}
|
|
361
|
+
},
|
|
362
|
+
|
|
363
|
+
readFile: async (filePath) => {
|
|
364
|
+
if (fs.existsSync(filePath)) {
|
|
365
|
+
const buf = await readFile(filePath).catch((e) => {
|
|
366
|
+
Util.logError(`read file: ${filePath} ${e.message || e}`);
|
|
367
|
+
});
|
|
368
|
+
if (Buffer.isBuffer(buf)) {
|
|
369
|
+
return buf.toString('utf8');
|
|
370
|
+
}
|
|
371
|
+
return buf;
|
|
372
|
+
}
|
|
373
|
+
},
|
|
374
|
+
|
|
375
|
+
writeFileSync: function(filePath, content) {
|
|
376
|
+
if (!fs.existsSync(filePath)) {
|
|
377
|
+
const p = path.dirname(filePath);
|
|
378
|
+
if (!fs.existsSync(p)) {
|
|
379
|
+
fs.mkdirSync(p, {
|
|
380
|
+
recursive: true
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
fs.writeFileSync(filePath, content);
|
|
385
|
+
},
|
|
386
|
+
|
|
387
|
+
writeFile: async (filePath, content) => {
|
|
388
|
+
if (!fs.existsSync(filePath)) {
|
|
389
|
+
const p = path.dirname(filePath);
|
|
390
|
+
if (!fs.existsSync(p)) {
|
|
391
|
+
fs.mkdirSync(p, {
|
|
392
|
+
recursive: true
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
await writeFile(filePath, content).catch((e) => {
|
|
397
|
+
Util.logError(`write file: ${filePath} ${e.message || e}`);
|
|
398
|
+
});
|
|
399
|
+
},
|
|
400
|
+
|
|
401
|
+
rmSync: (p) => {
|
|
402
|
+
if (!fs.existsSync(p)) {
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
try {
|
|
406
|
+
fs.rmSync(p, {
|
|
407
|
+
recursive: true,
|
|
408
|
+
force: true,
|
|
409
|
+
maxRetries: 10
|
|
410
|
+
});
|
|
411
|
+
} catch (err) {
|
|
412
|
+
console.log(err.message);
|
|
413
|
+
}
|
|
414
|
+
},
|
|
415
|
+
|
|
416
|
+
getTemplate: function(templatePath) {
|
|
417
|
+
if (!Util.templateCache) {
|
|
418
|
+
Util.templateCache = {};
|
|
419
|
+
}
|
|
420
|
+
let template = Util.templateCache[templatePath];
|
|
421
|
+
if (!template) {
|
|
422
|
+
template = Util.readFileSync(templatePath);
|
|
423
|
+
if (template) {
|
|
424
|
+
Util.templateCache[templatePath] = template;
|
|
425
|
+
} else {
|
|
426
|
+
Util.logError(`not found template: ${templatePath}`);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
return template;
|
|
430
|
+
},
|
|
431
|
+
|
|
432
|
+
getColorStrByStatus: (str, status) => {
|
|
433
|
+
if (status === 'low') {
|
|
434
|
+
return EC.red(str);
|
|
435
|
+
}
|
|
436
|
+
if (status === 'medium') {
|
|
437
|
+
return EC.yellow(str);
|
|
438
|
+
}
|
|
439
|
+
if (status === 'high') {
|
|
440
|
+
return EC.green(str);
|
|
441
|
+
}
|
|
442
|
+
return str;
|
|
443
|
+
},
|
|
444
|
+
|
|
445
|
+
// ==========================================================================================
|
|
446
|
+
|
|
447
|
+
loggingLevels: {
|
|
448
|
+
off: 0,
|
|
449
|
+
error: 10,
|
|
450
|
+
info: 20,
|
|
451
|
+
debug: 30
|
|
452
|
+
},
|
|
453
|
+
|
|
454
|
+
isDebug: () => {
|
|
455
|
+
return Util.loggingType === 'debug';
|
|
456
|
+
},
|
|
457
|
+
|
|
458
|
+
initLoggingLevel: (level) => {
|
|
459
|
+
|
|
460
|
+
const types = {
|
|
461
|
+
off: 'off',
|
|
462
|
+
error: 'error',
|
|
463
|
+
info: 'info',
|
|
464
|
+
debug: 'debug'
|
|
465
|
+
};
|
|
466
|
+
const type = types[level] || types.info;
|
|
467
|
+
Util.loggingType = type;
|
|
468
|
+
Util.loggingLevel = Util.loggingLevels[type];
|
|
469
|
+
|
|
470
|
+
// console.log('=========================================');
|
|
471
|
+
// console.log(from, Util.loggingType, Util.loggingLevel);
|
|
472
|
+
|
|
473
|
+
return type;
|
|
474
|
+
},
|
|
475
|
+
|
|
476
|
+
logError: (message) => {
|
|
477
|
+
if (Util.loggingLevel < Util.loggingLevels.error) {
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
EC.logRed(`[MCR] ${message}`);
|
|
481
|
+
},
|
|
482
|
+
|
|
483
|
+
logInfo: (message) => {
|
|
484
|
+
if (Util.loggingLevel < Util.loggingLevels.info) {
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
console.log(`[MCR] ${message}`);
|
|
488
|
+
},
|
|
489
|
+
|
|
490
|
+
// grid is info level
|
|
491
|
+
logGrid: (gridData) => {
|
|
492
|
+
if (Util.loggingLevel < Util.loggingLevels.info) {
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
CG(gridData);
|
|
496
|
+
},
|
|
497
|
+
|
|
498
|
+
logDebug: (message) => {
|
|
499
|
+
if (Util.loggingLevel < Util.loggingLevels.debug) {
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
console.log(`[MCR] ${message}`);
|
|
503
|
+
},
|
|
504
|
+
|
|
505
|
+
// time is debug level, fix the log info, for checking snapshot
|
|
506
|
+
logTime: (message, time_start) => {
|
|
507
|
+
if (Util.loggingLevel < Util.loggingLevels.debug) {
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
const duration = Date.now() - time_start;
|
|
511
|
+
const durationH = Util.TSF(duration);
|
|
512
|
+
const ls = [`[MCR] ${message}`, ' ('];
|
|
513
|
+
if (duration <= 100) {
|
|
514
|
+
ls.push(EC.green(durationH));
|
|
515
|
+
} else if (duration < 500) {
|
|
516
|
+
ls.push(EC.yellow(durationH));
|
|
517
|
+
} else {
|
|
518
|
+
ls.push(EC.red(durationH));
|
|
519
|
+
}
|
|
520
|
+
ls.push(')');
|
|
521
|
+
console.log(ls.join(''));
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
};
|
|
525
|
+
|
|
526
|
+
module.exports = Util;
|