1 | var chalk = require('chalk');
|
2 | var TokenAssert = require('./token-assert');
|
3 |
|
4 | var LINE_SEPARATOR = /\r\n|\r|\n/g;
|
5 |
|
6 | var EMPTY_POS = {
|
7 | line: 1,
|
8 | column: 0
|
9 | };
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 | var Errors = function(file) {
|
18 | this._errorList = [];
|
19 | this._file = file;
|
20 | this._currentRule = '';
|
21 |
|
22 | |
23 |
|
24 |
|
25 |
|
26 | this.assert = new TokenAssert(file);
|
27 | this.assert.on('error', this._addError.bind(this));
|
28 | };
|
29 |
|
30 | Errors.prototype = {
|
31 | |
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 | add: function(message, element, offset) {
|
39 | if (message instanceof Error) {
|
40 | this._addParseError(message);
|
41 | return;
|
42 | }
|
43 |
|
44 | this._addError({
|
45 | message: message,
|
46 | element: element,
|
47 | offset: offset
|
48 | });
|
49 | },
|
50 |
|
51 | |
52 |
|
53 |
|
54 |
|
55 |
|
56 | cast: function(errorInfo) {
|
57 | this._addError(errorInfo);
|
58 | },
|
59 |
|
60 | |
61 |
|
62 |
|
63 |
|
64 |
|
65 |
|
66 | _addParseError: function(errorInfo) {
|
67 | this._errorList.push({
|
68 | filename: this._file.getFilename(),
|
69 | rule: 'parseError',
|
70 | message: errorInfo.message,
|
71 | line: errorInfo.loc ? errorInfo.loc.line : 1,
|
72 | column: errorInfo.loc ? errorInfo.loc.column : 0
|
73 | });
|
74 | },
|
75 |
|
76 | |
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 | _addError: function(errorInfo) {
|
83 | this._errorList.push({
|
84 | filename: this._file.getFilename(),
|
85 | rule: this._currentRule,
|
86 | message: this._prepareMessage(errorInfo),
|
87 | element: errorInfo.element,
|
88 | offset: errorInfo.offset,
|
89 | additional: errorInfo.additional,
|
90 | fixed: false,
|
91 | fix: errorInfo.fix
|
92 | });
|
93 | },
|
94 |
|
95 | |
96 |
|
97 |
|
98 |
|
99 |
|
100 |
|
101 | _prepareMessage: function(errorInfo) {
|
102 | var rule = errorInfo instanceof Error ? 'parseError' : this._currentRule;
|
103 |
|
104 | if (rule) {
|
105 | return rule + ': ' + errorInfo.message;
|
106 | }
|
107 |
|
108 | return errorInfo.message;
|
109 | },
|
110 |
|
111 | |
112 |
|
113 |
|
114 |
|
115 |
|
116 | getErrorList: function() {
|
117 | return this._errorList;
|
118 | },
|
119 |
|
120 | |
121 |
|
122 |
|
123 |
|
124 |
|
125 | getFilename: function() {
|
126 | return this._file.getFilename();
|
127 | },
|
128 |
|
129 | |
130 |
|
131 |
|
132 |
|
133 |
|
134 | isEmpty: function() {
|
135 | return this._errorList.length === 0;
|
136 | },
|
137 |
|
138 | |
139 |
|
140 |
|
141 |
|
142 |
|
143 | getValidationErrorCount: function() {
|
144 | return this._errorList.filter(function(error) {
|
145 | return error.rule !== 'parseError' && error.rule !== 'internalError';
|
146 | });
|
147 | },
|
148 |
|
149 | |
150 |
|
151 |
|
152 |
|
153 |
|
154 | getErrorCount: function() {
|
155 | return this._errorList.length;
|
156 | },
|
157 |
|
158 | |
159 |
|
160 |
|
161 |
|
162 |
|
163 | stripErrorList: function(length) {
|
164 | this._errorList.splice(length);
|
165 | },
|
166 |
|
167 | |
168 |
|
169 |
|
170 |
|
171 |
|
172 | filter: function(filter) {
|
173 | this._errorList = this._errorList.filter(filter);
|
174 | },
|
175 |
|
176 | |
177 |
|
178 |
|
179 | calculateErrorLocations: function(tokenIndex) {
|
180 | this._errorList.forEach(function(error) {
|
181 | var pos = Errors.getPosition(error, tokenIndex);
|
182 | error.line = pos.line;
|
183 | error.column = pos.column;
|
184 | });
|
185 | },
|
186 |
|
187 | |
188 |
|
189 |
|
190 |
|
191 |
|
192 |
|
193 |
|
194 | explainError: function(error, colorize) {
|
195 | var lineNumber = error.line - 1;
|
196 | var lines = this._file.getLines();
|
197 | var result = [
|
198 | renderLine(lineNumber, lines[lineNumber], colorize),
|
199 | renderPointer(error.column, colorize)
|
200 | ];
|
201 | var i = lineNumber - 1;
|
202 | var linesAround = 2;
|
203 | while (i >= 0 && i >= (lineNumber - linesAround)) {
|
204 | result.unshift(renderLine(i, lines[i], colorize));
|
205 | i--;
|
206 | }
|
207 | i = lineNumber + 1;
|
208 | while (i < lines.length && i <= (lineNumber + linesAround)) {
|
209 | result.push(renderLine(i, lines[i], colorize));
|
210 | i++;
|
211 | }
|
212 | result.unshift(formatErrorMessage(error.message, this.getFilename(), colorize));
|
213 | return result.join('\n');
|
214 | },
|
215 |
|
216 | |
217 |
|
218 |
|
219 |
|
220 |
|
221 |
|
222 | setCurrentRule: function(rule) {
|
223 | this._currentRule = rule;
|
224 | }
|
225 |
|
226 | };
|
227 |
|
228 |
|
229 |
|
230 |
|
231 |
|
232 |
|
233 |
|
234 |
|
235 |
|
236 | function formatErrorMessage(message, filename, colorize) {
|
237 | return (colorize ? chalk.bold(message) : message) +
|
238 | ' at ' +
|
239 | (colorize ? chalk.green(filename) : filename) + ' :';
|
240 | }
|
241 |
|
242 |
|
243 |
|
244 |
|
245 |
|
246 |
|
247 |
|
248 |
|
249 | function prependSpaces(s, len) {
|
250 | while (s.length < len) {
|
251 | s = ' ' + s;
|
252 | }
|
253 | return s;
|
254 | }
|
255 |
|
256 |
|
257 |
|
258 |
|
259 |
|
260 |
|
261 |
|
262 |
|
263 |
|
264 | function renderLine(n, line, colorize) {
|
265 |
|
266 |
|
267 | line = line.replace(/\t/g, ' ');
|
268 |
|
269 |
|
270 | var lineNumber = prependSpaces((n + 1).toString(), 5) + ' |';
|
271 | return ' ' + (colorize ? chalk.grey(lineNumber) : lineNumber) + line;
|
272 | }
|
273 |
|
274 |
|
275 |
|
276 |
|
277 |
|
278 |
|
279 |
|
280 |
|
281 |
|
282 | function renderPointer(column, colorize) {
|
283 | var res = (new Array(column + 9)).join('-') + '^';
|
284 | return colorize ? chalk.grey(res) : res;
|
285 | }
|
286 |
|
287 |
|
288 |
|
289 |
|
290 |
|
291 |
|
292 |
|
293 |
|
294 | Errors.getPosition = function(error, tokenIndex) {
|
295 | var element = error.element;
|
296 | var offset = error.offset;
|
297 | var rule = error.rule;
|
298 |
|
299 | if (!element) {
|
300 | return EMPTY_POS;
|
301 | }
|
302 |
|
303 | if (offset === undefined) {
|
304 |
|
305 | if (rule === 'validateQuoteMarks') {
|
306 | offset = 0;
|
307 | } else if (element.getSourceCodeLength() === 1) {
|
308 | offset = 0;
|
309 | } else {
|
310 | offset = (element.getNewlineCount() === 0 && Math.ceil(element.getSourceCodeLength() / 2)) || 0;
|
311 | }
|
312 | }
|
313 |
|
314 | var pos = tokenIndex ? tokenIndex.getElementLoc(element) : element.getLoc().start;
|
315 | if (!pos) {
|
316 | return EMPTY_POS;
|
317 | }
|
318 |
|
319 | if (offset === 0) {
|
320 | return pos;
|
321 | }
|
322 |
|
323 | var newlineCount = element.getNewlineCount();
|
324 | if (newlineCount > 0) {
|
325 | var code = element.getSourceCode();
|
326 | LINE_SEPARATOR.lastIndex = 0;
|
327 | var lineOffset = 0;
|
328 | var match;
|
329 | var previousOffset = 0;
|
330 | var firstLineColumnOffset = pos.column;
|
331 | while ((match = LINE_SEPARATOR.exec(code)) !== null) {
|
332 | var currentOffset = match.index;
|
333 | if (offset <= currentOffset) {
|
334 | return {
|
335 | line: pos.line + lineOffset,
|
336 | column: firstLineColumnOffset + offset - previousOffset
|
337 | };
|
338 | }
|
339 | previousOffset = currentOffset + match[0].length;
|
340 | firstLineColumnOffset = 0;
|
341 | lineOffset++;
|
342 | }
|
343 | return {
|
344 | line: pos.line + newlineCount,
|
345 | column: offset - previousOffset
|
346 | };
|
347 | } else {
|
348 | return {
|
349 | line: pos.line,
|
350 | column: pos.column + offset
|
351 | };
|
352 | }
|
353 | };
|
354 |
|
355 | module.exports = Errors;
|