1- import { Kind } from 'graphql' ;
1+ import { VariableDefinitionNode , ArgumentNode , FieldNode , Kind } from 'graphql' ;
22import { GraphQLESLintRule } from '../types' ;
3+ import { GraphQLESTreeNode } from '../estree-parser' ;
4+ import { getLocation } from '../utils' ;
35
46const AVOID_DUPLICATE_FIELDS = 'AVOID_DUPLICATE_FIELDS' ;
57
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 = {
219 meta : {
2210 type : 'suggestion' ,
2311 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.` ,
2613 category : 'Stylistic Issues' ,
2714 url : 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/avoid-duplicate-fields.md' ,
2815 examples : [
2916 {
3017 title : 'Incorrect' ,
3118 code : /* GraphQL */ `
32- query getUserDetails {
19+ query {
3320 user {
34- name # first
21+ name
3522 email
36- name # second
23+ name # duplicate field
3724 }
3825 }
3926 ` ,
4027 } ,
4128 {
4229 title : 'Incorrect' ,
4330 code : /* GraphQL */ `
44- query getUsers {
31+ query {
4532 users(
4633 first: 100
4734 skip: 50
@@ -56,9 +43,11 @@ const rule: GraphQLESLintRule<[], false> = {
5643 {
5744 title : 'Incorrect' ,
5845 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) {
6251 id
6352 }
6453 }
@@ -67,61 +56,51 @@ const rule: GraphQLESLintRule<[], false> = {
6756 ] ,
6857 } ,
6958 messages : {
70- [ AVOID_DUPLICATE_FIELDS ] : `{{ type }} "{{ fieldName }}" defined multiple times. ` ,
59+ [ AVOID_DUPLICATE_FIELDS ] : `{{ type }} "{{ fieldName }}" defined multiple times` ,
7160 } ,
7261 schema : [ ] ,
7362 } ,
7463 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+
7586 return {
7687 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 ) ;
9091 }
9192 } ,
9293 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 ) ;
10697 }
10798 } ,
10899 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 ) {
112102 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 ) ;
125104 }
126105 }
127106 } ,
0 commit comments