2 * @fileoverview Rule to flag dangling underscores in variable declarations.
3 * @author Matt DuVall <http://www.mattduvall.com>
8 //------------------------------------------------------------------------------
10 //------------------------------------------------------------------------------
12 /** @type {import('../shared/types').Rule} */
18 description
: "Disallow dangling underscores in identifiers",
20 url
: "https://eslint.org/docs/rules/no-underscore-dangle"
41 allowAfterThisConstructor
: {
45 enforceInMethodNames
: {
49 allowFunctionParams
: {
53 enforceInClassFields
: {
58 additionalProperties
: false
63 unexpectedUnderscore
: "Unexpected dangling '_' in '{{identifier}}'."
69 const options
= context
.options
[0] || {};
70 const ALLOWED_VARIABLES
= options
.allow
? options
.allow
: [];
71 const allowAfterThis
= typeof options
.allowAfterThis
!== "undefined" ? options
.allowAfterThis
: false;
72 const allowAfterSuper
= typeof options
.allowAfterSuper
!== "undefined" ? options
.allowAfterSuper
: false;
73 const allowAfterThisConstructor
= typeof options
.allowAfterThisConstructor
!== "undefined" ? options
.allowAfterThisConstructor
: false;
74 const enforceInMethodNames
= typeof options
.enforceInMethodNames
!== "undefined" ? options
.enforceInMethodNames
: false;
75 const enforceInClassFields
= typeof options
.enforceInClassFields
!== "undefined" ? options
.enforceInClassFields
: false;
76 const allowFunctionParams
= typeof options
.allowFunctionParams
!== "undefined" ? options
.allowFunctionParams
: true;
78 //-------------------------------------------------------------------------
80 //-------------------------------------------------------------------------
83 * Check if identifier is present inside the allowed option
84 * @param {string} identifier name of the node
85 * @returns {boolean} true if its is present
88 function isAllowed(identifier
) {
89 return ALLOWED_VARIABLES
.includes(identifier
);
93 * Check if identifier has a dangling underscore
94 * @param {string} identifier name of the node
95 * @returns {boolean} true if its is present
98 function hasDanglingUnderscore(identifier
) {
99 const len
= identifier
.length
;
101 return identifier
!== "_" && (identifier
[0] === "_" || identifier
[len
- 1] === "_");
105 * Check if identifier is a special case member expression
106 * @param {string} identifier name of the node
107 * @returns {boolean} true if its is a special case
110 function isSpecialCaseIdentifierForMemberExpression(identifier
) {
111 return identifier
=== "__proto__";
115 * Check if identifier is a special case variable expression
116 * @param {string} identifier name of the node
117 * @returns {boolean} true if its is a special case
120 function isSpecialCaseIdentifierInVariableExpression(identifier
) {
122 // Checks for the underscore library usage here
123 return identifier
=== "_";
127 * Check if a node is a member reference of this.constructor
128 * @param {ASTNode} node node to evaluate
129 * @returns {boolean} true if it is a reference on this.constructor
132 function isThisConstructorReference(node
) {
133 return node
.object
.type
=== "MemberExpression" &&
134 node
.object
.property
.name
=== "constructor" &&
135 node
.object
.object
.type
=== "ThisExpression";
139 * Check if function parameter has a dangling underscore.
140 * @param {ASTNode} node function node to evaluate
144 function checkForDanglingUnderscoreInFunctionParameters(node
) {
145 if (!allowFunctionParams
) {
146 node
.params
.forEach(param
=> {
147 const { type
} = param
;
150 if (type
=== "RestElement") {
151 nodeToCheck
= param
.argument
;
152 } else if (type
=== "AssignmentPattern") {
153 nodeToCheck
= param
.left
;
158 if (nodeToCheck
.type
=== "Identifier") {
159 const identifier
= nodeToCheck
.name
;
161 if (hasDanglingUnderscore(identifier
) && !isAllowed(identifier
)) {
164 messageId
: "unexpectedUnderscore",
176 * Check if function has a dangling underscore
177 * @param {ASTNode} node node to evaluate
181 function checkForDanglingUnderscoreInFunction(node
) {
182 if (node
.type
=== "FunctionDeclaration" && node
.id
) {
183 const identifier
= node
.id
.name
;
185 if (typeof identifier
!== "undefined" && hasDanglingUnderscore(identifier
) && !isAllowed(identifier
)) {
188 messageId
: "unexpectedUnderscore",
195 checkForDanglingUnderscoreInFunctionParameters(node
);
199 * Check if variable expression has a dangling underscore
200 * @param {ASTNode} node node to evaluate
204 function checkForDanglingUnderscoreInVariableExpression(node
) {
205 const identifier
= node
.id
.name
;
207 if (typeof identifier
!== "undefined" && hasDanglingUnderscore(identifier
) &&
208 !isSpecialCaseIdentifierInVariableExpression(identifier
) && !isAllowed(identifier
)) {
211 messageId
: "unexpectedUnderscore",
220 * Check if member expression has a dangling underscore
221 * @param {ASTNode} node node to evaluate
225 function checkForDanglingUnderscoreInMemberExpression(node
) {
226 const identifier
= node
.property
.name
,
227 isMemberOfThis
= node
.object
.type
=== "ThisExpression",
228 isMemberOfSuper
= node
.object
.type
=== "Super",
229 isMemberOfThisConstructor
= isThisConstructorReference(node
);
231 if (typeof identifier
!== "undefined" && hasDanglingUnderscore(identifier
) &&
232 !(isMemberOfThis
&& allowAfterThis
) &&
233 !(isMemberOfSuper
&& allowAfterSuper
) &&
234 !(isMemberOfThisConstructor
&& allowAfterThisConstructor
) &&
235 !isSpecialCaseIdentifierForMemberExpression(identifier
) && !isAllowed(identifier
)) {
238 messageId
: "unexpectedUnderscore",
247 * Check if method declaration or method property has a dangling underscore
248 * @param {ASTNode} node node to evaluate
252 function checkForDanglingUnderscoreInMethod(node
) {
253 const identifier
= node
.key
.name
;
254 const isMethod
= node
.type
=== "MethodDefinition" || node
.type
=== "Property" && node
.method
;
256 if (typeof identifier
!== "undefined" && enforceInMethodNames
&& isMethod
&& hasDanglingUnderscore(identifier
) && !isAllowed(identifier
)) {
259 messageId
: "unexpectedUnderscore",
261 identifier
: node
.key
.type
=== "PrivateIdentifier"
270 * Check if a class field has a dangling underscore
271 * @param {ASTNode} node node to evaluate
275 function checkForDanglingUnderscoreInClassField(node
) {
276 const identifier
= node
.key
.name
;
278 if (typeof identifier
!== "undefined" && hasDanglingUnderscore(identifier
) &&
279 enforceInClassFields
&&
280 !isAllowed(identifier
)) {
283 messageId
: "unexpectedUnderscore",
285 identifier
: node
.key
.type
=== "PrivateIdentifier"
293 //--------------------------------------------------------------------------
295 //--------------------------------------------------------------------------
298 FunctionDeclaration
: checkForDanglingUnderscoreInFunction
,
299 VariableDeclarator
: checkForDanglingUnderscoreInVariableExpression
,
300 MemberExpression
: checkForDanglingUnderscoreInMemberExpression
,
301 MethodDefinition
: checkForDanglingUnderscoreInMethod
,
302 PropertyDefinition
: checkForDanglingUnderscoreInClassField
,
303 Property
: checkForDanglingUnderscoreInMethod
,
304 FunctionExpression
: checkForDanglingUnderscoreInFunction
,
305 ArrowFunctionExpression
: checkForDanglingUnderscoreInFunction