1 | 'use strict';
|
2 |
|
3 | const fs = require('fs');
|
4 | const pth = require('path');
|
5 | const escape = require('escape-html');
|
6 |
|
7 | const {
|
8 | hasEngRusLetters,
|
9 | replaceEngLettersWithAsterisk,
|
10 | replaceRusLettersWithAsterisk,
|
11 | } = require('../helpers/string');
|
12 | const { getTyposByCode, hasManyErrors } = require('../helpers/typos');
|
13 | const { isUrl } = require('../helpers/url');
|
14 | const { uptime } = require('../helpers/uptime');
|
15 | const { consoleError, consoleInfo } = require('../helpers/console');
|
16 |
|
17 | const yaspeller = require('../yaspeller');
|
18 |
|
19 | const buffer = [];
|
20 |
|
21 | function makeLink(resource) {
|
22 | let href = resource;
|
23 | let relativeFile = resource;
|
24 |
|
25 | if (!isUrl(resource)) {
|
26 | relativeFile = pth.relative('.', resource);
|
27 |
|
28 | if (relativeFile.indexOf('..') !== -1) {
|
29 | relativeFile = resource;
|
30 | }
|
31 |
|
32 | href = fileUrl(href);
|
33 | }
|
34 |
|
35 | return '<a target="_blank" href="' +
|
36 | escape(encodeURI(href)) + '" title="' +
|
37 | escape(resource) + '">' +
|
38 | escape(relativeFile) + '</a>';
|
39 | }
|
40 |
|
41 | function fileUrl(file) {
|
42 | return 'file://localhost' +
|
43 | (file.search(/^\//) === -1 ? '/' : '') +
|
44 | file.replace(/\\/g, '/');
|
45 | }
|
46 |
|
47 | function prepareTemplate(params) {
|
48 | return `<!DOCTYPE html>
|
49 | <html>
|
50 | <head>
|
51 | <meta charset="utf-8"/>
|
52 | <title>yaspeller report</title>
|
53 | <style>${params.css}</style>
|
54 | </head>
|
55 | <body class="show-only-errors_checked">
|
56 | <div class="page">${params.content}</div>
|
57 | <script>${params.js}</script>
|
58 | </body>
|
59 | </html>`;
|
60 | }
|
61 |
|
62 | function loadFile(filename) {
|
63 | return fs.readFileSync(pth.join(__dirname, 'html/' + filename)).toString();
|
64 | }
|
65 |
|
66 | const filename = 'yaspeller_report.html';
|
67 |
|
68 | module.exports = {
|
69 | name: 'html',
|
70 | onResourceComplete(err, data) {
|
71 | const html = [];
|
72 | if (err) {
|
73 | html.push('<div class="syserr">' + data + '</div>');
|
74 | } else {
|
75 | const time = data.time ? '<span class="time">' + data.time + ' ms</span>' : '';
|
76 | if (data.data && data.data.length) {
|
77 | html.push('<div class="err"><span class="sym-err">χ</span>' + makeLink(data.resource) + time + '</div>');
|
78 | } else {
|
79 | html.push('<div class="ok"><span class="sym-ok">✓</span>' + makeLink(data.resource) + time + '</div>');
|
80 | }
|
81 |
|
82 | html.push('<div class="typo">');
|
83 | if (hasManyErrors(data.data)) {
|
84 | html.push('<div class="title">Too many errors.</div>');
|
85 | }
|
86 |
|
87 | yaspeller.errors.forEach(function(el) {
|
88 | const typos = getTyposByCode(el.code, data.data);
|
89 | if (typos.length) {
|
90 | html.push('<table class="table"><caption>' + el.title + '</caption>');
|
91 | html.push('<tr><th class="table-num">Num.</th>' +
|
92 | '<th class="table-name">Typo</th>' +
|
93 | '<th class="table-line">Line:Col</th>' +
|
94 | '<th class="table-suggest">Suggest</th>' +
|
95 | '<th class="table-comment">Comment</th></tr>');
|
96 | typos.forEach(function(el, i) {
|
97 | const comment = [];
|
98 | const pos = el.position;
|
99 | const suggest = el.suggest ? el.suggest.map(w => '<span class="word">' + escape(w) + '</span>').join(', ') : '';
|
100 |
|
101 | if (hasEngRusLetters(el.word)) {
|
102 | comment.push('<code class="letters-en">en: ' + escape(replaceRusLettersWithAsterisk(el.word)) + '</code>');
|
103 | comment.push('<code class="letters-ru">ru: ' + escape(replaceEngLettersWithAsterisk(el.word)) + '</code>');
|
104 | }
|
105 |
|
106 | html.push('<tr>');
|
107 | html.push('<td class="table-num">' + (i + 1) + '.</td>');
|
108 | html.push('<td class="table-name"><span class="word">' + escape(el.word) + '</span>' +
|
109 | (el.count > 1 ? '<sup class="table-count">' + el.count + '</sup>' : '') +
|
110 | '</td>');
|
111 | html.push('<td class="table-line">' + (pos.length ? pos[0].line + ':' + pos[0].column : '') + '</td>');
|
112 | html.push('<td class="table-suggest">' + suggest + '</td>');
|
113 | html.push('<td class="table-comment">' + (comment.length ? comment.join('<br/>') : '') + '</td>');
|
114 | html.push('</tr>');
|
115 | });
|
116 |
|
117 | html.push('</table>');
|
118 | }
|
119 | });
|
120 |
|
121 | html.push('</div>');
|
122 | }
|
123 |
|
124 | buffer.push(html.join('\n'));
|
125 | },
|
126 | onComplete(data, stats) {
|
127 | const content = '<div class="total">Processed resources: ' + stats.total +
|
128 | ' (<span class="sym-err">χ</span>– ' + stats.errors +
|
129 | '</span>, <span class="sym-ok-group"><span class="sym-ok">✓</span>– ' + stats.ok + '</span>) ' +
|
130 | '<label><input class="show-only-errors" autocomplete="off" checked="checked" type="checkbox" /> Only errors</label>' +
|
131 | '<br/>Checking finished: ' + uptime() + '</div>' +
|
132 | buffer.join('');
|
133 |
|
134 | try {
|
135 | fs.writeFileSync(filename, prepareTemplate({
|
136 | css: loadFile('template.css'),
|
137 | js: loadFile('template.js'),
|
138 | content: content
|
139 | }));
|
140 | consoleInfo('HTML report: ./' + filename);
|
141 | } catch (e) {
|
142 | consoleError(e);
|
143 | }
|
144 | },
|
145 | };
|