Skip to content

Commit 0cc7edc

Browse files
committed
feat: add invalidate-queries-no-inline-query
1 parent 2958f48 commit 0cc7edc

File tree

8 files changed

+161
-38
lines changed

8 files changed

+161
-38
lines changed

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@ export default [
2121
💼 Configurations enabled in.\
2222
✅ Set in the `recommended` configuration.
2323

24-
| Name                      | Description | 💼 |
25-
| :------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------- | :- |
26-
| [use-query-no-inline-query](docs/rules/use-query-no-inline-query.md) | Enforces useQuery (and family) hooks use some form of query constructor pattern. Will error if queryKey or queryFn properties are passed to the hook ||
24+
| Name                               | Description | 💼 |
25+
| :------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------- | :- |
26+
| [invalidate-queries-no-inline-query](docs/rules/invalidate-queries-no-inline-query.md) | Enforces queryClient.invalidateQueries don't have inline queries. Will error if queryKey or queryFn properties are passed to the function ||
27+
| [use-query-no-inline-query](docs/rules/use-query-no-inline-query.md) | Enforces useQuery (and family) hooks use some form of query constructor pattern. Will error if queryKey or queryFn properties are passed to the hook ||
2728

2829
<!-- end auto-generated rules list -->
2930
<!-- prettier-ignore-end -->
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Enforces queryClient.invalidateQueries don't have inline queries. Will error if queryKey or queryFn properties are passed to the function (`react-query-options/invalidate-queries-no-inline-query`)
2+
3+
💼 This rule is enabled in the ✅ `recommended` config.
4+
5+
<!-- end auto-generated rule header -->
6+
7+
## Fail
8+
9+
```js
10+
queryClient.invalidateQueries({
11+
queryKey: [/**/],
12+
/* additional properties */
13+
})
14+
15+
queryClient.invalidateQueries({
16+
queryFn: () => { /**/ }
17+
/* additional properties */
18+
})
19+
```
20+
21+
## Pass
22+
23+
```js
24+
const query = {
25+
queryKey: [/**/],
26+
queryFn: () => { /**/ }
27+
}
28+
29+
const queryBuilder = () => ({
30+
queryKey: [/**/],
31+
queryFn: () => { /**/ }
32+
})
33+
34+
queryClient.invalidateQueries(query)
35+
queryClient.invalidateQueries(queryBuilder())
36+
queryClient.invalidateQueries({
37+
...queryBuilder(),
38+
/* additional properties */
39+
select: (data) => data
40+
})
41+
```

docs/rules/use-query-no-inline-query.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44

55
<!-- end auto-generated rule header -->
66

7-
Enforces useQuery (and family) hooks use some form of query constructor pattern. Will error if queryKey or queryFn properties are passed to the hook
8-
97
## Fail
108

119
```js

src/index.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import useQueryNoInlineQuery from "./rules/use-query-no-inline-query.js";
2+
import invalidateQueriesNoInlineQuery from "./rules/invalidate-queries-no-inline-query.js";
23

34
import pkg from "../package.json" with { type: "json" };
45

6+
const rules = [useQueryNoInlineQuery, invalidateQueriesNoInlineQuery];
7+
58
const reactQueryOptions = {
69
meta: {
710
name: pkg.name,
@@ -11,9 +14,7 @@ const reactQueryOptions = {
1114
get configs() {
1215
return configs;
1316
},
14-
rules: {
15-
[useQueryNoInlineQuery.name]: useQueryNoInlineQuery.rule,
16-
},
17+
rules: Object.fromEntries(rules.map((rule) => [rule.name, rule.rule])),
1718
};
1819

1920
const configs = {
@@ -22,10 +23,12 @@ const configs = {
2223
plugins: {
2324
[reactQueryOptions.meta.namespace]: reactQueryOptions,
2425
},
25-
rules: {
26-
[`${reactQueryOptions.meta.namespace}/${useQueryNoInlineQuery.name}`]:
26+
rules: Object.fromEntries(
27+
rules.map((rule) => [
28+
[`${reactQueryOptions.meta.namespace}/${rule.name}`],
2729
"error",
28-
},
30+
]),
31+
),
2932
},
3033
],
3134
};
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import "../_test/setup.js";
2+
3+
import { RuleTester } from "@typescript-eslint/rule-tester";
4+
5+
import requireQueryOptions from "./invalidate-queries-no-inline-query.js";
6+
7+
const ruleTester = new RuleTester({});
8+
9+
ruleTester.run(requireQueryOptions.name, requireQueryOptions.rule, {
10+
valid: [
11+
{ code: `queryClient.invalidateQueries(usersQuery)` },
12+
{ code: `queryClient.invalidateQueries({ ...usersQuery })` },
13+
{ code: `queryClient.invalidateQueries({ ...usersQuery() })` },
14+
],
15+
invalid: [
16+
{
17+
code: `queryClient.invalidateQueries({ queryKey: [] })`,
18+
errors: [{ messageId: "no-inline-query" }],
19+
},
20+
{
21+
code: `queryClient.invalidateQueries({ ...queryOptions, queryKey: [] })`,
22+
errors: [{ messageId: "no-inline-query" }],
23+
},
24+
{
25+
code: `queryClient.invalidateQueries({ queryFn: () => {} })`,
26+
errors: [{ messageId: "no-inline-query" }],
27+
},
28+
{
29+
code: `queryClient.invalidateQueries({ ...queryOptions, queryFn: () => {} })`,
30+
errors: [{ messageId: "no-inline-query" }],
31+
},
32+
],
33+
});
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { AST_NODE_TYPES } from "@typescript-eslint/utils";
2+
import { createRule, isValidQueryNode } from "../utils.js";
3+
4+
const name = "invalidate-queries-no-inline-query";
5+
6+
export default {
7+
name,
8+
rule: createRule({
9+
name,
10+
defaultOptions: [],
11+
meta: {
12+
type: "suggestion",
13+
messages: {
14+
"no-inline-query": "Expected query hook to use queryOptions pattern",
15+
},
16+
docs: {
17+
description:
18+
"Enforces queryClient.invalidateQueries don't have inline queries. Will error if queryKey or queryFn properties are passed to the function",
19+
},
20+
schema: [],
21+
},
22+
create(context) {
23+
return {
24+
CallExpression(node) {
25+
if (
26+
// check queryClient.invalidateQueries
27+
node.callee.type === AST_NODE_TYPES.MemberExpression &&
28+
node.callee.object.type === AST_NODE_TYPES.Identifier &&
29+
node.callee.object.name === "queryClient" &&
30+
node.callee.property.type === AST_NODE_TYPES.Identifier &&
31+
node.callee.property.name === "invalidateQueries"
32+
) {
33+
if (!node.arguments[0]) return;
34+
35+
if (!isValidQueryNode(node.arguments[0]))
36+
context.report({
37+
messageId: "no-inline-query",
38+
node,
39+
});
40+
}
41+
},
42+
};
43+
},
44+
}),
45+
};

src/rules/use-query-no-inline-query.ts

Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,5 @@
1-
import {
2-
AST_NODE_TYPES,
3-
ESLintUtils,
4-
TSESTree,
5-
} from "@typescript-eslint/utils";
6-
7-
const createRule = ESLintUtils.RuleCreator(
8-
(name) =>
9-
`https://github.com/danielpza/eslint-plugin-react-query/docs/rules/${name}.md`,
10-
);
1+
import { AST_NODE_TYPES } from "@typescript-eslint/utils";
2+
import { createRule, isValidQueryNode } from "../utils.js";
113

124
const useQueryHooks = [
135
// see https://tanstack.com/query/latest/docs/framework/react/reference/useQuery
@@ -19,23 +11,6 @@ const useQueryHooks = [
1911
"useSuspenseInfiniteQuery",
2012
];
2113

22-
const invalidProperties = ["queryKey", "queryFn"];
23-
24-
function isValidQueryNode(queryNode: TSESTree.Node) {
25-
// we only care about object expressions
26-
if (queryNode.type !== AST_NODE_TYPES.ObjectExpression) return true;
27-
28-
// check if any of the properties is queryKey or queryFn
29-
const hasInvalidProperties = queryNode.properties.find(
30-
(property) =>
31-
property.type === AST_NODE_TYPES.Property &&
32-
property.key.type === AST_NODE_TYPES.Identifier &&
33-
invalidProperties.includes(property.key.name),
34-
);
35-
36-
return !hasInvalidProperties;
37-
}
38-
3914
const name = "use-query-no-inline-query";
4015

4116
export default {

src/utils.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import {
2+
AST_NODE_TYPES,
3+
ESLintUtils,
4+
TSESTree,
5+
} from "@typescript-eslint/utils";
6+
7+
export const INVALID_QUERY_PROPERTIES = ["queryKey", "queryFn"];
8+
9+
export const createRule = ESLintUtils.RuleCreator(
10+
(name) =>
11+
`https://github.com/danielpza/eslint-plugin-react-query/docs/rules/${name}.md`,
12+
);
13+
14+
export function isValidQueryNode(queryNode: TSESTree.Node) {
15+
// we only care about object expressions
16+
if (queryNode.type !== AST_NODE_TYPES.ObjectExpression) return true;
17+
18+
// check if any of the properties is queryKey or queryFn
19+
const hasInvalidProperties = queryNode.properties.find(
20+
(property) =>
21+
property.type === AST_NODE_TYPES.Property &&
22+
property.key.type === AST_NODE_TYPES.Identifier &&
23+
INVALID_QUERY_PROPERTIES.includes(property.key.name),
24+
);
25+
26+
return !hasInvalidProperties;
27+
}

0 commit comments

Comments
 (0)