Skip to content

Commit d3ff768

Browse files
authored
NEW RULE: no-deprecated (#236)
1 parent 1594288 commit d3ff768

File tree

6 files changed

+266
-0
lines changed

6 files changed

+266
-0
lines changed

.changeset/lemon-singers-suffer.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+
NEW RULE: no-deprecated

docs/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
## Available Rules
22

33

4+
- [`no-deprecated`](./rules/no-deprecated.md)
45
- [`unique-fragment-name`](./rules/unique-fragment-name.md)
56
- [`unique-operation-name`](./rules/unique-operation-name.md)
67
- [`validate-against-schema`](./rules/validate-against-schema.md)

docs/rules/no-deprecated.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# `no-deprecated`
2+
3+
- Category: `Best Practices`
4+
- Rule name: `@graphql-eslint/no-deprecated`
5+
- Requires GraphQL Schema: `true` [ℹ️](../../README.md#extended-linting-rules-with-graphql-schema)
6+
- Requires GraphQL Operations: `false` [ℹ️](../../README.md#extended-linting-rules-with-siblings-operations)
7+
8+
This rule allow you to enforce that deprecated fields or enum values are not in use by operations.
9+
10+
## Usage Examples
11+
12+
### Incorrect (field)
13+
14+
```graphql
15+
# eslint @graphql-eslint/no-deprecated: ["error"]
16+
17+
# In your schema
18+
type User {
19+
id: ID!
20+
name: String! @deprecated(reason: "old field, please use fullName instead")
21+
fullName: String!
22+
}
23+
24+
# Query
25+
query user {
26+
user {
27+
name # This is deprecated, so you'll get an error
28+
}
29+
}
30+
```
31+
32+
### Incorrect (enum value)
33+
34+
```graphql
35+
# eslint @graphql-eslint/no-deprecated: ["error"]
36+
37+
# In your schema
38+
type Mutation {
39+
changeSomething(type: SomeType): Boolean!
40+
}
41+
42+
enum SomeType {
43+
NEW
44+
OLD @deprecated(reason: "old field, please use NEW instead")
45+
}
46+
47+
# Mutation
48+
mutation {
49+
changeSomething(
50+
type: OLD # This is deprecated, so you'll get an error
51+
) {
52+
...
53+
}
54+
}
55+
```
56+
57+
### Correct
58+
59+
```graphql
60+
# eslint @graphql-eslint/no-deprecated: ["error"]
61+
62+
# In your schema
63+
type User {
64+
id: ID!
65+
name: String! @deprecated(reason: "old field, please use fullName instead")
66+
fullName: String!
67+
}
68+
69+
# Query
70+
query user {
71+
user {
72+
id
73+
fullName
74+
}
75+
}
76+
```

packages/plugin/src/rules/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@ import namingConvention from './naming-convention';
1212
import inputName from './input-name';
1313
import uniqueFragmentName from './unique-fragment-name';
1414
import uniqueOperationName from './unique-operation-name';
15+
import noDeprecated from './no-deprecated';
1516
import { GraphQLESLintRule } from '../types';
1617
import { GRAPHQL_JS_VALIDATIONS } from './graphql-js-validation';
1718

1819
export const rules: Record<string, GraphQLESLintRule> = {
20+
'no-deprecated': noDeprecated,
1921
'unique-fragment-name': uniqueFragmentName,
2022
'unique-operation-name': uniqueOperationName,
2123
'validate-against-schema': validate,
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import { requireGraphQLSchemaFromContext } from '../utils';
2+
import { GraphQLESLintRule } from '../types';
3+
4+
const NO_DEPRECATED = 'NO_DEPRECATED';
5+
6+
const rule: GraphQLESLintRule<[], true> = {
7+
meta: {
8+
type: 'suggestion',
9+
docs: {
10+
category: 'Best Practices',
11+
description: `This rule allow you to enforce that deprecated fields or enum values are not in use by operations.`,
12+
url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/no-deprecated.md`,
13+
requiresSchema: true,
14+
requiresSiblings: false,
15+
examples: [
16+
{
17+
title: 'Incorrect (field)',
18+
code: /* GraphQL */ `
19+
# In your schema
20+
type User {
21+
id: ID!
22+
name: String! @deprecated(reason: "old field, please use fullName instead")
23+
fullName: String!
24+
}
25+
26+
# Query
27+
query user {
28+
user {
29+
name # This is deprecated, so you'll get an error
30+
}
31+
}
32+
`,
33+
},
34+
{
35+
title: 'Incorrect (enum value)',
36+
code: /* GraphQL */ `
37+
# In your schema
38+
type Mutation {
39+
changeSomething(type: SomeType): Boolean!
40+
}
41+
42+
enum SomeType {
43+
NEW
44+
OLD @deprecated(reason: "old field, please use NEW instead")
45+
}
46+
47+
# Mutation
48+
mutation {
49+
changeSomething(
50+
type: OLD # This is deprecated, so you'll get an error
51+
) {
52+
...
53+
}
54+
}
55+
`,
56+
},
57+
{
58+
title: 'Correct',
59+
code: /* GraphQL */ `
60+
# In your schema
61+
type User {
62+
id: ID!
63+
name: String! @deprecated(reason: "old field, please use fullName instead")
64+
fullName: String!
65+
}
66+
67+
# Query
68+
query user {
69+
user {
70+
id
71+
fullName
72+
}
73+
}
74+
`,
75+
},
76+
],
77+
},
78+
messages: {
79+
[NO_DEPRECATED]: `This {{ type }} is marked as deprecated in your GraphQL schema {{ reason }}`,
80+
},
81+
},
82+
create(context) {
83+
return {
84+
EnumValue(node) {
85+
requireGraphQLSchemaFromContext('no-deprecated', context);
86+
const typeInfo = node.typeInfo();
87+
88+
if (typeInfo && typeInfo.enumValue) {
89+
if (typeInfo.enumValue.isDeprecated) {
90+
context.report({
91+
loc: node.loc,
92+
messageId: NO_DEPRECATED,
93+
data: {
94+
type: 'enum value',
95+
reason: typeInfo.enumValue.deprecationReason ? `(reason: ${typeInfo.enumValue.deprecationReason})` : '',
96+
},
97+
});
98+
}
99+
}
100+
},
101+
Field(node) {
102+
requireGraphQLSchemaFromContext('no-deprecated', context);
103+
const typeInfo = node.typeInfo();
104+
105+
if (typeInfo && typeInfo.fieldDef) {
106+
if (typeInfo.fieldDef.isDeprecated) {
107+
context.report({
108+
loc: node.loc,
109+
messageId: NO_DEPRECATED,
110+
data: {
111+
type: 'field',
112+
reason: typeInfo.fieldDef.deprecationReason ? `(reason: ${typeInfo.fieldDef.deprecationReason})` : '',
113+
},
114+
});
115+
}
116+
}
117+
},
118+
};
119+
},
120+
};
121+
122+
export default rule;
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { GraphQLRuleTester } from '../src/testkit';
2+
import rule from '../src/rules/no-deprecated';
3+
import { ParserOptions } from '../src/types';
4+
5+
const TEST_SCHEMA = /* GraphQL */ `
6+
type Query {
7+
oldField: String @deprecated
8+
oldFieldWithReason: String @deprecated(reason: "test")
9+
newField: String!
10+
}
11+
12+
type Mutation {
13+
something(t: EnumType): Boolean!
14+
}
15+
16+
enum EnumType {
17+
OLD @deprecated
18+
OLD_WITH_REASON @deprecated(reason: "test")
19+
NEW
20+
}
21+
`;
22+
23+
const WITH_SCHEMA = {
24+
parserOptions: <ParserOptions>{
25+
schema: TEST_SCHEMA,
26+
operations: [],
27+
},
28+
};
29+
const ruleTester = new GraphQLRuleTester();
30+
31+
ruleTester.runGraphQLTests('no-deprecated', rule, {
32+
valid: [
33+
{ ...WITH_SCHEMA, code: `query { newField }` },
34+
{ ...WITH_SCHEMA, code: `mutation { something(t: NEW) }` },
35+
],
36+
invalid: [
37+
{
38+
...WITH_SCHEMA,
39+
code: `mutation { something(t: OLD) }`,
40+
errors: [
41+
{ message: 'This enum value is marked as deprecated in your GraphQL schema (reason: No longer supported)' },
42+
],
43+
},
44+
{
45+
...WITH_SCHEMA,
46+
code: `mutation { something(t: OLD_WITH_REASON) }`,
47+
errors: [{ message: 'This enum value is marked as deprecated in your GraphQL schema (reason: test)' }],
48+
},
49+
{
50+
...WITH_SCHEMA,
51+
code: `query { oldField }`,
52+
errors: [{ message: 'This field is marked as deprecated in your GraphQL schema (reason: No longer supported)' }],
53+
},
54+
{
55+
...WITH_SCHEMA,
56+
code: `query { oldFieldWithReason }`,
57+
errors: [{ message: 'This field is marked as deprecated in your GraphQL schema (reason: test)' }],
58+
},
59+
],
60+
});

0 commit comments

Comments
 (0)