Skip to content

Commit 57d6edf

Browse files
authored
check for deprecated arguments and object field nodes in graphql operations in no-deprecated rule (#2719)
* check for deprecated arguments and object field nodes in graphql operations in `no-deprecated` rule * check for deprecated arguments and object field nodes in graphql operations in `no-deprecated` rule * fix lint * upd snapshots
1 parent 219506a commit 57d6edf

File tree

6 files changed

+135
-41
lines changed

6 files changed

+135
-41
lines changed

.changeset/yellow-elephants-study.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@graphql-eslint/eslint-plugin': minor
3+
---
4+
5+
check for deprecated arguments and object field nodes in graphql operations in `no-deprecated` rule

packages/plugin/src/rules/no-deprecated/index.test.ts

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,17 @@ import { ParserOptionsForTests, ruleTester } from '../../../__tests__/test-utils
22
import { rule } from './index.js';
33

44
const TEST_SCHEMA = /* GraphQL */ `
5+
input TestInput {
6+
a: Int @deprecated(reason: "Use 'b' instead.")
7+
b: Boolean
8+
}
9+
510
type Query {
611
oldField: String @deprecated
712
oldFieldWithReason: String @deprecated(reason: "test")
813
newField: String!
14+
testArgument(a: Int @deprecated(reason: "Use 'b' instead."), b: Boolean): Boolean
15+
testObjectField(input: TestInput): Boolean
916
}
1017
1118
type Mutation {
@@ -29,7 +36,7 @@ const WITH_SCHEMA = {
2936

3037
ruleTester.run('no-deprecated', rule, {
3138
valid: [
32-
{ ...WITH_SCHEMA, code: 'query { newField }' },
39+
{ ...WITH_SCHEMA, code: '{ newField }' },
3340
{ ...WITH_SCHEMA, code: 'mutation { something(t: NEW) }' },
3441
],
3542
invalid: [
@@ -39,7 +46,7 @@ ruleTester.run('no-deprecated', rule, {
3946
errors: [
4047
{
4148
message:
42-
'This enum value is marked as deprecated in your GraphQL schema (reason: No longer supported)',
49+
'Enum "OLD" is marked as deprecated in your GraphQL schema (reason: No longer supported)',
4350
},
4451
],
4552
},
@@ -48,25 +55,49 @@ ruleTester.run('no-deprecated', rule, {
4855
code: 'mutation { something(t: OLD_WITH_REASON) }',
4956
errors: [
5057
{
51-
message: 'This enum value is marked as deprecated in your GraphQL schema (reason: test)',
58+
message:
59+
'Enum "OLD_WITH_REASON" is marked as deprecated in your GraphQL schema (reason: test)',
60+
},
61+
],
62+
},
63+
{
64+
...WITH_SCHEMA,
65+
code: '{ oldField }',
66+
errors: [
67+
{
68+
message:
69+
'Field "oldField" is marked as deprecated in your GraphQL schema (reason: No longer supported)',
70+
},
71+
],
72+
},
73+
{
74+
...WITH_SCHEMA,
75+
code: '{ oldFieldWithReason }',
76+
errors: [
77+
{
78+
message:
79+
'Field "oldFieldWithReason" is marked as deprecated in your GraphQL schema (reason: test)',
5280
},
5381
],
5482
},
5583
{
5684
...WITH_SCHEMA,
57-
code: 'query { oldField }',
85+
code: '{ testArgument(a: 2) }',
5886
errors: [
5987
{
6088
message:
61-
'This field is marked as deprecated in your GraphQL schema (reason: No longer supported)',
89+
'Argument "a" is marked as deprecated in your GraphQL schema (reason: Use \'b\' instead.)',
6290
},
6391
],
6492
},
6593
{
6694
...WITH_SCHEMA,
67-
code: 'query { oldFieldWithReason }',
95+
code: '{ testObjectField(input: { a: 2 }) }',
6896
errors: [
69-
{ message: 'This field is marked as deprecated in your GraphQL schema (reason: test)' },
97+
{
98+
message:
99+
'Object field "a" is marked as deprecated in your GraphQL schema (reason: Use \'b\' instead.)',
100+
},
70101
],
71102
},
72103
],

packages/plugin/src/rules/no-deprecated/index.ts

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { EnumValueNode, FieldNode, Kind } from 'graphql';
1+
import { ArgumentNode, EnumValueNode, FieldNode, ObjectFieldNode } from 'graphql';
22
import { GraphQLESTreeNode } from '../../estree-converter/index.js';
33
import { GraphQLESLintRule } from '../../types.js';
4-
import { requireGraphQLSchemaFromContext } from '../../utils.js';
4+
import { displayNodeName, requireGraphQLSchemaFromContext } from '../../utils.js';
55

66
const RULE_ID = 'no-deprecated';
77

@@ -79,30 +79,28 @@ export const rule: GraphQLESLintRule<[], true> = {
7979
recommended: true,
8080
},
8181
messages: {
82-
[RULE_ID]:
83-
'This {{ type }} is marked as deprecated in your GraphQL schema (reason: {{ reason }})',
82+
[RULE_ID]: '{{ type }} is marked as deprecated in your GraphQL schema (reason: {{ reason }})',
8483
},
8584
schema: [],
8685
},
8786
create(context) {
8887
requireGraphQLSchemaFromContext(RULE_ID, context);
8988

9089
function report(
91-
node: GraphQLESTreeNode<EnumValueNode | FieldNode, true>,
90+
node: GraphQLESTreeNode<EnumValueNode | FieldNode | ArgumentNode | ObjectFieldNode, true>,
9291
reason: string,
9392
): void {
94-
const nodeName = node.kind === Kind.ENUM ? node.value : node.name.value;
95-
const nodeType = node.kind === Kind.ENUM ? 'enum value' : 'field';
93+
const nodeType = displayNodeName(node);
9694
context.report({
9795
node,
9896
messageId: RULE_ID,
9997
data: {
100-
type: nodeType,
98+
type: nodeType[0].toUpperCase() + nodeType.slice(1),
10199
reason,
102100
},
103101
suggest: [
104102
{
105-
desc: `Remove \`${nodeName}\` ${nodeType}`,
103+
desc: `Remove ${nodeType}`,
106104
fix: fixer => fixer.remove(node as any),
107105
},
108106
],
@@ -126,6 +124,25 @@ export const rule: GraphQLESLintRule<[], true> = {
126124
report(node, reason);
127125
}
128126
},
127+
Argument(node) {
128+
const typeInfo = node.typeInfo();
129+
const reason = typeInfo.argument?.deprecationReason;
130+
if (reason) {
131+
report(node, reason);
132+
}
133+
},
134+
ObjectValue(node) {
135+
const typeInfo = node.typeInfo();
136+
// @ts-expect-error -- fixme
137+
const fields = typeInfo.inputType!.getFields();
138+
for (const field of node.fields) {
139+
const fieldName = field.name.value;
140+
const reason = fields[fieldName].deprecationReason;
141+
if (reason) {
142+
report(field, reason);
143+
}
144+
}
145+
},
129146
};
130147
},
131148
};

packages/plugin/src/rules/no-deprecated/snapshot.md

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ exports[`no-deprecated > invalid > Invalid #1 1`] = `
88
#### ❌ Error
99

1010
> 1 | mutation { something(t: OLD) }
11-
| ^^^ This enum value is marked as deprecated in your GraphQL schema (reason: No longer supported)
11+
| ^^^ Enum "OLD" is marked as deprecated in your GraphQL schema (reason: No longer supported)
1212

13-
#### 💡 Suggestion: Remove \`OLD\` enum value
13+
#### 💡 Suggestion: Remove enum "OLD"
1414

1515
1 | mutation { something(t: ) }
1616
`;
@@ -23,39 +23,69 @@ exports[`no-deprecated > invalid > Invalid #2 1`] = `
2323
#### ❌ Error
2424

2525
> 1 | mutation { something(t: OLD_WITH_REASON) }
26-
| ^^^^^^^^^^^^^^^ This enum value is marked as deprecated in your GraphQL schema (reason: test)
26+
| ^^^^^^^^^^^^^^^ Enum "OLD_WITH_REASON" is marked as deprecated in your GraphQL schema (reason: test)
2727

28-
#### 💡 Suggestion: Remove \`OLD_WITH_REASON\` enum value
28+
#### 💡 Suggestion: Remove enum "OLD_WITH_REASON"
2929

3030
1 | mutation { something(t: ) }
3131
`;
3232

3333
exports[`no-deprecated > invalid > Invalid #3 1`] = `
3434
#### ⌨️ Code
3535

36-
1 | query { oldField }
36+
1 | { oldField }
3737

3838
#### ❌ Error
3939

40-
> 1 | query { oldField }
41-
| ^^^^^^^^ This field is marked as deprecated in your GraphQL schema (reason: No longer supported)
40+
> 1 | { oldField }
41+
| ^^^^^^^^ Field "oldField" is marked as deprecated in your GraphQL schema (reason: No longer supported)
4242

43-
#### 💡 Suggestion: Remove \`oldField\` field
43+
#### 💡 Suggestion: Remove field "oldField"
4444

45-
1 | query { }
45+
1 | { }
4646
`;
4747

4848
exports[`no-deprecated > invalid > Invalid #4 1`] = `
4949
#### ⌨️ Code
5050

51-
1 | query { oldFieldWithReason }
51+
1 | { oldFieldWithReason }
5252

5353
#### ❌ Error
5454

55-
> 1 | query { oldFieldWithReason }
56-
| ^^^^^^^^^^^^^^^^^^ This field is marked as deprecated in your GraphQL schema (reason: test)
55+
> 1 | { oldFieldWithReason }
56+
| ^^^^^^^^^^^^^^^^^^ Field "oldFieldWithReason" is marked as deprecated in your GraphQL schema (reason: test)
5757

58-
#### 💡 Suggestion: Remove \`oldFieldWithReason\` field
58+
#### 💡 Suggestion: Remove field "oldFieldWithReason"
5959

60-
1 | query { }
60+
1 | { }
61+
`;
62+
63+
exports[`no-deprecated > invalid > Invalid #5 1`] = `
64+
#### ⌨️ Code
65+
66+
1 | { testArgument(a: 2) }
67+
68+
#### ❌ Error
69+
70+
> 1 | { testArgument(a: 2) }
71+
| ^^^ Argument "a" is marked as deprecated in your GraphQL schema (reason: Use 'b' instead.)
72+
73+
#### 💡 Suggestion: Remove argument "a"
74+
75+
1 | { testArgument() }
76+
`;
77+
78+
exports[`no-deprecated > invalid > Invalid #6 1`] = `
79+
#### ⌨️ Code
80+
81+
1 | { testObjectField(input: { a: 2 }) }
82+
83+
#### ❌ Error
84+
85+
> 1 | { testObjectField(input: { a: 2 }) }
86+
| ^^^ Object field "a" is marked as deprecated in your GraphQL schema (reason: Use 'b' instead.)
87+
88+
#### 💡 Suggestion: Remove object field "a"
89+
90+
1 | { testObjectField(input: { }) }
6191
`;

packages/plugin/src/utils.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,10 +173,11 @@ const DisplayNodeNameMap: Record<Kind, string> = {
173173
[Kind.VARIABLE]: 'variable',
174174
} as const;
175175

176-
export function displayNodeName(node: GraphQLESTreeNode<ASTNode>): string {
176+
export function displayNodeName(node: GraphQLESTreeNode<ASTNode, boolean>): string {
177177
return `${
178178
node.kind === Kind.OPERATION_DEFINITION ? node.operation : DisplayNodeNameMap[node.kind]
179-
} "${('alias' in node && node.alias?.value) || ('name' in node && node.name?.value)}"`;
179+
// @ts-expect-error -- fixme
180+
} "${('alias' in node && node.alias?.value) || ('name' in node && node.name?.value) || node.value}"`;
180181
}
181182

182183
export function getNodeName(node: GraphQLESTreeNode<ASTNode>): string {

website/src/pages/docs/usage.mdx

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,45 +60,55 @@ export default [
6060
<Cards.Card
6161
icon={<GraphQLIcon />}
6262
title="Usage with `.graphql` files"
63-
href="/usage/graphql"
63+
href="/docs/usage/graphql"
6464
arrow
6565
/>
6666
<Cards.Card
6767
icon={<JSIcon />}
6868
title="Usage with code files `.js/.jsx`"
69-
href="/usage/js"
69+
href="/docs/usage/js"
7070
arrow
7171
/>
7272
<Cards.Card
7373
icon={<HalfIcon />}
7474
title="Usage to lint both schema/documents"
75-
href="/usage/schema-and-documents"
75+
href="/docs/usage/schema-and-documents"
7676
arrow
7777
/>
7878
<Cards.Card
7979
icon={<StackIcon />}
8080
title="Usage to lint different schemas"
81-
href="/usage/multiple-projects"
81+
href="/docs/usage/multiple-projects"
82+
arrow
83+
/>
84+
<Cards.Card
85+
icon={<GearIcon />}
86+
title="Programmatic usage"
87+
href="/docs/usage/programmatic"
8288
arrow
8389
/>
84-
<Cards.Card icon={<GearIcon />} title="Programmatic usage" href="/usage/programmatic" arrow />
8590
</Cards>
8691

8792
### Advanced
8893

8994
<Cards num={2}>
90-
<Cards.Card icon={<SvelteIcon />} title="Usage with `.svelte` files" href="/usage/svelte" arrow />
91-
<Cards.Card icon={<VueIcon />} title="Usage with `.vue` files" href="/usage/vue" arrow />
95+
<Cards.Card
96+
icon={<SvelteIcon />}
97+
title="Usage with `.svelte` files"
98+
href="/docs/usage/svelte"
99+
arrow
100+
/>
101+
<Cards.Card icon={<VueIcon />} title="Usage with `.vue` files" href="/docs/usage/vue" arrow />
92102
<Cards.Card
93103
icon={<AstroIcon className="dark:fill-white" />}
94104
title="Usage with `.astro` files"
95-
href="/usage/astro"
105+
href="/docs/usage/astro"
96106
arrow
97107
/>
98108
<Cards.Card
99109
icon={<PrettierIcon />}
100110
title="Usage with `eslint-plugin-prettier`"
101-
href="/usage/prettier"
111+
href="/docs/usage/prettier"
102112
arrow
103113
/>
104114
</Cards>

0 commit comments

Comments
 (0)