Skip to content

Commit 503dd9f

Browse files
authored
fix error report location for graphql-js rules (#837)
1 parent 2c73cb7 commit 503dd9f

8 files changed

+105
-25
lines changed

.changeset/cold-vans-help.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': patch
3+
---
4+
5+
fix error report location for `graphql-js` rules

packages/plugin/src/rules/graphql-js-validation.ts

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { AST } from 'eslint';
12
import { validate, GraphQLSchema, DocumentNode, ASTNode, ValidationRule } from 'graphql';
23
import { validateSDL } from 'graphql/validation/validate';
34
import { parseImportLine, processImport } from '@graphql-tools/import';
@@ -7,36 +8,50 @@ import { GraphQLESLintRule, GraphQLESLintRuleContext } from '../types';
78
import { getLocation, requireGraphQLSchemaFromContext, requireSiblingsOperations } from '../utils';
89
import { GraphQLESTreeNode } from '../estree-parser';
910

10-
function extractRuleName(stack?: string): string | null {
11-
const match = (stack || '').match(/validation[/\\]rules[/\\](.*?)\.js:/) || [];
12-
return match[1] || null;
13-
}
14-
1511
export function validateDoc(
1612
sourceNode: GraphQLESTreeNode<ASTNode>,
1713
context: GraphQLESLintRuleContext,
1814
schema: GraphQLSchema | null,
1915
documentNode: DocumentNode,
20-
rules: ReadonlyArray<ValidationRule>,
21-
ruleName: string | null = null
16+
rules: ReadonlyArray<ValidationRule>
2217
): void {
23-
if (documentNode?.definitions?.length > 0) {
24-
try {
25-
const validationErrors = schema ? validate(schema, documentNode, rules) : validateSDL(documentNode, null, rules as any);
18+
if (documentNode.definitions.length === 0) {
19+
return;
20+
}
21+
try {
22+
const validationErrors = schema
23+
? validate(schema, documentNode, rules)
24+
: validateSDL(documentNode, null, rules as any);
25+
26+
for (const error of validationErrors) {
27+
/*
28+
* TODO: Fix ESTree-AST converter because currently it's incorrectly convert loc.end
29+
* Example: loc.end always equal loc.start
30+
* {
31+
* token: {
32+
* type: 'Name',
33+
* loc: { start: { line: 4, column: 13 }, end: { line: 4, column: 13 } },
34+
* value: 'veryBad',
35+
* range: [ 40, 47 ]
36+
* }
37+
* }
38+
*/
39+
const { line, column } = error.locations[0];
40+
const ancestors = context.getAncestors();
41+
const token = (ancestors[0] as AST.Program).tokens.find(
42+
token => token.loc.start.line === line && token.loc.start.column === column
43+
);
2644

27-
for (const error of validationErrors) {
28-
const validateRuleName = ruleName || `[${extractRuleName(error.stack)}]`;
29-
context.report({
30-
loc: getLocation({ start: error.locations[0] }),
31-
message: ruleName ? error.message : `${validateRuleName} ${error.message}`,
32-
});
33-
}
34-
} catch (e) {
3545
context.report({
36-
node: sourceNode,
37-
message: e.message,
46+
loc: getLocation({ start: error.locations[0] }, token?.value),
47+
message: error.message,
3848
});
3949
}
50+
} catch (e) {
51+
context.report({
52+
node: sourceNode,
53+
message: e.message,
54+
});
4055
}
4156
}
4257

@@ -94,7 +109,7 @@ const validationToRule = (
94109
if (isRealFile && getDocumentNode) {
95110
documentNode = getDocumentNode(context);
96111
}
97-
validateDoc(node, context, schema, documentNode || node.rawNode(), [ruleFn], ruleName);
112+
validateDoc(node, context, schema, documentNode || node.rawNode(), [ruleFn]);
98113
},
99114
};
100115
},

packages/plugin/tests/__snapshots__/executable-definitions.spec.ts.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22

33
exports[` 1`] = `
44
> 1 | type Query { t: String }
5-
| ^ The "Query" definition is not executable.
5+
| ^^^^ The "Query" definition is not executable.
66
`;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[` 1`] = `
4+
> 1 | fragment UserFields on User { id bad age }
5+
| ^^^ Cannot query field "bad" on type "User". Did you mean "id"?
6+
`;
7+
8+
exports[` 2`] = `
9+
1 |
10+
2 | {
11+
3 | user {
12+
4 | id
13+
> 5 | veryBad
14+
| ^^^^^^^ Cannot query field "veryBad" on type "User".
15+
6 | age
16+
7 | }
17+
8 | }
18+
9 |
19+
`;

packages/plugin/tests/__snapshots__/lone-schema-definition.spec.ts.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ exports[` 1`] = `
2424
21 | }
2525
22 |
2626
> 23 | schema {
27-
| ^ Must provide only one schema definition.
27+
| ^^^^^^ Must provide only one schema definition.
2828
24 | query: RootQuery
2929
25 | mutation: RootMutation
3030
26 | }

packages/plugin/tests/__snapshots__/possible-type-extension.spec.ts.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ exports[` 1`] = `
77
4 | }
88
5 |
99
> 6 | extend type OtherUser {
10-
| ^ Cannot extend type "OtherUser" because it is not defined.
10+
| ^^^^^^^^^ Cannot extend type "OtherUser" because it is not defined.
1111
7 | name: String!
1212
8 | }
1313
9 |

packages/plugin/tests/__snapshots__/unique-type-names.spec.ts.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
exports[` 1`] = `
44
1 |
55
> 2 | type Query {
6-
| ^ There can be only one type named "Query".
6+
| ^^^^^ There can be only one type named "Query".
77
3 | foo: String
88
4 | }
99
5 |
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { GraphQLRuleTester, rules, ParserOptions } from '../src';
2+
3+
const parserOptions: ParserOptions = {
4+
schema: /* GraphQL */ `
5+
type User {
6+
id: ID
7+
age: Int
8+
}
9+
10+
type Query {
11+
user: User
12+
}
13+
`,
14+
};
15+
const ruleTester = new GraphQLRuleTester();
16+
17+
ruleTester.runGraphQLTests('fields-on-correct-type', rules['fields-on-correct-type'], {
18+
valid: [],
19+
invalid: [
20+
{
21+
name: 'should highlight selection on single line',
22+
code: 'fragment UserFields on User { id bad age }',
23+
parserOptions,
24+
errors: [{ message: 'Cannot query field "bad" on type "User". Did you mean "id"?' }],
25+
},
26+
{
27+
name: 'should highlight selection on multi line',
28+
code: /* GraphQL */ `
29+
{
30+
user {
31+
id
32+
veryBad
33+
age
34+
}
35+
}
36+
`,
37+
parserOptions,
38+
errors: [{ message: 'Cannot query field "veryBad" on type "User".' }],
39+
},
40+
],
41+
});

0 commit comments

Comments
 (0)