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

feat: add options to check destructuring in no-underscore-dangle #16006

Merged
merged 14 commits into from Dec 21, 2022
Merged
35 changes: 35 additions & 0 deletions docs/src/rules/no-underscore-dangle.md
Expand Up @@ -29,6 +29,7 @@ Examples of **incorrect** code for this rule:
var foo_;
var __proto__ = {};
foo._bar();
const [_foo, ..._bar] = list;
```

Examples of **correct** code for this rule:
Expand All @@ -55,6 +56,8 @@ This rule has an object option:
* `"allowAfterThisConstructor": false` (default) disallows dangling underscores in members of the `this.constructor` object
* `"enforceInMethodNames": false` (default) allows dangling underscores in method names
* `"enforceInClassFields": false` (default) allows dangling underscores in es2022 class fields names
* `"allowInArrayDestructuring": true` (default) allows dangling underscores in variable names assigned by array destructuring
* `"allowInObjectDestructuring": true` (default) allows dangling underscores in variable names assigned by object destructuring
* `"allowFunctionParams": true` (default) allows dangling underscores in function parameter names

### allow
Expand Down Expand Up @@ -153,6 +156,38 @@ class Foo {
}
```

### allowInArrayDestructuring

Examples of **incorrect** code for this rule with the `{ "allowInArrayDestructuring": false }` option:
Kaltoft marked this conversation as resolved.
Show resolved Hide resolved

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

const [_foo, _bar] = list;
const [foo_, ..._bar] = list;
const [foo, [bar, _baz]] = list;
```

### allowInObjectDestructuring

Examples of **incorrect** code for this rule with the `{ "allowInObjectDestructuring": false }` option:

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

const { foo, bar: _bar } = collection;
const { foo, bar, _baz } = collection;
```

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

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

const { foo, bar, _baz: { a, b } } = collection;
const { foo, bar, _baz: baz } = collection;
```

### allowFunctionParams

Examples of **incorrect** code for this rule with the `{ "allowFunctionParams": false }` option:
Expand Down
60 changes: 60 additions & 0 deletions lib/rules/no-underscore-dangle.js
Expand Up @@ -53,6 +53,14 @@ module.exports = {
enforceInClassFields: {
type: "boolean",
default: false
},
allowInArrayDestructuring: {
type: "boolean",
default: true,
},
allowInObjectDestructuring: {
type: "boolean",
default: true,
}
},
additionalProperties: false
Expand All @@ -74,6 +82,8 @@ module.exports = {
const enforceInMethodNames = typeof options.enforceInMethodNames !== "undefined" ? options.enforceInMethodNames : false;
const enforceInClassFields = typeof options.enforceInClassFields !== "undefined" ? options.enforceInClassFields : false;
const allowFunctionParams = typeof options.allowFunctionParams !== "undefined" ? options.allowFunctionParams : true;
const allowInArrayDestructuring = typeof options.allowInArrayDestructuring !== "undefined" ? options.allowInArrayDestructuring : true;
const allowInObjectDestructuring = typeof options.allowInObjectDestructuring !== "undefined" ? options.allowInObjectDestructuring : true;

//-------------------------------------------------------------------------
// Helpers
Expand Down Expand Up @@ -195,13 +205,63 @@ module.exports = {
checkForDanglingUnderscoreInFunctionParameters(node);
}

/**
* Check if node has dangling underscore or if node is type of ArrayPattern check its elements recursively
* @param {ASTNode} node node to evaluate
* @returns {void}
* @private
*/
function deepCheckDestructured(node) {
let identifier;

switch (node?.type) {
case 'ArrayPattern':
node.elements.forEach(element => deepCheckDestructured(element));
break;
case 'ObjectPattern':
node.properties.forEach(property => deepCheckDestructured(property));
break;
case 'RestElement':
deepCheckDestructured(node.argument);
break;
case 'Property':
deepCheckDestructured(node.value);
break;
case 'Identifier':
identifier = node?.name;
break;
}

if (identifier && hasDanglingUnderscore(identifier) && !isSpecialCaseIdentifierInVariableExpression(identifier) && !isAllowed(identifier)) {
context.report({
node,
messageId: "unexpectedUnderscore",
data: {
identifier
}
});
}
}

/**
* Check if variable expression has a dangling underscore
* @param {ASTNode} node node to evaluate
* @returns {void}
* @private
*/
function checkForDanglingUnderscoreInVariableExpression(node) {
if (!allowInArrayDestructuring && node.id?.type === 'ArrayPattern' && Array.isArray(node.id.elements)) {
node.id.elements.forEach(element => {
deepCheckDestructured(element);
});
}

if (!allowInObjectDestructuring && node.id?.type === 'ObjectPattern' && Array.isArray(node.id.properties)) {
node.id.properties.forEach(element => {
deepCheckDestructured(element);
});
}

const identifier = node.id.name;

if (typeof identifier !== "undefined" && hasDanglingUnderscore(identifier) &&
Expand Down
36 changes: 36 additions & 0 deletions tests/lib/rules/no-underscore-dangle.js
Expand Up @@ -29,6 +29,7 @@ ruleTester.run("no-underscore-dangle", rule, {
"function foo(_bar) {}",
"function foo(bar_) {}",
"(function _foo() {})",
"var [foo, [bar, baz]] = [1, ['a', 'b']]",
{ code: "function foo(_bar) {}", options: [{}] },
{ code: "function foo( _bar = 0) {}", parserOptions: { ecmaVersion: 6 } },
{ code: "const foo = { onClick(_bar) { } }", parserOptions: { ecmaVersion: 6 } },
Expand Down Expand Up @@ -103,6 +104,41 @@ ruleTester.run("no-underscore-dangle", rule, {
{ code: "const foo = { onClick(..._bar) { } }", options: [{ allowFunctionParams: false }], parserOptions: { ecmaVersion: 6 }, errors: [{ messageId: "unexpectedUnderscore", data: { identifier: "_bar" }, type: "RestElement" }] },
{ code: "const foo = (..._bar) => {}", options: [{ allowFunctionParams: false }], parserOptions: { ecmaVersion: 6 }, errors: [{ messageId: "unexpectedUnderscore", data: { identifier: "_bar" }, type: "RestElement" }] },
{
code: "const [foo, _bar] = [1, 2]",
options: [{ allowInArrayDestructuring: false }],
parserOptions: { ecmaVersion: 2022 },
errors: [{ messageId: "unexpectedUnderscore", data: { identifier: "_bar" } }]
}, {
code: "const [foo, ..._rest] = [1, 2, 3]",
options: [{ allowInArrayDestructuring: false }],
parserOptions: { ecmaVersion: 2022 },
errors: [{ messageId: "unexpectedUnderscore", data: { identifier: "_rest" } }]
}, {
code: "const [foo, [bar_, baz]] = [1, [2, 3]]",
options: [{ allowInArrayDestructuring: false }],
parserOptions: { ecmaVersion: 2022 },
errors: [{ messageId: "unexpectedUnderscore", data: { identifier: "bar_" } }]
}, {
code: "const { _foo, bar } = { _foo: 1, bar: 2 }",
options: [{ allowInObjectDestructuring: false }],
parserOptions: { ecmaVersion: 2022 },
errors: [{ messageId: "unexpectedUnderscore", data: { identifier: "_foo" } }]
}, {
code: "const { foo: _foo, bar } = { foo: 1, bar: 2 }",
options: [{ allowInObjectDestructuring: false }],
parserOptions: { ecmaVersion: 2022 },
errors: [{ messageId: "unexpectedUnderscore", data: { identifier: "_foo" } }]
}, {
code: "const { foo, ..._rest} = { foo: 1, bar: 2, baz: 3 }",
options: [{ allowInObjectDestructuring: false }],
parserOptions: { ecmaVersion: 2022 },
errors: [{ messageId: "unexpectedUnderscore", data: { identifier: "_rest" } }]
}, {
code: "const { foo, bar: { baz, _qux } } = { foo: 1, bar: { baz: 3, _qux: 4 } }",
options: [{ allowInObjectDestructuring: false }],
parserOptions: { ecmaVersion: 2022 },
errors: [{ messageId: "unexpectedUnderscore", data: { identifier: "_qux" } }]
}, {
Kaltoft marked this conversation as resolved.
Show resolved Hide resolved
code: "class foo { #_bar() {} }",
options: [{ enforceInMethodNames: true }],
parserOptions: { ecmaVersion: 2022 },
Expand Down