|
| 1 | +/** |
| 2 | + * @fileoverview Codeframe reporter |
| 3 | + * @author Vitor Balocco |
| 4 | + */ |
| 5 | +"use strict"; |
| 6 | + |
| 7 | +const chalk = require("chalk"); |
| 8 | +const codeFrame = 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(/\.$/, ""))}`; |
| 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 | + codeFrame(sourceCode, message.line, 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 | + * @returns {string} The formatted output summary. |
| 78 | + */ |
| 79 | +function formatSummary(errors, warnings) { |
| 80 | + const summaryColor = errors > 0 ? "red" : "yellow"; |
| 81 | + const summary = []; |
| 82 | + |
| 83 | + if (errors > 0) { |
| 84 | + summary.push(`${errors} ${pluralize("error", errors)}`); |
| 85 | + } |
| 86 | + |
| 87 | + if (warnings > 0) { |
| 88 | + summary.push(`${warnings} ${pluralize("warning", warnings)}`); |
| 89 | + } |
| 90 | + |
| 91 | + return chalk[summaryColor].bold(`${summary.join(" and ")} found.`); |
| 92 | +} |
| 93 | + |
| 94 | +//------------------------------------------------------------------------------ |
| 95 | +// Public Interface |
| 96 | +//------------------------------------------------------------------------------ |
| 97 | + |
| 98 | +module.exports = function(results) { |
| 99 | + let errors = 0; |
| 100 | + let warnings = 0; |
| 101 | + const resultsWithMessages = results.filter(result => result.messages.length > 0); |
| 102 | + |
| 103 | + let output = resultsWithMessages.reduce((resultsOutput, result) => { |
| 104 | + const messages = result.messages.map(message => { |
| 105 | + if (message.fatal || message.severity === 2) { |
| 106 | + errors++; |
| 107 | + } else { |
| 108 | + warnings++; |
| 109 | + } |
| 110 | + |
| 111 | + return `${formatMessage(message, result)}\n\n`; |
| 112 | + }); |
| 113 | + |
| 114 | + return resultsOutput.concat(messages); |
| 115 | + }, []).join("\n"); |
| 116 | + |
| 117 | + output += "\n"; |
| 118 | + output += formatSummary(errors, warnings); |
| 119 | + |
| 120 | + return (errors + warnings) > 0 ? output : ""; |
| 121 | +}; |
0 commit comments