1
1
import type { TSESTree } from "@typescript-eslint/utils" ;
2
2
import { CONVEX_REGISTRARS , createRule } from "../util.js" ;
3
3
4
+ /**
5
+ * Helper function to check if an object expression has an args property
6
+ */
7
+ function hasArgsProperty ( objectExpr : TSESTree . ObjectExpression ) : boolean {
8
+ return objectExpr . properties . some (
9
+ ( prop ) =>
10
+ prop . type === "Property" &&
11
+ prop . key . type === "Identifier" &&
12
+ prop . key . name === "args" ,
13
+ ) ;
14
+ }
15
+
16
+ /**
17
+ * Helper function to check if a handler function has a second parameter (args parameter)
18
+ */
19
+ function handlerHasArgsParameter ( handler : TSESTree . Property ) : boolean {
20
+ if (
21
+ handler . value . type === "ArrowFunctionExpression" ||
22
+ handler . value . type === "FunctionExpression"
23
+ ) {
24
+ return handler . value . params . length >= 2 ;
25
+ }
26
+ return false ;
27
+ }
28
+
29
+ /**
30
+ * Helper function to get the handler property from an object expression
31
+ */
32
+ function getHandlerProperty (
33
+ objectExpr : TSESTree . ObjectExpression ,
34
+ ) : TSESTree . Property | undefined {
35
+ return objectExpr . properties . find (
36
+ ( prop ) =>
37
+ prop . type === "Property" &&
38
+ prop . key . type === "Identifier" &&
39
+ prop . key . name === "handler" ,
40
+ ) as TSESTree . Property | undefined ;
41
+ }
42
+
43
+ /**
44
+ * Helper function to create a fix for missing args property
45
+ */
46
+ function createArgsFix (
47
+ context : any ,
48
+ objectArg : TSESTree . ObjectExpression ,
49
+ ) : ( ( fixer : any ) => any ) | undefined {
50
+ return ( fixer ) => {
51
+ const sourceCode = context . getSourceCode ( ) ;
52
+ const objectText = sourceCode . getText ( objectArg ) ;
53
+ const firstBracePos = objectText . indexOf ( "{" ) ;
54
+
55
+ if ( firstBracePos === - 1 ) return null ;
56
+
57
+ const insertPos = objectArg . range [ 0 ] + firstBracePos + 1 ;
58
+ return fixer . insertTextAfterRange (
59
+ [ insertPos , insertPos ] ,
60
+ "\n args: {},\n" ,
61
+ ) ;
62
+ } ;
63
+ }
64
+
4
65
/**
5
66
* Rule to enforce that every registered Convex function has an args property
6
67
*/
@@ -17,61 +78,19 @@ export const noMissingArgs = createRule({
17
78
"Convex function is missing args validator but has parameter. Add appropriate args validator." ,
18
79
} ,
19
80
schema : [ ] ,
20
- fixable : "code" , // Now fixable when handler doesn't have second parameter
81
+ fixable : "code" ,
21
82
hasSuggestions : true ,
22
83
} ,
23
84
defaultOptions : [ ] ,
24
85
create : ( context ) => {
25
- // Skip generated files
26
86
const filename = context . getFilename ( ) ;
27
87
const isGenerated = filename . includes ( "_generated" ) ;
28
88
if ( isGenerated ) {
29
89
return { } ;
30
90
}
31
91
32
- /**
33
- * Checks if an object expression has an args property
34
- */
35
- function hasArgsProperty ( objectExpr : TSESTree . ObjectExpression ) : boolean {
36
- return objectExpr . properties . some (
37
- ( prop ) =>
38
- prop . type === "Property" &&
39
- prop . key . type === "Identifier" &&
40
- prop . key . name === "args" ,
41
- ) ;
42
- }
43
-
44
- /**
45
- * Checks if a handler function has a second parameter (args parameter)
46
- */
47
- function handlerHasArgsParameter ( handler : TSESTree . Property ) : boolean {
48
- if (
49
- handler . value . type === "ArrowFunctionExpression" ||
50
- handler . value . type === "FunctionExpression"
51
- ) {
52
- return handler . value . params . length >= 2 ;
53
- }
54
- return false ;
55
- }
56
-
57
- /**
58
- * Gets the handler property from an object expression
59
- */
60
- function getHandlerProperty (
61
- objectExpr : TSESTree . ObjectExpression ,
62
- ) : TSESTree . Property | undefined {
63
- return objectExpr . properties . find (
64
- ( prop ) =>
65
- prop . type === "Property" &&
66
- prop . key . type === "Identifier" &&
67
- prop . key . name === "handler" ,
68
- ) as TSESTree . Property | undefined ;
69
- }
70
-
71
92
return {
72
- // Check variable declarations for exports that use object syntax
73
93
VariableDeclarator ( node ) {
74
- // Only interested in export declarations
75
94
const parentDecl = node . parent ;
76
95
if ( ! parentDecl ) return ;
77
96
@@ -83,7 +102,6 @@ export const noMissingArgs = createRule({
83
102
return ;
84
103
}
85
104
86
- // Check if it's a call to a registrar with an object argument
87
105
if (
88
106
node . init ?. type === "CallExpression" &&
89
107
node . init . callee . type === "Identifier" &&
@@ -93,38 +111,17 @@ export const noMissingArgs = createRule({
93
111
) {
94
112
const objectArg = node . init . arguments [ 0 ] as TSESTree . ObjectExpression ;
95
113
96
- // Check if the object has an args property
97
114
if ( ! hasArgsProperty ( objectArg ) ) {
98
115
const handlerProp = getHandlerProperty ( objectArg ) ;
99
-
100
- // Determine if the handler has a second parameter
101
116
const handlerHasArgs =
102
117
handlerProp && handlerHasArgsParameter ( handlerProp ) ;
103
118
104
119
context . report ( {
105
120
node : objectArg ,
106
121
messageId : handlerHasArgs ? "missing-args" : "missing-empty-args" ,
107
- // Only provide a fix if the handler doesn't have a second parameter
108
122
fix : handlerHasArgs
109
123
? undefined
110
- : ( fixer ) => {
111
- // Find the position to insert args property
112
- const sourceCode = context . getSourceCode ( ) ;
113
- const objectText = sourceCode . getText ( objectArg ) ;
114
- const firstBracePos = objectText . indexOf ( "{" ) ;
115
-
116
- // Make sure we found the opening brace
117
- if ( firstBracePos === - 1 ) return null ;
118
-
119
- // Insert args property after the opening brace
120
- const insertPos = objectArg . range [ 0 ] + firstBracePos + 1 ;
121
-
122
- // Add args: {} at the beginning of the object
123
- return fixer . insertTextAfterRange (
124
- [ insertPos , insertPos ] ,
125
- "\n args: {},\n" ,
126
- ) ;
127
- } ,
124
+ : createArgsFix ( context , objectArg ) ,
128
125
} ) ;
129
126
}
130
127
}
@@ -133,4 +130,67 @@ export const noMissingArgs = createRule({
133
130
} ,
134
131
} ) ;
135
132
136
- export default noMissingArgs ;
133
+ /**
134
+ * Rule to enforce that Convex functions with args parameters have args validators
135
+ */
136
+ export const noArgsWithoutValidator = createRule ( {
137
+ name : "no-args-without-validator" ,
138
+ meta : {
139
+ type : "suggestion" ,
140
+ docs : {
141
+ description :
142
+ "Convex functions with args parameters should validate their arguments." ,
143
+ } ,
144
+ messages : {
145
+ "missing-args" :
146
+ "Convex function is missing args validator but has parameter. Add appropriate args validator." ,
147
+ } ,
148
+ schema : [ ] ,
149
+ fixable : "code" ,
150
+ hasSuggestions : true ,
151
+ } ,
152
+ defaultOptions : [ ] ,
153
+ create : ( context ) => {
154
+ const filename = context . getFilename ( ) ;
155
+ const isGenerated = filename . includes ( "_generated" ) ;
156
+ if ( isGenerated ) {
157
+ return { } ;
158
+ }
159
+
160
+ return {
161
+ VariableDeclarator ( node ) {
162
+ const parentDecl = node . parent ;
163
+ if ( ! parentDecl ) return ;
164
+
165
+ const exportDecl = parentDecl . parent ;
166
+ if (
167
+ exportDecl ?. type !== "ExportNamedDeclaration" &&
168
+ parentDecl . parent ?. parent ?. type !== "ExportNamedDeclaration"
169
+ ) {
170
+ return ;
171
+ }
172
+
173
+ if (
174
+ node . init ?. type === "CallExpression" &&
175
+ node . init . callee . type === "Identifier" &&
176
+ CONVEX_REGISTRARS . includes ( node . init . callee . name ) &&
177
+ node . init . arguments . length === 1 &&
178
+ node . init . arguments [ 0 ] . type === "ObjectExpression"
179
+ ) {
180
+ const objectArg = node . init . arguments [ 0 ] as TSESTree . ObjectExpression ;
181
+ const handlerProp = getHandlerProperty ( objectArg ) ;
182
+ const handlerHasArgs =
183
+ handlerProp && handlerHasArgsParameter ( handlerProp ) ;
184
+
185
+ if ( handlerHasArgs && ! hasArgsProperty ( objectArg ) ) {
186
+ context . report ( {
187
+ node : objectArg ,
188
+ messageId : "missing-args" ,
189
+ fix : createArgsFix ( context , objectArg ) ,
190
+ } ) ;
191
+ }
192
+ }
193
+ } ,
194
+ } ;
195
+ } ,
196
+ } ) ;
0 commit comments