escover 6.2.8 → 6.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.
package/ChangeLog CHANGED
@@ -1,3 +1,20 @@
1
+ 2026.05.13, v6.3.0
2
+
3
+ fix:
4
+ - 895b192 @escover/formatter-lines: badges
5
+
6
+ feature:
7
+ - c7460f1 escover: move out formatters
8
+ - f03afbb @escover/formatter-responsive: move out
9
+ - 06462d6 escover: formatter-istanbul: move out
10
+ - 9afc535 @escover/istanbul: move out
11
+ - 9f2930e escover: convert to monorepo
12
+
13
+ 2026.05.09, v6.2.9
14
+
15
+ feature:
16
+ - 2d5d9b3 build-grouped-table: indent files
17
+
1
18
  2026.05.09, v6.2.8
2
19
 
3
20
  feature:
package/lib/cli/cli.js CHANGED
@@ -3,9 +3,6 @@ import process from 'node:process';
3
3
  import {tryCatch} from 'try-catch';
4
4
  import yargsParser from 'yargs-parser';
5
5
  import {version} from './version.js';
6
- import reportLines from '../formatters/lines.js';
7
- import reportFiles from '../formatters/files.js';
8
- import reportResponsive from '../formatters/responsive/responsive.js';
9
6
  import {readConfig} from '../config.js';
10
7
  import {help} from './help.js';
11
8
 
@@ -19,7 +16,7 @@ export const createNodeOptions = (options = NODE_OPTIONS || '') => {
19
16
  return options;
20
17
  };
21
18
 
22
- export const cli = ({argv, exit, readCoverage}) => {
19
+ export const cli = async ({argv, exit, readCoverage}) => {
23
20
  const {skipFull} = readConfig();
24
21
  const args = yargsParser(argv.slice(2), {
25
22
  string: ['format'],
@@ -30,7 +27,7 @@ export const cli = ({argv, exit, readCoverage}) => {
30
27
  h: 'help',
31
28
  },
32
29
  default: {
33
- 'format': ESCOVER_FORMAT || 'responsive',
30
+ 'format': ESCOVER_FORMAT || 'istanbul',
34
31
  'skip-full': skipFull,
35
32
  },
36
33
  });
@@ -57,17 +54,24 @@ export const cli = ({argv, exit, readCoverage}) => {
57
54
 
58
55
  const coverage = readCoverage();
59
56
 
60
- let output = '';
57
+ const {default: report} = await import(`@escover/formatter-${args.format}`);
61
58
 
59
+ const output = report(coverage, {
60
+ skipFull,
61
+ });
62
+
63
+ /*
62
64
  if (args.format === 'lines')
63
65
  output = reportLines(coverage);
64
66
  else if (args.format === 'responsive')
65
67
  output = reportResponsive(coverage, {
66
68
  skipFull: args.skipFull,
67
69
  });
70
+ else if (args.format === 'istanbul')
71
+ output = reportIstanbul(coverage);
68
72
  else
69
73
  output = reportFiles(coverage);
70
-
74
+ */
71
75
  process.stdout.write(output);
72
76
  };
73
77
 
package/lib/register.js CHANGED
@@ -5,6 +5,4 @@ import {writeCoverage} from './coverage-file/coverage-file.js';
5
5
 
6
6
  registerHooks(escoverLoader);
7
7
 
8
- process.on('exit', () => {
9
- writeCoverage();
10
- });
8
+ process.once('exit', writeCoverage);
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "escover",
3
- "version": "6.2.8",
3
+ "version": "6.3.0",
4
4
  "author": "coderaiser <mnemonic.enemy@gmail.com> (https://github.com/coderaiser)",
5
5
  "description": "Coverage for EcmaScript Modules",
6
- "main": "lib/escover.js",
6
+ "main": "packages/escover/lib/escover.js",
7
7
  "type": "module",
8
8
  "bin": {
9
9
  "escover": "bin/escover.js"
@@ -13,10 +13,7 @@
13
13
  "url": "git+https://github.com/coderaiser/escover.git"
14
14
  },
15
15
  "exports": {
16
- ".": {
17
- "import": "./lib/escover.js"
18
- },
19
- "./fresh": "./lib/fresh.js",
16
+ ".": "./lib/escover.js",
20
17
  "./register": "./lib/register.js",
21
18
  "./plugin": "./lib/instrument/plugin-mark/index.js",
22
19
  "./instrument": "./lib/instrument/index.js"
@@ -41,6 +38,8 @@
41
38
  "prewisdom": "madrun prewisdom"
42
39
  },
43
40
  "dependencies": {
41
+ "@escover/formatter-files": "^1.0.1",
42
+ "@escover/formatter-lines": "^1.0.1",
44
43
  "@putout/plugin-convert-optional-to-logical": "^4.0.0",
45
44
  "chalk": "^5.0.0",
46
45
  "find-cache-dir": "^5.0.0",
@@ -1,116 +0,0 @@
1
- import process from 'node:process';
2
- import {
3
- table,
4
- getBorderCharacters,
5
- } from 'table';
6
- import chalk from 'chalk';
7
-
8
- const CWD = process.cwd();
9
-
10
- const {entries, keys} = Object;
11
-
12
- const makeGreen = chalk.hex('#4caf50');
13
- const makeRed = chalk.hex('#f44336');
14
-
15
- export default (coverageFile) => {
16
- const files = parseCoverageFile(coverageFile);
17
-
18
- const tableData = [
19
- ['File', '% Lines', 'Uncovered Lines #s'],
20
- ];
21
-
22
- for (const {filename, covered, lines, percentLines} of files) {
23
- const uncoveredLines = formatLines(lines);
24
-
25
- if (covered) {
26
- tableData.push([
27
- makeGreen(filename),
28
- makeGreen(percentLines),
29
- '',
30
- ]);
31
- continue;
32
- }
33
-
34
- tableData.push([
35
- makeRed(filename),
36
- makeRed(percentLines),
37
- makeRed(uncoveredLines),
38
- ]);
39
- }
40
-
41
- return table(tableData, {
42
- drawHorizontalLine: (raw) => {
43
- return !raw || raw === 1 || raw === files.length + 1;
44
- },
45
- columns: [{
46
- paddingLeft: 0,
47
- }, {
48
- alignment: 'center',
49
- }],
50
- border: {
51
- ...getBorderCharacters('void'),
52
- topBody: '-',
53
- bottomBody: '-',
54
- joinBody: '-',
55
- topJoin: '|',
56
- bottomJoin: '|',
57
- joinJoin: '|',
58
- bodyJoin: '|',
59
- },
60
- });
61
- };
62
-
63
- export function formatLines(array) {
64
- const lines = array
65
- .slice(0, 10)
66
- .join(', ');
67
-
68
- if (array.length <= 10)
69
- return lines;
70
-
71
- const latest = array.at(-1);
72
-
73
- return `${lines}..${latest}`;
74
- }
75
-
76
- function parseUncoveredLines(lines) {
77
- const uncoveredLines = [];
78
-
79
- for (const [line, covered] of entries(lines)) {
80
- if (covered)
81
- continue;
82
-
83
- uncoveredLines.push(line);
84
- }
85
-
86
- return uncoveredLines;
87
- }
88
-
89
- function parseCoverageFile(coverageFile) {
90
- const files = [];
91
-
92
- for (const {name, lines} of coverageFile) {
93
- const filename = name.replace(`${CWD}/`, '');
94
- const uncoveredLines = parseUncoveredLines(lines);
95
-
96
- const linesCount = keys(lines).length;
97
- const uncoveredLinesCount = uncoveredLines.length;
98
- const percentLines = getLinesPercent(linesCount, uncoveredLinesCount);
99
-
100
- files.push({
101
- filename,
102
- covered: !uncoveredLinesCount,
103
- lines: uncoveredLines,
104
- percentLines,
105
- });
106
- }
107
-
108
- return files;
109
- }
110
-
111
- export function getLinesPercent(linesCount, uncoveredLinesCount) {
112
- if (!linesCount)
113
- return 100;
114
-
115
- return 100 - Math.round(100 / linesCount * uncoveredLinesCount);
116
- }
@@ -1,74 +0,0 @@
1
- import chalk from 'chalk';
2
-
3
- const {entries} = Object;
4
- const createOut = (output) => (a) => output.push(a);
5
-
6
- export default (coverageFile) => {
7
- const output = [];
8
- const out = createOut(output);
9
- const files = [];
10
-
11
- const coverage = {
12
- files,
13
- coveredCount: 0,
14
- uncoveredCount: 0,
15
- };
16
-
17
- out('# CAP version 13');
18
- out('');
19
-
20
- for (const {name, lines} of coverageFile) {
21
- const uncoveredLines = [];
22
-
23
- for (const [line, covered] of entries(lines)) {
24
- if (covered)
25
- continue;
26
-
27
- uncoveredLines.push(line);
28
- }
29
-
30
- const file = {
31
- name,
32
- covered: !uncoveredLines.length,
33
- uncoveredLines,
34
- };
35
-
36
- if (file.covered)
37
- ++coverage.coveredCount;
38
-
39
- if (!file.covered)
40
- ++coverage.uncoveredCount;
41
-
42
- files.push(file);
43
- }
44
-
45
- for (const {name, covered, uncoveredLines} of files) {
46
- if (!covered) {
47
- out(`# ${name}`);
48
- out('🧨 should be covered');
49
- out('---');
50
- out(`lines:`);
51
-
52
- for (const line of uncoveredLines) {
53
- out(`️- ${chalk.red(line)} at file://${name}:${line}`);
54
- }
55
-
56
- out('');
57
- }
58
- }
59
-
60
- out(`1..${files.length}`);
61
- out(`# files: ${files.length}`);
62
- out(`# covered: ${coverage.coveredCount}`);
63
- out('');
64
-
65
- if (!coverage.uncoveredCount)
66
- out('# 🌴 ok');
67
-
68
- if (coverage.uncoveredCount)
69
- out(`# 🧨 fail: ${coverage.uncoveredCount}`);
70
-
71
- out('');
72
-
73
- return output.join('\n');
74
- };
@@ -1,78 +0,0 @@
1
- import chalk from 'chalk';
2
- import {formatLines} from './format-lines.js';
3
- import {groupByFolder} from './group-by-folder.js';
4
-
5
- const makeGreen = chalk.hex('#4caf50');
6
- const makeRed = chalk.hex('#f44336');
7
-
8
- export const getColor = (value) => value === 100 ? makeGreen : makeRed;
9
-
10
- function truncateLeft(str, maxLength) {
11
- if (str.length <= maxLength)
12
- return str;
13
-
14
- return `...${str.slice(str.length - maxLength + 5)}`;
15
- }
16
-
17
- export function buildGroupedTable({files, showPercent, linesColWidth, fileColWidth}) {
18
- const tableData = [];
19
-
20
- if (showPercent)
21
- tableData.push([
22
- 'File',
23
- '% Lines',
24
- 'Uncovered Lines #s',
25
- ]);
26
- else
27
- tableData.push([
28
- 'File',
29
- 'Uncovered Lines #s',
30
- ]);
31
-
32
- const groups = groupByFolder(files);
33
- const hideFolders = groups.size === 1;
34
-
35
- for (const [folder, group] of groups) {
36
- let sum = 0;
37
-
38
- for (const f of group.files)
39
- sum += f.percentLines;
40
-
41
- const coverage = Math.round(sum / group.files.length);
42
-
43
- if (!hideFolders) {
44
- const row = [];
45
-
46
- const shortFolder = truncateLeft(folder, fileColWidth);
47
- row.push(getColor(coverage)(shortFolder));
48
-
49
- if (showPercent)
50
- row.push(getColor(coverage)(`${coverage}%`));
51
-
52
- row.push('');
53
- tableData.push(row);
54
- }
55
-
56
- for (const {covered, lines, fileName, percentLines} of group.files) {
57
- const row = [];
58
- const shortName = truncateLeft(fileName, fileColWidth);
59
- const coloredShortName = covered ? makeGreen(shortName) : makeRed(shortName);
60
-
61
- row.push(coloredShortName);
62
-
63
- if (showPercent) {
64
- const percent = percentLines === 100 ? makeGreen(`${percentLines}%`) : makeRed(`${percentLines}%`);
65
- row.push(percent);
66
- }
67
-
68
- row.push(covered ? '' : makeRed(formatLines(
69
- lines,
70
- linesColWidth,
71
- )));
72
-
73
- tableData.push(row);
74
- }
75
- }
76
-
77
- return tableData;
78
- }
@@ -1,60 +0,0 @@
1
- export function formatLines(nums, maxLen = 20) {
2
- const ranges = [];
3
-
4
- for (let i = 0; i < nums.length; i++) {
5
- const [j, range] = maybeRange(nums, i);
6
- ranges.push(range);
7
- i = j - 1;
8
- }
9
-
10
- const joined = ranges.join(', ');
11
-
12
- if (joined.length <= maxLen)
13
- return joined;
14
-
15
- return getShortenedRange(ranges, maxLen);
16
- }
17
-
18
- export function getShortenedRange(ranges, maxLen) {
19
- const last = ranges
20
- .at(-1)
21
- .toString();
22
-
23
- for (let i = 1; i < ranges.length - 1; i++) {
24
- const joined = `${ranges
25
- .slice(0, -i)
26
- .join(', ')} ... ${last}`;
27
-
28
- if (joined.length <= maxLen)
29
- return joined;
30
- }
31
-
32
- const first = ranges.at(0);
33
- const firstLast = `${first} ... ${last}`;
34
-
35
- if (firstLast.length <= maxLen)
36
- return firstLast;
37
-
38
- return `${ranges.at(0)} ...`;
39
- }
40
-
41
- function maybeRange(nums, i) {
42
- const start = nums.at(i);
43
- let prev = nums.at(i);
44
-
45
- for (++i; i < nums.length; i++) {
46
- const n = nums[i];
47
-
48
- if (n === prev + 1) {
49
- prev = n;
50
- continue;
51
- }
52
-
53
- break;
54
- }
55
-
56
- if (start === prev)
57
- return [i, start];
58
-
59
- return [i, `${start}..${prev}`];
60
- }
@@ -1,28 +0,0 @@
1
- export function groupByFolder(files) {
2
- const groups = new Map();
3
-
4
- for (const f of files) {
5
- const parts = f.filename.split('/');
6
- const folder = parts.length > 1 ? parts
7
- .slice(0, -1)
8
- .join('/') : '';
9
-
10
- const fileName = parts.at(-1);
11
-
12
- let group = groups.get(folder);
13
-
14
- if (!group) {
15
- group = {
16
- files: [],
17
- };
18
- groups.set(folder, group);
19
- }
20
-
21
- group.files.push({
22
- ...f,
23
- fileName,
24
- });
25
- }
26
-
27
- return groups;
28
- }
@@ -1,122 +0,0 @@
1
- import process from 'node:process';
2
- import {
3
- table,
4
- getBorderCharacters,
5
- } from 'table';
6
- import {buildGroupedTable} from './build-grouped-table.js';
7
-
8
- const CWD = process.cwd();
9
- const {entries, keys} = Object;
10
-
11
- export default (coverageFile, {skipFull = false} = {}) => {
12
- const files = parseCoverageFile(coverageFile, skipFull);
13
-
14
- if (skipFull && !files.length)
15
- return '💪 coverage 100% Good Job!\n';
16
-
17
- const totalWidth = process.stdout.columns || 80;
18
- const showPercent = totalWidth >= 70;
19
-
20
- const linesColWidth = Math.floor(totalWidth / 2);
21
- const percentColWidth = showPercent ? 7 : 0;
22
-
23
- const fileColWidth = Math.max(10, totalWidth - linesColWidth - percentColWidth - 6 - 4);
24
- const tableData = buildGroupedTable({
25
- files,
26
- showPercent,
27
- linesColWidth,
28
- fileColWidth,
29
- });
30
-
31
- const options = createTableOptions({
32
- showPercent,
33
- tableData,
34
- fileColWidth,
35
- percentColWidth,
36
- linesColWidth,
37
- });
38
-
39
- return table(tableData, options);
40
- };
41
-
42
- export function createTableOptions({showPercent, tableData, fileColWidth, percentColWidth, linesColWidth}) {
43
- const columns = [{
44
- paddingLeft: 1,
45
- paddingRight: 1,
46
- width: fileColWidth,
47
- wrapWord: false,
48
- }];
49
-
50
- if (showPercent)
51
- columns.push({
52
- alignment: 'center',
53
- paddingLeft: 1,
54
- paddingRight: 1,
55
- width: percentColWidth,
56
- });
57
-
58
- columns.push({
59
- paddingLeft: 1,
60
- paddingRight: 1,
61
- width: linesColWidth,
62
- wrapWord: false,
63
- });
64
-
65
- return {
66
- drawHorizontalLine: (i) => !i || i === 1 || i === tableData.length,
67
- columns,
68
- border: {
69
- ...getBorderCharacters('void'),
70
- topBody: '-',
71
- bottomBody: '-',
72
- joinBody: '-',
73
- topJoin: '|',
74
- bottomJoin: '|',
75
- joinJoin: '|',
76
- bodyJoin: '|',
77
- },
78
- };
79
- }
80
-
81
- export function parseCoverageFile(coverageFile, skipFull) {
82
- const files = [];
83
-
84
- for (const {name, lines} of coverageFile) {
85
- const uncovered = parseUncoveredLines(lines);
86
- const linesCount = keys(lines).length;
87
- const uncoveredLinesCount = uncovered.length;
88
- const percentLines = getLinesPercent(linesCount, uncoveredLinesCount);
89
- const covered = uncoveredLinesCount === 0;
90
-
91
- if (skipFull && covered)
92
- continue;
93
-
94
- files.push({
95
- filename: name.replace(`${CWD}/`, ''),
96
- covered,
97
- lines: uncovered,
98
- percentLines,
99
- });
100
- }
101
-
102
- return files;
103
- }
104
-
105
- export function parseUncoveredLines(lines) {
106
- const out = [];
107
-
108
- for (const [line, covered] of entries(lines))
109
- if (!covered)
110
- out.push(Number(line));
111
-
112
- return out;
113
- }
114
-
115
- export function getLinesPercent(linesCount, uncoveredLinesCount) {
116
- if (!linesCount)
117
- return 100;
118
-
119
- const ratio = 100 / linesCount * uncoveredLinesCount;
120
-
121
- return 100 - Math.round(ratio);
122
- }