diff --git a/docs/rules/no-underscore-dangle.md b/docs/rules/no-underscore-dangle.md index d09c6d6ade41..e51a020dbe5f 100644 --- a/docs/rules/no-underscore-dangle.md +++ b/docs/rules/no-underscore-dangle.md @@ -42,6 +42,7 @@ This rule has an object option: * `"allow"` allows specified identifiers to have dangling underscores * `"allowAfterThis": false` (default) disallows dangling underscores in members of the `this` object * `"allowAfterSuper": false` (default) disallows dangling underscores in members of the `super` object +* `"enforceInMethodNames": false (default) allows dangling underscores in method names` ### allow @@ -76,6 +77,30 @@ var a = super.foo_; super._bar(); ``` +### enforceInMethodNames + +Examples of incorrect code for this rule with the `{ "enforceInMethodNames": true }` option: + +```js +/*eslint no-underscore-dangle: ["error", { "enforceInMethodNames": true }]*/ + +class Foo { + _bar() {} +} + +class Foo { + bar_() {} +} + +const o = { + _bar() {} +}; + +const o = { + bar_() = {} +}; +``` + ## When Not To Use It If you want to allow dangling underscores in identifiers, then you can safely turn this rule off. diff --git a/lib/rules/no-underscore-dangle.js b/lib/rules/no-underscore-dangle.js index 6803cc68fc7e..2c716f1f477a 100644 --- a/lib/rules/no-underscore-dangle.js +++ b/lib/rules/no-underscore-dangle.js @@ -32,6 +32,9 @@ module.exports = { }, allowAfterSuper: { type: "boolean" + }, + enforceInMethodNames: { + type: "boolean" } }, additionalProperties: false @@ -45,6 +48,7 @@ module.exports = { const ALLOWED_VARIABLES = options.allow ? options.allow : []; const allowAfterThis = typeof options.allowAfterThis !== "undefined" ? options.allowAfterThis : false; const allowAfterSuper = typeof options.allowAfterSuper !== "undefined" ? options.allowAfterSuper : false; + const enforceInMethodNames = typeof options.enforceInMethodNames !== "undefined" ? options.enforceInMethodNames : false; //------------------------------------------------------------------------- // Helpers @@ -162,6 +166,47 @@ module.exports = { } } + /** + * Check if method declaration has a underscore at the end + * @param {ASTNode} node node to evaluate + * @returns {void} + * @private + */ + function checkForTrailingUnderscoreInMethodDefinition(node) { + const identifier = node.key.name; + + if (typeof identifier !== "undefined" && hasTrailingUnderscore(identifier) && enforceInMethodNames) { + context.report({ + node, + message: "Unexpected dangling '_' in '{{identifier}}'", + data: { + identifier + } + }); + } + } + + /** + * Check if method property has a underscore at the end + * @param {ASTNode} node node to evaluate + * @returns {void} + * @private + */ + function checkForTrailingUnderscoreInMethodProperty(node) { + const identifier = node.key.name; + const isMethod = node.method; + + if (typeof identifier !== "undefined" && hasTrailingUnderscore(identifier) && enforceInMethodNames && isMethod) { + context.report({ + node, + message: "Unexpected dangling '_' in '{{identifier}}'", + data: { + identifier + } + }); + } + } + //-------------------------------------------------------------------------- // Public API //-------------------------------------------------------------------------- @@ -169,7 +214,9 @@ module.exports = { return { FunctionDeclaration: checkForTrailingUnderscoreInFunctionDeclaration, VariableDeclarator: checkForTrailingUnderscoreInVariableExpression, - MemberExpression: checkForTrailingUnderscoreInMemberExpression + MemberExpression: checkForTrailingUnderscoreInMemberExpression, + MethodDefinition: checkForTrailingUnderscoreInMethodDefinition, + Property: checkForTrailingUnderscoreInMethodProperty }; } diff --git a/tests/lib/rules/no-underscore-dangle.js b/tests/lib/rules/no-underscore-dangle.js index 0f7a8ad4352a..c0298765c14a 100644 --- a/tests/lib/rules/no-underscore-dangle.js +++ b/tests/lib/rules/no-underscore-dangle.js @@ -32,7 +32,11 @@ ruleTester.run("no-underscore-dangle", rule, { { code: "foo._bar;", options: [{ allow: ["_bar"] }] }, { code: "function _foo() {}", options: [{ allow: ["_foo"] }] }, { code: "this._bar;", options: [{ allowAfterThis: true }] }, - { code: "class foo { constructor() { super._bar; } }", parserOptions: { ecmaVersion: 6 }, options: [{ allowAfterSuper: true }] } + { code: "class foo { constructor() { super._bar; } }", parserOptions: { ecmaVersion: 6 }, options: [{ allowAfterSuper: true }] }, + { code: "class foo { _onClick() { } }", parserOptions: { ecmaVersion: 6 }}, + { code: "class foo { onClick_() { } }", parserOptions: { ecmaVersion: 6 }}, + { code: "const o = { _onClick() { } }", parserOptions: { ecmaVersion: 6 }}, + { code: "const o = { onClick_() { } }", parserOptions: { ecmaVersion: 6 }} ], invalid: [ { code: "var _foo = 1", errors: [{ message: "Unexpected dangling '_' in '_foo'.", type: "VariableDeclarator" }] }, @@ -44,5 +48,9 @@ ruleTester.run("no-underscore-dangle", rule, { { code: "this._prop;", errors: [{ message: "Unexpected dangling '_' in '_prop'.", type: "MemberExpression" }] }, { code: "class foo { constructor() { super._prop; } }", parserOptions: { ecmaVersion: 6 }, errors: [{ message: "Unexpected dangling '_' in '_prop'.", type: "MemberExpression" }] }, { code: "class foo { constructor() { this._prop; } }", options: [{ allowAfterSuper: true }], parserOptions: { ecmaVersion: 6 }, errors: [{ message: "Unexpected dangling '_' in '_prop'.", type: "MemberExpression" }] } + { code: "class foo { _onClick() { } }", options: [{enforceInMethodNames: true}], parserOptions: { ecmaVersion: 6 }, errors: [{ message: "Unexpected dangling '_' in '_onClick'", type: "MethodDefinition" }] }, + { code: "class foo { onClick_() { } }", options: [{enforceInMethodNames: true}], parserOptions: { ecmaVersion: 6 }, errors: [{ message: "Unexpected dangling '_' in 'onClick_'", type: "MethodDefinition" }] }, + { code: "const o = { _onClick() { } }", options: [{enforceInMethodNames: true}], parserOptions: { ecmaVersion: 6 }, errors: [{ message: "Unexpected dangling '_' in '_onClick'", type: "Property" }] }, + { code: "const o = { onClick_() { } }", options: [{enforceInMethodNames: true}], parserOptions: { ecmaVersion: 6 }, errors: [{ message: "Unexpected dangling '_' in 'onClick_'", type: "Property" }] } ] });