1/** 2 * @fileoverview Codeframe reporter 3 * @author Vitor Balocco 4 */ 5"use strict"; 6 7const chalk = require("chalk"); 8const { codeFrameColumns } = require("@babel/code-frame"); 9const 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 */ 21function 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 */ 32function 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 */ 48function 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 */ 81function 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 115module.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}; 139