1
- import { Kind } from 'graphql' ;
1
+ import { VariableDefinitionNode , ArgumentNode , FieldNode , Kind } from 'graphql' ;
2
2
import { GraphQLESLintRule } from '../types' ;
3
+ import { GraphQLESTreeNode } from '../estree-parser' ;
4
+ import { getLocation } from '../utils' ;
3
5
4
6
const AVOID_DUPLICATE_FIELDS = 'AVOID_DUPLICATE_FIELDS' ;
5
7
6
- const ensureUnique = ( ) => {
7
- const set = new Set < string > ( ) ;
8
-
9
- return {
10
- add : ( item : string , onError : ( ) => void ) => {
11
- if ( set . has ( item ) ) {
12
- onError ( ) ;
13
- } else {
14
- set . add ( item ) ;
15
- }
16
- } ,
17
- } ;
18
- } ;
19
-
20
- const rule : GraphQLESLintRule < [ ] , false > = {
8
+ const rule : GraphQLESLintRule = {
21
9
meta : {
22
10
type : 'suggestion' ,
23
11
docs : {
24
- description :
25
- 'Checks for duplicate fields in selection set, variables in operation definition, or in arguments set of a field.' ,
12
+ description : `Checks for duplicate fields in selection set, variables in operation definition, or in arguments set of a field.` ,
26
13
category : 'Stylistic Issues' ,
27
14
url : 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/avoid-duplicate-fields.md' ,
28
15
examples : [
29
16
{
30
17
title : 'Incorrect' ,
31
18
code : /* GraphQL */ `
32
- query getUserDetails {
19
+ query {
33
20
user {
34
- name # first
21
+ name
35
22
email
36
- name # second
23
+ name # duplicate field
37
24
}
38
25
}
39
26
` ,
40
27
} ,
41
28
{
42
29
title : 'Incorrect' ,
43
30
code : /* GraphQL */ `
44
- query getUsers {
31
+ query {
45
32
users(
46
33
first: 100
47
34
skip: 50
@@ -56,9 +43,11 @@ const rule: GraphQLESLintRule<[], false> = {
56
43
{
57
44
title : 'Incorrect' ,
58
45
code : /* GraphQL */ `
59
- query getUsers($first: Int!, $first: Int!) {
60
- # Duplicate variable
61
- users(first: 100, skip: 50, after: "cji629tngfgou0b73kt7vi5jo") {
46
+ query (
47
+ $first: Int!
48
+ $first: Int! # duplicate variable
49
+ ) {
50
+ users(first: $first, skip: 50) {
62
51
id
63
52
}
64
53
}
@@ -67,61 +56,51 @@ const rule: GraphQLESLintRule<[], false> = {
67
56
] ,
68
57
} ,
69
58
messages : {
70
- [ AVOID_DUPLICATE_FIELDS ] : `{{ type }} "{{ fieldName }}" defined multiple times. ` ,
59
+ [ AVOID_DUPLICATE_FIELDS ] : `{{ type }} "{{ fieldName }}" defined multiple times` ,
71
60
} ,
72
61
schema : [ ] ,
73
62
} ,
74
63
create ( context ) {
64
+ function checkNode (
65
+ usedFields : Set < string > ,
66
+ fieldName : string ,
67
+ type : string ,
68
+ node : GraphQLESTreeNode < VariableDefinitionNode | ArgumentNode | FieldNode >
69
+ ) : void {
70
+ if ( usedFields . has ( fieldName ) ) {
71
+ context . report ( {
72
+ loc : getLocation ( ( node . kind === Kind . FIELD && node . alias ? node . alias : node ) . loc , fieldName , {
73
+ offsetEnd : node . kind === Kind . VARIABLE_DEFINITION ? 0 : 1 ,
74
+ } ) ,
75
+ messageId : AVOID_DUPLICATE_FIELDS ,
76
+ data : {
77
+ type,
78
+ fieldName,
79
+ } ,
80
+ } ) ;
81
+ } else {
82
+ usedFields . add ( fieldName ) ;
83
+ }
84
+ }
85
+
75
86
return {
76
87
OperationDefinition ( node ) {
77
- const uniqueCheck = ensureUnique ( ) ;
78
-
79
- for ( const arg of node . variableDefinitions || [ ] ) {
80
- uniqueCheck . add ( arg . variable . name . value , ( ) => {
81
- context . report ( {
82
- messageId : AVOID_DUPLICATE_FIELDS ,
83
- data : {
84
- type : 'Operation variable' ,
85
- fieldName : arg . variable . name . value ,
86
- } ,
87
- node : arg ,
88
- } ) ;
89
- } ) ;
88
+ const set = new Set < string > ( ) ;
89
+ for ( const varDef of node . variableDefinitions ) {
90
+ checkNode ( set , varDef . variable . name . value , 'Operation variable' , varDef ) ;
90
91
}
91
92
} ,
92
93
Field ( node ) {
93
- const uniqueCheck = ensureUnique ( ) ;
94
-
95
- for ( const arg of node . arguments || [ ] ) {
96
- uniqueCheck . add ( arg . name . value , ( ) => {
97
- context . report ( {
98
- messageId : AVOID_DUPLICATE_FIELDS ,
99
- data : {
100
- type : 'Field argument' ,
101
- fieldName : arg . name . value ,
102
- } ,
103
- node : arg ,
104
- } ) ;
105
- } ) ;
94
+ const set = new Set < string > ( ) ;
95
+ for ( const arg of node . arguments ) {
96
+ checkNode ( set , arg . name . value , 'Field argument' , arg ) ;
106
97
}
107
98
} ,
108
99
SelectionSet ( node ) {
109
- const uniqueCheck = ensureUnique ( ) ;
110
-
111
- for ( const selection of node . selections || [ ] ) {
100
+ const set = new Set < string > ( ) ;
101
+ for ( const selection of node . selections ) {
112
102
if ( selection . kind === Kind . FIELD ) {
113
- const nameToCheck = selection . alias ?. value || selection . name . value ;
114
-
115
- uniqueCheck . add ( nameToCheck , ( ) => {
116
- context . report ( {
117
- messageId : AVOID_DUPLICATE_FIELDS ,
118
- data : {
119
- type : 'Field' ,
120
- fieldName : nameToCheck ,
121
- } ,
122
- node : selection ,
123
- } ) ;
124
- } ) ;
103
+ checkNode ( set , selection . alias ?. value || selection . name . value , 'Field' , selection ) ;
125
104
}
126
105
}
127
106
} ,
0 commit comments