Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New: allowAfterThisConstructor for no-underscore-dangle (fixes #11488) #11489

Merged
merged 1 commit into from Oct 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 12 additions & 0 deletions docs/rules/no-underscore-dangle.md
Expand Up @@ -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
* `"allowAfterThisConstructor": false` (default) disallows dangling underscores in members of the `this.constructor` object
btmills marked this conversation as resolved.
Show resolved Hide resolved
* `"enforceInMethodNames": false` (default) allows dangling underscores in method names

### allow
Expand Down Expand Up @@ -77,6 +78,17 @@ var a = super.foo_;
super._bar();
```

### allowAfterThisConstructor

Examples of **correct** code for this rule with the `{ "allowAfterThisConstructor": true }` option:

```js
/*eslint no-underscore-dangle: ["error", { "allowAfterThisConstructor": true }]*/

var a = this.constructor.foo_;
this.constructor._bar();
```

### enforceInMethodNames

Examples of incorrect code for this rule with the `{ "enforceInMethodNames": true }` option:
Expand Down
27 changes: 23 additions & 4 deletions lib/rules/no-underscore-dangle.js
Expand Up @@ -38,6 +38,10 @@ module.exports = {
type: "boolean",
default: false
},
allowAfterThisConstructor: {
type: "boolean",
default: false
},
enforceInMethodNames: {
type: "boolean",
default: false
Expand All @@ -54,6 +58,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 allowAfterThisConstructor = typeof options.allowAfterThisConstructor !== "undefined" ? options.allowAfterThisConstructor : false;
const enforceInMethodNames = typeof options.enforceInMethodNames !== "undefined" ? options.enforceInMethodNames : false;

//-------------------------------------------------------------------------
Expand All @@ -72,7 +77,7 @@ module.exports = {

/**
* Check if identifier has a underscore at the end
* @param {ASTNode} identifier node to evaluate
* @param {string} identifier name of the node
* @returns {boolean} true if its is present
* @private
*/
Expand All @@ -84,7 +89,7 @@ module.exports = {

/**
* Check if identifier is a special case member expression
* @param {ASTNode} identifier node to evaluate
* @param {string} identifier name of the node
* @returns {boolean} true if its is a special case
* @private
*/
Expand All @@ -94,7 +99,7 @@ module.exports = {

/**
* Check if identifier is a special case variable expression
* @param {ASTNode} identifier node to evaluate
* @param {string} identifier name of the node
* @returns {boolean} true if its is a special case
* @private
*/
Expand All @@ -104,6 +109,18 @@ module.exports = {
return identifier === "_";
}

/**
* Check if a node is a member reference of this.constructor
* @param {ASTNode} node node to evaluate
* @returns {boolean} true if it is a reference on this.constructor
* @private
*/
function isThisConstructorReference(node) {
return node.object.type === "MemberExpression" &&
node.object.property.name === "constructor" &&
node.object.object.type === "ThisExpression";
}

/**
* Check if function has a underscore at the end
* @param {ASTNode} node node to evaluate
Expand Down Expand Up @@ -156,11 +173,13 @@ module.exports = {
function checkForTrailingUnderscoreInMemberExpression(node) {
const identifier = node.property.name,
isMemberOfThis = node.object.type === "ThisExpression",
isMemberOfSuper = node.object.type === "Super";
isMemberOfSuper = node.object.type === "Super",
isMemberOfThisConstructor = isThisConstructorReference(node);

if (typeof identifier !== "undefined" && hasTrailingUnderscore(identifier) &&
!(isMemberOfThis && allowAfterThis) &&
!(isMemberOfSuper && allowAfterSuper) &&
!(isMemberOfThisConstructor && allowAfterThisConstructor) &&
!isSpecialCaseIdentifierForMemberExpression(identifier) && !isAllowed(identifier)) {
context.report({
node,
Expand Down
7 changes: 5 additions & 2 deletions tests/lib/rules/no-underscore-dangle.js
Expand Up @@ -38,7 +38,8 @@ ruleTester.run("no-underscore-dangle", rule, {
{ code: "const o = { _onClick() { } }", parserOptions: { ecmaVersion: 6 } },
{ code: "const o = { onClick_() { } }", parserOptions: { ecmaVersion: 6 } },
{ code: "const o = { _foo: 'bar' }", parserOptions: { ecmaVersion: 6 } },
{ code: "const o = { foo_: 'bar' }", parserOptions: { ecmaVersion: 6 } }
{ code: "const o = { foo_: 'bar' }", parserOptions: { ecmaVersion: 6 } },
{ code: "this.constructor._bar", options: [{ allowAfterThisConstructor: true }] }
],
invalid: [
{ code: "var _foo = 1", errors: [{ message: "Unexpected dangling '_' in '_foo'.", type: "VariableDeclarator" }] },
Expand All @@ -53,6 +54,8 @@ ruleTester.run("no-underscore-dangle", rule, {
{ 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" }] }
{ code: "const o = { onClick_() { } }", options: [{ enforceInMethodNames: true }], parserOptions: { ecmaVersion: 6 }, errors: [{ message: "Unexpected dangling '_' in 'onClick_'.", type: "Property" }] },
{ code: "this.constructor._bar", errors: [{ message: "Unexpected dangling '_' in '_bar'.", type: "MemberExpression" }] },
{ code: "foo.constructor._bar", options: [{ allowAfterThisConstructor: true }], errors: [{ message: "Unexpected dangling '_' in '_bar'.", type: "MemberExpression" }] }
]
});