1 | ;
|
2 |
|
3 | const fs = require('fs');
|
4 | const isutf8 = require('isutf8');
|
5 | const minimatch = require('minimatch');
|
6 | const pth = require('path');
|
7 | const stripJsonComments = require('strip-json-comments');
|
8 | const isWin = process.platform === 'win32';
|
9 |
|
10 | module.exports = {
|
11 | /**
|
12 | * Is directory?
|
13 | *
|
14 | * @param {string} path
|
15 | * @returns {boolean}
|
16 | */
|
17 | isDir(path) {
|
18 | return fs.statSync(path).isDirectory();
|
19 | },
|
20 | /**
|
21 | * Load file as JSON with comments.
|
22 | *
|
23 | * @param {string} file
|
24 | * @param {boolean} [throwIfFileNotExists]
|
25 | * @returns {*}
|
26 | */
|
27 | loadFileAsJson(file, throwIfFileNotExists) {
|
28 | let data;
|
29 |
|
30 | if (fs.existsSync(file)) {
|
31 | let json = fs.readFileSync(file);
|
32 | if (isutf8(json)) {
|
33 | json = json.toString('utf-8');
|
34 | if (pth.extname(file) === '.js') {
|
35 | try {
|
36 | data = require(pth.resolve(file));
|
37 | } catch (e) {
|
38 | throw new Error(e);
|
39 | }
|
40 | } else {
|
41 | try {
|
42 | json = stripJsonComments(json);
|
43 | data = JSON.parse(json);
|
44 | } catch (e) {
|
45 | throw new Error('Error parsing in the file: ' + file);
|
46 | }
|
47 | }
|
48 | } else {
|
49 | throw new Error(file + ': is not utf-8.');
|
50 | }
|
51 | } else if (throwIfFileNotExists) {
|
52 | throw new Error(file + ': is not exists.');
|
53 | }
|
54 |
|
55 | return data;
|
56 | },
|
57 | /**
|
58 | * Find files to search for typos.
|
59 | *
|
60 | * @param {Object[]} dir - Array of typos.
|
61 | * @param {string[]} fileExtensions
|
62 | * @param {string[]} excludeFiles
|
63 | * @returns {Object[]}
|
64 | */
|
65 | findFiles(dir, fileExtensions, excludeFiles) {
|
66 | const result = [];
|
67 | const find = path => {
|
68 | fs.readdirSync(path).forEach(function(el) {
|
69 | const file = pth.resolve(path, el);
|
70 | if (!this.isExcludedFile(file, excludeFiles)) {
|
71 | if (this.isDir(file)) {
|
72 | find(file);
|
73 | } else if (this.isReqFileExtension(file, fileExtensions)) {
|
74 | result.push(file);
|
75 | }
|
76 | }
|
77 | }, this);
|
78 | };
|
79 |
|
80 | if (this.isDir(dir)) {
|
81 | find(dir);
|
82 | } else {
|
83 | result.push(dir);
|
84 | }
|
85 |
|
86 | return result;
|
87 | },
|
88 | /**
|
89 | * Is required file extension?
|
90 | *
|
91 | * @param {string} file
|
92 | * @param {string[]} fileExtensions
|
93 | * @returns {boolean}
|
94 | */
|
95 | isReqFileExtension(file, fileExtensions) {
|
96 | const buf = fileExtensions.filter(ext => ext.trim());
|
97 | return !buf.length || buf.some(ext => ext === file.slice(ext.length * -1));
|
98 | },
|
99 | /**
|
100 | * Is excluded file?
|
101 | *
|
102 | * @param {string} file
|
103 | * @param {string[]} excludeFiles
|
104 | * @returns {boolean}
|
105 | */
|
106 | isExcludedFile(file, excludeFiles) {
|
107 | return excludeFiles.some(el => minimatch(file, pth.resolve(el), {dot: true}));
|
108 | },
|
109 | /**
|
110 | * Has english and russian letters?
|
111 | *
|
112 | * @param {string} text
|
113 | * @returns {boolean}
|
114 | */
|
115 | hasEnRu(text) {
|
116 | return text.search(/[a-z]/i) > -1 && text.search(/[а-яё]/i) > -1;
|
117 | },
|
118 | /**
|
119 | * Replace Russian symbols on a asterisk.
|
120 | *
|
121 | * @param {string} word
|
122 | * @returns {string}
|
123 | */
|
124 | replaceRu(word) {
|
125 | return word.replace(/[а-яё]/gi, '*');
|
126 | },
|
127 | /**
|
128 | * Replace Latin symbols on a asterisk.
|
129 | *
|
130 | * @param {string} word
|
131 | * @returns {string}
|
132 | */
|
133 | replaceEn(word) {
|
134 | return word.replace(/[a-z]/gi, '*');
|
135 | },
|
136 | /**
|
137 | * Is url?
|
138 | *
|
139 | * @param {string} path
|
140 | * @returns {boolean}
|
141 | */
|
142 | isUrl(path) {
|
143 | return path.search(/^https?:/) > -1;
|
144 | },
|
145 | /**
|
146 | * Is sitemap?
|
147 | *
|
148 | * @param {string} path
|
149 | * @returns {boolean}
|
150 | */
|
151 | isSitemap(path) {
|
152 | return path.search(/sitemap\.xml$/) > -1;
|
153 | },
|
154 | /**
|
155 | * Get typos by code.
|
156 | *
|
157 | * @param {number} code
|
158 | * @param {Array} data
|
159 | * @returns {Array}
|
160 | */
|
161 | getTyposByCode(code, data) {
|
162 | const typos = [];
|
163 | data.forEach(function(el) {
|
164 | if (el.code !== code) {
|
165 | return;
|
166 | }
|
167 |
|
168 | typos.push(el);
|
169 | });
|
170 |
|
171 | return typos;
|
172 | },
|
173 | /**
|
174 | * Has many errors.
|
175 | *
|
176 | * @param {Array} data
|
177 | * @returns {boolean}
|
178 | */
|
179 | hasManyErrors(data) {
|
180 | return data.some(el => el.code === 4); // ERROR_TOO_MANY_ERRORS
|
181 | },
|
182 | /**
|
183 | * Get uptime in sec.
|
184 | * @returns {string}
|
185 | */
|
186 | uptime() {
|
187 | return (Math.floor(process.uptime() * 1000) / 1000) + ' sec.';
|
188 | },
|
189 | /**
|
190 | * Get a array with unique values.
|
191 | * @param {Array} arr
|
192 | * @returns {Array}
|
193 | */
|
194 | uniq(arr) {
|
195 | return Array.from(new Set(arr));
|
196 | },
|
197 | /**
|
198 | * Converts string to kebab case.
|
199 | *
|
200 | * @param {string} text
|
201 | * @returns {string}
|
202 | */
|
203 | kebabCase(text) {
|
204 | return text.replace(/[A-Z]/g, function($) {
|
205 | return '-' + $.toLowerCase();
|
206 | });
|
207 | },
|
208 | /**
|
209 | * Ok symbol.
|
210 | *
|
211 | * @type {string}
|
212 | */
|
213 | okSym: isWin ? '[OK]' : '✓',
|
214 | /**
|
215 | * Error symbol.
|
216 | *
|
217 | * @type {string}
|
218 | */
|
219 | errSym: isWin ? '[ERR]' : '✗'
|
220 | };
|