Skip to content

Commit a44d426

Browse files
author
Dimitri POSTOLOV
authored
🎉 New feature: Support #import for known-fragment-names rule (#441)
1 parent 152ecc6 commit a44d426

File tree

8 files changed

+148
-7
lines changed

8 files changed

+148
-7
lines changed

.changeset/dry-vans-smile.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+
support #import in known-fragment-names rule

docs/rules/known-fragment-names.md

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,52 @@
77

88
A GraphQL document is only valid if all `...Fragment` fragment spreads refer to fragments defined in the same document.
99

10-
> This rule is a wrapper around a `graphql-js` validation function. [You can find it's source code here](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/KnownFragmentNamesRule.ts).
10+
> This rule is a wrapper around a `graphql-js` validation function. [You can find it's source code here](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/KnownFragmentNamesRule.ts).
11+
12+
## Usage Examples
13+
14+
### Incorrect (fragment not defined in the document)
15+
16+
```graphql
17+
# eslint @graphql-eslint/known-fragment-names: ["error"]
18+
19+
query {
20+
user {
21+
id
22+
...UserFields
23+
}
24+
}
25+
```
26+
27+
### Correct
28+
29+
```graphql
30+
# eslint @graphql-eslint/known-fragment-names: ["error"]
31+
32+
fragment UserFields on User {
33+
firstName
34+
lastName
35+
}
36+
37+
query {
38+
user {
39+
id
40+
...UserFields
41+
}
42+
}
43+
```
44+
45+
### Correct (existing import to UserFields fragment)
46+
47+
```graphql
48+
# eslint @graphql-eslint/known-fragment-names: ["error"]
49+
50+
#import '../UserFields.gql'
51+
52+
query {
53+
user {
54+
id
55+
...UserFields
56+
}
57+
}
58+
```

packages/plugin/src/parser.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { convertToESTree } from './estree-parser/converter';
2-
import { parseGraphQLSDL } from '@graphql-tools/utils';
1+
import { convertToESTree } from './estree-parser';
2+
import { GraphQLFileLoader } from '@graphql-tools/graphql-file-loader';
33
import { GraphQLError, TypeInfo } from 'graphql';
44
import { Linter } from 'eslint';
5+
import fs from 'fs';
56
import { GraphQLESLintParseResult, ParserOptions } from './types';
67
import { extractTokens } from './utils';
78
import { getSchema } from './schema';
@@ -26,9 +27,14 @@ export function parseForESLint(code: string, options?: ParserOptions): GraphQLES
2627
};
2728

2829
try {
29-
const graphqlAst = parseGraphQLSDL(options.filePath || '', code, {
30-
...(options.graphQLParserOptions || {}),
30+
const filePath = options.filePath || '';
31+
const isVirtualFile = !fs.existsSync(options.filePath);
32+
const fileLoader = new GraphQLFileLoader();
33+
34+
const graphqlAst = fileLoader.handleFileContent(code, filePath, {
35+
...options.graphQLParserOptions,
3136
noLocation: false,
37+
skipGraphQLImport: isVirtualFile,
3238
});
3339

3440
const { rootTree, comments } = convertToESTree(graphqlAst.document, schema ? new TypeInfo(schema) : null);

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

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,48 @@ export const GRAPHQL_JS_VALIDATIONS = Object.assign(
128128
validationToRule('known-fragment-names', 'KnownFragmentNames', {
129129
docs: {
130130
description: `A GraphQL document is only valid if all \`...Fragment\` fragment spreads refer to fragments defined in the same document.`,
131+
examples: [
132+
{
133+
title: 'Incorrect (fragment not defined in the document)',
134+
code: /* GraphQL */ `
135+
query {
136+
user {
137+
id
138+
...UserFields
139+
}
140+
}
141+
`,
142+
},
143+
{
144+
title: 'Correct',
145+
code: /* GraphQL */ `
146+
fragment UserFields on User {
147+
firstName
148+
lastName
149+
}
150+
151+
query {
152+
user {
153+
id
154+
...UserFields
155+
}
156+
}
157+
`,
158+
},
159+
{
160+
title: 'Correct (existing import to UserFields fragment)',
161+
code: /* GraphQL */ `
162+
#import '../UserFields.gql'
163+
164+
query {
165+
user {
166+
id
167+
...UserFields
168+
}
169+
}
170+
`,
171+
},
172+
],
131173
},
132174
}),
133175
validationToRule('known-type-names', 'KnownTypeNames', {

packages/plugin/src/sibling-operations.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
} from 'graphql';
1414
import { ParserOptions } from './types';
1515
import { GraphQLConfig } from 'graphql-config';
16-
import { dirname, join } from 'path';
16+
import { dirname } from 'path';
1717

1818
export type FragmentSource = { filePath: string; document: FragmentDefinitionNode };
1919
export type OperationSource = { filePath: string; document: OperationDefinitionNode };
@@ -77,7 +77,7 @@ export function getSiblingOperations(options: ParserOptions, gqlConfig: GraphQLC
7777
}
7878

7979
if (!siblings && options?.operations) {
80-
const loadPaths = Array.isArray(options.operations) ? options.operations : [options.operations] || [];
80+
const loadPaths = Array.isArray(options.operations) ? options.operations : [options.operations];
8181
const loadKey = loadPaths.join(',');
8282

8383
if (operationsCache.has(loadKey)) {
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { join } from 'path';
2+
import { GraphQLRuleTester } from '../src';
3+
import { GRAPHQL_JS_VALIDATIONS } from '../src/rules/graphql-js-validation';
4+
5+
const TEST_SCHEMA = /* GraphQL */ `
6+
type User {
7+
id: ID!
8+
firstName: String!
9+
}
10+
11+
type Query {
12+
user: User
13+
}
14+
`;
15+
16+
const ruleTester = new GraphQLRuleTester();
17+
18+
ruleTester.runGraphQLTests('known-fragment-names', GRAPHQL_JS_VALIDATIONS['known-fragment-names'], {
19+
valid: [
20+
{
21+
filename: join(__dirname, 'mocks/user.graphql'),
22+
code: ruleTester.fromMockFile('user.graphql'),
23+
parserOptions: {
24+
schema: TEST_SCHEMA,
25+
},
26+
},
27+
],
28+
invalid: [],
29+
});
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
fragment UserFields on User {
2+
id
3+
firstName
4+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#import "./user-fields.graphql"
2+
3+
query User {
4+
user {
5+
...UserFields
6+
}
7+
}

0 commit comments

Comments
 (0)