UNPKG

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