]> git.proxmox.com Git - pve-eslint.git/blob - eslint/lib/rules/id-length.js
3701c66e347a7287321da25d25d743ca7ff9305f
[pve-eslint.git] / eslint / lib / rules / id-length.js
1 /**
2 * @fileoverview Rule that warns when identifier names are shorter or longer
3 * than the values provided in configuration.
4 * @author Burak Yigit Kaya aka BYK
5 */
6
7 "use strict";
8
9 //------------------------------------------------------------------------------
10 // Rule Definition
11 //------------------------------------------------------------------------------
12
13 /** @type {import('../shared/types').Rule} */
14 module.exports = {
15 meta: {
16 type: "suggestion",
17
18 docs: {
19 description: "enforce minimum and maximum identifier lengths",
20 recommended: false,
21 url: "https://eslint.org/docs/rules/id-length"
22 },
23
24 schema: [
25 {
26 type: "object",
27 properties: {
28 min: {
29 type: "integer",
30 default: 2
31 },
32 max: {
33 type: "integer"
34 },
35 exceptions: {
36 type: "array",
37 uniqueItems: true,
38 items: {
39 type: "string"
40 }
41 },
42 exceptionPatterns: {
43 type: "array",
44 uniqueItems: true,
45 items: {
46 type: "string"
47 }
48 },
49 properties: {
50 enum: ["always", "never"]
51 }
52 },
53 additionalProperties: false
54 }
55 ],
56 messages: {
57 tooShort: "Identifier name '{{name}}' is too short (< {{min}}).",
58 tooShortPrivate: "Identifier name '#{{name}}' is too short (< {{min}}).",
59 tooLong: "Identifier name '{{name}}' is too long (> {{max}}).",
60 tooLongPrivate: "Identifier name #'{{name}}' is too long (> {{max}})."
61 }
62 },
63
64 create(context) {
65 const options = context.options[0] || {};
66 const minLength = typeof options.min !== "undefined" ? options.min : 2;
67 const maxLength = typeof options.max !== "undefined" ? options.max : Infinity;
68 const properties = options.properties !== "never";
69 const exceptions = new Set(options.exceptions);
70 const exceptionPatterns = (options.exceptionPatterns || []).map(pattern => new RegExp(pattern, "u"));
71 const reportedNodes = new Set();
72
73 /**
74 * Checks if a string matches the provided exception patterns
75 * @param {string} name The string to check.
76 * @returns {boolean} if the string is a match
77 * @private
78 */
79 function matchesExceptionPattern(name) {
80 return exceptionPatterns.some(pattern => pattern.test(name));
81 }
82
83 const SUPPORTED_EXPRESSIONS = {
84 MemberExpression: properties && function(parent) {
85 return !parent.computed && (
86
87 // regular property assignment
88 (parent.parent.left === parent && parent.parent.type === "AssignmentExpression" ||
89
90 // or the last identifier in an ObjectPattern destructuring
91 parent.parent.type === "Property" && parent.parent.value === parent &&
92 parent.parent.parent.type === "ObjectPattern" && parent.parent.parent.parent.left === parent.parent.parent)
93 );
94 },
95 AssignmentPattern(parent, node) {
96 return parent.left === node;
97 },
98 VariableDeclarator(parent, node) {
99 return parent.id === node;
100 },
101 Property(parent, node) {
102
103 if (parent.parent.type === "ObjectPattern") {
104 const isKeyAndValueSame = parent.value.name === parent.key.name;
105
106 return (
107 !isKeyAndValueSame && parent.value === node ||
108 isKeyAndValueSame && parent.key === node && properties
109 );
110 }
111 return properties && !parent.computed && parent.key.name === node.name;
112 },
113 ImportDefaultSpecifier: true,
114 RestElement: true,
115 FunctionExpression: true,
116 ArrowFunctionExpression: true,
117 ClassDeclaration: true,
118 FunctionDeclaration: true,
119 MethodDefinition: true,
120 PropertyDefinition: true,
121 CatchClause: true,
122 ArrayPattern: true
123 };
124
125 return {
126 [[
127 "Identifier",
128 "PrivateIdentifier"
129 ]](node) {
130 const name = node.name;
131 const parent = node.parent;
132
133 const isShort = name.length < minLength;
134 const isLong = name.length > maxLength;
135
136 if (!(isShort || isLong) || exceptions.has(name) || matchesExceptionPattern(name)) {
137 return; // Nothing to report
138 }
139
140 const isValidExpression = SUPPORTED_EXPRESSIONS[parent.type];
141
142 /*
143 * We used the range instead of the node because it's possible
144 * for the same identifier to be represented by two different
145 * nodes, with the most clear example being shorthand properties:
146 * { foo }
147 * In this case, "foo" is represented by one node for the name
148 * and one for the value. The only way to know they are the same
149 * is to look at the range.
150 */
151 if (isValidExpression && !reportedNodes.has(node.range.toString()) && (isValidExpression === true || isValidExpression(parent, node))) {
152 reportedNodes.add(node.range.toString());
153
154 let messageId = isShort ? "tooShort" : "tooLong";
155
156 if (node.type === "PrivateIdentifier") {
157 messageId += "Private";
158 }
159
160 context.report({
161 node,
162 messageId,
163 data: { name, min: minLength, max: maxLength }
164 });
165 }
166 }
167 };
168 }
169 };