1/** 2 * @fileoverview HTML reporter 3 * @author Julian Laval 4 */ 5"use strict"; 6 7const lodash = require("lodash"); 8const fs = require("fs"); 9const path = require("path"); 10 11//------------------------------------------------------------------------------ 12// Helpers 13//------------------------------------------------------------------------------ 14 15const pageTemplate = lodash.template(fs.readFileSync(path.join(__dirname, "html-template-page.html"), "utf-8")); 16const messageTemplate = lodash.template(fs.readFileSync(path.join(__dirname, "html-template-message.html"), "utf-8")); 17const resultTemplate = lodash.template(fs.readFileSync(path.join(__dirname, "html-template-result.html"), "utf-8")); 18 19/** 20 * Given a word and a count, append an s if count is not one. 21 * @param {string} word A word in its singular form. 22 * @param {int} count A number controlling whether word should be pluralized. 23 * @returns {string} The original word with an s on the end if count is not one. 24 */ 25function pluralize(word, count) { 26 return (count === 1 ? word : `${word}s`); 27} 28 29/** 30 * Renders text along the template of x problems (x errors, x warnings) 31 * @param {string} totalErrors Total errors 32 * @param {string} totalWarnings Total warnings 33 * @returns {string} The formatted string, pluralized where necessary 34 */ 35function renderSummary(totalErrors, totalWarnings) { 36 const totalProblems = totalErrors + totalWarnings; 37 let renderedText = `${totalProblems} ${pluralize("problem", totalProblems)}`; 38 39 if (totalProblems !== 0) { 40 renderedText += ` (${totalErrors} ${pluralize("error", totalErrors)}, ${totalWarnings} ${pluralize("warning", totalWarnings)})`; 41 } 42 return renderedText; 43} 44 45/** 46 * Get the color based on whether there are errors/warnings... 47 * @param {string} totalErrors Total errors 48 * @param {string} totalWarnings Total warnings 49 * @returns {int} The color code (0 = green, 1 = yellow, 2 = red) 50 */ 51function renderColor(totalErrors, totalWarnings) { 52 if (totalErrors !== 0) { 53 return 2; 54 } 55 if (totalWarnings !== 0) { 56 return 1; 57 } 58 return 0; 59} 60 61/** 62 * Get HTML (table rows) describing the messages. 63 * @param {Array} messages Messages. 64 * @param {int} parentIndex Index of the parent HTML row. 65 * @param {Object} rulesMeta Dictionary containing metadata for each rule executed by the analysis. 66 * @returns {string} HTML (table rows) describing the messages. 67 */ 68function renderMessages(messages, parentIndex, rulesMeta) { 69 70 /** 71 * Get HTML (table row) describing a message. 72 * @param {Object} message Message. 73 * @returns {string} HTML (table row) describing a message. 74 */ 75 return lodash.map(messages, message => { 76 const lineNumber = message.line || 0; 77 const columnNumber = message.column || 0; 78 let ruleUrl; 79 80 if (rulesMeta) { 81 const meta = rulesMeta[message.ruleId]; 82 83 ruleUrl = lodash.get(meta, "docs.url", null); 84 } 85 86 return messageTemplate({ 87 parentIndex, 88 lineNumber, 89 columnNumber, 90 severityNumber: message.severity, 91 severityName: message.severity === 1 ? "Warning" : "Error", 92 message: message.message, 93 ruleId: message.ruleId, 94 ruleUrl 95 }); 96 }).join("\n"); 97} 98 99// eslint-disable-next-line jsdoc/require-description 100/** 101 * @param {Array} results Test results. 102 * @param {Object} rulesMeta Dictionary containing metadata for each rule executed by the analysis. 103 * @returns {string} HTML string describing the results. 104 */ 105function renderResults(results, rulesMeta) { 106 return lodash.map(results, (result, index) => resultTemplate({ 107 index, 108 color: renderColor(result.errorCount, result.warningCount), 109 filePath: result.filePath, 110 summary: renderSummary(result.errorCount, result.warningCount) 111 112 }) + renderMessages(result.messages, index, rulesMeta)).join("\n"); 113} 114 115//------------------------------------------------------------------------------ 116// Public Interface 117//------------------------------------------------------------------------------ 118 119module.exports = function(results, data) { 120 let totalErrors, 121 totalWarnings; 122 123 const metaData = data ? data.rulesMeta : {}; 124 125 totalErrors = 0; 126 totalWarnings = 0; 127 128 // Iterate over results to get totals 129 results.forEach(result => { 130 totalErrors += result.errorCount; 131 totalWarnings += result.warningCount; 132 }); 133 134 return pageTemplate({ 135 date: new Date(), 136 reportColor: renderColor(totalErrors, totalWarnings), 137 reportSummary: renderSummary(totalErrors, totalWarnings), 138 results: renderResults(results, metaData) 139 }); 140}; 141