Skip to content

Commit cf3cc4f

Browse files
author
Dimitri POSTOLOV
authored
fix error report for require-deprecation-reason and require-field-of-type-query-in-mutation-result rule (#746)
1 parent a285de3 commit cf3cc4f

8 files changed

+76
-92
lines changed

.changeset/curly-rocks-brush.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@graphql-eslint/eslint-plugin': patch
3+
---
4+
5+
fix error report for `require-deprecation-reason` and `require-field-of-type-query-in-mutation-result` rule

docs/rules/avoid-duplicate-fields.md

+9-7
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ Checks for duplicate fields in selection set, variables in operation definition,
1414
```graphql
1515
# eslint @graphql-eslint/avoid-duplicate-fields: 'error'
1616

17-
query getUserDetails {
17+
query {
1818
user {
19-
name # first
19+
name
2020
email
21-
name # second
21+
name # duplicate field
2222
}
2323
}
2424
```
@@ -28,7 +28,7 @@ query getUserDetails {
2828
```graphql
2929
# eslint @graphql-eslint/avoid-duplicate-fields: 'error'
3030

31-
query getUsers {
31+
query {
3232
users(
3333
first: 100
3434
skip: 50
@@ -45,9 +45,11 @@ query getUsers {
4545
```graphql
4646
# eslint @graphql-eslint/avoid-duplicate-fields: 'error'
4747

48-
query getUsers($first: Int!, $first: Int!) {
49-
# Duplicate variable
50-
users(first: 100, skip: 50, after: "cji629tngfgou0b73kt7vi5jo") {
48+
query (
49+
$first: Int!
50+
$first: Int! # duplicate variable
51+
) {
52+
users(first: $first, skip: 50) {
5153
id
5254
}
5355
}

packages/plugin/src/rules/avoid-duplicate-fields.ts

+46-67
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,34 @@
1-
import { Kind } from 'graphql';
1+
import { VariableDefinitionNode, ArgumentNode, FieldNode, Kind } from 'graphql';
22
import { GraphQLESLintRule } from '../types';
3+
import { GraphQLESTreeNode } from '../estree-parser';
4+
import { getLocation } from '../utils';
35

46
const 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
},

packages/plugin/src/rules/no-hashtag-description.ts

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { TokenKind } from 'graphql';
22
import { GraphQLESLintRule } from '../types';
3+
import { getLocation } from '../utils';
34

45
const HASHTAG_COMMENT = 'HASHTAG_COMMENT';
56

@@ -69,10 +70,7 @@ const rule: GraphQLESLintRule = {
6970
if (!isEslintComment && line !== prev.line && next.kind === TokenKind.NAME && linesAfter < 2) {
7071
context.report({
7172
messageId: HASHTAG_COMMENT,
72-
loc: {
73-
start: { line, column },
74-
end: { line, column },
75-
},
73+
loc: getLocation({ start: { line, column } }),
7674
});
7775
}
7876
}

packages/plugin/tests/__snapshots__/avoid-duplicate-fields.spec.ts.snap

+4-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
exports[` 1`] = `
44
1 |
55
> 2 | query test($v: String, $t: String, $v: String) {
6-
| ^^^^ Operation variable "v" defined multiple times.
6+
| ^^ Operation variable "v" defined multiple times
77
3 | id
88
4 | }
99
5 |
@@ -13,7 +13,7 @@ exports[` 2`] = `
1313
1 |
1414
2 | query test {
1515
> 3 | users(first: 100, after: 10, filter: "test", first: 50) {
16-
| ^^^^^^^ Field argument "first" defined multiple times.
16+
| ^^^^^ Field argument "first" defined multiple times
1717
4 | id
1818
5 | }
1919
6 | }
@@ -28,7 +28,7 @@ exports[` 3`] = `
2828
5 | name
2929
6 | email
3030
> 7 | name
31-
| ^ Field "name" defined multiple times.
31+
| ^^^^ Field "name" defined multiple times
3232
8 | }
3333
9 | }
3434
10 |
@@ -42,7 +42,7 @@ exports[` 4`] = `
4242
5 | name
4343
6 | email
4444
> 7 | email: somethingElse
45-
| ^^^^^^^ Field "email" defined multiple times.
45+
| ^^^^^ Field "email" defined multiple times
4646
8 | }
4747
9 | }
4848
10 |

packages/plugin/tests/__snapshots__/examples.spec.ts.snap

+1-1
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ Array [
191191
filePath: examples/graphql-config/operations/user.fragment.graphql,
192192
messages: Array [
193193
Object {
194-
message: Field "name" defined multiple times.,
194+
message: Field "name" defined multiple times,
195195
ruleId: @graphql-eslint/avoid-duplicate-fields,
196196
},
197197
],

packages/plugin/tests/__snapshots__/no-hashtag-description.spec.ts.snap

+5-5
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
exports[` 1`] = `
44
1 |
55
> 2 | # Bad
6-
| ^ Using hashtag (#) for adding GraphQL descriptions is not allowed. Prefer using """ for multiline, or " for a single line description.
6+
| ^ Using hashtag (#) for adding GraphQL descriptions is not allowed. Prefer using """ for multiline, or " for a single line description.
77
3 | type Query {
88
4 | foo: String
99
5 | }
@@ -14,7 +14,7 @@ exports[` 2`] = `
1414
1 |
1515
2 | # multiline
1616
> 3 | # multiline
17-
| ^ Using hashtag (#) for adding GraphQL descriptions is not allowed. Prefer using """ for multiline, or " for a single line description.
17+
| ^ Using hashtag (#) for adding GraphQL descriptions is not allowed. Prefer using """ for multiline, or " for a single line description.
1818
4 | type Query {
1919
5 | foo: String
2020
6 | }
@@ -25,7 +25,7 @@ exports[` 3`] = `
2525
1 |
2626
2 | type Query {
2727
> 3 | # Bad
28-
| ^ Using hashtag (#) for adding GraphQL descriptions is not allowed. Prefer using """ for multiline, or " for a single line description.
28+
| ^ Using hashtag (#) for adding GraphQL descriptions is not allowed. Prefer using """ for multiline, or " for a single line description.
2929
4 | foo: String
3030
5 | }
3131
6 |
@@ -36,7 +36,7 @@ exports[` 4`] = `
3636
2 | type Query {
3737
3 | bar: ID
3838
> 4 | # Bad
39-
| ^ Using hashtag (#) for adding GraphQL descriptions is not allowed. Prefer using """ for multiline, or " for a single line description.
39+
| ^ Using hashtag (#) for adding GraphQL descriptions is not allowed. Prefer using """ for multiline, or " for a single line description.
4040
5 | foo: ID
4141
6 | # Good
4242
7 | }
@@ -48,7 +48,7 @@ exports[` 5`] = `
4848
2 | type Query {
4949
3 | user(
5050
> 4 | # Bad
51-
| ^ Using hashtag (#) for adding GraphQL descriptions is not allowed. Prefer using """ for multiline, or " for a single line description.
51+
| ^ Using hashtag (#) for adding GraphQL descriptions is not allowed. Prefer using """ for multiline, or " for a single line description.
5252
5 | id: Int!
5353
6 | ): User
5454
7 | }

packages/plugin/tests/avoid-duplicate-fields.spec.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ ruleTester.runGraphQLTests('avoid-duplicate-fields', rule, {
1212
id
1313
}
1414
`,
15-
errors: [{ message: 'Operation variable "v" defined multiple times.' }],
15+
errors: [{ message: 'Operation variable "v" defined multiple times' }],
1616
},
1717
{
1818
code: /* GraphQL */ `
@@ -22,7 +22,7 @@ ruleTester.runGraphQLTests('avoid-duplicate-fields', rule, {
2222
}
2323
}
2424
`,
25-
errors: [{ message: 'Field argument "first" defined multiple times.' }],
25+
errors: [{ message: 'Field argument "first" defined multiple times' }],
2626
},
2727
{
2828
code: /* GraphQL */ `
@@ -35,7 +35,7 @@ ruleTester.runGraphQLTests('avoid-duplicate-fields', rule, {
3535
}
3636
}
3737
`,
38-
errors: [{ message: 'Field "name" defined multiple times.' }],
38+
errors: [{ message: 'Field "name" defined multiple times' }],
3939
},
4040
{
4141
code: /* GraphQL */ `
@@ -48,7 +48,7 @@ ruleTester.runGraphQLTests('avoid-duplicate-fields', rule, {
4848
}
4949
}
5050
`,
51-
errors: [{ message: 'Field "email" defined multiple times.' }],
51+
errors: [{ message: 'Field "email" defined multiple times' }],
5252
},
5353
],
5454
});

0 commit comments

Comments
 (0)