Skip to content

Commit

Permalink
fix(eslint-plugin-query): improve external reference relevance (#8334)
Browse files Browse the repository at this point in the history
* fix: handle external variable in queryFn without failure

Adjusted tests to ensure that the rule does not fail when a queryFn inside queryOptions contains a reference to an external variable. Also updated other tests to wrap query calls in a function component for consistency.

fix #8326

* ci: apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
  • Loading branch information
Newbie012 and autofix-ci[bot] authored Nov 23, 2024
1 parent 102b6a8 commit 80a49ff
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 48 deletions.
137 changes: 91 additions & 46 deletions packages/eslint-plugin-query/src/__tests__/exhaustive-deps.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,19 @@ ruleTester.run('exhaustive-deps', rule, {
});
`,
},
{
name: 'should not fail when queryFn inside queryOptions contains a reference to an external variable',
code: normalizeIndent`
const EXTERNAL = 1;
export const queries = {
foo: queryOptions({
queryKey: ['foo'],
queryFn: () => Promise.resolve(EXTERNAL),
}),
};
`,
},
],
invalid: [
{
Expand Down Expand Up @@ -492,8 +505,10 @@ ruleTester.run('exhaustive-deps', rule, {
{
name: 'should fail when no deps are passed (react)',
code: normalizeIndent`
const id = 1;
useQuery({ queryKey: ["entity"], queryFn: () => api.getEntity(id) });
function Component() {
const id = 1;
useQuery({ queryKey: ["entity"], queryFn: () => api.getEntity(id) });
}
`,
errors: [
{
Expand All @@ -504,8 +519,10 @@ ruleTester.run('exhaustive-deps', rule, {
messageId: 'fixTo',
data: { result: '["entity", id]' },
output: normalizeIndent`
const id = 1;
useQuery({ queryKey: ["entity", id], queryFn: () => api.getEntity(id) });
function Component() {
const id = 1;
useQuery({ queryKey: ["entity", id], queryFn: () => api.getEntity(id) });
}
`,
},
],
Expand All @@ -515,8 +532,10 @@ ruleTester.run('exhaustive-deps', rule, {
{
name: 'should fail when no deps are passed (solid)',
code: normalizeIndent`
const id = 1;
createQuery({ queryKey: ["entity"], queryFn: () => api.getEntity(id) });
function Component() {
const id = 1;
createQuery({ queryKey: ["entity"], queryFn: () => api.getEntity(id) });
}
`,
errors: [
{
Expand All @@ -527,8 +546,10 @@ ruleTester.run('exhaustive-deps', rule, {
messageId: 'fixTo',
data: { result: '["entity", id]' },
output: normalizeIndent`
const id = 1;
createQuery({ queryKey: ["entity", id], queryFn: () => api.getEntity(id) });
function Component() {
const id = 1;
createQuery({ queryKey: ["entity", id], queryFn: () => api.getEntity(id) });
}
`,
},
],
Expand All @@ -538,8 +559,10 @@ ruleTester.run('exhaustive-deps', rule, {
{
name: 'should fail when deps are passed incorrectly',
code: normalizeIndent`
const id = 1;
useQuery({ queryKey: ["entity/\${id}"], queryFn: () => api.getEntity(id) });
function Component() {
const id = 1;
useQuery({ queryKey: ["entity/\${id}"], queryFn: () => api.getEntity(id) });
}
`,
errors: [
{
Expand All @@ -550,8 +573,10 @@ ruleTester.run('exhaustive-deps', rule, {
messageId: 'fixTo',
data: { result: '["entity/${id}", id]' },
output: normalizeIndent`
const id = 1;
useQuery({ queryKey: ["entity/\${id}", id], queryFn: () => api.getEntity(id) });
function Component() {
const id = 1;
useQuery({ queryKey: ["entity/\${id}", id], queryFn: () => api.getEntity(id) });
}
`,
},
],
Expand All @@ -561,9 +586,11 @@ ruleTester.run('exhaustive-deps', rule, {
{
name: 'should pass missing dep while key has a template literal',
code: normalizeIndent`
const a = 1;
const b = 2;
useQuery({ queryKey: [\`entity/\${a}\`], queryFn: () => api.getEntity(a, b) });
function Component() {
const a = 1;
const b = 2;
useQuery({ queryKey: [\`entity/\${a}\`], queryFn: () => api.getEntity(a, b) });
}
`,
errors: [
{
Expand All @@ -574,9 +601,11 @@ ruleTester.run('exhaustive-deps', rule, {
messageId: 'fixTo',
data: { result: '[`entity/${a}`, b]' },
output: normalizeIndent`
const a = 1;
const b = 2;
useQuery({ queryKey: [\`entity/\${a}\`, b], queryFn: () => api.getEntity(a, b) });
function Component() {
const a = 1;
const b = 2;
useQuery({ queryKey: [\`entity/\${a}\`, b], queryFn: () => api.getEntity(a, b) });
}
`,
},
],
Expand All @@ -586,14 +615,16 @@ ruleTester.run('exhaustive-deps', rule, {
{
name: 'should fail when dep exists inside setter and missing in queryKey',
code: normalizeIndent`
const [id] = React.useState(1);
useQuery({
function Component() {
const [id] = React.useState(1);
useQuery({
queryKey: ["entity"],
queryFn: () => {
const { data } = axios.get(\`.../\${id}\`);
return data;
const { data } = axios.get(\`.../\${id}\`);
return data;
}
});
});
}
`,
errors: [
{
Expand All @@ -604,14 +635,16 @@ ruleTester.run('exhaustive-deps', rule, {
messageId: 'fixTo',
data: { result: '["entity", id]' },
output: normalizeIndent`
const [id] = React.useState(1);
useQuery({
function Component() {
const [id] = React.useState(1);
useQuery({
queryKey: ["entity", id],
queryFn: () => {
const { data } = axios.get(\`.../\${id}\`);
return data;
const { data } = axios.get(\`.../\${id}\`);
return data;
}
});
});
}
`,
},
],
Expand Down Expand Up @@ -713,9 +746,11 @@ ruleTester.run('exhaustive-deps', rule, {
{
name: 'should fail when a queryKey is a reference of an array expression with a missing dep',
code: normalizeIndent`
const x = 5;
const queryKey = ['foo']
useQuery({ queryKey, queryFn: () => x })
function Component() {
const x = 5;
const queryKey = ['foo']
useQuery({ queryKey, queryFn: () => x })
}
`,
errors: [
{
Expand All @@ -728,9 +763,11 @@ ruleTester.run('exhaustive-deps', rule, {
result: "['foo', x]",
},
output: normalizeIndent`
const x = 5;
const queryKey = ['foo', x]
useQuery({ queryKey, queryFn: () => x })
function Component() {
const x = 5;
const queryKey = ['foo', x]
useQuery({ queryKey, queryFn: () => x })
}
`,
},
],
Expand Down Expand Up @@ -758,25 +795,29 @@ ruleTester.run('exhaustive-deps', rule, {
{
name: 'should fail if queryFn is using multiple object props when only one of them is in the queryKey',
code: normalizeIndent`
const state = { foo: 'foo', bar: 'bar' }
function Component() {
const state = { foo: 'foo', bar: 'bar' }
useQuery({
useQuery({
queryKey: ['state', state.foo],
queryFn: () => Promise.resolve({ foo: state.foo, bar: state.bar })
})
})
}
`,
errors: [
{
suggestions: [
{
messageId: 'fixTo',
output: normalizeIndent`
const state = { foo: 'foo', bar: 'bar' }
function Component() {
const state = { foo: 'foo', bar: 'bar' }
useQuery({
useQuery({
queryKey: ['state', state.foo, state.bar],
queryFn: () => Promise.resolve({ foo: state.foo, bar: state.bar })
})
})
}
`,
},
],
Expand All @@ -788,29 +829,33 @@ ruleTester.run('exhaustive-deps', rule, {
{
name: 'should fail if queryFn is invalid while using FunctionExpression syntax',
code: normalizeIndent`
const id = 1;
function Component() {
const id = 1;
useQuery({
useQuery({
queryKey: [],
queryFn() {
Promise.resolve(id)
}
})
});
}
`,
errors: [
{
suggestions: [
{
messageId: 'fixTo',
output: normalizeIndent`
const id = 1;
function Component() {
const id = 1;
useQuery({
useQuery({
queryKey: [id],
queryFn() {
Promise.resolve(id)
}
})
});
}
`,
},
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@ export const ExhaustiveDepsUtils = {
const { sourceCode, reference, scopeManager, node } = params
const component = ASTUtils.getFunctionAncestor(sourceCode, node)

if (component === undefined) {
return false
}

if (
component !== undefined &&
!ASTUtils.isDeclaredInNode({
scopeManager,
reference,
Expand Down
8 changes: 7 additions & 1 deletion packages/eslint-plugin-query/src/utils/ast-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,13 @@ export const ASTUtils = {
node: TSESTree.Node,
) {
for (const ancestor of sourceCode.getAncestors(node)) {
if (ancestor.type === AST_NODE_TYPES.FunctionDeclaration) {
if (
ASTUtils.isNodeOfOneOf(ancestor, [
AST_NODE_TYPES.FunctionDeclaration,
AST_NODE_TYPES.FunctionExpression,
AST_NODE_TYPES.ArrowFunctionExpression,
])
) {
return ancestor
}

Expand Down

0 comments on commit 80a49ff

Please sign in to comment.