Skip to content

Commit af086e0

Browse files
Newbie012TkDodo
andauthored
fix(eslint-plugin): ignore nodes outside of scope (#5217)
Co-authored-by: Dominik Dorfmeister <[email protected]>
1 parent 86150d6 commit af086e0

File tree

3 files changed

+68
-0
lines changed

3 files changed

+68
-0
lines changed

packages/eslint-plugin-query/src/rules/exhaustive-deps/exhaustive-deps.rule.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,20 @@ export const rule = createRule({
7979

8080
const sourceCode = context.getSourceCode()
8181
const queryKeyValue = queryKeyNode
82+
const reactComponent = ASTUtils.getReactComponentOrHookAncestor(context)
8283
const refs = ASTUtils.getExternalRefs({
8384
scopeManager,
8485
sourceCode,
8586
node: queryFn.value,
87+
}).filter((ref) => {
88+
return (
89+
reactComponent === undefined ||
90+
ASTUtils.isDeclaredInNode({
91+
scopeManager,
92+
functionNode: reactComponent,
93+
reference: ref,
94+
})
95+
)
8696
})
8797

8898
const relevantRefs = refs.filter((ref) => {

packages/eslint-plugin-query/src/rules/exhaustive-deps/exhaustive-deps.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,30 @@ ruleTester.run('exhaustive-deps', rule, {
260260
});
261261
`,
262262
},
263+
{
264+
name: 'should ignore constants defined out of scope (react component)',
265+
code: `
266+
const CONST_VAL = 1
267+
function MyComponent() {
268+
useQuery({
269+
queryKey: ["foo"],
270+
queryFn: () => CONST_VAL
271+
});
272+
}
273+
`,
274+
},
275+
{
276+
name: 'should ignore constants defined out of scope (react hook)',
277+
code: `
278+
const CONST_VAL = 1
279+
function useHook() {
280+
useQuery({
281+
queryKey: ["foo"],
282+
queryFn: () => CONST_VAL
283+
});
284+
}
285+
`,
286+
},
263287
],
264288
invalid: [
265289
{

packages/eslint-plugin-query/src/utils/ast-utils.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { TSESLint, TSESTree } from '@typescript-eslint/utils'
2+
import type TSESLintScopeManager from '@typescript-eslint/scope-manager'
23
import { AST_NODE_TYPES } from '@typescript-eslint/utils'
34
import type { RuleContext } from '@typescript-eslint/utils/dist/ts-eslint'
45
import { uniqueBy } from './unique-by'
@@ -140,6 +141,20 @@ export const ASTUtils = {
140141

141142
return identifier
142143
},
144+
isDeclaredInNode(params: {
145+
functionNode: TSESTree.Node
146+
reference: TSESLintScopeManager.Reference
147+
scopeManager: TSESLint.Scope.ScopeManager
148+
}) {
149+
const { functionNode, reference, scopeManager } = params
150+
const scope = scopeManager.acquire(functionNode)
151+
152+
if (scope === null) {
153+
return false
154+
}
155+
156+
return scope.set.has(reference.identifier.name)
157+
},
143158
getExternalRefs(params: {
144159
scopeManager: TSESLint.Scope.ScopeManager
145160
sourceCode: Readonly<TSESLint.SourceCode>
@@ -188,6 +203,25 @@ export const ASTUtils = {
188203
]),
189204
)
190205
},
206+
getReactComponentOrHookAncestor(
207+
context: Readonly<RuleContext<string, readonly unknown[]>>,
208+
) {
209+
return context.getAncestors().find((x) => {
210+
return (
211+
ASTUtils.isNodeOfOneOf(x, [
212+
AST_NODE_TYPES.FunctionDeclaration,
213+
AST_NODE_TYPES.FunctionExpression,
214+
AST_NODE_TYPES.ArrowFunctionExpression,
215+
]) &&
216+
x.id !== null &&
217+
/^(use|[A-Z])/.test(x.id.name)
218+
)
219+
}) as
220+
| TSESTree.FunctionDeclaration
221+
| TSESTree.FunctionExpression
222+
| TSESTree.ArrowFunctionExpression
223+
| undefined
224+
},
191225
getReferencedExpressionByIdentifier(params: {
192226
node: TSESTree.Node
193227
context: Readonly<RuleContext<string, readonly unknown[]>>

0 commit comments

Comments
 (0)