From e1fdc3352e764cb581cfc98324a80aff7b444cc4 Mon Sep 17 00:00:00 2001 From: Eliya Cohen Date: Thu, 13 Mar 2025 20:09:29 +0200 Subject: [PATCH] feat(eslint-plugin-query): enhance no-rest-destructuring rule to detect spread operations --- .../__tests__/no-rest-destructuring.test.ts | 25 +++++++++++ .../no-rest-destructuring.rule.ts | 43 +++++++++++++++++-- 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/packages/eslint-plugin-query/src/__tests__/no-rest-destructuring.test.ts b/packages/eslint-plugin-query/src/__tests__/no-rest-destructuring.test.ts index 50169bff2b..fbe65f6770 100644 --- a/packages/eslint-plugin-query/src/__tests__/no-rest-destructuring.test.ts +++ b/packages/eslint-plugin-query/src/__tests__/no-rest-destructuring.test.ts @@ -365,5 +365,30 @@ ruleTester.run('no-rest-destructuring', rule, { `, errors: [{ messageId: 'objectRestDestructure' }], }, + { + name: 'useQuery result is spread in return statement', + code: normalizeIndent` + import { useQuery } from '@tanstack/react-query' + + function Component() { + const query = useQuery() + return { ...query, data: query.data[0] } + } + `, + errors: [{ messageId: 'objectRestDestructure' }], + }, + { + name: 'useQuery result is spread in object expression', + code: normalizeIndent` + import { useQuery } from '@tanstack/react-query' + + function Component() { + const query = useQuery() + const result = { ...query, data: query.data[0] } + return result + } + `, + errors: [{ messageId: 'objectRestDestructure' }], + }, ], }) diff --git a/packages/eslint-plugin-query/src/rules/no-rest-destructuring/no-rest-destructuring.rule.ts b/packages/eslint-plugin-query/src/rules/no-rest-destructuring/no-rest-destructuring.rule.ts index 29b68389c9..42c116a702 100644 --- a/packages/eslint-plugin-query/src/rules/no-rest-destructuring/no-rest-destructuring.rule.ts +++ b/packages/eslint-plugin-query/src/rules/no-rest-destructuring/no-rest-destructuring.rule.ts @@ -34,33 +34,45 @@ export const rule = createRule({ defaultOptions: [], create: detectTanstackQueryImports((context, _, helpers) => { + const queryResultVariables = new Set() + return { CallExpression: (node) => { if ( !ASTUtils.isIdentifierWithOneOfNames(node.callee, queryHooks) || - !helpers.isTanstackQueryImport(node.callee) || - node.parent.type !== AST_NODE_TYPES.VariableDeclarator + node.parent.type !== AST_NODE_TYPES.VariableDeclarator || + !helpers.isTanstackQueryImport(node.callee) ) { return } const returnValue = node.parent.id + if ( node.callee.name !== 'useQueries' && node.callee.name !== 'useSuspenseQueries' ) { if (NoRestDestructuringUtils.isObjectRestDestructuring(returnValue)) { - context.report({ + return context.report({ node: node.parent, messageId: 'objectRestDestructure', }) } + + if (returnValue.type === AST_NODE_TYPES.Identifier) { + queryResultVariables.add(returnValue.name) + } + return } if (returnValue.type !== AST_NODE_TYPES.ArrayPattern) { + if (returnValue.type === AST_NODE_TYPES.Identifier) { + queryResultVariables.add(returnValue.name) + } return } + returnValue.elements.forEach((queryResult) => { if (queryResult === null) { return @@ -73,6 +85,31 @@ export const rule = createRule({ } }) }, + + VariableDeclarator: (node) => { + if ( + node.init?.type === AST_NODE_TYPES.Identifier && + queryResultVariables.has(node.init.name) && + NoRestDestructuringUtils.isObjectRestDestructuring(node.id) + ) { + context.report({ + node, + messageId: 'objectRestDestructure', + }) + } + }, + + SpreadElement: (node) => { + if ( + node.argument.type === AST_NODE_TYPES.Identifier && + queryResultVariables.has(node.argument.name) + ) { + context.report({ + node, + messageId: 'objectRestDestructure', + }) + } + }, } }), })