@tanstack/eslint-plugin-query 5.59.4 → 5.59.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,219 +0,0 @@
1
- import { RuleTester } from '@typescript-eslint/rule-tester'
2
- import combinate from 'combinate'
3
-
4
- import {
5
- checkedProperties,
6
- infiniteQueryFunctions,
7
- } from '../rules/infinite-query-property-order/constants'
8
- import {
9
- name,
10
- rule,
11
- } from '../rules/infinite-query-property-order/infinite-query-property-order.rule'
12
- import {
13
- generateInterleavedCombinations,
14
- generatePartialCombinations,
15
- generatePermutations,
16
- normalizeIndent,
17
- } from './test-utils'
18
- import type { InfiniteQueryFunctions } from '../rules/infinite-query-property-order/constants'
19
-
20
- const ruleTester = new RuleTester()
21
-
22
- type CheckedProperties = (typeof checkedProperties)[number]
23
- const orderIndependentProps = [
24
- 'queryKey',
25
- '...objectExpressionSpread',
26
- '...callExpressionSpread',
27
- ] as const
28
- type OrderIndependentProps = (typeof orderIndependentProps)[number]
29
-
30
- interface TestCase {
31
- infiniteQueryFunction: InfiniteQueryFunctions
32
- properties: Array<CheckedProperties | OrderIndependentProps>
33
- }
34
-
35
- const validTestMatrix = combinate({
36
- infiniteQueryFunction: [...infiniteQueryFunctions],
37
- properties: generatePartialCombinations(checkedProperties, 2),
38
- })
39
-
40
- export function generateInvalidPermutations(
41
- arr: ReadonlyArray<CheckedProperties>,
42
- ): Array<{
43
- invalid: Array<CheckedProperties>
44
- valid: Array<CheckedProperties>
45
- }> {
46
- const combinations = generatePartialCombinations(arr, 2)
47
- const allPermutations: Array<{
48
- invalid: Array<CheckedProperties>
49
- valid: Array<CheckedProperties>
50
- }> = []
51
-
52
- for (const combination of combinations) {
53
- const permutations = generatePermutations(combination)
54
- // skip the first permutation as it matches the original combination
55
- const invalidPermutations = permutations.slice(1)
56
-
57
- if (
58
- combination.includes('getNextPageParam') &&
59
- combination.includes('getPreviousPageParam')
60
- ) {
61
- if (
62
- combination.indexOf('getNextPageParam') <
63
- combination.indexOf('getPreviousPageParam')
64
- ) {
65
- // since we ignore the relative order of 'getPreviousPageParam' and 'getNextPageParam', we skip this combination (but keep the other one where `getPreviousPageParam` is before `getNextPageParam`)
66
-
67
- continue
68
- }
69
- }
70
-
71
- allPermutations.push(
72
- ...invalidPermutations
73
- .map((p) => {
74
- // ignore the relative order of 'getPreviousPageParam' and 'getNextPageParam'
75
- const correctedValid = [...combination].sort((a, b) => {
76
- if (
77
- (a === 'getNextPageParam' && b === 'getPreviousPageParam') ||
78
- (a === 'getPreviousPageParam' && b === 'getNextPageParam')
79
- ) {
80
- return p.indexOf(a) - p.indexOf(b)
81
- }
82
- return checkedProperties.indexOf(a) - checkedProperties.indexOf(b)
83
- })
84
- return { invalid: p, valid: correctedValid }
85
- })
86
- .filter(
87
- ({ invalid }) =>
88
- // if `getPreviousPageParam` and `getNextPageParam` are next to each other and `queryFn` is not present, we skip this invalid permutation
89
- Math.abs(
90
- invalid.indexOf('getNextPageParam') -
91
- invalid.indexOf('getPreviousPageParam'),
92
- ) !== 1,
93
- ),
94
- )
95
- }
96
-
97
- return allPermutations
98
- }
99
-
100
- const invalidPermutations = generateInvalidPermutations(checkedProperties)
101
-
102
- type Interleaved = CheckedProperties | OrderIndependentProps
103
- const interleavedInvalidPermutations: Array<{
104
- invalid: Array<Interleaved>
105
- valid: Array<Interleaved>
106
- }> = []
107
- for (const invalidPermutation of invalidPermutations) {
108
- const invalid = generateInterleavedCombinations(
109
- invalidPermutation.invalid,
110
- orderIndependentProps,
111
- )
112
- const valid = generateInterleavedCombinations(
113
- invalidPermutation.valid,
114
- orderIndependentProps,
115
- )
116
-
117
- for (let i = 0; i < invalid.length; i++) {
118
- interleavedInvalidPermutations.push({
119
- invalid: invalid[i]!,
120
- valid: valid[i]!,
121
- })
122
- }
123
- }
124
-
125
- const invalidTestMatrix = combinate({
126
- infiniteQueryFunction: [...infiniteQueryFunctions],
127
- properties: interleavedInvalidPermutations,
128
- })
129
-
130
- const callExpressionSpread = normalizeIndent`
131
- ...communitiesQuery({
132
- filters: {
133
- ...fieldValues,
134
- placementFormats: [],
135
- },
136
- })`
137
-
138
- function getCode({
139
- infiniteQueryFunction: infiniteQueryFunction,
140
- properties,
141
- }: TestCase) {
142
- function getPropertyCode(
143
- property: CheckedProperties | OrderIndependentProps,
144
- ) {
145
- switch (property) {
146
- case '...objectExpressionSpread':
147
- return `...objectExpressionSpread`
148
- case '...callExpressionSpread':
149
- return callExpressionSpread
150
- case 'queryKey':
151
- return `queryKey: ['projects']`
152
- case 'queryFn':
153
- return 'queryFn: async ({ pageParam }) => { \n await fetch(`/api/projects?cursor=${pageParam}`) \n return await response.json() \n }'
154
- case 'getPreviousPageParam':
155
- return 'getPreviousPageParam: (firstPage) => firstPage.previousId ?? undefined'
156
- case 'getNextPageParam':
157
- return 'getNextPageParam: (lastPage) => lastPage.nextId ?? undefined'
158
- }
159
- }
160
- return `
161
- import { ${infiniteQueryFunction} } from '@tanstack/react-query'
162
-
163
- ${infiniteQueryFunction}({
164
- ${properties.map(getPropertyCode).join(',\n ')}
165
- })
166
- `
167
- }
168
-
169
- const validTestCases = validTestMatrix.map(
170
- ({ infiniteQueryFunction, properties }) => ({
171
- name: `should pass when order is correct for ${infiniteQueryFunction} with order: ${properties.join(', ')}`,
172
- code: getCode({ infiniteQueryFunction, properties }),
173
- }),
174
- )
175
-
176
- const invalidTestCases = invalidTestMatrix.map(
177
- ({ infiniteQueryFunction, properties }) => ({
178
- name: `incorrect property order is detected for ${infiniteQueryFunction} with invalid order: ${properties.invalid.join(', ')}, valid order: ${properties.valid.join(', ')}`,
179
- code: getCode({
180
- infiniteQueryFunction: infiniteQueryFunction,
181
- properties: properties.invalid,
182
- }),
183
- errors: [{ messageId: 'invalidOrder' }],
184
- output: getCode({
185
- infiniteQueryFunction: infiniteQueryFunction,
186
- properties: properties.valid,
187
- }),
188
- }),
189
- )
190
-
191
- ruleTester.run(name, rule, {
192
- valid: validTestCases,
193
- invalid: invalidTestCases,
194
- })
195
-
196
- // regression tests
197
-
198
- const regressionTestCases = {
199
- valid: [
200
- {
201
- name: 'should pass with call expression spread',
202
- code: normalizeIndent`
203
- import { useInfiniteQuery } from '@tanstack/react-query'
204
- const { data, isFetching, isLoading, hasNextPage, fetchNextPage } =
205
- useInfiniteQuery({
206
- ...communitiesQuery({
207
- filters: {
208
- ...fieldValues,
209
- placementFormats: [],
210
- },
211
- }),
212
- refetchOnMount: false,
213
- })`,
214
- },
215
- ],
216
- invalid: [],
217
- }
218
-
219
- ruleTester.run(name, rule, regressionTestCases)
@@ -1,79 +0,0 @@
1
- import { describe, expect, test } from 'vitest'
2
- import { sortDataByOrder } from '../rules/infinite-query-property-order/infinite-query-property-order.utils'
3
-
4
- describe('create-route-property-order utils', () => {
5
- describe('sortDataByOrder', () => {
6
- const testCases = [
7
- {
8
- data: [{ key: 'a' }, { key: 'c' }, { key: 'b' }],
9
- orderArray: [
10
- [['a'], ['b']],
11
- [['b'], ['c']],
12
- ],
13
- key: 'key',
14
- expected: [{ key: 'a' }, { key: 'b' }, { key: 'c' }],
15
- },
16
- {
17
- data: [{ key: 'b' }, { key: 'a' }, { key: 'c' }],
18
- orderArray: [
19
- [['a'], ['b']],
20
- [['b'], ['c']],
21
- ],
22
- key: 'key',
23
- expected: [{ key: 'a' }, { key: 'b' }, { key: 'c' }],
24
- },
25
- {
26
- data: [{ key: 'a' }, { key: 'b' }, { key: 'c' }],
27
- orderArray: [
28
- [['a'], ['b']],
29
- [['b'], ['c']],
30
- ],
31
- key: 'key',
32
- expected: null,
33
- },
34
- {
35
- data: [{ key: 'a' }, { key: 'b' }, { key: 'c' }, { key: 'd' }],
36
- orderArray: [
37
- [['a'], ['b']],
38
- [['b'], ['c']],
39
- ],
40
- key: 'key',
41
- expected: null,
42
- },
43
- {
44
- data: [{ key: 'a' }, { key: 'b' }, { key: 'd' }, { key: 'c' }],
45
- orderArray: [
46
- [['a'], ['b']],
47
- [['b'], ['c']],
48
- ],
49
- key: 'key',
50
- expected: null,
51
- },
52
- {
53
- data: [{ key: 'd' }, { key: 'a' }, { key: 'b' }, { key: 'c' }],
54
- orderArray: [
55
- [['a'], ['b']],
56
- [['b'], ['c']],
57
- ],
58
- key: 'key',
59
- expected: null,
60
- },
61
- {
62
- data: [{ key: 'd' }, { key: 'b' }, { key: 'a' }, { key: 'c' }],
63
- orderArray: [
64
- [['a'], ['b']],
65
- [['b'], ['c']],
66
- ],
67
- key: 'key',
68
- expected: [{ key: 'd' }, { key: 'a' }, { key: 'b' }, { key: 'c' }],
69
- },
70
- ] as const
71
- test.each(testCases)(
72
- '$data $orderArray $key $expected',
73
- ({ data, orderArray, key, expected }) => {
74
- const sortedData = sortDataByOrder(data, orderArray, key)
75
- expect(sortedData).toEqual(expected)
76
- },
77
- )
78
- })
79
- })
@@ -1,192 +0,0 @@
1
- import { RuleTester } from '@typescript-eslint/rule-tester'
2
- import { rule } from '../rules/no-rest-destructuring/no-rest-destructuring.rule'
3
- import { normalizeIndent } from './test-utils'
4
-
5
- const ruleTester = new RuleTester()
6
-
7
- ruleTester.run('no-rest-destructuring', rule, {
8
- valid: [
9
- {
10
- name: 'useQuery is not captured',
11
- code: normalizeIndent`
12
- import { useQuery } from '@tanstack/react-query'
13
-
14
- function Component() {
15
- useQuery()
16
- return
17
- }
18
- `,
19
- },
20
- {
21
- name: 'useQuery is not destructured',
22
- code: normalizeIndent`
23
- import { useQuery } from '@tanstack/react-query'
24
-
25
- function Component() {
26
- const query = useQuery()
27
- return
28
- }
29
- `,
30
- },
31
- {
32
- name: 'useQuery is destructured without rest',
33
- code: normalizeIndent`
34
- import { useQuery } from '@tanstack/react-query'
35
-
36
- function Component() {
37
- const { data, isLoading, isError } = useQuery()
38
- return
39
- }
40
- `,
41
- },
42
- {
43
- name: 'useInfiniteQuery is not captured',
44
- code: normalizeIndent`
45
- import { useInfiniteQuery } from '@tanstack/react-query'
46
-
47
- function Component() {
48
- useInfiniteQuery()
49
- return
50
- }
51
- `,
52
- },
53
- {
54
- name: 'useInfiniteQuery is not destructured',
55
- code: normalizeIndent`
56
- import { useInfiniteQuery } from '@tanstack/react-query'
57
-
58
- function Component() {
59
- const query = useInfiniteQuery()
60
- return
61
- }
62
- `,
63
- },
64
- {
65
- name: 'useInfiniteQuery is destructured without rest',
66
- code: normalizeIndent`
67
- import { useInfiniteQuery } from '@tanstack/react-query'
68
-
69
- function Component() {
70
- const { data, isLoading, isError } = useInfiniteQuery()
71
- return
72
- }
73
- `,
74
- },
75
- {
76
- name: 'useQueries is not captured',
77
- code: normalizeIndent`
78
- import { useQueries } from '@tanstack/react-query'
79
-
80
- function Component() {
81
- useQueries([])
82
- return
83
- }
84
- `,
85
- },
86
- {
87
- name: 'useQueries is not destructured',
88
- code: normalizeIndent`
89
- import { useQueries } from '@tanstack/react-query'
90
-
91
- function Component() {
92
- const queries = useQueries([])
93
- return
94
- }
95
- `,
96
- },
97
- {
98
- name: 'useQueries array has no rest destructured element',
99
- code: normalizeIndent`
100
- import { useQueries } from '@tanstack/react-query'
101
-
102
- function Component() {
103
- const [query1, { data, isLoading },, ...others] = useQueries([
104
- { queryKey: ['key1'], queryFn: () => {} },
105
- { queryKey: ['key2'], queryFn: () => {} },
106
- { queryKey: ['key3'], queryFn: () => {} },
107
- { queryKey: ['key4'], queryFn: () => {} },
108
- { queryKey: ['key5'], queryFn: () => {} },
109
- ])
110
- return
111
- }
112
- `,
113
- },
114
- {
115
- name: 'useQuery is destructured with rest but not from tanstack query',
116
- code: normalizeIndent`
117
- import { useQuery } from 'other-package'
118
-
119
- function Component() {
120
- const { data, ...rest } = useQuery()
121
- return
122
- }
123
- `,
124
- },
125
- {
126
- name: 'useInfiniteQuery is destructured with rest but not from tanstack query',
127
- code: normalizeIndent`
128
- import { useInfiniteQuery } from 'other-package'
129
-
130
- function Component() {
131
- const { data, ...rest } = useInfiniteQuery()
132
- return
133
- }
134
- `,
135
- },
136
- {
137
- name: 'useQueries array has rest destructured element but not from tanstack query',
138
- code: normalizeIndent`
139
- import { useQueries } from 'other-package'
140
-
141
- function Component() {
142
- const [query1, { data, ...rest }] = useQueries([
143
- { queryKey: ['key1'], queryFn: () => {} },
144
- { queryKey: ['key2'], queryFn: () => {} },
145
- ])
146
- return
147
- }
148
- `,
149
- },
150
- ],
151
- invalid: [
152
- {
153
- name: 'useQuery is destructured with rest',
154
- code: normalizeIndent`
155
- import { useQuery } from '@tanstack/react-query'
156
-
157
- function Component() {
158
- const { data, ...rest } = useQuery()
159
- return
160
- }
161
- `,
162
- errors: [{ messageId: 'objectRestDestructure' }],
163
- },
164
- {
165
- name: 'useInfiniteQuery is destructured with rest',
166
- code: normalizeIndent`
167
- import { useInfiniteQuery } from '@tanstack/react-query'
168
-
169
- function Component() {
170
- const { data, ...rest } = useInfiniteQuery()
171
- return
172
- }
173
- `,
174
- errors: [{ messageId: 'objectRestDestructure' }],
175
- },
176
- {
177
- name: 'useQueries array has rest destructured element',
178
- code: normalizeIndent`
179
- import { useQueries } from '@tanstack/react-query'
180
-
181
- function Component() {
182
- const [query1, { data, ...rest }] = useQueries([
183
- { queryKey: ['key1'], queryFn: () => {} },
184
- { queryKey: ['key2'], queryFn: () => {} },
185
- ])
186
- return
187
- }
188
- `,
189
- errors: [{ messageId: 'objectRestDestructure' }],
190
- },
191
- ],
192
- })
@@ -1,129 +0,0 @@
1
- import { RuleTester } from '@typescript-eslint/rule-tester'
2
- import {
3
- reactHookNames,
4
- rule,
5
- useQueryHookNames,
6
- } from '../rules/no-unstable-deps/no-unstable-deps.rule'
7
-
8
- const ruleTester = new RuleTester()
9
-
10
- interface TestCase {
11
- reactHookImport: string
12
- reactHookInvocation: string
13
- reactHookAlias: string
14
- }
15
- const baseTestCases = {
16
- valid: ({ reactHookImport, reactHookInvocation, reactHookAlias }: TestCase) =>
17
- [
18
- {
19
- name: `should pass when destructured mutate is passed to ${reactHookAlias} as dependency`,
20
- code: `
21
- ${reactHookImport}
22
- import { useMutation } from "@tanstack/react-query";
23
-
24
- function Component() {
25
- const { mutate } = useMutation({ mutationFn: (value: string) => value });
26
- const callback = ${reactHookInvocation}(() => { mutate('hello') }, [mutate]);
27
- return;
28
- }
29
- `,
30
- },
31
- ].concat(
32
- useQueryHookNames.map((queryHook) => ({
33
- name: `should pass result of ${queryHook} is passed to ${reactHookInvocation} as dependency`,
34
- code: `
35
- ${reactHookImport}
36
- import { ${queryHook} } from "@tanstack/react-query";
37
-
38
- function Component() {
39
- const { refetch } = ${queryHook}({ queryFn: (value: string) => value });
40
- const callback = ${reactHookInvocation}(() => { query.refetch() }, [refetch]);
41
- return;
42
- }
43
- `,
44
- })),
45
- ),
46
- invalid: ({
47
- reactHookImport,
48
- reactHookInvocation,
49
- reactHookAlias,
50
- }: TestCase) =>
51
- [
52
- {
53
- name: `result of useMutation is passed to ${reactHookInvocation} as dependency `,
54
- code: `
55
- ${reactHookImport}
56
- import { useMutation } from "@tanstack/react-query";
57
-
58
- function Component() {
59
- const mutation = useMutation({ mutationFn: (value: string) => value });
60
- const callback = ${reactHookInvocation}(() => { mutation.mutate('hello') }, [mutation]);
61
- return;
62
- }
63
- `,
64
- errors: [
65
- {
66
- messageId: 'noUnstableDeps',
67
- data: { reactHook: reactHookAlias, queryHook: 'useMutation' },
68
- },
69
- ],
70
- },
71
- ].concat(
72
- useQueryHookNames.map((queryHook) => ({
73
- name: `result of ${queryHook} is passed to ${reactHookInvocation} as dependency`,
74
- code: `
75
- ${reactHookImport}
76
- import { ${queryHook} } from "@tanstack/react-query";
77
-
78
- function Component() {
79
- const query = ${queryHook}({ queryFn: (value: string) => value });
80
- const callback = ${reactHookInvocation}(() => { query.refetch() }, [query]);
81
- return;
82
- }
83
- `,
84
- errors: [
85
- {
86
- messageId: 'noUnstableDeps',
87
- data: { reactHook: reactHookAlias, queryHook },
88
- },
89
- ],
90
- })),
91
- ),
92
- }
93
-
94
- const testCases = (reactHookName: string) => [
95
- {
96
- reactHookImport: 'import * as React from "React";',
97
- reactHookInvocation: `React.${reactHookName}`,
98
- reactHookAlias: reactHookName,
99
- },
100
- {
101
- reactHookImport: `import { ${reactHookName} } from "React";`,
102
- reactHookInvocation: reactHookName,
103
- reactHookAlias: reactHookName,
104
- },
105
- {
106
- reactHookImport: `import { ${reactHookName} as useAlias } from "React";`,
107
- reactHookInvocation: 'useAlias',
108
- reactHookAlias: 'useAlias',
109
- },
110
- ]
111
-
112
- reactHookNames.forEach((reactHookName) => {
113
- testCases(reactHookName).forEach(
114
- ({ reactHookInvocation, reactHookAlias, reactHookImport }) => {
115
- ruleTester.run('no-unstable-deps', rule, {
116
- valid: baseTestCases.valid({
117
- reactHookImport,
118
- reactHookInvocation,
119
- reactHookAlias,
120
- }),
121
- invalid: baseTestCases.invalid({
122
- reactHookImport,
123
- reactHookInvocation,
124
- reactHookAlias,
125
- }),
126
- })
127
- },
128
- )
129
- })