7
7
8
8
'use strict' ;
9
9
10
- const utils = require ( 'eslint-plugin-relay/src /utils' ) ;
10
+ const utils = require ( '. /utils' ) ;
11
11
const shouldLint = utils . shouldLint ;
12
12
const getGraphQLAST = utils . getGraphQLAST ;
13
13
@@ -90,7 +90,7 @@ function getPropTypeProperty(
90
90
}
91
91
visitedProps . add ( propType ) ;
92
92
const spreadsToVisit = [ ] ;
93
- if ( propType . type === 'GenericTypeAnnotation ' ) {
93
+ if ( propType . type === 'TSTypeReference ' ) {
94
94
return getPropTypeProperty (
95
95
context ,
96
96
typeAliasMap ,
@@ -99,28 +99,14 @@ function getPropTypeProperty(
99
99
visitedProps
100
100
) ;
101
101
}
102
- if ( propType . type !== 'ObjectTypeAnnotation ' ) {
102
+ if ( propType . type !== 'TSTypeLiteral ' ) {
103
103
return null ;
104
104
}
105
- for ( const property of propType . properties ) {
105
+ for ( const property of propType . members ) {
106
106
if ( property . type === 'ObjectTypeSpreadProperty' ) {
107
107
spreadsToVisit . push ( property ) ;
108
108
} else {
109
- // HACK: Type annotations don't currently expose a 'key' property:
110
- // https://github.com/babel/babel-eslint/issues/307
111
-
112
- let tokenIndex = 0 ;
113
- if ( property . static ) {
114
- tokenIndex ++ ;
115
- }
116
- if ( property . variance ) {
117
- tokenIndex ++ ;
118
- }
119
-
120
- if (
121
- context . getSourceCode ( ) . getFirstToken ( property , tokenIndex ) . value ===
122
- propName
123
- ) {
109
+ if ( property . key . name === propName ) {
124
110
return property ;
125
111
}
126
112
}
@@ -165,7 +151,7 @@ function validateObjectTypeAnnotation(
165
151
propName
166
152
) ;
167
153
168
- const atleastOnePropertyExists = ! ! propType . properties [ 0 ] ;
154
+ const atleastOnePropertyExists = ! ! propType . members [ 0 ] ;
169
155
170
156
if ( ! propTypeProperty ) {
171
157
if ( onlyVerify ) {
@@ -195,7 +181,7 @@ function validateObjectTypeAnnotation(
195
181
if ( atleastOnePropertyExists ) {
196
182
fixes . push (
197
183
fixer . insertTextBefore (
198
- propType . properties [ 0 ] ,
184
+ propType . members [ 0 ] ,
199
185
`${ propName } : ${ type } , `
200
186
)
201
187
) ;
@@ -210,14 +196,54 @@ function validateObjectTypeAnnotation(
210
196
return false ;
211
197
}
212
198
if (
213
- propTypeProperty . value . type === 'NullableTypeAnnotation' &&
214
- propTypeProperty . value . typeAnnotation . type === 'GenericTypeAnnotation' &&
215
- propTypeProperty . value . typeAnnotation . id . name === type
199
+ propTypeProperty . type === 'TSPropertySignature' &&
200
+ propTypeProperty . typeAnnotation . type === 'TSTypeAnnotation'
216
201
) {
217
- return true ;
202
+ // If we have a TSTypeAnnotation here, it must be a TSTypeReference to the generated type, otherwise we have an invalid reference here
203
+ if (
204
+ propTypeProperty . typeAnnotation . typeAnnotation . type ===
205
+ 'TSTypeReference' &&
206
+ propTypeProperty . typeAnnotation . typeAnnotation . typeName . name === type
207
+ ) {
208
+ return true ;
209
+ }
210
+
211
+ if ( onlyVerify ) {
212
+ return false ;
213
+ }
214
+
215
+ context . report ( {
216
+ message :
217
+ 'Component property `{{prop}}` expects to use the generated ' +
218
+ '`{{type}}` typescript type. See https://facebook.github.io/relay/docs/en/graphql-in-relay.html#importing-generated-definitions' ,
219
+ data : {
220
+ prop : propName ,
221
+ type
222
+ } ,
223
+ fix : options . fix
224
+ ? fixer => {
225
+ const whitespace = ' ' . repeat ( Component . parent . loc . start . column ) ;
226
+ return [
227
+ genImportFixer (
228
+ fixer ,
229
+ importFixRange ,
230
+ type ,
231
+ options . haste ,
232
+ whitespace
233
+ ) ,
234
+ fixer . replaceText (
235
+ propTypeProperty . typeAnnotation . typeAnnotation ,
236
+ type
237
+ )
238
+ ] ;
239
+ }
240
+ : null ,
241
+ loc : Component . loc
242
+ } ) ;
243
+ return false ;
218
244
}
219
245
if (
220
- propTypeProperty . value . type !== 'GenericTypeAnnotation ' ||
246
+ propTypeProperty . type !== 'TSTypeReference ' ||
221
247
propTypeProperty . value . id . name !== type
222
248
) {
223
249
if ( onlyVerify ) {
@@ -226,7 +252,7 @@ function validateObjectTypeAnnotation(
226
252
context . report ( {
227
253
message :
228
254
'Component property `{{prop}}` expects to use the generated ' +
229
- '`{{type}}` flow type. See https://facebook.github.io/relay/docs/en/graphql-in-relay.html#importing-generated-definitions' ,
255
+ '`{{type}}` typescript type. See https://facebook.github.io/relay/docs/en/graphql-in-relay.html#importing-generated-definitions' ,
230
256
data : {
231
257
prop : propName ,
232
258
type
@@ -257,7 +283,7 @@ function extractReadOnlyType(genericType) {
257
283
let currentType = genericType ;
258
284
while (
259
285
currentType != null &&
260
- currentType . type === 'GenericTypeAnnotation ' &&
286
+ currentType . type === 'TSTypeReference ' &&
261
287
currentType . id . name === '$ReadOnly' &&
262
288
currentType . typeParameters &&
263
289
currentType . typeParameters . type === 'TypeParameterInstantiation' &&
@@ -273,10 +299,10 @@ function resolveTypeAlias(genericType, typeAliasMap) {
273
299
let currentType = genericType ;
274
300
while (
275
301
currentType != null &&
276
- currentType . type === 'GenericTypeAnnotation ' &&
277
- typeAliasMap [ currentType . id . name ] != null
302
+ currentType . type === 'TSTypeReference ' &&
303
+ typeAliasMap [ currentType . typeName . name ] != null
278
304
) {
279
- currentType = typeAliasMap [ currentType . id . name ] ;
305
+ currentType = typeAliasMap [ currentType . typeName . name ] ;
280
306
}
281
307
return currentType ;
282
308
}
@@ -325,7 +351,7 @@ module.exports = {
325
351
if ( arg . type === 'Identifier' ) {
326
352
const name = arg . name ;
327
353
let scope = context . getScope ( ) ;
328
- while ( scope && scope . type != 'global' ) {
354
+ while ( scope != null ) {
329
355
for ( const variable of scope . variables ) {
330
356
if ( variable . name === name ) {
331
357
const definition = variable . defs . find (
@@ -435,8 +461,8 @@ module.exports = {
435
461
requires . push ( node ) ;
436
462
}
437
463
} ,
438
- TypeAlias ( node ) {
439
- typeAliasMap [ node . id . name ] = node . right ;
464
+ TSTypeAliasDeclaration ( node ) {
465
+ typeAliasMap [ node . id . name ] = node . typeAnnotation ;
440
466
} ,
441
467
442
468
/**
@@ -564,6 +590,23 @@ module.exports = {
564
590
} ) ;
565
591
} ,
566
592
593
+ /**
594
+ * Find useMutation() calls without type arguments.
595
+ */
596
+ 'CallExpression[callee.name=useMutation]:not([typeParameters])' ( node ) {
597
+ const queryName = getDefinitionName ( node . arguments [ 0 ] ) ;
598
+ context . report ( {
599
+ node,
600
+ message : `The \`useMutation\` hook should be used with an explicit generated Typescript type, e.g.: useMutation<{{queryName}}>(...)` ,
601
+ data : {
602
+ queryName : queryName
603
+ } ,
604
+ fix :
605
+ queryName != null && options . fix
606
+ ? createTypeImportFixer ( node , queryName , queryName )
607
+ : null
608
+ } ) ;
609
+ } ,
567
610
/**
568
611
* Find usePaginationFragment() calls without type arguments.
569
612
*/
@@ -794,7 +837,7 @@ module.exports = {
794
837
// There exists a prop typeAnnotation. Let's look at how it's
795
838
// structured
796
839
switch ( propType . type ) {
797
- case 'ObjectTypeAnnotation ' : {
840
+ case 'TSTypeLiteral ' : {
798
841
validateObjectTypeAnnotation (
799
842
context ,
800
843
Component ,
@@ -806,7 +849,7 @@ module.exports = {
806
849
) ;
807
850
break ;
808
851
}
809
- case 'GenericTypeAnnotation ' : {
852
+ case 'TSTypeReference ' : {
810
853
const aliasedObjectType = extractReadOnlyType (
811
854
resolveTypeAlias ( propType , typeAliasMap )
812
855
) ;
@@ -816,7 +859,7 @@ module.exports = {
816
859
break ;
817
860
}
818
861
switch ( aliasedObjectType . type ) {
819
- case 'ObjectTypeAnnotation ' : {
862
+ case 'TSTypeLiteral ' : {
820
863
validateObjectTypeAnnotation (
821
864
context ,
822
865
Component ,
@@ -828,23 +871,23 @@ module.exports = {
828
871
) ;
829
872
break ;
830
873
}
831
- case 'IntersectionTypeAnnotation ' : {
874
+ case 'TSIntersectionType ' : {
832
875
const objectTypes = aliasedObjectType . types
833
876
. map ( intersectedType => {
834
- if ( intersectedType . type === 'GenericTypeAnnotation ' ) {
877
+ if ( intersectedType . type === 'TSTypeReference ' ) {
835
878
return extractReadOnlyType (
836
879
resolveTypeAlias ( intersectedType , typeAliasMap )
837
880
) ;
838
881
}
839
- if ( intersectedType . type === 'ObjectTypeAnnotation ' ) {
882
+ if ( intersectedType . type === 'TSTypeLiteral ' ) {
840
883
return intersectedType ;
841
884
}
842
885
} )
843
886
. filter ( maybeObjectType => {
844
- // GenericTypeAnnotation may not map to an object type
887
+ // TSTypeReference may not map to an object type
845
888
return (
846
889
maybeObjectType &&
847
- maybeObjectType . type === 'ObjectTypeAnnotation '
890
+ maybeObjectType . type === 'TSTypeLiteral '
848
891
) ;
849
892
} ) ;
850
893
if ( ! objectTypes . length ) {
@@ -887,7 +930,7 @@ module.exports = {
887
930
context . report ( {
888
931
message :
889
932
'Component property `{{prop}}` expects to use the ' +
890
- 'generated `{{type}}` flow type. See https://facebook.github.io/relay/docs/en/graphql-in-relay.html#importing-generated-definitions' ,
933
+ 'generated `{{type}}` typescript type. See https://facebook.github.io/relay/docs/en/graphql-in-relay.html#importing-generated-definitions' ,
891
934
data : {
892
935
prop : propName ,
893
936
type : importedPropType
0 commit comments