Skip to content

Commit f3aad7c

Browse files
feat(eslint-plugin): use typescript-eslint v8 stable (#7968)
* feat(eslint-plugin-query): use @typescript-eslint/utils stable v8 * test(eslint-plugin-query): fix missing parser * Remove @typescript-eslint/parser * Fix suggestion for empty queryKey array --------- Co-authored-by: Lachlan Collins <1667261+lachlancollins@users.noreply.github.com>
1 parent 9e75547 commit f3aad7c

File tree

7 files changed

+153
-86
lines changed

7 files changed

+153
-86
lines changed

packages/eslint-plugin-query/package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,13 @@
5050
"src"
5151
],
5252
"dependencies": {
53-
"@typescript-eslint/utils": "8.0.0-alpha.30"
53+
"@typescript-eslint/utils": "^8.3.0"
5454
},
5555
"devDependencies": {
56-
"@typescript-eslint/rule-tester": "8.0.0-alpha.30",
57-
"eslint": "^9.5.0"
56+
"@typescript-eslint/rule-tester": "^8.3.0",
57+
"eslint": "^9.9.1"
5858
},
5959
"peerDependencies": {
60-
"eslint": "^8 || ^9"
60+
"eslint": "^8.57.0 || ^9.0.0"
6161
}
6262
}

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

Lines changed: 56 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@ import { RuleTester } from '@typescript-eslint/rule-tester'
22
import { rule } from '../rules/exhaustive-deps/exhaustive-deps.rule'
33
import { normalizeIndent } from './test-utils'
44

5-
const ruleTester = new RuleTester({
6-
parser: '@typescript-eslint/parser',
7-
settings: {},
8-
})
5+
const ruleTester = new RuleTester()
96

107
ruleTester.run('exhaustive-deps', rule, {
118
valid: [
@@ -146,7 +143,7 @@ ruleTester.run('exhaustive-deps', rule, {
146143
foo: () => ['foo'] as const,
147144
num: (num: number) => [...fooQueryKeyFactory.foo(), num] as const,
148145
}
149-
146+
150147
const useFoo = (num: number) =>
151148
useQuery({
152149
queryKey: fooQueryKeyFactory.foo(num),
@@ -161,7 +158,7 @@ ruleTester.run('exhaustive-deps', rule, {
161158
foo: () => ['foo'] as const,
162159
num: (num: number) => [...fooQueryKeyFactory.foo(), num] as const,
163160
}
164-
161+
165162
const useFoo = (num: number) =>
166163
useQuery({
167164
queryKey: fooQueryKeyFactory.foo({ x: num }),
@@ -176,7 +173,7 @@ ruleTester.run('exhaustive-deps', rule, {
176173
foo: () => ['foo'] as const,
177174
num: (num: number) => [...fooQueryKeyFactory.foo(), num] as const,
178175
}
179-
176+
180177
const useFoo = (num: number) =>
181178
useQuery({
182179
queryKey: fooQueryKeyFactory.foo({ num }),
@@ -191,7 +188,7 @@ ruleTester.run('exhaustive-deps', rule, {
191188
foo: () => ['foo'] as const,
192189
num: (num: number) => [...fooQueryKeyFactory.foo(), num] as const,
193190
}
194-
191+
195192
const useFoo = (num: number) =>
196193
useQuery({
197194
queryKey: fooQueryKeyFactory.foo([num]),
@@ -206,7 +203,7 @@ ruleTester.run('exhaustive-deps', rule, {
206203
foo: () => ['foo'] as const,
207204
num: (num: number) => [...fooQueryKeyFactory.foo(), num] as const,
208205
}
209-
206+
210207
const useFoo = (num: number) =>
211208
useQuery({
212209
queryKey: fooQueryKeyFactory.foo(1, num),
@@ -221,7 +218,7 @@ ruleTester.run('exhaustive-deps', rule, {
221218
foo: () => ['foo'] as const,
222219
num: (num: number) => [...fooQueryKeyFactory.foo(), num] as const,
223220
}
224-
221+
225222
const useFoo = (obj: { num: number }) =>
226223
useQuery({
227224
queryKey: fooQueryKeyFactory.foo(obj.num),
@@ -253,7 +250,7 @@ ruleTester.run('exhaustive-deps', rule, {
253250
name: 'should not fail if queryKey is having the whole object while queryFn uses some props of it',
254251
code: normalizeIndent`
255252
const state = { foo: 'foo', bar: 'bar' }
256-
253+
257254
useQuery({
258255
queryKey: ['state', state],
259256
queryFn: () => Promise.resolve({ foo: state.foo, bar: state.bar })
@@ -407,7 +404,7 @@ ruleTester.run('exhaustive-deps', rule, {
407404
code: normalizeIndent`
408405
import { useQuery, skipToken } from "@tanstack/react-query";
409406
const fetch = true
410-
407+
411408
function Component({ id }) {
412409
useQuery({
413410
queryKey: [id],
@@ -715,14 +712,27 @@ ruleTester.run('exhaustive-deps', rule, {
715712
name: 'should fail if queryFn is using multiple object props when only one of them is in the queryKey',
716713
code: normalizeIndent`
717714
const state = { foo: 'foo', bar: 'bar' }
718-
715+
719716
useQuery({
720717
queryKey: ['state', state.foo],
721718
queryFn: () => Promise.resolve({ foo: state.foo, bar: state.bar })
722719
})
723720
`,
724721
errors: [
725722
{
723+
suggestions: [
724+
{
725+
messageId: 'fixTo',
726+
output: normalizeIndent`
727+
const state = { foo: 'foo', bar: 'bar' }
728+
729+
useQuery({
730+
queryKey: ['state', state.foo, state.bar],
731+
queryFn: () => Promise.resolve({ foo: state.foo, bar: state.bar })
732+
})
733+
`,
734+
},
735+
],
726736
messageId: 'missingDeps',
727737
data: { deps: 'state.bar' },
728738
},
@@ -732,7 +742,7 @@ ruleTester.run('exhaustive-deps', rule, {
732742
name: 'should fail if queryFn is invalid while using FunctionExpression syntax',
733743
code: normalizeIndent`
734744
const id = 1;
735-
745+
736746
useQuery({
737747
queryKey: [],
738748
queryFn() {
@@ -742,6 +752,21 @@ ruleTester.run('exhaustive-deps', rule, {
742752
`,
743753
errors: [
744754
{
755+
suggestions: [
756+
{
757+
messageId: 'fixTo',
758+
output: normalizeIndent`
759+
const id = 1;
760+
761+
useQuery({
762+
queryKey: [id],
763+
queryFn() {
764+
Promise.resolve(id)
765+
}
766+
})
767+
`,
768+
},
769+
],
745770
messageId: 'missingDeps',
746771
data: { deps: 'id' },
747772
},
@@ -752,7 +777,7 @@ ruleTester.run('exhaustive-deps', rule, {
752777
code: normalizeIndent`
753778
import { useQuery, skipToken } from "@tanstack/react-query";
754779
const fetch = true
755-
780+
756781
function Component({ id }) {
757782
useQuery({
758783
queryKey: [],
@@ -762,6 +787,22 @@ ruleTester.run('exhaustive-deps', rule, {
762787
`,
763788
errors: [
764789
{
790+
suggestions: [
791+
{
792+
messageId: 'fixTo',
793+
output: normalizeIndent`
794+
import { useQuery, skipToken } from "@tanstack/react-query";
795+
const fetch = true
796+
797+
function Component({ id }) {
798+
useQuery({
799+
queryKey: [id],
800+
queryFn: fetch ? () => Promise.resolve(id) : skipToken
801+
})
802+
}
803+
`,
804+
},
805+
],
765806
messageId: 'missingDeps',
766807
data: { deps: 'id' },
767808
},

packages/eslint-plugin-query/src/__tests__/no-rest-destructuring.test.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@ import { RuleTester } from '@typescript-eslint/rule-tester'
22
import { rule } from '../rules/no-rest-destructuring/no-rest-destructuring.rule'
33
import { normalizeIndent } from './test-utils'
44

5-
const ruleTester = new RuleTester({
6-
parser: '@typescript-eslint/parser',
7-
settings: {},
8-
})
5+
const ruleTester = new RuleTester()
96

107
ruleTester.run('no-rest-destructuring', rule, {
118
valid: [

packages/eslint-plugin-query/src/__tests__/no-unstable-deps.test.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,7 @@ import {
55
useQueryHookNames,
66
} from '../rules/no-unstable-deps/no-unstable-deps.rule'
77

8-
const ruleTester = new RuleTester({
9-
parser: '@typescript-eslint/parser',
10-
settings: {},
11-
})
8+
const ruleTester = new RuleTester()
129

1310
interface TestCase {
1411
reactHookImport: string
@@ -37,7 +34,7 @@ const baseTestCases = {
3734
code: `
3835
${reactHookImport}
3936
import { ${queryHook} } from "@tanstack/react-query";
40-
37+
4138
function Component() {
4239
const { refetch } = ${queryHook}({ queryFn: (value: string) => value });
4340
const callback = ${reactHookInvocation}(() => { query.refetch() }, [refetch]);
@@ -57,7 +54,7 @@ const baseTestCases = {
5754
code: `
5855
${reactHookImport}
5956
import { useMutation } from "@tanstack/react-query";
60-
57+
6158
function Component() {
6259
const mutation = useMutation({ mutationFn: (value: string) => value });
6360
const callback = ${reactHookInvocation}(() => { mutation.mutate('hello') }, [mutation]);
@@ -77,7 +74,7 @@ const baseTestCases = {
7774
code: `
7875
${reactHookImport}
7976
import { ${queryHook} } from "@tanstack/react-query";
80-
77+
8178
function Component() {
8279
const query = ${queryHook}({ queryFn: (value: string) => value });
8380
const callback = ${reactHookInvocation}(() => { query.refetch() }, [query]);

packages/eslint-plugin-query/src/__tests__/stable-query-client.test.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@ import { RuleTester } from '@typescript-eslint/rule-tester'
22
import { rule } from '../rules/stable-query-client/stable-query-client.rule'
33
import { normalizeIndent } from './test-utils'
44

5-
const ruleTester = new RuleTester({
6-
parser: '@typescript-eslint/parser',
7-
settings: {},
8-
})
5+
const ruleTester = new RuleTester()
96

107
ruleTester.run('stable-query-client', rule, {
118
valid: [
@@ -35,7 +32,7 @@ ruleTester.run('stable-query-client', rule, {
3532
name: 'QueryClient is stable when wrapped in React.useMemo',
3633
code: normalizeIndent`
3734
import { QueryClient } from "@tanstack/react-query";
38-
35+
3936
function Component() {
4037
const [queryClient] = React.useMemo(() => new QueryClient(), []);
4138
return;
@@ -46,7 +43,7 @@ ruleTester.run('stable-query-client', rule, {
4643
name: 'QueryClient is stable when wrapped in useAnything',
4744
code: normalizeIndent`
4845
import { QueryClient } from "@tanstack/react-query";
49-
46+
5047
function Component() {
5148
const [queryClient] = useAnything(() => new QueryClient());
5249
return;

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

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,6 @@ export const rule = createRule({
8585
}
8686
}
8787

88-
const queryKeyValue = queryKeyNode
8988
const externalRefs = ASTUtils.getExternalRefs({
9089
scopeManager,
9190
sourceCode: context.sourceCode,
@@ -101,7 +100,7 @@ export const rule = createRule({
101100
}),
102101
)
103102

104-
const existingKeys = ASTUtils.getNestedIdentifiers(queryKeyValue).map(
103+
const existingKeys = ASTUtils.getNestedIdentifiers(queryKeyNode).map(
105104
(identifier) =>
106105
ASTUtils.mapKeyNodeToText(identifier, context.sourceCode),
107106
)
@@ -133,9 +132,12 @@ export const rule = createRule({
133132
)
134133
.join(', ')
135134

136-
const existingWithMissing = context.sourceCode
137-
.getText(queryKeyValue)
138-
.replace(/\]$/, `, ${missingAsText}]`)
135+
const queryKeyValue = context.sourceCode.getText(queryKeyNode)
136+
137+
const existingWithMissing =
138+
queryKeyValue === '[]'
139+
? `[${missingAsText}]`
140+
: queryKeyValue.replace(/\]$/, `, ${missingAsText}]`)
139141

140142
const suggestions: TSESLint.ReportSuggestionArray<string> = []
141143

@@ -144,7 +146,7 @@ export const rule = createRule({
144146
messageId: 'fixTo',
145147
data: { result: existingWithMissing },
146148
fix(fixer) {
147-
return fixer.replaceText(queryKeyValue, existingWithMissing)
149+
return fixer.replaceText(queryKeyNode, existingWithMissing)
148150
},
149151
})
150152
}

0 commit comments

Comments
 (0)