massive update, probably broken
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / cli-engine / formatters / html.js
1 /**
2  * @fileoverview HTML reporter
3  * @author Julian Laval
4  */
5 "use strict";
6
7 //------------------------------------------------------------------------------
8 // Helpers
9 //------------------------------------------------------------------------------
10
11 const encodeHTML = (function() {
12     const encodeHTMLRules = {
13         "&": "&",
14         "<": "&#60;",
15         ">": "&#62;",
16         '"': "&#34;",
17         "'": "&#39;"
18     };
19     const matchHTML = /[&<>"']/ug;
20
21     return function(code) {
22         return code
23             ? code.toString().replace(matchHTML, m => encodeHTMLRules[m] || m)
24             : "";
25     };
26 }());
27
28 /**
29  * Get the final HTML document.
30  * @param {Object} it data for the document.
31  * @returns {string} HTML document.
32  */
33 function pageTemplate(it) {
34     const { reportColor, reportSummary, date, results } = it;
35
36     return `
37 <!DOCTYPE html>
38 <html>
39     <head>
40         <meta charset="UTF-8">
41         <title>ESLint Report</title>
42         <style>
43             body {
44                 font-family:Arial, "Helvetica Neue", Helvetica, sans-serif;
45                 font-size:16px;
46                 font-weight:normal;
47                 margin:0;
48                 padding:0;
49                 color:#333
50             }
51             #overview {
52                 padding:20px 30px
53             }
54             td, th {
55                 padding:5px 10px
56             }
57             h1 {
58                 margin:0
59             }
60             table {
61                 margin:30px;
62                 width:calc(100% - 60px);
63                 max-width:1000px;
64                 border-radius:5px;
65                 border:1px solid #ddd;
66                 border-spacing:0px;
67             }
68             th {
69                 font-weight:400;
70                 font-size:medium;
71                 text-align:left;
72                 cursor:pointer
73             }
74             td.clr-1, td.clr-2, th span {
75                 font-weight:700
76             }
77             th span {
78                 float:right;
79                 margin-left:20px
80             }
81             th span:after {
82                 content:"";
83                 clear:both;
84                 display:block
85             }
86             tr:last-child td {
87                 border-bottom:none
88             }
89             tr td:first-child, tr td:last-child {
90                 color:#9da0a4
91             }
92             #overview.bg-0, tr.bg-0 th {
93                 color:#468847;
94                 background:#dff0d8;
95                 border-bottom:1px solid #d6e9c6
96             }
97             #overview.bg-1, tr.bg-1 th {
98                 color:#f0ad4e;
99                 background:#fcf8e3;
100                 border-bottom:1px solid #fbeed5
101             }
102             #overview.bg-2, tr.bg-2 th {
103                 color:#b94a48;
104                 background:#f2dede;
105                 border-bottom:1px solid #eed3d7
106             }
107             td {
108                 border-bottom:1px solid #ddd
109             }
110             td.clr-1 {
111                 color:#f0ad4e
112             }
113             td.clr-2 {
114                 color:#b94a48
115             }
116             td a {
117                 color:#3a33d1;
118                 text-decoration:none
119             }
120             td a:hover {
121                 color:#272296;
122                 text-decoration:underline
123             }
124         </style>
125     </head>
126     <body>
127         <div id="overview" class="bg-${reportColor}">
128             <h1>ESLint Report</h1>
129             <div>
130                 <span>${reportSummary}</span> - Generated on ${date}
131             </div>
132         </div>
133         <table>
134             <tbody>
135                 ${results}
136             </tbody>
137         </table>
138         <script type="text/javascript">
139             var groups = document.querySelectorAll("tr[data-group]");
140             for (i = 0; i < groups.length; i++) {
141                 groups[i].addEventListener("click", function() {
142                     var inGroup = document.getElementsByClassName(this.getAttribute("data-group"));
143                     this.innerHTML = (this.innerHTML.indexOf("+") > -1) ? this.innerHTML.replace("+", "-") : this.innerHTML.replace("-", "+");
144                     for (var j = 0; j < inGroup.length; j++) {
145                         inGroup[j].style.display = (inGroup[j].style.display !== "none") ? "none" : "table-row";
146                     }
147                 });
148             }
149         </script>
150     </body>
151 </html>
152 `.trimLeft();
153 }
154
155 /**
156  * Given a word and a count, append an s if count is not one.
157  * @param {string} word A word in its singular form.
158  * @param {int} count A number controlling whether word should be pluralized.
159  * @returns {string} The original word with an s on the end if count is not one.
160  */
161 function pluralize(word, count) {
162     return (count === 1 ? word : `${word}s`);
163 }
164
165 /**
166  * Renders text along the template of x problems (x errors, x warnings)
167  * @param {string} totalErrors Total errors
168  * @param {string} totalWarnings Total warnings
169  * @returns {string} The formatted string, pluralized where necessary
170  */
171 function renderSummary(totalErrors, totalWarnings) {
172     const totalProblems = totalErrors + totalWarnings;
173     let renderedText = `${totalProblems} ${pluralize("problem", totalProblems)}`;
174
175     if (totalProblems !== 0) {
176         renderedText += ` (${totalErrors} ${pluralize("error", totalErrors)}, ${totalWarnings} ${pluralize("warning", totalWarnings)})`;
177     }
178     return renderedText;
179 }
180
181 /**
182  * Get the color based on whether there are errors/warnings...
183  * @param {string} totalErrors Total errors
184  * @param {string} totalWarnings Total warnings
185  * @returns {int} The color code (0 = green, 1 = yellow, 2 = red)
186  */
187 function renderColor(totalErrors, totalWarnings) {
188     if (totalErrors !== 0) {
189         return 2;
190     }
191     if (totalWarnings !== 0) {
192         return 1;
193     }
194     return 0;
195 }
196
197 /**
198  * Get HTML (table row) describing a single message.
199  * @param {Object} it data for the message.
200  * @returns {string} HTML (table row) describing the message.
201  */
202 function messageTemplate(it) {
203     const {
204         parentIndex,
205         lineNumber,
206         columnNumber,
207         severityNumber,
208         severityName,
209         message,
210         ruleUrl,
211         ruleId
212     } = it;
213
214     return `
215 <tr style="display:none" class="f-${parentIndex}">
216     <td>${lineNumber}:${columnNumber}</td>
217     <td class="clr-${severityNumber}">${severityName}</td>
218     <td>${encodeHTML(message)}</td>
219     <td>
220         <a href="${ruleUrl ? ruleUrl : ""}" target="_blank" rel="noopener noreferrer">${ruleId ? ruleId : ""}</a>
221     </td>
222 </tr>
223 `.trimLeft();
224 }
225
226 /**
227  * Get HTML (table rows) describing the messages.
228  * @param {Array} messages Messages.
229  * @param {int} parentIndex Index of the parent HTML row.
230  * @param {Object} rulesMeta Dictionary containing metadata for each rule executed by the analysis.
231  * @returns {string} HTML (table rows) describing the messages.
232  */
233 function renderMessages(messages, parentIndex, rulesMeta) {
234
235     /**
236      * Get HTML (table row) describing a message.
237      * @param {Object} message Message.
238      * @returns {string} HTML (table row) describing a message.
239      */
240     return messages.map(message => {
241         const lineNumber = message.line || 0;
242         const columnNumber = message.column || 0;
243         let ruleUrl;
244
245         if (rulesMeta) {
246             const meta = rulesMeta[message.ruleId];
247
248             if (meta && meta.docs && meta.docs.url) {
249                 ruleUrl = meta.docs.url;
250             }
251         }
252
253         return messageTemplate({
254             parentIndex,
255             lineNumber,
256             columnNumber,
257             severityNumber: message.severity,
258             severityName: message.severity === 1 ? "Warning" : "Error",
259             message: message.message,
260             ruleId: message.ruleId,
261             ruleUrl
262         });
263     }).join("\n");
264 }
265
266 /**
267  * Get HTML (table row) describing the result for a single file.
268  * @param {Object} it data for the file.
269  * @returns {string} HTML (table row) describing the result for the file.
270  */
271 function resultTemplate(it) {
272     const { color, index, filePath, summary } = it;
273
274     return `
275 <tr class="bg-${color}" data-group="f-${index}">
276     <th colspan="4">
277         [+] ${encodeHTML(filePath)}
278         <span>${encodeHTML(summary)}</span>
279     </th>
280 </tr>
281 `.trimLeft();
282 }
283
284 // eslint-disable-next-line jsdoc/require-description
285 /**
286  * @param {Array} results Test results.
287  * @param {Object} rulesMeta Dictionary containing metadata for each rule executed by the analysis.
288  * @returns {string} HTML string describing the results.
289  */
290 function renderResults(results, rulesMeta) {
291     return results.map((result, index) => resultTemplate({
292         index,
293         color: renderColor(result.errorCount, result.warningCount),
294         filePath: result.filePath,
295         summary: renderSummary(result.errorCount, result.warningCount)
296     }) + renderMessages(result.messages, index, rulesMeta)).join("\n");
297 }
298
299 //------------------------------------------------------------------------------
300 // Public Interface
301 //------------------------------------------------------------------------------
302
303 module.exports = function(results, data) {
304     let totalErrors,
305         totalWarnings;
306
307     const metaData = data ? data.rulesMeta : {};
308
309     totalErrors = 0;
310     totalWarnings = 0;
311
312     // Iterate over results to get totals
313     results.forEach(result => {
314         totalErrors += result.errorCount;
315         totalWarnings += result.warningCount;
316     });
317
318     return pageTemplate({
319         date: new Date(),
320         reportColor: renderColor(totalErrors, totalWarnings),
321         reportSummary: renderSummary(totalErrors, totalWarnings),
322         results: renderResults(results, metaData)
323     });
324 };