1 | 'use strict';
|
2 |
|
3 | const chalk = require('chalk');
|
4 | const debug = require('./debug');
|
5 | const exitCodes = require('./exit-codes');
|
6 | const utils = require('./utils');
|
7 | const printDebug = debug.print;
|
8 | const letters = '[a-zа-яё\\d-]';
|
9 | const reNotOptimized = new RegExp('(^|' + letters + ')' +
|
10 | '(\\(|\\[)' + letters + '(\\)|\\])(' + letters + '|\\/$|$)', 'i');
|
11 | const rePrepare = new RegExp('^(' + letters + ')(' + letters + '|$)');
|
12 |
|
13 | module.exports = {
|
14 | |
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 | set(files, configDictionary) {
|
21 | let commonUniqueWords = [];
|
22 | let count = 0;
|
23 | let result = [];
|
24 |
|
25 | const prepare = (words, file) => {
|
26 | result = result.concat(words);
|
27 | this.checkDuplicates(words, `Dictionary duplicate words in "${file}":`);
|
28 | this.checkTyposInDictionary(words, file);
|
29 |
|
30 | commonUniqueWords = commonUniqueWords.concat(utils.uniq(words));
|
31 |
|
32 | count++;
|
33 | };
|
34 |
|
35 | if (configDictionary) {
|
36 | prepare(configDictionary, '.yaspellerrc');
|
37 | }
|
38 |
|
39 | files && files.forEach(function(file) {
|
40 | prepare(this.loadDictionary(file), file);
|
41 | }, this);
|
42 |
|
43 | if (count > 1) {
|
44 | this.checkDuplicates(commonUniqueWords, 'Duplicate words in dictionaries:');
|
45 | }
|
46 |
|
47 | this._dict = this.prepareDictionary(result);
|
48 | },
|
49 | |
50 |
|
51 |
|
52 |
|
53 |
|
54 | get() {
|
55 | return this._dict;
|
56 | },
|
57 | _dict: [],
|
58 | |
59 |
|
60 |
|
61 |
|
62 |
|
63 |
|
64 | loadDictionary(file) {
|
65 | let data = [];
|
66 |
|
67 | printDebug('Get/check dictionary: ' + file);
|
68 |
|
69 | try {
|
70 | data = utils.loadFileAsJson(file, true);
|
71 |
|
72 | printDebug('Use dictionary: ' + file);
|
73 | } catch (e) {
|
74 | console.error(chalk.red(e));
|
75 | process.exit(exitCodes.ERROR_DICTIONARY);
|
76 | }
|
77 |
|
78 | return data;
|
79 | },
|
80 | |
81 |
|
82 |
|
83 |
|
84 |
|
85 |
|
86 |
|
87 | checkDuplicates(words, title) {
|
88 | const duplicates = this.getDuplicates(words);
|
89 | if (duplicates.length) {
|
90 | console.log(chalk.cyan(title + '\n' + duplicates.join('\n') + '\n'));
|
91 |
|
92 | return true;
|
93 | }
|
94 |
|
95 | return false;
|
96 | },
|
97 | |
98 |
|
99 |
|
100 |
|
101 |
|
102 |
|
103 |
|
104 | checkTyposInDictionary(words, file) {
|
105 | const typos = [];
|
106 | words.forEach(function(word) {
|
107 | if (utils.hasEnRu(word)) {
|
108 | typos.push(word);
|
109 | }
|
110 | });
|
111 |
|
112 | const hasTypos = typos.length ? true : false;
|
113 | if (hasTypos) {
|
114 | console.log(chalk.cyan('Has typos in "' + file + '":'));
|
115 | typos.forEach(function(typo) {
|
116 | console.log(chalk.cyan(typo + ' - en: ' + utils.replaceRu(typo) +
|
117 | ', ru: ' + utils.replaceEn(typo)));
|
118 | });
|
119 | console.log('');
|
120 | }
|
121 |
|
122 | return hasTypos;
|
123 | },
|
124 | |
125 |
|
126 |
|
127 |
|
128 |
|
129 |
|
130 | removeDictWords(data) {
|
131 | const result = [];
|
132 | const dictionary = this.get();
|
133 |
|
134 | data.forEach(function(typo) {
|
135 |
|
136 | if (typo.code === 4 || this.isTypo(typo.word, dictionary)) {
|
137 | result.push(typo);
|
138 | }
|
139 | }, this);
|
140 |
|
141 | return result;
|
142 | },
|
143 | |
144 |
|
145 |
|
146 |
|
147 |
|
148 |
|
149 |
|
150 | isTypo(word, dictionary) {
|
151 | return !dictionary.some(function(dictWord) {
|
152 | return dictWord.test(word);
|
153 | });
|
154 | },
|
155 | |
156 |
|
157 |
|
158 |
|
159 |
|
160 |
|
161 | prepareDictionary(dict) {
|
162 | const result = [];
|
163 | dict.forEach(function(word) {
|
164 | if (this.isNotOptimizedRegExp(word)) {
|
165 | console.log(chalk.cyan('Not optimized dictionary RegExp in "' + word + '"'));
|
166 | }
|
167 |
|
168 |
|
169 |
|
170 |
|
171 | const preparedWord = word.replace(rePrepare, ($, $1, $2) => '[' + $1 + $1.toUpperCase() + ']' + $2);
|
172 |
|
173 | try {
|
174 | result.push(new RegExp(preparedWord));
|
175 | } catch (e) {
|
176 | console.error(chalk.red('Incorrect dictionary RegExp in "' + word + '", ' + e));
|
177 | }
|
178 | }, this);
|
179 |
|
180 | return result;
|
181 | },
|
182 | |
183 |
|
184 |
|
185 |
|
186 |
|
187 |
|
188 | isNotOptimizedRegExp(text) {
|
189 | if (text.search(/(\(\)|\[\])/) !== -1) {
|
190 | return true;
|
191 | }
|
192 |
|
193 | if (text.search(reNotOptimized) !== -1) {
|
194 | return true;
|
195 | }
|
196 |
|
197 | return false;
|
198 | },
|
199 | |
200 |
|
201 |
|
202 |
|
203 |
|
204 |
|
205 | getDuplicates(words) {
|
206 | const buffer = {};
|
207 | const result = [];
|
208 |
|
209 | words.forEach(function(word) {
|
210 | if (!buffer[word]) {
|
211 | buffer[word] = 1;
|
212 | } else if (buffer[word] === 1) {
|
213 | buffer[word]++;
|
214 | result.push(word);
|
215 | }
|
216 | });
|
217 |
|
218 | return result;
|
219 | },
|
220 | |
221 |
|
222 |
|
223 |
|
224 |
|
225 |
|
226 | isLetter(symbol) {
|
227 | return symbol.toLowerCase() !== symbol.toUpperCase();
|
228 | },
|
229 | |
230 |
|
231 |
|
232 |
|
233 |
|
234 |
|
235 | isUpperCase(letter) {
|
236 | return letter === letter.toUpperCase();
|
237 | },
|
238 | |
239 |
|
240 |
|
241 |
|
242 |
|
243 |
|
244 | isLowerCase(letter) {
|
245 | return letter === letter.toLowerCase();
|
246 | }
|
247 | };
|