-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
/
prefer-query-object-syntax.ts
129 lines (111 loc) 路 3.87 KB
/
prefer-query-object-syntax.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import type { TSESLint } from '@typescript-eslint/utils'
import { createRule } from '../../utils/create-rule'
import { ASTUtils } from '../../utils/ast-utils'
const QUERY_CALLS = ['useQuery', 'createQuery']
export const name = 'prefer-query-object-syntax'
export const rule = createRule({
name,
meta: {
type: 'problem',
docs: {
description: 'Prefer object syntax for useQuery',
recommended: 'error',
},
messages: {
preferObjectSyntax: `Objects syntax for useQuery is preferred`,
},
fixable: 'code',
schema: [],
},
defaultOptions: [],
create(context, _, helpers) {
const sourceCode = context.getSourceCode()
return {
CallExpression(node) {
const isUseQuery =
node.callee.type === 'Identifier' &&
QUERY_CALLS.includes(node.callee.name) &&
helpers.isReactQueryImport(node.callee)
if (!isUseQuery) {
return
}
let firstArgument = node.arguments[0]
if (!firstArgument) {
return
}
const reference = context
.getScope()
.references.find((ref) => ref.identifier === firstArgument)
if (
reference?.resolved?.defs[0]?.node.type === 'VariableDeclarator' &&
reference.resolved.defs[0].node.init?.type === 'ObjectExpression'
) {
firstArgument = reference.resolved.defs[0].node.init
}
const hasFirstObjectArgument = firstArgument.type === 'ObjectExpression'
if (hasFirstObjectArgument) {
return
}
const secondArgument = node.arguments[1]
const thirdArgument = node.arguments[2]
const optionsObject =
secondArgument?.type === 'ObjectExpression'
? secondArgument
: thirdArgument?.type === 'ObjectExpression'
? thirdArgument
: undefined
if (
secondArgument &&
!thirdArgument &&
secondArgument !== optionsObject &&
secondArgument.type === 'Identifier'
) {
// Unable to determine if the secondArgument identifier is the options object or query fn.
// User has to fix the code manually.
context.report({ node, messageId: 'preferObjectSyntax' })
return
}
context.report({
node,
messageId: 'preferObjectSyntax',
fix(fixer) {
const ruleFixes: TSESLint.RuleFix[] = []
const optionsObjectProperties: string[] = []
// queryKey
const queryKey = sourceCode.getText(firstArgument)
const queryKeyProperty =
queryKey === 'queryKey' ? 'queryKey' : `queryKey: ${queryKey}`
optionsObjectProperties.push(queryKeyProperty)
// queryFn
if (secondArgument && secondArgument !== optionsObject) {
const queryFn = sourceCode.getText(secondArgument)
const queryFnProperty =
queryFn === 'queryFn' ? 'queryFn' : `queryFn: ${queryFn}`
optionsObjectProperties.push(queryFnProperty)
}
// options
if (optionsObject) {
const existingObjectProperties = optionsObject.properties.map(
(objectLiteral) => {
return sourceCode.getText(objectLiteral)
},
)
optionsObjectProperties.push(...existingObjectProperties)
}
const argumentsRange = ASTUtils.getRangeOfArguments(node)
if (argumentsRange) {
ruleFixes.push(fixer.removeRange(argumentsRange))
}
ruleFixes.push(
fixer.insertTextAfterRange(
[node.range[0], node.range[1] - 1],
`{ ${optionsObjectProperties.join(', ')} }`,
),
)
return ruleFixes
},
})
},
}
},
})