From fd289284fc06d0562880af3b810c3c9b0e0ffa87 Mon Sep 17 00:00:00 2001 From: ALOHACREPES345 Date: Sun, 27 Mar 2022 17:04:32 +0900 Subject: [PATCH 1/3] feat(eslint-plugin): [no-unused-vars] add destructuredArrayIgnorePattern options --- .../eslint-plugin/src/rules/no-unused-vars.ts | 74 ++++- .../no-unused-vars-eslint.test.ts | 275 ++++++++++++++++++ 2 files changed, 335 insertions(+), 14 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-unused-vars.ts b/packages/eslint-plugin/src/rules/no-unused-vars.ts index 139089a74486..302ec5c95e36 100644 --- a/packages/eslint-plugin/src/rules/no-unused-vars.ts +++ b/packages/eslint-plugin/src/rules/no-unused-vars.ts @@ -14,6 +14,7 @@ export type Options = [ argsIgnorePattern?: string; caughtErrors?: 'all' | 'none'; caughtErrorsIgnorePattern?: string; + destructuredArrayIgnorePattern?: string; }, ]; @@ -25,6 +26,7 @@ interface TranslatedOptions { argsIgnorePattern?: RegExp; caughtErrors: 'all' | 'none'; caughtErrorsIgnorePattern?: RegExp; + destructuredArrayIgnorePattern?: RegExp; } export default util.createRule({ @@ -66,6 +68,9 @@ export default util.createRule({ caughtErrorsIgnorePattern: { type: 'string', }, + destructuredArrayIgnorePattern: { + type: 'string', + }, }, additionalProperties: false, }, @@ -123,12 +128,33 @@ export default util.createRule({ 'u', ); } + + if (firstOption.destructuredArrayIgnorePattern) { + options.destructuredArrayIgnorePattern = new RegExp( + firstOption.destructuredArrayIgnorePattern, + 'u', + ); + } } } return options; })(); function collectUnusedVariables(): TSESLint.Scope.Variable[] { + /** + * Checks whether a node is a sibling of the rest property or not. + * @param {ASTNode} node a node to check + * @returns {boolean} True if the node is a sibling of the rest property, otherwise false. + */ + function hasRestSibling(node: TSESTree.Node): boolean { + return ( + node.type === AST_NODE_TYPES.Property && + node.parent?.type === AST_NODE_TYPES.ObjectPattern && + node.parent.properties[node.parent.properties.length - 1].type === + AST_NODE_TYPES.RestElement + ); + } + /** * Determines if a variable has a sibling rest property * @param variable eslint-scope variable object. @@ -138,17 +164,14 @@ export default util.createRule({ variable: TSESLint.Scope.Variable, ): boolean { if (options.ignoreRestSiblings) { - return variable.defs.some(def => { - const propertyNode = def.name.parent!; - const patternNode = propertyNode.parent!; - - return ( - propertyNode.type === AST_NODE_TYPES.Property && - patternNode.type === AST_NODE_TYPES.ObjectPattern && - patternNode.properties[patternNode.properties.length - 1].type === - AST_NODE_TYPES.RestElement - ); - }); + const hasRestSiblingDefinition = variable.defs.some(def => + hasRestSibling(def.name.parent!), + ); + const hasRestSiblingReference = variable.references.some(ref => + hasRestSibling(ref.identifier.parent!), + ); + + return hasRestSiblingDefinition || hasRestSiblingReference; } return false; @@ -188,6 +211,20 @@ export default util.createRule({ continue; } + const refUsedInArrayPatterns = variable.references.some( + ref => ref.identifier.parent?.type === AST_NODE_TYPES.ArrayPattern, + ); + + // skip elements of array destructuring patterns + if ( + (def.name.parent?.type === AST_NODE_TYPES.ArrayPattern || + refUsedInArrayPatterns) && + 'name' in def.name && + options.destructuredArrayIgnorePattern?.test(def.name.name) + ) { + continue; + } + // skip catch variables if (def.type === TSESLint.Scope.DefinitionType.CatchClause) { if (options.caughtErrors === 'none') { @@ -361,9 +398,18 @@ export default util.createRule({ function getAssignedMessageData( unusedVar: TSESLint.Scope.Variable, ): Record { - const additional = options.varsIgnorePattern - ? `. Allowed unused vars must match ${options.varsIgnorePattern.toString()}` - : ''; + const def = unusedVar.defs[0]; + let additional = ''; + + if ( + options.destructuredArrayIgnorePattern && + def && + def.name.parent?.type === AST_NODE_TYPES.ArrayPattern + ) { + additional = `. Allowed unused elements of array destructuring patterns must match ${options.destructuredArrayIgnorePattern.toString()}`; + } else if (options.varsIgnorePattern) { + additional = `. Allowed unused vars must match ${options.varsIgnorePattern.toString()}`; + } return { varName: unusedVar.name, diff --git a/packages/eslint-plugin/tests/rules/no-unused-vars/no-unused-vars-eslint.test.ts b/packages/eslint-plugin/tests/rules/no-unused-vars/no-unused-vars-eslint.test.ts index 9ddde4e9f24e..6f26085bfd7b 100644 --- a/packages/eslint-plugin/tests/rules/no-unused-vars/no-unused-vars-eslint.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unused-vars/no-unused-vars-eslint.test.ts @@ -701,6 +701,111 @@ console.log(secondItem); options: [{ vars: 'all', varsIgnorePattern: '[iI]gnored' }], parserOptions: { ecmaVersion: 6 }, }, + { + code: ` +const [a, _b, c] = items; +console.log(a + c); + `, + options: [{ destructuredArrayIgnorePattern: '^_' }], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: ` +const [[a, _b, c]] = items; +console.log(a + c); + `, + options: [{ destructuredArrayIgnorePattern: '^_' }], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: ` +const { + x: [_a, foo], +} = bar; +console.log(foo); + `, + options: [{ destructuredArrayIgnorePattern: '^_' }], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: ` +function baz([_b, foo]) { + foo; +} +baz(); + `, + options: [{ destructuredArrayIgnorePattern: '^_' }], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: ` +function baz({ x: [_b, foo] }) { + foo; +} +baz(); + `, + options: [{ destructuredArrayIgnorePattern: '^_' }], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: ` +function baz([ + { + x: [_b, foo], + }, +]) { + foo; +} +baz(); + `, + options: [{ destructuredArrayIgnorePattern: '^_' }], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: ` +let _a, b; +foo.forEach(item => { + [_a, b] = item; + doSomething(b); +}); + `, + options: [{ destructuredArrayIgnorePattern: '^_' }], + parserOptions: { ecmaVersion: 6 }, + }, + { + code: ` +// doesn't report _x +let _x, y; +_x = 1; +[_x, y] = foo; +y; +// doesn't report _a +let _a, b; +[_a, b] = foo; +_a = 1; +b; + `, + options: [{ destructuredArrayIgnorePattern: '^_' }], + parserOptions: { ecmaVersion: 2018 }, + }, + { + code: ` +// doesn't report _x +let _x, y; +_x = 1; +[_x, y] = foo; +y; +// doesn't report _a +let _a, b; +_a = 1; +({ _a, ...b } = foo); +b; + `, + options: [ + { destructuredArrayIgnorePattern: '^_', ignoreRestSiblings: true }, + ], + parserOptions: { ecmaVersion: 2018 }, + }, // for-in loops (see #2342) ` @@ -1017,6 +1122,13 @@ console.log(Foo); parserOptions: { ecmaVersion: 2018 }, }, + // https://github.com/eslint/eslint/issues/14163 + { + code: 'let foo, rest;\n({ foo, ...rest } = something);\nconsole.log(rest);', + options: [{ ignoreRestSiblings: true }], + parserOptions: { ecmaVersion: 2020 }, + }, + // https://github.com/eslint/eslint/issues/10952 ` /*eslint use-every-a:1*/ !function (b, a) { @@ -1504,6 +1616,148 @@ foo(); }, ], }, + // https://github.com/eslint/eslint/issues/15611 + { + code: ` +const array = ['a', 'b', 'c']; +const [a, _b, c] = array; +const newArray = [a, c]; + `, + options: [{ destructuredArrayIgnorePattern: '^_' }], + parserOptions: { ecmaVersion: 2020 }, + errors: [ + // should report only `newArray` + { ...assignedError('newArray'), line: 4, column: 7 }, + ], + }, + { + code: ` +const array = ['a', 'b', 'c', 'd', 'e']; +const [a, _b, c] = array; + `, + options: [{ destructuredArrayIgnorePattern: '^_' }], + parserOptions: { ecmaVersion: 2020 }, + errors: [ + { + ...assignedError( + 'a', + '. Allowed unused elements of array destructuring patterns must match /^_/u', + ), + line: 3, + column: 8, + }, + { + ...assignedError( + 'c', + '. Allowed unused elements of array destructuring patterns must match /^_/u', + ), + line: 3, + column: 15, + }, + ], + }, + { + code: ` +const array = ['a', 'b', 'c']; +const [a, _b, c] = array; +const fooArray = ['foo']; +const barArray = ['bar']; +const ignoreArray = ['ignore']; + `, + options: [ + { destructuredArrayIgnorePattern: '^_', varsIgnorePattern: 'ignore' }, + ], + parserOptions: { ecmaVersion: 2020 }, + errors: [ + { + ...assignedError( + 'a', + '. Allowed unused elements of array destructuring patterns must match /^_/u', + ), + line: 3, + column: 8, + }, + { + ...assignedError( + 'c', + '. Allowed unused elements of array destructuring patterns must match /^_/u', + ), + line: 3, + column: 15, + }, + { + ...assignedError( + 'fooArray', + '. Allowed unused vars must match /ignore/u', + ), + line: 4, + column: 7, + }, + { + ...assignedError( + 'barArray', + '. Allowed unused vars must match /ignore/u', + ), + line: 5, + column: 7, + }, + ], + }, + { + code: ` +const array = [obj]; +const [{ _a, foo }] = array; +console.log(foo); + `, + options: [{ destructuredArrayIgnorePattern: '^_' }], + parserOptions: { ecmaVersion: 2020 }, + errors: [ + { + ...assignedError('_a'), + line: 3, + column: 10, + }, + ], + }, + { + code: ` +function foo([{ _a, bar }]) { + bar; +} +foo(); + `, + options: [{ destructuredArrayIgnorePattern: '^_' }], + parserOptions: { ecmaVersion: 2020 }, + errors: [ + { + ...definedError('_a'), + line: 2, + column: 17, + }, + ], + }, + { + code: ` +let _a, b; +foo.forEach(item => { + [a, b] = item; +}); + `, + options: [{ destructuredArrayIgnorePattern: '^_' }], + parserOptions: { ecmaVersion: 2020 }, + errors: [ + { + ...definedError('_a'), + line: 2, + column: 5, + }, + { + ...assignedError('b'), + line: 4, + column: 7, + }, + ], + }, // for-in loops (see #2342) { @@ -1684,6 +1938,27 @@ console.log(type); }, ], }, + { + code: ` +let type, coords; +({ type, ...coords } = data); +console.log(type); + `, + options: [{ ignoreRestSiblings: true }], + parserOptions: { ecmaVersion: 2018 }, + errors: [ + { + line: 3, + column: 13, + messageId: 'unusedVar', + data: { + varName: 'coords', + action: 'assigned a value', + additional: '', + }, + }, + ], + }, // Unused rest property without ignoreRestSiblings { From b6192d4cd9a7f6245be0e018585a103c2b255f54 Mon Sep 17 00:00:00 2001 From: ALOHACREPES345 Date: Sun, 27 Mar 2022 17:32:17 +0900 Subject: [PATCH 2/3] feat(eslint-plugin): added typing --- packages/eslint-plugin/typings/eslint-rules.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/eslint-plugin/typings/eslint-rules.d.ts b/packages/eslint-plugin/typings/eslint-rules.d.ts index fa7d5934b263..7e3b9db43e36 100644 --- a/packages/eslint-plugin/typings/eslint-rules.d.ts +++ b/packages/eslint-plugin/typings/eslint-rules.d.ts @@ -416,6 +416,7 @@ declare module 'eslint/lib/rules/no-unused-vars' { argsIgnorePattern?: string; caughtErrors?: 'all' | 'none'; caughtErrorsIgnorePattern?: string; + destructuredArrayIgnorePattern?: string; }, ], { From d4374e861a09da103d0544496e9c88309d98265e Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sun, 27 Mar 2022 16:35:12 -0500 Subject: [PATCH 3/3] Update packages/eslint-plugin/src/rules/no-unused-vars.ts --- packages/eslint-plugin/src/rules/no-unused-vars.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-unused-vars.ts b/packages/eslint-plugin/src/rules/no-unused-vars.ts index 302ec5c95e36..4fb54dda7473 100644 --- a/packages/eslint-plugin/src/rules/no-unused-vars.ts +++ b/packages/eslint-plugin/src/rules/no-unused-vars.ts @@ -403,8 +403,7 @@ export default util.createRule({ if ( options.destructuredArrayIgnorePattern && - def && - def.name.parent?.type === AST_NODE_TYPES.ArrayPattern + def?.name.parent?.type === AST_NODE_TYPES.ArrayPattern ) { additional = `. Allowed unused elements of array destructuring patterns must match ${options.destructuredArrayIgnorePattern.toString()}`; } else if (options.varsIgnorePattern) { pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy