-
-
Notifications
You must be signed in to change notification settings - Fork 64
/
Copy pathoutput.js
294 lines (276 loc) · 12 KB
/
output.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
// Output management
import ansiColors from "ansi-colors";
import fs from "fs-extra";
import { SarifBuilder, SarifRunBuilder, SarifResultBuilder, SarifRuleBuilder } from "node-sarif-builder";
import * as path from "path";
import { isErrorInLogLevelScope, getNpmGroovyLintVersion } from "./utils.js";
// Compute statistics for output
function computeStats(lintResult) {
const counterResultsSummary = {
totalFoundNumber: 0,
totalFixedNumber: 0,
totalRemainingNumber: 0,
totalFoundErrorNumber: 0,
totalFoundWarningNumber: 0,
totalFoundInfoNumber: 0,
totalFixedErrorNumber: 0,
totalFixedWarningNumber: 0,
totalFixedInfoNumber: 0,
totalRemainingErrorNumber: 0,
totalRemainingWarningNumber: 0,
totalRemainingInfoNumber: 0,
detectedRules: {},
fixedRules: {},
};
// No files = no result (mostly happens when lint has been cancelled before being completed)
if (lintResult.files == null) {
return lintResult;
}
for (const fileName of Object.keys(lintResult.files)) {
const fileResults = lintResult.files[fileName];
const fileErrors = fileResults.errors || [];
const fileErrorStats = appendErrorTypeStats("error", fileErrors, counterResultsSummary);
const fileWarningStats = appendErrorTypeStats("warning", fileErrors, counterResultsSummary);
const fileInfoStats = appendErrorTypeStats("info", fileErrors, counterResultsSummary);
// Total ignoring severity
counterResultsSummary.totalFoundNumber =
counterResultsSummary.totalFoundNumber + fileErrorStats.found + fileWarningStats.found + fileInfoStats.found;
counterResultsSummary.totalFixedNumber =
counterResultsSummary.totalFixedNumber + fileErrorStats.fixed + fileWarningStats.fixed + fileInfoStats.fixed;
counterResultsSummary.totalRemainingNumber =
counterResultsSummary.totalRemainingNumber + fileErrorStats.remaining + fileWarningStats.remaining + fileInfoStats.remaining;
}
// Set summary
lintResult.summary = Object.assign(lintResult.summary || {}, counterResultsSummary);
return lintResult;
}
// Collect stats about errors in a linted / formatted / fixed file
function appendErrorTypeStats(severity, fileErrors, counterResultsSummary) {
// Found
const fileFoundSeverityNumber = fileErrors.filter((err) => {
return err.severity === severity;
}).length;
// Fixed
const fileFixedSeverityNumber = fileErrors.filter((err) => {
return err.severity === severity && err.fixed && err.fixed === true;
}).length;
// Remaining
const fileRemainingSeverityNumber = fileFoundSeverityNumber - fileFixedSeverityNumber;
// Add counters for each problem type
for (const err of fileErrors) {
if (err.severity === severity) {
counterResultsSummary.detectedRules[err.rule] = counterResultsSummary.detectedRules[err.rule]
? counterResultsSummary.detectedRules[err.rule] + 1
: 1;
if (err.fixed && err.fixed === true) {
counterResultsSummary.fixedRules[err.rule] = counterResultsSummary.fixedRules[err.rule]
? counterResultsSummary.fixedRules[err.rule] + 1
: 1;
}
}
}
// Update summary & build result
const severityCapital = `${severity[0].toUpperCase()}${severity.slice(1)}`;
const totalFoundKey = `totalFound${severityCapital}Number`;
const totalFixedKey = `totalFixed${severityCapital}Number`;
const totalRemainingKey = `totalRemaining${severityCapital}Number`;
counterResultsSummary[totalFoundKey] = counterResultsSummary[totalFoundKey] + fileFoundSeverityNumber;
counterResultsSummary[totalFixedKey] = counterResultsSummary[totalFixedKey] + fileFixedSeverityNumber;
counterResultsSummary[totalRemainingKey] = counterResultsSummary[totalRemainingKey] + fileRemainingSeverityNumber;
return {
found: fileFoundSeverityNumber,
fixed: fileFixedSeverityNumber,
remaining: fileRemainingSeverityNumber,
};
}
// Reformat output if requested in command line
async function processOutput(outputType, output, lintResult, options, fixer = null) {
let outputString = "";
// Display as console log
if (outputType === "txt") {
// Disable colors if output results in text file or no output result
if (output.includes(".txt") || output === "none") {
// Disable ansi colors if output in txt file
ansiColors.enabled = false;
}
// Errors
for (const fileNm of Object.keys(lintResult.files)) {
const fileErrors = lintResult.files[fileNm].errors;
let fileOutputString = ansiColors.underline(fileNm) + "\n";
let showFileInOutput = false;
for (const err of fileErrors) {
if (!isErrorInLogLevelScope(err.severity, options.loglevel)) {
continue;
}
showFileInOutput = true;
let color = "grey";
switch (err.severity) {
case "error":
color = "red";
break;
case "warning":
color = "yellow";
break;
case "info":
color = "grey";
break;
}
// Display fixed errors only if --verbose is called
if (err.fixed === true) {
if (options.verbose === true) {
color = "green";
err.severity = "fixed";
} else {
continue;
}
}
// Build error output line
fileOutputString +=
" " +
err.line.toString().padEnd(4, " ") +
" " +
ansiColors[color](err.severity.padEnd(7, " ")) +
" " +
err.msg +
" " +
err.rule.padEnd(24, " ") +
"\n";
}
fileOutputString += "\n";
if (showFileInOutput || options.verbose) {
outputString += fileOutputString;
}
}
outputString += "\nnpm-groovy-lint results in " + ansiColors.bold(lintResult.summary.totalFilesLinted) + " linted files:";
// Summary table
const errorTableLine = {
Severity: "Error",
"Total found": lintResult.summary.totalFoundErrorNumber,
"Total fixed": lintResult.summary.totalFixedErrorNumber,
"Total remaining": lintResult.summary.totalRemainingErrorNumber,
};
const warningTableLine = {
Severity: "Warning",
"Total found": lintResult.summary.totalFoundWarningNumber,
"Total fixed": lintResult.summary.totalFixedWarningNumber,
"Total remaining": lintResult.summary.totalRemainingWarningNumber,
};
const infoTableLine = {
Severity: "Info",
"Total found": lintResult.summary.totalFoundInfoNumber,
"Total fixed": lintResult.summary.totalFixedInfoNumber,
"Total remaining": lintResult.summary.totalRemainingInfoNumber,
};
const summaryTable = [];
if (isErrorInLogLevelScope("error", options.loglevel)) {
summaryTable.push(errorTableLine);
}
if (isErrorInLogLevelScope("warning", options.loglevel)) {
summaryTable.push(warningTableLine);
}
if (isErrorInLogLevelScope("warning", options.loglevel)) {
summaryTable.push(infoTableLine);
}
// Output text log in file
if (output.endsWith(".txt")) {
const fullFileContent = outputString;
fs.writeFileSync(output, fullFileContent);
console.table(summaryTable, fixer ? ["Severity", "Total found", "Total fixed", "Total remaining"] : ["Severity", "Total found"]);
const absolutePath = path.resolve(".", output);
console.info("GroovyLint: Logged results in file " + absolutePath);
} else {
// Output text log in console
console.log(outputString);
console.table(summaryTable, fixer ? ["Severity", "Total found", "Total fixed", "Total remaining"] : ["Severity", "Total found"]);
}
}
// Display as json
else if (outputType === "json") {
// Output log
if (output.endsWith(".json")) {
const fullFileContent = JSON.stringify(lintResult, null, 2);
fs.writeFileSync(output, fullFileContent);
const absolutePath = path.resolve(".", output);
console.info("GroovyLint: Logged results in file " + absolutePath);
} else {
outputString = JSON.stringify(lintResult);
console.log(outputString);
}
}
// SARIF results
else if (outputType === "sarif") {
const sarifJsonString = buildSarifResult(lintResult);
// SARIF file
if (output.endsWith(".sarif")) {
fs.writeFileSync(output, sarifJsonString);
const absolutePath = path.resolve(".", output);
outputString = "GroovyLint: Logged SARIF results in file " + absolutePath;
console.info(outputString);
} else {
// SARIF in stdout
outputString = sarifJsonString;
console.log(sarifJsonString);
}
}
// stdout result for format / fix with source sent as stdin
else if (outputType === "stdout") {
console.log(lintResult.files[0].updatedSource);
}
return outputString;
}
function buildSarifResult(lintResult) {
// SARIF builder
const sarifBuilder = new SarifBuilder();
// SARIF Run builder
const sarifRunBuilder = new SarifRunBuilder().initSimple({
toolDriverName: "npm-groovy-lint",
toolDriverVersion: getNpmGroovyLintVersion(),
url: "https://nvuillam.github.io/npm-groovy-lint/",
});
// SARIF rules
for (const ruleId of Object.keys(lintResult.rules || {})) {
const rule = lintResult.rules[ruleId];
const sarifRuleBuilder = new SarifRuleBuilder().initSimple({
ruleId: ruleId,
shortDescriptionText: rule.description,
helpUri: rule.docUrl,
});
sarifRunBuilder.addRule(sarifRuleBuilder);
}
// Add SARIF results (individual errors)
for (const fileNm of Object.keys(lintResult.files)) {
const fileErrors = lintResult.files[fileNm].errors;
for (const err of fileErrors) {
const sarifResultBuilder = new SarifResultBuilder();
const sarifResultInit = {
level: err.severity === "info" ? "note" : err.severity, // Other values can be "warning" or "error"
messageText: err.msg,
ruleId: err.rule,
fileUri: process.env.SARIF_URI_ABSOLUTE ? "file:///" + fileNm.replace(/\\/g, "/") : path.relative(process.cwd(), fileNm),
};
if (err && err.range && err.range.start && (err.range.start.line === 0 || err.range.start.line > 0)) {
sarifResultInit.startLine = fixLine(err.range.start.line);
sarifResultInit.startColumn = fixCol(err.range.start.character);
sarifResultInit.endLine = fixLine(err.range.end.line);
sarifResultInit.endColumn = fixCol(err.range.end.character);
}
sarifResultBuilder.initSimple(sarifResultInit);
sarifRunBuilder.addResult(sarifResultBuilder);
}
}
sarifBuilder.addRun(sarifRunBuilder);
return sarifBuilder.buildSarifJsonString({ indent: false });
}
function fixLine(val) {
if (val === null) {
return undefined;
}
return val === 0 ? 1 : val;
}
function fixCol(val) {
if (val === null) {
return undefined;
}
return val === 0 ? 1 : val + 1;
}
export { computeStats, processOutput };