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/lib/cli.js CHANGED
@@ -1,216 +1,251 @@
1
1
  #!/usr/bin/env node
2
-
3
- const fs = require('fs');
4
- const path = require('path');
5
- const EC = require('eight-colors');
6
- const { fileURLToPath, pathToFileURL } = require('url');
7
- const {
8
- foregroundChild,
9
- program
10
- } = require('./packages/monocart-coverage-vendor.js');
11
-
12
- const MCR = require('./index.js');
13
- const Util = require('./utils/util.js');
14
- const defaultOptions = require('./default/options.js');
15
- const { resolveSourceMap } = require('./converter/collect-source-maps.js');
16
-
17
- const version = require('../package.json').version;
18
-
19
- const cleanDir = (dir) => {
20
- Util.rmSync(dir);
21
- };
22
-
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);
78
- if (configPath) {
79
- const configOptions = await resolveConfigOptions(configPath);
80
- if (configOptions) {
81
- cliOptions = {
82
- ... configOptions,
83
- ... cliOptions
84
- };
85
- }
86
- }
87
-
88
- // report options
89
- const options = {
90
- ... defaultOptions
91
- };
92
- Object.keys(cliOptions).forEach((k) => {
93
- if (Util.hasOwn(options, k)) {
94
- options[k] = cliOptions[k];
95
- }
96
- });
97
-
98
- return options;
99
- };
100
-
101
- const executeCommand = async (command, cliOptions) => {
102
-
103
- const options = await initOptions(cliOptions);
104
-
105
- Util.logInfo(`Execute: ${EC.cyan(command)}`);
106
- // console.log(options);
107
-
108
- const nodeV8CoverageDir = path.resolve(options.outputDir, '.v8-coverage');
109
- process.env.NODE_V8_COVERAGE = nodeV8CoverageDir;
110
-
111
- cleanDir(nodeV8CoverageDir);
112
-
113
- // console.log('nodeV8CoverageDir', nodeV8CoverageDir);
114
-
115
- const coverageReport = MCR(options);
116
- coverageReport.cleanCache();
117
-
118
- // on start hook
119
- const onStart = cliOptions.onStart;
120
- if (typeof onStart === 'function') {
121
- await onStart(coverageReport);
122
- }
123
-
124
- foregroundChild(command, {
125
- shell: true
126
- }, async () => {
127
-
128
- if (!fs.existsSync(nodeV8CoverageDir)) {
129
- EC.logRed(`Not found coverage data: ${Util.relativePath(nodeV8CoverageDir)}`);
130
- return 1;
131
- }
132
-
133
- const files = fs.readdirSync(nodeV8CoverageDir);
134
- for (const filename of files) {
135
- const content = fs.readFileSync(path.resolve(nodeV8CoverageDir, filename)).toString('utf-8');
136
- const json = JSON.parse(content);
137
- let coverageList = json.result;
138
-
139
- // for node sourcemap
140
- const sourceMapCache = json['source-map-cache'] || {};
141
-
142
- // filter node internal files
143
- coverageList = coverageList.filter((entry) => entry.url && entry.url.startsWith('file:'));
144
-
145
- if (!coverageList.length) {
146
- continue;
147
- }
148
- // attached source content
149
- coverageList.forEach((entry) => {
150
- // source content
151
- const filePath = fileURLToPath(entry.url);
152
- if (fs.existsSync(filePath)) {
153
- entry.source = fs.readFileSync(filePath).toString('utf8');
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
- }
172
- }
173
- });
174
-
175
- await coverageReport.add(coverageList);
176
- }
177
-
178
- await coverageReport.generate();
179
-
180
- // remove nodeV8CoverageDir
181
- if (options.logging !== 'debug') {
182
- cleanDir(nodeV8CoverageDir);
183
- }
184
-
185
- return process.exitCode;
186
- });
187
-
188
- };
189
-
190
- program
191
- .name('mcr')
192
- .description('CLI to generate coverage reports')
193
- .version(version)
194
- .argument('<command>', 'command to execute')
195
- .option('-c, --config <path>', 'custom config path')
196
- .option('-o, --outputDir <dir>', 'output dir for reports')
197
- .option('-r, --reports <name[,name]>', 'coverage reports to use')
198
- .option('-n, --name <name>', 'report name for title')
199
- .option('-i, --inputDir <dir>', 'input dir for merging raw files')
200
-
201
- .option('--entryFilter <pattern>', 'entry url filter')
202
- .option('--sourceFilter <pattern>', 'source path filter')
203
-
204
- .option('--outputFile <path>', 'output file for v8 report')
205
- .option('--inline', 'inline html for v8 report')
206
- .option('--assetsPath <path>', 'assets path if not inline')
207
-
208
- .option('--lcov', 'generate lcov.info file')
209
-
210
- .option('--logging <logging>', 'off, error, info, debug')
211
-
212
- .action((command, cliOptions) => {
213
- executeCommand(command, cliOptions);
214
- });
215
-
216
- program.parse();
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const EC = require('eight-colors');
6
+ const { fileURLToPath, pathToFileURL } = require('url');
7
+
8
+ const {
9
+ foregroundChild,
10
+ program
11
+ } = require('./packages/monocart-coverage-vendor.js');
12
+
13
+ const MCR = require('./index.js');
14
+ const Util = require('./utils/util.js');
15
+ const defaultOptions = require('./default/options.js');
16
+ const { resolveSourceMap } = require('./converter/collect-source-maps.js');
17
+
18
+ const version = require('../package.json').version;
19
+
20
+ const cleanDir = (dir) => {
21
+ Util.rmSync(dir);
22
+ };
23
+
24
+ const resolveConfigPath = (customConfigPath) => {
25
+
26
+ const configList = [
27
+ '.mcrrc',
28
+ 'mcr.config.json',
29
+ 'mcr.config.mjs',
30
+ 'mcr.config.cjs',
31
+ 'mcr.config.js'
32
+ ];
33
+
34
+ if (customConfigPath) {
35
+ configList.push(customConfigPath);
36
+ }
37
+
38
+ const existsList = configList.filter((p) => fs.existsSync(p));
39
+ if (!existsList.length) {
40
+ return;
41
+ }
42
+
43
+ // last one
44
+ const configPath = existsList.pop();
45
+ Util.logInfo(`Config: ${EC.cyan(configPath)}`);
46
+
47
+ return configPath;
48
+ };
49
+
50
+ const resolveConfigOptions = async (configPath) => {
51
+
52
+ // json format
53
+ const ext = path.extname(configPath);
54
+ if (ext === '.json' || configPath.slice(-2) === 'rc') {
55
+ return JSON.parse(Util.readFileSync(configPath));
56
+ }
57
+
58
+ let configOptions;
59
+ let err;
60
+ try {
61
+ configOptions = await import(pathToFileURL(configPath));
62
+ } catch (ee) {
63
+ err = ee;
64
+ }
65
+
66
+ if (!configOptions) {
67
+ Util.logError(`ERROR: failed to import "${configPath}" ${err.message} `);
68
+ return;
69
+ }
70
+
71
+ configOptions = configOptions.default || configOptions;
72
+
73
+ return configOptions;
74
+ };
75
+
76
+ const initOptions = async (cliOptions) => {
77
+
78
+ const configPath = resolveConfigPath(cliOptions.config);
79
+ if (configPath) {
80
+ const configOptions = await resolveConfigOptions(configPath);
81
+ if (configOptions) {
82
+ cliOptions = {
83
+ ... configOptions,
84
+ ... cliOptions
85
+ };
86
+ }
87
+ }
88
+
89
+ // report options
90
+ const options = {
91
+ ... defaultOptions
92
+ };
93
+ Object.keys(cliOptions).forEach((k) => {
94
+ if (Util.hasOwn(options, k)) {
95
+ options[k] = cliOptions[k];
96
+ }
97
+ });
98
+
99
+ return options;
100
+ };
101
+
102
+ const resolveEntrySource = (entry, sourceMapCache = {}) => {
103
+ let source;
104
+ const filePath = fileURLToPath(entry.url);
105
+ const extname = path.extname(filePath);
106
+ if (fs.existsSync(filePath)) {
107
+ source = fs.readFileSync(filePath).toString('utf8');
108
+ }
109
+
110
+ // not for typescript
111
+ if (source && !['.ts', '.tsx'].includes(extname)) {
112
+ entry.source = source;
113
+ return;
114
+ }
115
+
116
+ const sourcemapData = sourceMapCache[entry.url];
117
+ const lineLengths = sourcemapData && sourcemapData.lineLengths;
118
+
119
+ // for fake source file (can not parse to AST)
120
+ if (lineLengths) {
121
+ // get runtime code with ts-node
122
+ let fakeSource = '';
123
+ sourcemapData.lineLengths.forEach((length) => {
124
+ fakeSource += `${''.padEnd(length, '*')}\n`;
125
+ });
126
+ entry.fake = true;
127
+ entry.source = fakeSource;
128
+ }
129
+
130
+ // Note: no runtime code in source map cache
131
+ // This is a problem for typescript
132
+
133
+ };
134
+
135
+ const resolveEntrySourceMap = (entry, sourceMapCache = {}) => {
136
+ // sourcemap data
137
+ const sourcemapData = sourceMapCache[entry.url];
138
+ if (sourcemapData) {
139
+ if (sourcemapData.data) {
140
+ entry.sourceMap = resolveSourceMap(sourcemapData.data, entry.url);
141
+ }
142
+ }
143
+ };
144
+
145
+ const collectCoverageData = (coverageList, entryFilterHandler, sourceMapCache = {}) => {
146
+
147
+ if (!Util.isList(coverageList)) {
148
+ return;
149
+ }
150
+
151
+ // filter node internal files
152
+ coverageList = coverageList.filter((entry) => entry.url && entry.url.startsWith('file:'));
153
+ coverageList = coverageList.filter(entryFilterHandler);
154
+
155
+ if (!Util.isList(coverageList)) {
156
+ return;
157
+ }
158
+
159
+ for (const entry of coverageList) {
160
+ resolveEntrySource(entry, sourceMapCache);
161
+ resolveEntrySourceMap(entry, sourceMapCache);
162
+ }
163
+
164
+ return coverageList;
165
+ };
166
+
167
+ const executeCommand = async (command, cliOptions) => {
168
+
169
+ const options = await initOptions(cliOptions);
170
+
171
+ Util.logInfo(`Execute: ${EC.cyan(command)}`);
172
+ // console.log(options);
173
+
174
+ const nodeV8CoverageDir = path.resolve(options.outputDir, '.v8-coverage');
175
+ process.env.NODE_V8_COVERAGE = nodeV8CoverageDir;
176
+
177
+ cleanDir(nodeV8CoverageDir);
178
+
179
+ // console.log('nodeV8CoverageDir', nodeV8CoverageDir);
180
+
181
+ const coverageReport = MCR(options);
182
+ coverageReport.cleanCache();
183
+
184
+ // on start hook
185
+ const onStart = cliOptions.onStart;
186
+ if (typeof onStart === 'function') {
187
+ await onStart(coverageReport);
188
+ }
189
+
190
+ foregroundChild(command, {
191
+ shell: true
192
+ }, async () => {
193
+
194
+ if (!fs.existsSync(nodeV8CoverageDir)) {
195
+ EC.logRed(`Not found coverage data: ${Util.relativePath(nodeV8CoverageDir)}`);
196
+ return 1;
197
+ }
198
+
199
+ const entryFilterHandler = coverageReport.getEntryFilter();
200
+
201
+ const files = fs.readdirSync(nodeV8CoverageDir);
202
+ for (const filename of files) {
203
+ const content = fs.readFileSync(path.resolve(nodeV8CoverageDir, filename)).toString('utf-8');
204
+ const json = JSON.parse(content);
205
+ const coverageList = json.result;
206
+ const sourceMapCache = json['source-map-cache'];
207
+ const coverageData = await collectCoverageData(coverageList, entryFilterHandler, sourceMapCache);
208
+ if (coverageData) {
209
+ await coverageReport.add(coverageData);
210
+ }
211
+ }
212
+
213
+ await coverageReport.generate();
214
+
215
+ // remove nodeV8CoverageDir
216
+ if (options.logging !== 'debug') {
217
+ cleanDir(nodeV8CoverageDir);
218
+ }
219
+
220
+ return process.exitCode;
221
+ });
222
+
223
+ };
224
+
225
+ program
226
+ .name('mcr')
227
+ .description('CLI to generate coverage reports')
228
+ .version(version)
229
+ .argument('<command>', 'command to execute')
230
+ .option('-c, --config <path>', 'custom config path')
231
+ .option('-o, --outputDir <dir>', 'output dir for reports')
232
+ .option('-r, --reports <name[,name]>', 'coverage reports to use')
233
+ .option('-n, --name <name>', 'report name for title')
234
+ .option('-i, --inputDir <dir>', 'input dir for merging raw files')
235
+
236
+ .option('--entryFilter <pattern>', 'entry url filter')
237
+ .option('--sourceFilter <pattern>', 'source path filter')
238
+
239
+ .option('--outputFile <path>', 'output file for v8 report')
240
+ .option('--inline', 'inline html for v8 report')
241
+ .option('--assetsPath <path>', 'assets path if not inline')
242
+
243
+ .option('--lcov', 'generate lcov.info file')
244
+
245
+ .option('--logging <logging>', 'off, error, info, debug')
246
+
247
+ .action((command, cliOptions) => {
248
+ executeCommand(command, cliOptions);
249
+ });
250
+
251
+ program.parse();