UNPKG

6.32 kBJavaScriptView Raw
1'use strict';
2
3const chalk = require('chalk');
4const debug = require('./debug');
5const exitCodes = require('./exit-codes');
6const utils = require('./utils');
7const printDebug = debug.print;
8const letters = '[a-zа-яё\\d-]';
9const reNotOptimized = new RegExp('(^|' + letters + ')' +
10 '(\\(|\\[)' + letters + '(\\)|\\])(' + letters + '|\\/$|$)', 'i');
11const rePrepare = new RegExp('^(' + letters + ')(' + letters + '|$)');
12
13module.exports = {
14 /**
15 * Set dictionary.
16 *
17 * @param {Array} files
18 * @param {Array} configDictionary - Dictionary from .yaspellerrc
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 * Get dictionary.
51 *
52 * @returns {Array}
53 */
54 get() {
55 return this._dict;
56 },
57 _dict: [],
58 /**
59 * Load dictionary.
60 *
61 * @param {string} file - JSON file.
62 * @returns {Array}
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 * Check duplicate words in dictionary.
82 *
83 * @param {string[]} words
84 * @param {string} title
85 * @returns {boolean}
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 * Check typos in dictionary.
99 *
100 * @param {string[]} words
101 * @param {string} file
102 * @returns {boolean}
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 * Remove typos that is in the dictionary.
126 *
127 * @param {Object[]} data - Array of typos.
128 * @returns {Object[]}
129 */
130 removeDictWords(data) {
131 const result = [];
132 const dictionary = this.get();
133
134 data.forEach(function(typo) {
135 // ERROR_TOO_MANY_ERRORS = 4
136 if (typo.code === 4 || this.isTypo(typo.word, dictionary)) {
137 result.push(typo);
138 }
139 }, this);
140
141 return result;
142 },
143 /**
144 * It's a typo?
145 *
146 * @param {string} word
147 * @param {string[]|RegExp[]} dictionary
148 * @returns {boolean}
149 */
150 isTypo(word, dictionary) {
151 return !dictionary.some(function(dictWord) {
152 return dictWord.test(word);
153 });
154 },
155 /**
156 * Prepare dictionary.
157 *
158 * @param {string[]} dict
159 * @returns {Array}
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 // unknownWord(s)? = unknownWord(s)? and UnknownWord(s)?
169 // UnknownWord(s)? = UnknownWord(s)?
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 * Is not optimized RegExp?
184 *
185 * @param {string} text
186 * @returns {boolean}
187 */
188 isNotOptimizedRegExp(text) {
189 if (text.search(/(\(\)|\[\])/) !== -1) { // /[]Unknownword()/
190 return true;
191 }
192
193 if (text.search(reNotOptimized) !== -1) { // /Unknow(n)wo[r]d/
194 return true;
195 }
196
197 return false;
198 },
199 /**
200 * Get duplicate words.
201 *
202 * @param {string[]} words
203 * @returns {string[]}
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 * Is a letter?
222 *
223 * @param {string} symbol
224 * @returns {boolean}
225 */
226 isLetter(symbol) {
227 return symbol.toLowerCase() !== symbol.toUpperCase();
228 },
229 /**
230 * Is a letter in upper case?
231 *
232 * @param {string} letter
233 * @returns {boolean}
234 */
235 isUpperCase(letter) {
236 return letter === letter.toUpperCase();
237 },
238 /**
239 * Is a letter in lower case?
240 *
241 * @param {string} letter
242 * @returns {boolean}
243 */
244 isLowerCase(letter) {
245 return letter === letter.toLowerCase();
246 }
247};