UNPKG

7.62 kBJavaScriptView Raw
1/**
2 * @fileoverview Rule to flag trailing underscores in variable declarations.
3 * @author Matt DuVall <http://www.mattduvall.com>
4 */
5
6"use strict";
7
8//------------------------------------------------------------------------------
9// Rule Definition
10//------------------------------------------------------------------------------
11
12module.exports = {
13 meta: {
14 type: "suggestion",
15
16 docs: {
17 description: "disallow dangling underscores in identifiers",
18 category: "Stylistic Issues",
19 recommended: false,
20 url: "https://eslint.org/docs/rules/no-underscore-dangle"
21 },
22
23 schema: [
24 {
25 type: "object",
26 properties: {
27 allow: {
28 type: "array",
29 items: {
30 type: "string"
31 }
32 },
33 allowAfterThis: {
34 type: "boolean",
35 default: false
36 },
37 allowAfterSuper: {
38 type: "boolean",
39 default: false
40 },
41 enforceInMethodNames: {
42 type: "boolean",
43 default: false
44 }
45 },
46 additionalProperties: false
47 }
48 ]
49 },
50
51 create(context) {
52
53 const options = context.options[0] || {};
54 const ALLOWED_VARIABLES = options.allow ? options.allow : [];
55 const allowAfterThis = typeof options.allowAfterThis !== "undefined" ? options.allowAfterThis : false;
56 const allowAfterSuper = typeof options.allowAfterSuper !== "undefined" ? options.allowAfterSuper : false;
57 const enforceInMethodNames = typeof options.enforceInMethodNames !== "undefined" ? options.enforceInMethodNames : false;
58
59 //-------------------------------------------------------------------------
60 // Helpers
61 //-------------------------------------------------------------------------
62
63 /**
64 * Check if identifier is present inside the allowed option
65 * @param {string} identifier name of the node
66 * @returns {boolean} true if its is present
67 * @private
68 */
69 function isAllowed(identifier) {
70 return ALLOWED_VARIABLES.some(ident => ident === identifier);
71 }
72
73 /**
74 * Check if identifier has a underscore at the end
75 * @param {ASTNode} identifier node to evaluate
76 * @returns {boolean} true if its is present
77 * @private
78 */
79 function hasTrailingUnderscore(identifier) {
80 const len = identifier.length;
81
82 return identifier !== "_" && (identifier[0] === "_" || identifier[len - 1] === "_");
83 }
84
85 /**
86 * Check if identifier is a special case member expression
87 * @param {ASTNode} identifier node to evaluate
88 * @returns {boolean} true if its is a special case
89 * @private
90 */
91 function isSpecialCaseIdentifierForMemberExpression(identifier) {
92 return identifier === "__proto__";
93 }
94
95 /**
96 * Check if identifier is a special case variable expression
97 * @param {ASTNode} identifier node to evaluate
98 * @returns {boolean} true if its is a special case
99 * @private
100 */
101 function isSpecialCaseIdentifierInVariableExpression(identifier) {
102
103 // Checks for the underscore library usage here
104 return identifier === "_";
105 }
106
107 /**
108 * Check if function has a underscore at the end
109 * @param {ASTNode} node node to evaluate
110 * @returns {void}
111 * @private
112 */
113 function checkForTrailingUnderscoreInFunctionDeclaration(node) {
114 if (node.id) {
115 const identifier = node.id.name;
116
117 if (typeof identifier !== "undefined" && hasTrailingUnderscore(identifier) && !isAllowed(identifier)) {
118 context.report({
119 node,
120 message: "Unexpected dangling '_' in '{{identifier}}'.",
121 data: {
122 identifier
123 }
124 });
125 }
126 }
127 }
128
129 /**
130 * Check if variable expression has a underscore at the end
131 * @param {ASTNode} node node to evaluate
132 * @returns {void}
133 * @private
134 */
135 function checkForTrailingUnderscoreInVariableExpression(node) {
136 const identifier = node.id.name;
137
138 if (typeof identifier !== "undefined" && hasTrailingUnderscore(identifier) &&
139 !isSpecialCaseIdentifierInVariableExpression(identifier) && !isAllowed(identifier)) {
140 context.report({
141 node,
142 message: "Unexpected dangling '_' in '{{identifier}}'.",
143 data: {
144 identifier
145 }
146 });
147 }
148 }
149
150 /**
151 * Check if member expression has a underscore at the end
152 * @param {ASTNode} node node to evaluate
153 * @returns {void}
154 * @private
155 */
156 function checkForTrailingUnderscoreInMemberExpression(node) {
157 const identifier = node.property.name,
158 isMemberOfThis = node.object.type === "ThisExpression",
159 isMemberOfSuper = node.object.type === "Super";
160
161 if (typeof identifier !== "undefined" && hasTrailingUnderscore(identifier) &&
162 !(isMemberOfThis && allowAfterThis) &&
163 !(isMemberOfSuper && allowAfterSuper) &&
164 !isSpecialCaseIdentifierForMemberExpression(identifier) && !isAllowed(identifier)) {
165 context.report({
166 node,
167 message: "Unexpected dangling '_' in '{{identifier}}'.",
168 data: {
169 identifier
170 }
171 });
172 }
173 }
174
175 /**
176 * Check if method declaration or method property has a underscore at the end
177 * @param {ASTNode} node node to evaluate
178 * @returns {void}
179 * @private
180 */
181 function checkForTrailingUnderscoreInMethod(node) {
182 const identifier = node.key.name;
183 const isMethod = node.type === "MethodDefinition" || node.type === "Property" && node.method;
184
185 if (typeof identifier !== "undefined" && enforceInMethodNames && isMethod && hasTrailingUnderscore(identifier)) {
186 context.report({
187 node,
188 message: "Unexpected dangling '_' in '{{identifier}}'.",
189 data: {
190 identifier
191 }
192 });
193 }
194 }
195
196 //--------------------------------------------------------------------------
197 // Public API
198 //--------------------------------------------------------------------------
199
200 return {
201 FunctionDeclaration: checkForTrailingUnderscoreInFunctionDeclaration,
202 VariableDeclarator: checkForTrailingUnderscoreInVariableExpression,
203 MemberExpression: checkForTrailingUnderscoreInMemberExpression,
204 MethodDefinition: checkForTrailingUnderscoreInMethod,
205 Property: checkForTrailingUnderscoreInMethod
206 };
207
208 }
209};