@tanstack/eslint-plugin-query 5.59.7 → 5.60.1

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,222 +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
- '...memberCallExpressionSpread',
28
- ] as const
29
- type OrderIndependentProps = (typeof orderIndependentProps)[number]
30
-
31
- interface TestCase {
32
- infiniteQueryFunction: InfiniteQueryFunctions
33
- properties: Array<CheckedProperties | OrderIndependentProps>
34
- }
35
-
36
- const validTestMatrix = combinate({
37
- infiniteQueryFunction: [...infiniteQueryFunctions],
38
- properties: generatePartialCombinations(checkedProperties, 2),
39
- })
40
-
41
- export function generateInvalidPermutations(
42
- arr: ReadonlyArray<CheckedProperties>,
43
- ): Array<{
44
- invalid: Array<CheckedProperties>
45
- valid: Array<CheckedProperties>
46
- }> {
47
- const combinations = generatePartialCombinations(arr, 2)
48
- const allPermutations: Array<{
49
- invalid: Array<CheckedProperties>
50
- valid: Array<CheckedProperties>
51
- }> = []
52
-
53
- for (const combination of combinations) {
54
- const permutations = generatePermutations(combination)
55
- // skip the first permutation as it matches the original combination
56
- const invalidPermutations = permutations.slice(1)
57
-
58
- if (
59
- combination.includes('getNextPageParam') &&
60
- combination.includes('getPreviousPageParam')
61
- ) {
62
- if (
63
- combination.indexOf('getNextPageParam') <
64
- combination.indexOf('getPreviousPageParam')
65
- ) {
66
- // since we ignore the relative order of 'getPreviousPageParam' and 'getNextPageParam', we skip this combination (but keep the other one where `getPreviousPageParam` is before `getNextPageParam`)
67
-
68
- continue
69
- }
70
- }
71
-
72
- allPermutations.push(
73
- ...invalidPermutations
74
- .map((p) => {
75
- // ignore the relative order of 'getPreviousPageParam' and 'getNextPageParam'
76
- const correctedValid = [...combination].sort((a, b) => {
77
- if (
78
- (a === 'getNextPageParam' && b === 'getPreviousPageParam') ||
79
- (a === 'getPreviousPageParam' && b === 'getNextPageParam')
80
- ) {
81
- return p.indexOf(a) - p.indexOf(b)
82
- }
83
- return checkedProperties.indexOf(a) - checkedProperties.indexOf(b)
84
- })
85
- return { invalid: p, valid: correctedValid }
86
- })
87
- .filter(
88
- ({ invalid }) =>
89
- // if `getPreviousPageParam` and `getNextPageParam` are next to each other and `queryFn` is not present, we skip this invalid permutation
90
- Math.abs(
91
- invalid.indexOf('getNextPageParam') -
92
- invalid.indexOf('getPreviousPageParam'),
93
- ) !== 1,
94
- ),
95
- )
96
- }
97
-
98
- return allPermutations
99
- }
100
-
101
- const invalidPermutations = generateInvalidPermutations(checkedProperties)
102
-
103
- type Interleaved = CheckedProperties | OrderIndependentProps
104
- const interleavedInvalidPermutations: Array<{
105
- invalid: Array<Interleaved>
106
- valid: Array<Interleaved>
107
- }> = []
108
- for (const invalidPermutation of invalidPermutations) {
109
- const invalid = generateInterleavedCombinations(
110
- invalidPermutation.invalid,
111
- orderIndependentProps,
112
- )
113
- const valid = generateInterleavedCombinations(
114
- invalidPermutation.valid,
115
- orderIndependentProps,
116
- )
117
-
118
- for (let i = 0; i < invalid.length; i++) {
119
- interleavedInvalidPermutations.push({
120
- invalid: invalid[i]!,
121
- valid: valid[i]!,
122
- })
123
- }
124
- }
125
-
126
- const invalidTestMatrix = combinate({
127
- infiniteQueryFunction: [...infiniteQueryFunctions],
128
- properties: interleavedInvalidPermutations,
129
- })
130
-
131
- const callExpressionSpread = normalizeIndent`
132
- ...communitiesQuery({
133
- filters: {
134
- ...fieldValues,
135
- placementFormats: [],
136
- },
137
- })`
138
-
139
- function getCode({
140
- infiniteQueryFunction: infiniteQueryFunction,
141
- properties,
142
- }: TestCase) {
143
- function getPropertyCode(
144
- property: CheckedProperties | OrderIndependentProps,
145
- ) {
146
- switch (property) {
147
- case '...objectExpressionSpread':
148
- return `...objectExpressionSpread`
149
- case '...callExpressionSpread':
150
- return callExpressionSpread
151
- case '...memberCallExpressionSpread':
152
- return '...myOptions.infiniteQueryOptions()'
153
- case 'queryKey':
154
- return `queryKey: ['projects']`
155
- case 'queryFn':
156
- return 'queryFn: async ({ pageParam }) => { \n await fetch(`/api/projects?cursor=${pageParam}`) \n return await response.json() \n }'
157
- case 'getPreviousPageParam':
158
- return 'getPreviousPageParam: (firstPage) => firstPage.previousId ?? undefined'
159
- case 'getNextPageParam':
160
- return 'getNextPageParam: (lastPage) => lastPage.nextId ?? undefined'
161
- }
162
- }
163
- return `
164
- import { ${infiniteQueryFunction} } from '@tanstack/react-query'
165
-
166
- ${infiniteQueryFunction}({
167
- ${properties.map(getPropertyCode).join(',\n ')}
168
- })
169
- `
170
- }
171
-
172
- const validTestCases = validTestMatrix.map(
173
- ({ infiniteQueryFunction, properties }) => ({
174
- name: `should pass when order is correct for ${infiniteQueryFunction} with order: ${properties.join(', ')}`,
175
- code: getCode({ infiniteQueryFunction, properties }),
176
- }),
177
- )
178
-
179
- const invalidTestCases = invalidTestMatrix.map(
180
- ({ infiniteQueryFunction, properties }) => ({
181
- name: `incorrect property order is detected for ${infiniteQueryFunction} with invalid order: ${properties.invalid.join(', ')}, valid order: ${properties.valid.join(', ')}`,
182
- code: getCode({
183
- infiniteQueryFunction: infiniteQueryFunction,
184
- properties: properties.invalid,
185
- }),
186
- errors: [{ messageId: 'invalidOrder' }],
187
- output: getCode({
188
- infiniteQueryFunction: infiniteQueryFunction,
189
- properties: properties.valid,
190
- }),
191
- }),
192
- )
193
-
194
- ruleTester.run(name, rule, {
195
- valid: validTestCases,
196
- invalid: invalidTestCases,
197
- })
198
-
199
- // regression tests
200
-
201
- const regressionTestCases = {
202
- valid: [
203
- {
204
- name: 'should pass with call expression spread',
205
- code: normalizeIndent`
206
- import { useInfiniteQuery } from '@tanstack/react-query'
207
- const { data, isFetching, isLoading, hasNextPage, fetchNextPage } =
208
- useInfiniteQuery({
209
- ...communitiesQuery({
210
- filters: {
211
- ...fieldValues,
212
- placementFormats: [],
213
- },
214
- }),
215
- refetchOnMount: false,
216
- })`,
217
- },
218
- ],
219
- invalid: [],
220
- }
221
-
222
- 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
- })