source: imaps-frontend/node_modules/eslint/lib/rules/max-len.js@ d565449

main
Last change on this file since d565449 was d565449, checked in by stefan toskovski <stefantoska84@…>, 4 weeks ago

Update repo after prototype presentation

  • Property mode set to 100644
File size: 15.8 KB
Line 
1/**
2 * @fileoverview Rule to check for max length on a line.
3 * @author Matt DuVall <http://www.mattduvall.com>
4 * @deprecated in ESLint v8.53.0
5 */
6
7"use strict";
8
9//------------------------------------------------------------------------------
10// Constants
11//------------------------------------------------------------------------------
12
13const OPTIONS_SCHEMA = {
14 type: "object",
15 properties: {
16 code: {
17 type: "integer",
18 minimum: 0
19 },
20 comments: {
21 type: "integer",
22 minimum: 0
23 },
24 tabWidth: {
25 type: "integer",
26 minimum: 0
27 },
28 ignorePattern: {
29 type: "string"
30 },
31 ignoreComments: {
32 type: "boolean"
33 },
34 ignoreStrings: {
35 type: "boolean"
36 },
37 ignoreUrls: {
38 type: "boolean"
39 },
40 ignoreTemplateLiterals: {
41 type: "boolean"
42 },
43 ignoreRegExpLiterals: {
44 type: "boolean"
45 },
46 ignoreTrailingComments: {
47 type: "boolean"
48 }
49 },
50 additionalProperties: false
51};
52
53const OPTIONS_OR_INTEGER_SCHEMA = {
54 anyOf: [
55 OPTIONS_SCHEMA,
56 {
57 type: "integer",
58 minimum: 0
59 }
60 ]
61};
62
63//------------------------------------------------------------------------------
64// Rule Definition
65//------------------------------------------------------------------------------
66
67/** @type {import('../shared/types').Rule} */
68module.exports = {
69 meta: {
70 deprecated: true,
71 replacedBy: [],
72 type: "layout",
73
74 docs: {
75 description: "Enforce a maximum line length",
76 recommended: false,
77 url: "https://eslint.org/docs/latest/rules/max-len"
78 },
79
80 schema: [
81 OPTIONS_OR_INTEGER_SCHEMA,
82 OPTIONS_OR_INTEGER_SCHEMA,
83 OPTIONS_SCHEMA
84 ],
85 messages: {
86 max: "This line has a length of {{lineLength}}. Maximum allowed is {{maxLength}}.",
87 maxComment: "This line has a comment length of {{lineLength}}. Maximum allowed is {{maxCommentLength}}."
88 }
89 },
90
91 create(context) {
92
93 /*
94 * Inspired by http://tools.ietf.org/html/rfc3986#appendix-B, however:
95 * - They're matching an entire string that we know is a URI
96 * - We're matching part of a string where we think there *might* be a URL
97 * - We're only concerned about URLs, as picking out any URI would cause
98 * too many false positives
99 * - We don't care about matching the entire URL, any small segment is fine
100 */
101 const URL_REGEXP = /[^:/?#]:\/\/[^?#]/u;
102
103 const sourceCode = context.sourceCode;
104
105 /**
106 * Computes the length of a line that may contain tabs. The width of each
107 * tab will be the number of spaces to the next tab stop.
108 * @param {string} line The line.
109 * @param {int} tabWidth The width of each tab stop in spaces.
110 * @returns {int} The computed line length.
111 * @private
112 */
113 function computeLineLength(line, tabWidth) {
114 let extraCharacterCount = 0;
115
116 line.replace(/\t/gu, (match, offset) => {
117 const totalOffset = offset + extraCharacterCount,
118 previousTabStopOffset = tabWidth ? totalOffset % tabWidth : 0,
119 spaceCount = tabWidth - previousTabStopOffset;
120
121 extraCharacterCount += spaceCount - 1; // -1 for the replaced tab
122 });
123 return Array.from(line).length + extraCharacterCount;
124 }
125
126 // The options object must be the last option specified…
127 const options = Object.assign({}, context.options[context.options.length - 1]);
128
129 // …but max code length…
130 if (typeof context.options[0] === "number") {
131 options.code = context.options[0];
132 }
133
134 // …and tabWidth can be optionally specified directly as integers.
135 if (typeof context.options[1] === "number") {
136 options.tabWidth = context.options[1];
137 }
138
139 const maxLength = typeof options.code === "number" ? options.code : 80,
140 tabWidth = typeof options.tabWidth === "number" ? options.tabWidth : 4,
141 ignoreComments = !!options.ignoreComments,
142 ignoreStrings = !!options.ignoreStrings,
143 ignoreTemplateLiterals = !!options.ignoreTemplateLiterals,
144 ignoreRegExpLiterals = !!options.ignoreRegExpLiterals,
145 ignoreTrailingComments = !!options.ignoreTrailingComments || !!options.ignoreComments,
146 ignoreUrls = !!options.ignoreUrls,
147 maxCommentLength = options.comments;
148 let ignorePattern = options.ignorePattern || null;
149
150 if (ignorePattern) {
151 ignorePattern = new RegExp(ignorePattern, "u");
152 }
153
154 //--------------------------------------------------------------------------
155 // Helpers
156 //--------------------------------------------------------------------------
157
158 /**
159 * Tells if a given comment is trailing: it starts on the current line and
160 * extends to or past the end of the current line.
161 * @param {string} line The source line we want to check for a trailing comment on
162 * @param {number} lineNumber The one-indexed line number for line
163 * @param {ASTNode} comment The comment to inspect
164 * @returns {boolean} If the comment is trailing on the given line
165 */
166 function isTrailingComment(line, lineNumber, comment) {
167 return comment &&
168 (comment.loc.start.line === lineNumber && lineNumber <= comment.loc.end.line) &&
169 (comment.loc.end.line > lineNumber || comment.loc.end.column === line.length);
170 }
171
172 /**
173 * Tells if a comment encompasses the entire line.
174 * @param {string} line The source line with a trailing comment
175 * @param {number} lineNumber The one-indexed line number this is on
176 * @param {ASTNode} comment The comment to remove
177 * @returns {boolean} If the comment covers the entire line
178 */
179 function isFullLineComment(line, lineNumber, comment) {
180 const start = comment.loc.start,
181 end = comment.loc.end,
182 isFirstTokenOnLine = !line.slice(0, comment.loc.start.column).trim();
183
184 return comment &&
185 (start.line < lineNumber || (start.line === lineNumber && isFirstTokenOnLine)) &&
186 (end.line > lineNumber || (end.line === lineNumber && end.column === line.length));
187 }
188
189 /**
190 * Check if a node is a JSXEmptyExpression contained in a single line JSXExpressionContainer.
191 * @param {ASTNode} node A node to check.
192 * @returns {boolean} True if the node is a JSXEmptyExpression contained in a single line JSXExpressionContainer.
193 */
194 function isJSXEmptyExpressionInSingleLineContainer(node) {
195 if (!node || !node.parent || node.type !== "JSXEmptyExpression" || node.parent.type !== "JSXExpressionContainer") {
196 return false;
197 }
198
199 const parent = node.parent;
200
201 return parent.loc.start.line === parent.loc.end.line;
202 }
203
204 /**
205 * Gets the line after the comment and any remaining trailing whitespace is
206 * stripped.
207 * @param {string} line The source line with a trailing comment
208 * @param {ASTNode} comment The comment to remove
209 * @returns {string} Line without comment and trailing whitespace
210 */
211 function stripTrailingComment(line, comment) {
212
213 // loc.column is zero-indexed
214 return line.slice(0, comment.loc.start.column).replace(/\s+$/u, "");
215 }
216
217 /**
218 * Ensure that an array exists at [key] on `object`, and add `value` to it.
219 * @param {Object} object the object to mutate
220 * @param {string} key the object's key
221 * @param {any} value the value to add
222 * @returns {void}
223 * @private
224 */
225 function ensureArrayAndPush(object, key, value) {
226 if (!Array.isArray(object[key])) {
227 object[key] = [];
228 }
229 object[key].push(value);
230 }
231
232 /**
233 * Retrieves an array containing all strings (" or ') in the source code.
234 * @returns {ASTNode[]} An array of string nodes.
235 */
236 function getAllStrings() {
237 return sourceCode.ast.tokens.filter(token => (token.type === "String" ||
238 (token.type === "JSXText" && sourceCode.getNodeByRangeIndex(token.range[0] - 1).type === "JSXAttribute")));
239 }
240
241 /**
242 * Retrieves an array containing all template literals in the source code.
243 * @returns {ASTNode[]} An array of template literal nodes.
244 */
245 function getAllTemplateLiterals() {
246 return sourceCode.ast.tokens.filter(token => token.type === "Template");
247 }
248
249
250 /**
251 * Retrieves an array containing all RegExp literals in the source code.
252 * @returns {ASTNode[]} An array of RegExp literal nodes.
253 */
254 function getAllRegExpLiterals() {
255 return sourceCode.ast.tokens.filter(token => token.type === "RegularExpression");
256 }
257
258 /**
259 *
260 * reduce an array of AST nodes by line number, both start and end.
261 * @param {ASTNode[]} arr array of AST nodes
262 * @returns {Object} accululated AST nodes
263 */
264 function groupArrayByLineNumber(arr) {
265 const obj = {};
266
267 for (let i = 0; i < arr.length; i++) {
268 const node = arr[i];
269
270 for (let j = node.loc.start.line; j <= node.loc.end.line; ++j) {
271 ensureArrayAndPush(obj, j, node);
272 }
273 }
274 return obj;
275 }
276
277 /**
278 * Returns an array of all comments in the source code.
279 * If the element in the array is a JSXEmptyExpression contained with a single line JSXExpressionContainer,
280 * the element is changed with JSXExpressionContainer node.
281 * @returns {ASTNode[]} An array of comment nodes
282 */
283 function getAllComments() {
284 const comments = [];
285
286 sourceCode.getAllComments()
287 .forEach(commentNode => {
288 const containingNode = sourceCode.getNodeByRangeIndex(commentNode.range[0]);
289
290 if (isJSXEmptyExpressionInSingleLineContainer(containingNode)) {
291
292 // push a unique node only
293 if (comments[comments.length - 1] !== containingNode.parent) {
294 comments.push(containingNode.parent);
295 }
296 } else {
297 comments.push(commentNode);
298 }
299 });
300
301 return comments;
302 }
303
304 /**
305 * Check the program for max length
306 * @param {ASTNode} node Node to examine
307 * @returns {void}
308 * @private
309 */
310 function checkProgramForMaxLength(node) {
311
312 // split (honors line-ending)
313 const lines = sourceCode.lines,
314
315 // list of comments to ignore
316 comments = ignoreComments || maxCommentLength || ignoreTrailingComments ? getAllComments() : [];
317
318 // we iterate over comments in parallel with the lines
319 let commentsIndex = 0;
320
321 const strings = getAllStrings();
322 const stringsByLine = groupArrayByLineNumber(strings);
323
324 const templateLiterals = getAllTemplateLiterals();
325 const templateLiteralsByLine = groupArrayByLineNumber(templateLiterals);
326
327 const regExpLiterals = getAllRegExpLiterals();
328 const regExpLiteralsByLine = groupArrayByLineNumber(regExpLiterals);
329
330 lines.forEach((line, i) => {
331
332 // i is zero-indexed, line numbers are one-indexed
333 const lineNumber = i + 1;
334
335 /*
336 * if we're checking comment length; we need to know whether this
337 * line is a comment
338 */
339 let lineIsComment = false;
340 let textToMeasure;
341
342 /*
343 * We can short-circuit the comment checks if we're already out of
344 * comments to check.
345 */
346 if (commentsIndex < comments.length) {
347 let comment = null;
348
349 // iterate over comments until we find one past the current line
350 do {
351 comment = comments[++commentsIndex];
352 } while (comment && comment.loc.start.line <= lineNumber);
353
354 // and step back by one
355 comment = comments[--commentsIndex];
356
357 if (isFullLineComment(line, lineNumber, comment)) {
358 lineIsComment = true;
359 textToMeasure = line;
360 } else if (ignoreTrailingComments && isTrailingComment(line, lineNumber, comment)) {
361 textToMeasure = stripTrailingComment(line, comment);
362
363 // ignore multiple trailing comments in the same line
364 let lastIndex = commentsIndex;
365
366 while (isTrailingComment(textToMeasure, lineNumber, comments[--lastIndex])) {
367 textToMeasure = stripTrailingComment(textToMeasure, comments[lastIndex]);
368 }
369 } else {
370 textToMeasure = line;
371 }
372 } else {
373 textToMeasure = line;
374 }
375 if (ignorePattern && ignorePattern.test(textToMeasure) ||
376 ignoreUrls && URL_REGEXP.test(textToMeasure) ||
377 ignoreStrings && stringsByLine[lineNumber] ||
378 ignoreTemplateLiterals && templateLiteralsByLine[lineNumber] ||
379 ignoreRegExpLiterals && regExpLiteralsByLine[lineNumber]
380 ) {
381
382 // ignore this line
383 return;
384 }
385
386 const lineLength = computeLineLength(textToMeasure, tabWidth);
387 const commentLengthApplies = lineIsComment && maxCommentLength;
388
389 if (lineIsComment && ignoreComments) {
390 return;
391 }
392
393 const loc = {
394 start: {
395 line: lineNumber,
396 column: 0
397 },
398 end: {
399 line: lineNumber,
400 column: textToMeasure.length
401 }
402 };
403
404 if (commentLengthApplies) {
405 if (lineLength > maxCommentLength) {
406 context.report({
407 node,
408 loc,
409 messageId: "maxComment",
410 data: {
411 lineLength,
412 maxCommentLength
413 }
414 });
415 }
416 } else if (lineLength > maxLength) {
417 context.report({
418 node,
419 loc,
420 messageId: "max",
421 data: {
422 lineLength,
423 maxLength
424 }
425 });
426 }
427 });
428 }
429
430
431 //--------------------------------------------------------------------------
432 // Public API
433 //--------------------------------------------------------------------------
434
435 return {
436 Program: checkProgramForMaxLength
437 };
438
439 }
440};
Note: See TracBrowser for help on using the repository browser.