-
-
Notifications
You must be signed in to change notification settings - Fork 100
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
NEW RULE: avoid-duplicate-fields (#245)
* NEW RULE: avoid-duplicate-fields * added more tests
- Loading branch information
1 parent
4942b58
commit 5e1bbe6
Showing
6 changed files
with
251 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@graphql-eslint/eslint-plugin': minor | ||
--- | ||
|
||
NEW RULE: avoid-duplicate-fields |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
# `avoid-duplicate-fields` | ||
|
||
- Category: `Stylistic Issues` | ||
- Rule name: `@graphql-eslint/avoid-duplicate-fields` | ||
- Requires GraphQL Schema: `false` [ℹ️](../../README.md#extended-linting-rules-with-graphql-schema) | ||
- Requires GraphQL Operations: `false` [ℹ️](../../README.md#extended-linting-rules-with-siblings-operations) | ||
|
||
Checks for duplicate fields in selection set, variables in operation definition, or in arguments set of a field. | ||
|
||
## Usage Examples | ||
|
||
### Incorrect | ||
|
||
```graphql | ||
# eslint @graphql-eslint/avoid-duplicate-fields: ["error"] | ||
|
||
query getUserDetails { | ||
user { | ||
name # first | ||
name # second | ||
} | ||
} | ||
``` | ||
|
||
### Incorrect | ||
|
||
```graphql | ||
# eslint @graphql-eslint/avoid-duplicate-fields: ["error"] | ||
|
||
query getUsers { | ||
users( | ||
first: 100 | ||
skip: 50 | ||
after: "cji629tngfgou0b73kt7vi5jo" | ||
first: 100 # duplicate argument | ||
) { | ||
id | ||
} | ||
} | ||
``` | ||
|
||
### Incorrect | ||
|
||
```graphql | ||
# eslint @graphql-eslint/avoid-duplicate-fields: ["error"] | ||
|
||
query getUsers($first: Int!, $first: Int!) { | ||
# Duplicate variable | ||
users(first: 100, skip: 50, after: "cji629tngfgou0b73kt7vi5jo") { | ||
id | ||
} | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
import { Kind } from 'graphql'; | ||
import { GraphQLESLintRule } from '../types'; | ||
|
||
const AVOID_DUPLICATE_FIELDS = 'AVOID_DUPLICATE_FIELDS'; | ||
|
||
const ensureUnique = () => { | ||
const set = new Set<string>(); | ||
|
||
return { | ||
add: (item: string, onError: () => void) => { | ||
if (set.has(item)) { | ||
onError(); | ||
} else { | ||
set.add(item); | ||
} | ||
}, | ||
}; | ||
}; | ||
|
||
const rule: GraphQLESLintRule<[], false> = { | ||
meta: { | ||
type: 'suggestion', | ||
docs: { | ||
requiresSchema: false, | ||
requiresSiblings: false, | ||
description: | ||
'Checks for duplicate fields in selection set, variables in operation definition, or in arguments set of a field.', | ||
category: 'Stylistic Issues', | ||
url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/avoid-duplicate-fields.md', | ||
examples: [ | ||
{ | ||
title: 'Incorrect', | ||
code: /* GraphQL */ ` | ||
query getUserDetails { | ||
user { | ||
name # first | ||
name # second | ||
} | ||
} | ||
`, | ||
}, | ||
{ | ||
title: 'Incorrect', | ||
code: /* GraphQL */ ` | ||
query getUsers { | ||
users( | ||
first: 100 | ||
skip: 50 | ||
after: "cji629tngfgou0b73kt7vi5jo" | ||
first: 100 # duplicate argument | ||
) { | ||
id | ||
} | ||
} | ||
`, | ||
}, | ||
{ | ||
title: 'Incorrect', | ||
code: /* GraphQL */ ` | ||
query getUsers($first: Int!, $first: Int!) { | ||
# Duplicate variable | ||
users(first: 100, skip: 50, after: "cji629tngfgou0b73kt7vi5jo") { | ||
id | ||
} | ||
} | ||
`, | ||
}, | ||
], | ||
}, | ||
messages: { | ||
[AVOID_DUPLICATE_FIELDS]: `{{ type }} "{{ fieldName }}" defined multiple times.`, | ||
}, | ||
}, | ||
create(context) { | ||
return { | ||
OperationDefinition(node) { | ||
const uniqueCheck = ensureUnique(); | ||
|
||
for (const arg of node.variableDefinitions || []) { | ||
uniqueCheck.add(arg.variable.name.value, () => { | ||
context.report({ | ||
messageId: AVOID_DUPLICATE_FIELDS, | ||
data: { | ||
type: 'Operation variable', | ||
fieldName: arg.variable.name.value, | ||
}, | ||
node: arg, | ||
}); | ||
}); | ||
} | ||
}, | ||
Field(node) { | ||
const uniqueCheck = ensureUnique(); | ||
|
||
for (const arg of node.arguments || []) { | ||
uniqueCheck.add(arg.name.value, () => { | ||
context.report({ | ||
messageId: AVOID_DUPLICATE_FIELDS, | ||
data: { | ||
type: 'Field argument', | ||
fieldName: arg.name.value, | ||
}, | ||
node: arg, | ||
}); | ||
}); | ||
} | ||
}, | ||
SelectionSet(node) { | ||
const uniqueCheck = ensureUnique(); | ||
|
||
for (const selection of node.selections || []) { | ||
if (selection.kind === Kind.FIELD) { | ||
const nameToCheck = selection.alias?.value || selection.name.value; | ||
|
||
uniqueCheck.add(nameToCheck, () => { | ||
context.report({ | ||
messageId: AVOID_DUPLICATE_FIELDS, | ||
data: { | ||
type: 'Field', | ||
fieldName: nameToCheck, | ||
}, | ||
node: selection, | ||
}); | ||
}); | ||
} | ||
} | ||
}, | ||
}; | ||
}, | ||
}; | ||
|
||
export default rule; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import { GraphQLRuleTester } from '../src/testkit'; | ||
import rule from '../src/rules/avoid-duplicate-fields'; | ||
|
||
const ruleTester = new GraphQLRuleTester(); | ||
|
||
ruleTester.runGraphQLTests('avoid-duplicate-fields', rule, { | ||
valid: [], | ||
invalid: [ | ||
{ | ||
code: /* GraphQL */ ` | ||
query test($v: String, $t: String, $v: String) { | ||
id | ||
} | ||
`, | ||
errors: [{ message: 'Operation variable "v" defined multiple times.' }], | ||
}, | ||
{ | ||
code: /* GraphQL */ ` | ||
query test { | ||
users(first: 100, after: 10, filter: "test", first: 50) { | ||
id | ||
} | ||
} | ||
`, | ||
errors: [{ message: 'Field argument "first" defined multiple times.' }], | ||
}, | ||
{ | ||
code: /* GraphQL */ ` | ||
query test { | ||
users { | ||
id | ||
name | ||
name | ||
} | ||
} | ||
`, | ||
errors: [{ message: 'Field "name" defined multiple times.' }], | ||
}, | ||
{ | ||
code: /* GraphQL */ ` | ||
query test { | ||
users { | ||
id | ||
name | ||
email: somethingElse | ||
} | ||
} | ||
`, | ||
errors: [{ message: 'Field "email" defined multiple times.' }], | ||
}, | ||
], | ||
}); |