From 4573ef30124116de1362dfae27847b1557ee0b28 Mon Sep 17 00:00:00 2001 From: Eliya Cohen Date: Thu, 28 Nov 2024 10:23:57 +0200 Subject: [PATCH 1/2] fix: handle optional and non-null chaining in exhaustive-deps Fixed a false positive in the exhaustive-deps rule when using optional chaining and non-null assertions in query keys and functions. --- .../src/__tests__/exhaustive-deps.test.ts | 24 +++++++++++++++++++ .../exhaustive-deps/exhaustive-deps.rule.ts | 4 ++-- .../src/utils/ast-utils.ts | 10 ++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/packages/eslint-plugin-query/src/__tests__/exhaustive-deps.test.ts b/packages/eslint-plugin-query/src/__tests__/exhaustive-deps.test.ts index c7611775b5..32db49f1c4 100644 --- a/packages/eslint-plugin-query/src/__tests__/exhaustive-deps.test.ts +++ b/packages/eslint-plugin-query/src/__tests__/exhaustive-deps.test.ts @@ -473,6 +473,30 @@ ruleTester.run('exhaustive-deps', rule, { }; `, }, + { + name: 'should pass with optional chaining as key', + code: ` + function useTest(data?: any) { + return useQuery({ + queryKey: ['query-name', data?.address], + queryFn: async () => sendQuery(data.address), + enabled: !!data?.address, + }) + } + `, + }, + { + name: 'should pass with optional chaining as key and non-null assertion in queryFn', + code: ` + function useTest(data?: any) { + return useQuery({ + queryKey: ['query-name', data?.address], + queryFn: async () => sendQuery(data!.address), + enabled: !!data?.address, + }) + } + `, + }, ], invalid: [ { diff --git a/packages/eslint-plugin-query/src/rules/exhaustive-deps/exhaustive-deps.rule.ts b/packages/eslint-plugin-query/src/rules/exhaustive-deps/exhaustive-deps.rule.ts index 7fead1b6ff..3004798cc8 100644 --- a/packages/eslint-plugin-query/src/rules/exhaustive-deps/exhaustive-deps.rule.ts +++ b/packages/eslint-plugin-query/src/rules/exhaustive-deps/exhaustive-deps.rule.ts @@ -102,13 +102,13 @@ export const rule = createRule({ const existingKeys = ASTUtils.getNestedIdentifiers(queryKeyNode).map( (identifier) => - ASTUtils.mapKeyNodeToText(identifier, context.sourceCode), + ASTUtils.mapKeyNodeToBaseText(identifier, context.sourceCode), ) const missingRefs = relevantRefs .map((ref) => ({ ref: ref, - text: ASTUtils.mapKeyNodeToText(ref.identifier, context.sourceCode), + text: ASTUtils.mapKeyNodeToBaseText(ref.identifier, context.sourceCode), })) .filter(({ ref, text }) => { return ( diff --git a/packages/eslint-plugin-query/src/utils/ast-utils.ts b/packages/eslint-plugin-query/src/utils/ast-utils.ts index 051b336402..a31640cb43 100644 --- a/packages/eslint-plugin-query/src/utils/ast-utils.ts +++ b/packages/eslint-plugin-query/src/utils/ast-utils.ts @@ -224,10 +224,20 @@ export const ASTUtils = { return sourceCode.getText( ASTUtils.traverseUpOnly(node, [ AST_NODE_TYPES.MemberExpression, + AST_NODE_TYPES.TSNonNullExpression, AST_NODE_TYPES.Identifier, ]), ) }, + mapKeyNodeToBaseText( + node: TSESTree.Node, + sourceCode: Readonly, + ) { + return ASTUtils.mapKeyNodeToText(node, sourceCode).replace( + /(\?\.|!\.)/g, + '.', + ) + }, isValidReactComponentOrHookName( identifier: TSESTree.Identifier | null | undefined, ) { From 0822f5a10453dc831e50315f5fd72ce52f25331b Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Thu, 28 Nov 2024 08:25:47 +0000 Subject: [PATCH 2/2] ci: apply automated fixes --- .../src/rules/exhaustive-deps/exhaustive-deps.rule.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/eslint-plugin-query/src/rules/exhaustive-deps/exhaustive-deps.rule.ts b/packages/eslint-plugin-query/src/rules/exhaustive-deps/exhaustive-deps.rule.ts index 3004798cc8..bb87b04552 100644 --- a/packages/eslint-plugin-query/src/rules/exhaustive-deps/exhaustive-deps.rule.ts +++ b/packages/eslint-plugin-query/src/rules/exhaustive-deps/exhaustive-deps.rule.ts @@ -108,7 +108,10 @@ export const rule = createRule({ const missingRefs = relevantRefs .map((ref) => ({ ref: ref, - text: ASTUtils.mapKeyNodeToBaseText(ref.identifier, context.sourceCode), + text: ASTUtils.mapKeyNodeToBaseText( + ref.identifier, + context.sourceCode, + ), })) .filter(({ ref, text }) => { return (