]>
Commit | Line | Data |
---|---|---|
eb39fafa DC |
1 | /** |
2 | * @fileoverview Codeframe reporter | |
3 | * @author Vitor Balocco | |
4 | */ | |
5 | "use strict"; | |
6 | ||
7 | const chalk = require("chalk"); | |
8 | const { codeFrameColumns } = require("@babel/code-frame"); | |
9 | const path = require("path"); | |
10 | ||
11 | //------------------------------------------------------------------------------ | |
12 | // Helpers | |
13 | //------------------------------------------------------------------------------ | |
14 | ||
15 | /** | |
16 | * Given a word and a count, append an s if count is not one. | |
17 | * @param {string} word A word in its singular form. | |
18 | * @param {number} count A number controlling whether word should be pluralized. | |
19 | * @returns {string} The original word with an s on the end if count is not one. | |
20 | */ | |
21 | function pluralize(word, count) { | |
22 | return (count === 1 ? word : `${word}s`); | |
23 | } | |
24 | ||
25 | /** | |
26 | * Gets a formatted relative file path from an absolute path and a line/column in the file. | |
27 | * @param {string} filePath The absolute file path to format. | |
28 | * @param {number} line The line from the file to use for formatting. | |
29 | * @param {number} column The column from the file to use for formatting. | |
30 | * @returns {string} The formatted file path. | |
31 | */ | |
32 | function formatFilePath(filePath, line, column) { | |
33 | let relPath = path.relative(process.cwd(), filePath); | |
34 | ||
35 | if (line && column) { | |
36 | relPath += `:${line}:${column}`; | |
37 | } | |
38 | ||
39 | return chalk.green(relPath); | |
40 | } | |
41 | ||
42 | /** | |
43 | * Gets the formatted output for a given message. | |
44 | * @param {Object} message The object that represents this message. | |
45 | * @param {Object} parentResult The result object that this message belongs to. | |
46 | * @returns {string} The formatted output. | |
47 | */ | |
48 | function formatMessage(message, parentResult) { | |
49 | const type = (message.fatal || message.severity === 2) ? chalk.red("error") : chalk.yellow("warning"); | |
50 | const msg = `${chalk.bold(message.message.replace(/([^ ])\.$/u, "$1"))}`; | |
51 | const ruleId = message.fatal ? "" : chalk.dim(`(${message.ruleId})`); | |
52 | const filePath = formatFilePath(parentResult.filePath, message.line, message.column); | |
53 | const sourceCode = parentResult.output ? parentResult.output : parentResult.source; | |
54 | ||
55 | const firstLine = [ | |
56 | `${type}:`, | |
57 | `${msg}`, | |
58 | ruleId ? `${ruleId}` : "", | |
59 | sourceCode ? `at ${filePath}:` : `at ${filePath}` | |
60 | ].filter(String).join(" "); | |
61 | ||
62 | const result = [firstLine]; | |
63 | ||
64 | if (sourceCode) { | |
65 | result.push( | |
66 | codeFrameColumns(sourceCode, { start: { line: message.line, column: message.column } }, { highlightCode: false }) | |
67 | ); | |
68 | } | |
69 | ||
70 | return result.join("\n"); | |
71 | } | |
72 | ||
73 | /** | |
74 | * Gets the formatted output summary for a given number of errors and warnings. | |
75 | * @param {number} errors The number of errors. | |
76 | * @param {number} warnings The number of warnings. | |
77 | * @param {number} fixableErrors The number of fixable errors. | |
78 | * @param {number} fixableWarnings The number of fixable warnings. | |
79 | * @returns {string} The formatted output summary. | |
80 | */ | |
81 | function formatSummary(errors, warnings, fixableErrors, fixableWarnings) { | |
82 | const summaryColor = errors > 0 ? "red" : "yellow"; | |
83 | const summary = []; | |
84 | const fixablesSummary = []; | |
85 | ||
86 | if (errors > 0) { | |
87 | summary.push(`${errors} ${pluralize("error", errors)}`); | |
88 | } | |
89 | ||
90 | if (warnings > 0) { | |
91 | summary.push(`${warnings} ${pluralize("warning", warnings)}`); | |
92 | } | |
93 | ||
94 | if (fixableErrors > 0) { | |
95 | fixablesSummary.push(`${fixableErrors} ${pluralize("error", fixableErrors)}`); | |
96 | } | |
97 | ||
98 | if (fixableWarnings > 0) { | |
99 | fixablesSummary.push(`${fixableWarnings} ${pluralize("warning", fixableWarnings)}`); | |
100 | } | |
101 | ||
102 | let output = chalk[summaryColor].bold(`${summary.join(" and ")} found.`); | |
103 | ||
104 | if (fixableErrors || fixableWarnings) { | |
105 | output += chalk[summaryColor].bold(`\n${fixablesSummary.join(" and ")} potentially fixable with the \`--fix\` option.`); | |
106 | } | |
107 | ||
108 | return output; | |
109 | } | |
110 | ||
111 | //------------------------------------------------------------------------------ | |
112 | // Public Interface | |
113 | //------------------------------------------------------------------------------ | |
114 | ||
115 | module.exports = function(results) { | |
116 | let errors = 0; | |
117 | let warnings = 0; | |
118 | let fixableErrors = 0; | |
119 | let fixableWarnings = 0; | |
120 | ||
121 | const resultsWithMessages = results.filter(result => result.messages.length > 0); | |
122 | ||
123 | let output = resultsWithMessages.reduce((resultsOutput, result) => { | |
124 | const messages = result.messages.map(message => `${formatMessage(message, result)}\n\n`); | |
125 | ||
126 | errors += result.errorCount; | |
127 | warnings += result.warningCount; | |
128 | fixableErrors += result.fixableErrorCount; | |
129 | fixableWarnings += result.fixableWarningCount; | |
130 | ||
131 | return resultsOutput.concat(messages); | |
132 | }, []).join("\n"); | |
133 | ||
134 | output += "\n"; | |
135 | output += formatSummary(errors, warnings, fixableErrors, fixableWarnings); | |
136 | ||
137 | return (errors + warnings) > 0 ? output : ""; | |
138 | }; |