@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.
- package/dist/cjs/rules/infinite-query-property-order/infinite-query-property-order.rule.cjs +2 -12
- package/dist/cjs/rules/infinite-query-property-order/infinite-query-property-order.rule.cjs.map +1 -1
- package/dist/esm/rules/infinite-query-property-order/infinite-query-property-order.rule.js +2 -12
- package/dist/esm/rules/infinite-query-property-order/infinite-query-property-order.rule.js.map +1 -1
- package/package.json +3 -2
- package/src/rules/infinite-query-property-order/infinite-query-property-order.rule.ts +2 -12
- package/src/__tests__/exhaustive-deps.test.ts +0 -859
- package/src/__tests__/infinite-query-property-order.rule.test.ts +0 -219
- package/src/__tests__/infinite-query-property-order.utils.test.ts +0 -79
- package/src/__tests__/no-rest-destructuring.test.ts +0 -192
- package/src/__tests__/no-unstable-deps.test.ts +0 -129
- package/src/__tests__/stable-query-client.test.ts +0 -192
- package/src/__tests__/test-utils.test.ts +0 -104
- package/src/__tests__/test-utils.ts +0 -108
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
import { RuleTester } from '@typescript-eslint/rule-tester'
|
|
2
|
-
import { rule } from '../rules/stable-query-client/stable-query-client.rule'
|
|
3
|
-
import { normalizeIndent } from './test-utils'
|
|
4
|
-
|
|
5
|
-
const ruleTester = new RuleTester()
|
|
6
|
-
|
|
7
|
-
ruleTester.run('stable-query-client', rule, {
|
|
8
|
-
valid: [
|
|
9
|
-
{
|
|
10
|
-
name: 'QueryClient is stable when wrapped in React.useState',
|
|
11
|
-
code: normalizeIndent`
|
|
12
|
-
import { QueryClient } from "@tanstack/react-query";
|
|
13
|
-
|
|
14
|
-
function Component() {
|
|
15
|
-
const [queryClient] = React.useState(() => new QueryClient());
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
`,
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
name: 'QueryClient is stable when wrapped in useState',
|
|
22
|
-
code: normalizeIndent`
|
|
23
|
-
import { QueryClient } from "@tanstack/react-query";
|
|
24
|
-
|
|
25
|
-
function Component() {
|
|
26
|
-
const [queryClient] = useState(() => new QueryClient());
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
`,
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
name: 'QueryClient is stable when wrapped in React.useMemo',
|
|
33
|
-
code: normalizeIndent`
|
|
34
|
-
import { QueryClient } from "@tanstack/react-query";
|
|
35
|
-
|
|
36
|
-
function Component() {
|
|
37
|
-
const [queryClient] = React.useMemo(() => new QueryClient(), []);
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
`,
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
name: 'QueryClient is stable when wrapped in useAnything',
|
|
44
|
-
code: normalizeIndent`
|
|
45
|
-
import { QueryClient } from "@tanstack/react-query";
|
|
46
|
-
|
|
47
|
-
function Component() {
|
|
48
|
-
const [queryClient] = useAnything(() => new QueryClient());
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
`,
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
name: 'QueryClient is imported from a non-tanstack package',
|
|
55
|
-
code: normalizeIndent`
|
|
56
|
-
import { QueryClient } from "other-library";
|
|
57
|
-
|
|
58
|
-
function Component() {
|
|
59
|
-
const queryClient = new QueryClient();
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
`,
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
name: 'QueryClient is not imported from @tanstack/react-query',
|
|
66
|
-
code: normalizeIndent`
|
|
67
|
-
import { QueryClient } from "@tanstack/solid-query";
|
|
68
|
-
|
|
69
|
-
function Component() {
|
|
70
|
-
const queryClient = new QueryClient();
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
`,
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
name: 'QueryClient is invoked outside of a function',
|
|
77
|
-
code: normalizeIndent`
|
|
78
|
-
import { QueryClient } from "@tanstack/solid-query";
|
|
79
|
-
|
|
80
|
-
const queryClient = new QueryClient();
|
|
81
|
-
|
|
82
|
-
function Component() {
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
`,
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
name: 'QueryClient is invoked in a non-component function',
|
|
89
|
-
code: normalizeIndent`
|
|
90
|
-
import { QueryClient } from "@tanstack/solid-query";
|
|
91
|
-
|
|
92
|
-
function someFn() {
|
|
93
|
-
const queryClient = new QueryClient();
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
`,
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
name: 'QueryClient is invoked in an async (react server) component',
|
|
100
|
-
code: normalizeIndent`
|
|
101
|
-
import { QueryClient } from "@tanstack/solid-query";
|
|
102
|
-
|
|
103
|
-
async function AsyncComponent() {
|
|
104
|
-
const queryClient = new QueryClient();
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
`,
|
|
108
|
-
},
|
|
109
|
-
],
|
|
110
|
-
invalid: [
|
|
111
|
-
{
|
|
112
|
-
name: 'QueryClient is not stable when it is not wrapped in React.useState in component',
|
|
113
|
-
code: normalizeIndent`
|
|
114
|
-
import { QueryClient } from "@tanstack/react-query";
|
|
115
|
-
|
|
116
|
-
function Component() {
|
|
117
|
-
const queryClient = new QueryClient();
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
`,
|
|
121
|
-
output: normalizeIndent`
|
|
122
|
-
import { QueryClient } from "@tanstack/react-query";
|
|
123
|
-
|
|
124
|
-
function Component() {
|
|
125
|
-
const [queryClient] = React.useState(() => new QueryClient());
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
`,
|
|
129
|
-
errors: [{ messageId: 'unstable' }],
|
|
130
|
-
},
|
|
131
|
-
{
|
|
132
|
-
name: 'QueryClient is not stable when it is not wrapped in React.useState in custom hook',
|
|
133
|
-
code: normalizeIndent`
|
|
134
|
-
import { QueryClient } from "@tanstack/react-query";
|
|
135
|
-
|
|
136
|
-
function useHook() {
|
|
137
|
-
const queryClient = new QueryClient();
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
`,
|
|
141
|
-
output: normalizeIndent`
|
|
142
|
-
import { QueryClient } from "@tanstack/react-query";
|
|
143
|
-
|
|
144
|
-
function useHook() {
|
|
145
|
-
const [queryClient] = React.useState(() => new QueryClient());
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
`,
|
|
149
|
-
errors: [{ messageId: 'unstable' }],
|
|
150
|
-
},
|
|
151
|
-
{
|
|
152
|
-
name: 'preserve QueryClient options',
|
|
153
|
-
code: normalizeIndent`
|
|
154
|
-
import { QueryClient } from "@tanstack/react-query";
|
|
155
|
-
|
|
156
|
-
function Component() {
|
|
157
|
-
const queryClient = new QueryClient({ defaultOptions: { /* */ } });
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
`,
|
|
161
|
-
output: normalizeIndent`
|
|
162
|
-
import { QueryClient } from "@tanstack/react-query";
|
|
163
|
-
|
|
164
|
-
function Component() {
|
|
165
|
-
const [queryClient] = React.useState(() => new QueryClient({ defaultOptions: { /* */ } }));
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
`,
|
|
169
|
-
errors: [{ messageId: 'unstable' }],
|
|
170
|
-
},
|
|
171
|
-
{
|
|
172
|
-
name: 'preserve QueryClient variable declarator name',
|
|
173
|
-
code: normalizeIndent`
|
|
174
|
-
import { QueryClient } from "@tanstack/react-query";
|
|
175
|
-
|
|
176
|
-
function Component() {
|
|
177
|
-
const customName = new QueryClient();
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
`,
|
|
181
|
-
output: normalizeIndent`
|
|
182
|
-
import { QueryClient } from "@tanstack/react-query";
|
|
183
|
-
|
|
184
|
-
function Component() {
|
|
185
|
-
const [customName] = React.useState(() => new QueryClient());
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
`,
|
|
189
|
-
errors: [{ messageId: 'unstable' }],
|
|
190
|
-
},
|
|
191
|
-
],
|
|
192
|
-
})
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from 'vitest'
|
|
2
|
-
import {
|
|
3
|
-
expectArrayEqualIgnoreOrder,
|
|
4
|
-
generateInterleavedCombinations,
|
|
5
|
-
generatePartialCombinations,
|
|
6
|
-
generatePermutations,
|
|
7
|
-
} from './test-utils'
|
|
8
|
-
|
|
9
|
-
describe('test-utils', () => {
|
|
10
|
-
describe('generatePermutations', () => {
|
|
11
|
-
const testCases = [
|
|
12
|
-
{
|
|
13
|
-
input: ['a', 'b', 'c'],
|
|
14
|
-
expected: [
|
|
15
|
-
['a', 'b', 'c'],
|
|
16
|
-
['a', 'c', 'b'],
|
|
17
|
-
['b', 'a', 'c'],
|
|
18
|
-
['b', 'c', 'a'],
|
|
19
|
-
['c', 'a', 'b'],
|
|
20
|
-
['c', 'b', 'a'],
|
|
21
|
-
],
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
input: ['a', 'b'],
|
|
25
|
-
expected: [
|
|
26
|
-
['a', 'b'],
|
|
27
|
-
['b', 'a'],
|
|
28
|
-
],
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
input: ['a'],
|
|
32
|
-
expected: [['a']],
|
|
33
|
-
},
|
|
34
|
-
]
|
|
35
|
-
test.each(testCases)('$input $expected', ({ input, expected }) => {
|
|
36
|
-
const permutations = generatePermutations(input)
|
|
37
|
-
expect(permutations).toEqual(expected)
|
|
38
|
-
})
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
describe('generatePartialCombinations', () => {
|
|
42
|
-
const testCases = [
|
|
43
|
-
{
|
|
44
|
-
input: ['a', 'b', 'c'],
|
|
45
|
-
minLength: 2,
|
|
46
|
-
expected: [
|
|
47
|
-
['a', 'b'],
|
|
48
|
-
['a', 'c'],
|
|
49
|
-
['b', 'c'],
|
|
50
|
-
['a', 'b', 'c'],
|
|
51
|
-
],
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
input: ['a', 'b'],
|
|
55
|
-
expected: [['a', 'b']],
|
|
56
|
-
minLength: 2,
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
input: ['a'],
|
|
60
|
-
expected: [],
|
|
61
|
-
minLength: 2,
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
input: ['a'],
|
|
65
|
-
expected: [['a']],
|
|
66
|
-
minLength: 1,
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
input: ['a'],
|
|
70
|
-
expected: [[], ['a']],
|
|
71
|
-
minLength: 0,
|
|
72
|
-
},
|
|
73
|
-
]
|
|
74
|
-
test.each(testCases)(
|
|
75
|
-
'$input $minLength $expected',
|
|
76
|
-
({ input, minLength, expected }) => {
|
|
77
|
-
const combinations = generatePartialCombinations(input, minLength)
|
|
78
|
-
expectArrayEqualIgnoreOrder(combinations, expected)
|
|
79
|
-
},
|
|
80
|
-
)
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
describe('generateInterleavedCombinations', () => {
|
|
84
|
-
const testCases = [
|
|
85
|
-
{
|
|
86
|
-
data: ['a', 'b'],
|
|
87
|
-
additional: ['x'],
|
|
88
|
-
expected: [
|
|
89
|
-
['a', 'b'],
|
|
90
|
-
['x', 'a', 'b'],
|
|
91
|
-
['a', 'x', 'b'],
|
|
92
|
-
['a', 'b', 'x'],
|
|
93
|
-
],
|
|
94
|
-
},
|
|
95
|
-
]
|
|
96
|
-
test.each(testCases)(
|
|
97
|
-
'$input $expected',
|
|
98
|
-
({ data, additional, expected }) => {
|
|
99
|
-
const combinations = generateInterleavedCombinations(data, additional)
|
|
100
|
-
expectArrayEqualIgnoreOrder(combinations, expected)
|
|
101
|
-
},
|
|
102
|
-
)
|
|
103
|
-
})
|
|
104
|
-
})
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import { expect } from 'vitest'
|
|
2
|
-
|
|
3
|
-
export function normalizeIndent(template: TemplateStringsArray) {
|
|
4
|
-
const codeLines = template[0]?.split('\n') ?? ['']
|
|
5
|
-
const leftPadding = codeLines[1]?.match(/\s+/)?.[0] ?? ''
|
|
6
|
-
return codeLines.map((line) => line.slice(leftPadding.length)).join('\n')
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function generatePermutations<T>(arr: Array<T>): Array<Array<T>> {
|
|
10
|
-
if (arr.length <= 1) {
|
|
11
|
-
return [arr]
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const result: Array<Array<T>> = []
|
|
15
|
-
for (let i = 0; i < arr.length; i++) {
|
|
16
|
-
const rest = arr.slice(0, i).concat(arr.slice(i + 1))
|
|
17
|
-
const restPermutations = generatePermutations(rest)
|
|
18
|
-
for (const perm of restPermutations) {
|
|
19
|
-
result.push([arr[i]!, ...perm])
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
return result
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function generatePartialCombinations<T>(
|
|
27
|
-
arr: ReadonlyArray<T>,
|
|
28
|
-
minLength: number,
|
|
29
|
-
): Array<Array<T>> {
|
|
30
|
-
const result: Array<Array<T>> = []
|
|
31
|
-
|
|
32
|
-
function backtrack(start: number, current: Array<T>) {
|
|
33
|
-
if (current.length > minLength - 1) {
|
|
34
|
-
result.push([...current])
|
|
35
|
-
}
|
|
36
|
-
for (let i = start; i < arr.length; i++) {
|
|
37
|
-
current.push(arr[i]!)
|
|
38
|
-
backtrack(i + 1, current)
|
|
39
|
-
current.pop()
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
backtrack(0, [])
|
|
43
|
-
return result
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function expectArrayEqualIgnoreOrder<T>(a: Array<T>, b: Array<T>) {
|
|
47
|
-
expect([...a].sort()).toEqual([...b].sort())
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export function generateInterleavedCombinations<
|
|
51
|
-
TData,
|
|
52
|
-
TAdditional,
|
|
53
|
-
TResult extends TData | TAdditional,
|
|
54
|
-
>(
|
|
55
|
-
data: Array<TData> | ReadonlyArray<TData>,
|
|
56
|
-
additional: Array<TAdditional> | ReadonlyArray<TAdditional>,
|
|
57
|
-
): Array<Array<TResult>> {
|
|
58
|
-
const result: Array<Array<TResult>> = []
|
|
59
|
-
|
|
60
|
-
function getSubsets(array: Array<TAdditional>): Array<Array<TAdditional>> {
|
|
61
|
-
return array.reduce(
|
|
62
|
-
(subsets, value) => {
|
|
63
|
-
return subsets.concat(subsets.map((set) => [...set, value]))
|
|
64
|
-
},
|
|
65
|
-
[[]] as Array<Array<TAdditional>>,
|
|
66
|
-
)
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function insertAtPositions(
|
|
70
|
-
baseData: Array<TResult>,
|
|
71
|
-
subset: Array<TResult>,
|
|
72
|
-
): Array<Array<TResult>> {
|
|
73
|
-
const combinations: Array<Array<TResult>> = []
|
|
74
|
-
|
|
75
|
-
const recurse = (
|
|
76
|
-
currentData: Array<TResult>,
|
|
77
|
-
currentSubset: Array<TResult>,
|
|
78
|
-
start: number,
|
|
79
|
-
): void => {
|
|
80
|
-
if (currentSubset.length === 0) {
|
|
81
|
-
combinations.push([...currentData])
|
|
82
|
-
return
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
for (let i = start; i <= currentData.length; i++) {
|
|
86
|
-
const newData = [
|
|
87
|
-
...currentData.slice(0, i),
|
|
88
|
-
currentSubset[0]!,
|
|
89
|
-
...currentData.slice(i),
|
|
90
|
-
]
|
|
91
|
-
recurse(newData, currentSubset.slice(1), i + 1)
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
recurse(baseData, subset, 0)
|
|
96
|
-
return combinations
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const subsets = getSubsets(additional as Array<TAdditional>)
|
|
100
|
-
|
|
101
|
-
subsets.forEach((subset) => {
|
|
102
|
-
result.push(
|
|
103
|
-
...insertAtPositions(data as Array<TResult>, subset as Array<TResult>),
|
|
104
|
-
)
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
return result
|
|
108
|
-
}
|