UNPKG

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