Skip to content

Commit 4e94259

Browse files
authored
improve error messages for some rules (#1534)
* description-style.ts * input-name.ts * no-case-insensitive-enum-values-duplicates.ts * require-deprecation-date.ts * no-scalar-result-type-on-mutation.ts * strict-id-in-types.ts * require-type-pattern-with-oneof.ts * require-nullable-fields-with-oneof.ts * no-hashtag-description.ts * no-hashtag-description.ts * alphabetize.ts * alphabetize.ts * fix type issues * update snapshots * set chageset to minor * improve enum/input value name
1 parent f42f44b commit 4e94259

34 files changed

+321
-252
lines changed

.changeset/curly-eagles-attack.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+
improve error messages for some rules

.eslintrc.cjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ module.exports = {
99
rules: {
1010
'unicorn/prefer-array-some': 'error',
1111
'unicorn/better-regex': 'error',
12-
'prefer-destructuring': ['error', { object: true }],
12+
'prefer-destructuring': ['error', { VariableDeclarator: { object: true } }],
1313
quotes: ['error', 'single', { avoidEscape: true }], // Matches Prettier, but also replaces backticks
1414
},
1515
overrides: [

packages/plugin/src/rules/alphabetize.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import lowerCase from 'lodash.lowercase';
2323
import { GraphQLESTreeNode } from '../estree-converter/index.js';
2424
import { GraphQLESLintRuleListener } from '../testkit.js';
2525
import { GraphQLESLintRule } from '../types.js';
26-
import { ARRAY_DEFAULT_OPTIONS, truthy } from '../utils.js';
26+
import { ARRAY_DEFAULT_OPTIONS, displayNodeName, truthy } from '../utils.js';
2727

2828
const RULE_ID = 'alphabetize';
2929

@@ -218,7 +218,7 @@ export const rule: GraphQLESLintRule<RuleOptions> = {
218218
},
219219
},
220220
messages: {
221-
[RULE_ID]: '`{{ currName }}` should be before {{ prevName }}.',
221+
[RULE_ID]: '{{ currNode }} should be before {{ prevNode }}',
222222
},
223223
schema,
224224
},
@@ -320,8 +320,8 @@ export const rule: GraphQLESLintRule<RuleOptions> = {
320320
node: ('alias' in currNode && currNode.alias) || currNode.name,
321321
messageId: RULE_ID,
322322
data: {
323-
currName,
324-
prevName: prevName ? `\`${prevName}\`` : lowerCase(prevNode.kind),
323+
currNode: displayNodeName(currNode),
324+
prevNode: prevName ? displayNodeName(prevNode) : lowerCase(prevNode.kind),
325325
},
326326
*fix(fixer) {
327327
const prevRange = getRangeWithComments(prevNode);

packages/plugin/src/rules/description-style.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { StringValueNode } from 'graphql';
22
import { FromSchema } from 'json-schema-to-ts';
33
import { GraphQLESTreeNode } from '../estree-converter/index.js';
44
import { GraphQLESLintRule } from '../types.js';
5+
import { getNodeName } from '../utils.js';
56

67
const schema = {
78
type: 'array',
@@ -64,7 +65,9 @@ export const rule: GraphQLESLintRule<RuleOptions> = {
6465
) {
6566
context.report({
6667
loc: isBlock ? node.loc : node.loc.start,
67-
message: `Unexpected ${isBlock ? 'inline' : 'block'} description.`,
68+
message: `Unexpected ${isBlock ? 'inline' : 'block'} description for ${getNodeName(
69+
(node as any).parent,
70+
)}`,
6871
suggest: [
6972
{
7073
desc: `Change to ${isBlock ? 'block' : 'inline'} style description`,

packages/plugin/src/rules/input-name.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,13 +114,12 @@ export const rule: GraphQLESLintRule<RuleOptions> = {
114114
'FieldDefinition > InputValueDefinition[name.value!=input] > Name'(
115115
node: GraphQLESTreeNode<NameNode>,
116116
) {
117-
if (
118-
shouldCheckType((node.parent.parent as GraphQLESTreeNode<FieldDefinitionNode>).parent)
119-
) {
117+
const fieldDef = node.parent.parent as GraphQLESTreeNode<FieldDefinitionNode>;
118+
if (shouldCheckType(fieldDef.parent)) {
120119
const inputName = node.value;
121120
context.report({
122121
node,
123-
message: `Input \`${inputName}\` should be called \`input\`.`,
122+
message: `Input "${inputName}" should be named "input" for "${fieldDef.parent.name.value}.${fieldDef.name.value}"`,
124123
suggest: [
125124
{
126125
desc: 'Rename to `input`',

packages/plugin/src/rules/no-case-insensitive-enum-values-duplicates.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { EnumTypeDefinitionNode, EnumTypeExtensionNode, Kind } from 'graphql';
22
import { GraphQLESTreeNode } from '../estree-converter/index.js';
33
import { GraphQLESLintRule } from '../types.js';
4+
import { getNodeName } from '../utils.js';
45

56
export const rule: GraphQLESLintRule = {
67
meta: {
@@ -49,7 +50,9 @@ export const rule: GraphQLESLintRule = {
4950
const enumName = duplicate.name.value;
5051
context.report({
5152
node: duplicate.name,
52-
message: `Case-insensitive enum values duplicates are not allowed! Found: \`${enumName}\`.`,
53+
message: `Unexpected case-insensitive enum values duplicates for ${getNodeName(
54+
duplicate,
55+
)}`,
5356
suggest: [
5457
{
5558
desc: `Remove \`${enumName}\` enum value`,

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

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
import { DocumentNode, Token, TokenKind } from 'graphql';
22
import { GraphQLESTreeNode } from '../estree-converter/index.js';
33
import { GraphQLESLintRule } from '../types.js';
4+
import { getNodeName } from '../utils.js';
45

5-
const HASHTAG_COMMENT = 'HASHTAG_COMMENT';
6+
export const RULE_ID = 'HASHTAG_COMMENT';
67

78
export const rule: GraphQLESLintRule = {
89
meta: {
910
type: 'suggestion',
1011
hasSuggestions: true,
1112
schema: [],
1213
messages: {
13-
[HASHTAG_COMMENT]:
14-
'Using hashtag `#` for adding GraphQL descriptions is not allowed. Prefer using `"""` for multiline, or `"` for a single line description.',
14+
[RULE_ID]:
15+
'Unexpected GraphQL descriptions as hashtag `#` for {{ nodeName }}.\nPrefer using `"""` for multiline, or `"` for a single line description.',
1516
},
1617
docs: {
1718
description:
@@ -69,15 +70,28 @@ export const rule: GraphQLESLintRule = {
6970
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion -- TODO: remove `!` when drop support of graphql@15
7071
const isEslintComment = value!.trimStart().startsWith('eslint');
7172
const linesAfter = next.line - line;
72-
7373
if (
7474
!isEslintComment &&
7575
line !== prev.line &&
7676
next.kind === TokenKind.NAME &&
7777
linesAfter < 2
7878
) {
79+
const sourceCode = context.getSourceCode();
80+
const { tokens } = sourceCode.ast;
81+
82+
const t = tokens.find(
83+
token =>
84+
token.loc.start.line === next.line && token.loc.start.column === next.column - 1,
85+
)!;
86+
const nextNode = sourceCode.getNodeByRangeIndex(t.range[1] + 1)!;
87+
7988
context.report({
80-
messageId: HASHTAG_COMMENT,
89+
messageId: RULE_ID,
90+
data: {
91+
nodeName: getNodeName(
92+
'name' in nextNode ? (nextNode as any) : (nextNode as any).parent,
93+
),
94+
},
8195
loc: {
8296
line,
8397
column: column - 1,

packages/plugin/src/rules/no-scalar-result-type-on-mutation.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { isScalarType, NameNode } from 'graphql';
1+
import { isScalarType, Kind, NameNode } from 'graphql';
22
import { GraphQLESTreeNode } from '../estree-converter/index.js';
33
import { GraphQLESLintRule } from '../types.js';
4-
import { requireGraphQLSchemaFromContext } from '../utils.js';
4+
import { getNodeName, requireGraphQLSchemaFromContext } from '../utils.js';
55

66
const RULE_ID = 'no-scalar-result-type-on-mutation';
77

@@ -52,9 +52,14 @@ export const rule: GraphQLESLintRule = {
5252
const typeName = node.value;
5353
const graphQLType = schema.getType(typeName);
5454
if (isScalarType(graphQLType)) {
55+
let fieldDef = node.parent as any;
56+
while (fieldDef.kind !== Kind.FIELD_DEFINITION) {
57+
fieldDef = fieldDef.parent;
58+
}
59+
5560
context.report({
5661
node,
57-
message: `Unexpected scalar result type \`${typeName}\`.`,
62+
message: `Unexpected scalar result type \`${typeName}\` for ${getNodeName(fieldDef)}`,
5863
suggest: [
5964
{
6065
desc: `Remove \`${typeName}\``,

packages/plugin/src/rules/require-deprecation-date.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { DirectiveNode } from 'graphql';
22
import { FromSchema } from 'json-schema-to-ts';
33
import { GraphQLESTreeNode, valueFromNode } from '../estree-converter/index.js';
44
import { GraphQLESLintRule } from '../types.js';
5+
import { getNodeName } from '../utils.js';
56

67
// eslint-disable-next-line unicorn/better-regex
78
const DATE_REGEX = /^\d{2}\/\d{2}\/\d{4}$/;
@@ -68,10 +69,11 @@ export const rule: GraphQLESLintRule<RuleOptions> = {
6869
],
6970
},
7071
messages: {
71-
[MESSAGE_REQUIRE_DATE]: 'Directive "@deprecated" must have a deletion date',
72-
[MESSAGE_INVALID_FORMAT]: 'Deletion date must be in format "DD/MM/YYYY"',
73-
[MESSAGE_INVALID_DATE]: 'Invalid "{{ deletionDate }}" deletion date',
74-
[MESSAGE_CAN_BE_REMOVED]: '"{{ nodeName }}" сan be removed',
72+
[MESSAGE_REQUIRE_DATE]:
73+
'Directive "@deprecated" must have a deletion date for {{ nodeName }}',
74+
[MESSAGE_INVALID_FORMAT]: 'Deletion date must be in format "DD/MM/YYYY" for {{ nodeName }}',
75+
[MESSAGE_INVALID_DATE]: 'Invalid "{{ deletionDate }}" deletion date for {{ nodeName }}',
76+
[MESSAGE_CAN_BE_REMOVED]: '{{ nodeName }} сan be removed',
7577
},
7678
schema,
7779
},
@@ -85,14 +87,21 @@ export const rule: GraphQLESLintRule<RuleOptions> = {
8587
context.report({
8688
node: node.name,
8789
messageId: MESSAGE_REQUIRE_DATE,
90+
data: {
91+
nodeName: getNodeName(node.parent),
92+
},
8893
});
8994
return;
9095
}
9196
const deletionDate = valueFromNode(deletionDateNode.value as any);
9297
const isValidDate = DATE_REGEX.test(deletionDate);
9398

9499
if (!isValidDate) {
95-
context.report({ node: deletionDateNode.value, messageId: MESSAGE_INVALID_FORMAT });
100+
context.report({
101+
node: deletionDateNode.value,
102+
messageId: MESSAGE_INVALID_FORMAT,
103+
data: { nodeName: getNodeName(node.parent) },
104+
});
96105
return;
97106
}
98107
let [day, month, year] = deletionDate.split('/');
@@ -106,6 +115,7 @@ export const rule: GraphQLESLintRule<RuleOptions> = {
106115
messageId: MESSAGE_INVALID_DATE,
107116
data: {
108117
deletionDate,
118+
nodeName: getNodeName(node.parent),
109119
},
110120
});
111121
return;
@@ -119,7 +129,7 @@ export const rule: GraphQLESLintRule<RuleOptions> = {
119129
context.report({
120130
node: parent.name,
121131
messageId: MESSAGE_CAN_BE_REMOVED,
122-
data: { nodeName },
132+
data: { nodeName: getNodeName(parent) },
123133
suggest: [
124134
{
125135
desc: `Remove \`${nodeName}\``,

packages/plugin/src/rules/require-nullable-fields-with-oneof.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { DirectiveNode, Kind } from 'graphql';
22
import { GraphQLESTreeNode } from '../estree-converter/index.js';
33
import { GraphQLESLintRule } from '../types.js';
4+
import { getNodeName } from '../utils.js';
45

56
const RULE_ID = 'require-nullable-fields-with-oneof';
67

@@ -33,7 +34,7 @@ export const rule: GraphQLESLintRule = {
3334
],
3435
},
3536
messages: {
36-
[RULE_ID]: 'Field `{{fieldName}}` must be nullable.',
37+
[RULE_ID]: '{{ nodeName }} must be nullable when "@oneOf" is in use',
3738
},
3839
schema: [],
3940
},
@@ -53,7 +54,7 @@ export const rule: GraphQLESLintRule = {
5354
context.report({
5455
node: field.name,
5556
messageId: RULE_ID,
56-
data: { fieldName: field.name.value },
57+
data: { nodeName: getNodeName(field) },
5758
});
5859
}
5960
}

0 commit comments

Comments
 (0)