@graphql-eslint/eslint-plugin 3.13.1 → 3.14.0-alpha-20221220000306-eefc8db
Sign up to get free protection for your applications and to get access to all the features.
- package/documents.d.ts +3 -3
- package/estree-converter/types.d.ts +8 -8
- package/estree-converter/utils.d.ts +1 -1
- package/index.js +215 -91
- package/index.mjs +215 -91
- package/package.json +1 -1
- package/processor.d.ts +1 -1
- package/rules/alphabetize.d.ts +1 -1
- package/rules/description-style.d.ts +1 -1
- package/rules/graphql-js-validation.d.ts +1 -1
- package/rules/input-name.d.ts +1 -1
- package/rules/match-document-filename.d.ts +3 -3
- package/rules/naming-convention.d.ts +5 -5
- package/rules/no-root-type.d.ts +1 -1
- package/rules/relay-arguments.d.ts +1 -1
- package/rules/relay-edge-types.d.ts +1 -1
- package/rules/require-description.d.ts +2 -2
- package/rules/require-id-when-available.d.ts +1 -1
- package/rules/selection-set-depth.d.ts +1 -1
- package/rules/strict-id-in-types.d.ts +1 -1
- package/testkit.d.ts +3 -3
- package/types.d.ts +15 -15
- package/utils.d.ts +1 -1
- package/README.md +0 -266
- package/docs/README.md +0 -76
- package/docs/custom-rules.md +0 -148
- package/docs/deprecated-rules.md +0 -21
- package/docs/parser-options.md +0 -85
- package/docs/parser.md +0 -49
- package/docs/rules/alphabetize.md +0 -178
- package/docs/rules/description-style.md +0 -54
- package/docs/rules/executable-definitions.md +0 -17
- package/docs/rules/fields-on-correct-type.md +0 -17
- package/docs/rules/fragments-on-composite-type.md +0 -17
- package/docs/rules/input-name.md +0 -76
- package/docs/rules/known-argument-names.md +0 -17
- package/docs/rules/known-directives.md +0 -44
- package/docs/rules/known-fragment-names.md +0 -69
- package/docs/rules/known-type-names.md +0 -17
- package/docs/rules/lone-anonymous-operation.md +0 -17
- package/docs/rules/lone-schema-definition.md +0 -17
- package/docs/rules/match-document-filename.md +0 -156
- package/docs/rules/naming-convention.md +0 -300
- package/docs/rules/no-anonymous-operations.md +0 -39
- package/docs/rules/no-case-insensitive-enum-values-duplicates.md +0 -43
- package/docs/rules/no-deprecated.md +0 -85
- package/docs/rules/no-duplicate-fields.md +0 -65
- package/docs/rules/no-fragment-cycles.md +0 -17
- package/docs/rules/no-hashtag-description.md +0 -59
- package/docs/rules/no-root-type.md +0 -53
- package/docs/rules/no-scalar-result-type-on-mutation.md +0 -37
- package/docs/rules/no-typename-prefix.md +0 -39
- package/docs/rules/no-undefined-variables.md +0 -17
- package/docs/rules/no-unreachable-types.md +0 -49
- package/docs/rules/no-unused-fields.md +0 -62
- package/docs/rules/no-unused-fragments.md +0 -17
- package/docs/rules/no-unused-variables.md +0 -17
- package/docs/rules/one-field-subscriptions.md +0 -17
- package/docs/rules/overlapping-fields-can-be-merged.md +0 -17
- package/docs/rules/possible-fragment-spread.md +0 -17
- package/docs/rules/possible-type-extension.md +0 -15
- package/docs/rules/provided-required-arguments.md +0 -17
- package/docs/rules/relay-arguments.md +0 -57
- package/docs/rules/relay-connection-types.md +0 -42
- package/docs/rules/relay-edge-types.md +0 -56
- package/docs/rules/relay-page-info.md +0 -32
- package/docs/rules/require-deprecation-date.md +0 -57
- package/docs/rules/require-deprecation-reason.md +0 -47
- package/docs/rules/require-description.md +0 -115
- package/docs/rules/require-field-of-type-query-in-mutation-result.md +0 -47
- package/docs/rules/require-id-when-available.md +0 -88
- package/docs/rules/scalar-leafs.md +0 -17
- package/docs/rules/selection-set-depth.md +0 -76
- package/docs/rules/strict-id-in-types.md +0 -130
- package/docs/rules/unique-argument-names.md +0 -17
- package/docs/rules/unique-directive-names-per-location.md +0 -17
- package/docs/rules/unique-directive-names.md +0 -17
- package/docs/rules/unique-enum-value-names.md +0 -15
- package/docs/rules/unique-field-definition-names.md +0 -17
- package/docs/rules/unique-fragment-name.md +0 -51
- package/docs/rules/unique-input-field-names.md +0 -17
- package/docs/rules/unique-operation-name.md +0 -55
- package/docs/rules/unique-operation-types.md +0 -17
- package/docs/rules/unique-type-names.md +0 -17
- package/docs/rules/unique-variable-names.md +0 -17
- package/docs/rules/value-literals-of-correct-type.md +0 -17
- package/docs/rules/variables-are-input-types.md +0 -17
- package/docs/rules/variables-in-allowed-position.md +0 -17
package/documents.d.ts
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
import { FragmentDefinitionNode, OperationDefinitionNode, SelectionSetNode, OperationTypeNode } from 'graphql';
|
2
2
|
import { GraphQLProjectConfig } from 'graphql-config';
|
3
|
-
export
|
3
|
+
export type FragmentSource = {
|
4
4
|
filePath: string;
|
5
5
|
document: FragmentDefinitionNode;
|
6
6
|
};
|
7
|
-
export
|
7
|
+
export type OperationSource = {
|
8
8
|
filePath: string;
|
9
9
|
document: OperationDefinitionNode;
|
10
10
|
};
|
11
|
-
export
|
11
|
+
export type SiblingOperations = {
|
12
12
|
available: boolean;
|
13
13
|
getFragment(fragmentName: string): FragmentSource[];
|
14
14
|
getFragments(): FragmentSource[];
|
@@ -1,15 +1,15 @@
|
|
1
1
|
import type { ASTNode, TypeInfo, TypeNode, DocumentNode, ExecutableDefinitionNode, NameNode, TypeDefinitionNode, FieldDefinitionNode, ObjectTypeExtensionNode, ObjectTypeDefinitionNode, InterfaceTypeDefinitionNode, InterfaceTypeExtensionNode, SelectionSetNode, SelectionNode, DefinitionNode, TypeExtensionNode, DirectiveDefinitionNode, VariableNode, FieldNode, FragmentSpreadNode, EnumValueDefinitionNode, ArgumentNode, NamedTypeNode, EnumTypeDefinitionNode, EnumTypeExtensionNode, InputValueDefinitionNode, InputObjectTypeDefinitionNode, InputObjectTypeExtensionNode, InlineFragmentNode, VariableDefinitionNode, ListTypeNode, NonNullTypeNode, OperationTypeDefinitionNode } from 'graphql';
|
2
2
|
import type { SourceLocation, Comment } from 'estree';
|
3
3
|
import type { AST } from 'eslint';
|
4
|
-
|
4
|
+
type SafeGraphQLType<T extends ASTNode> = T extends {
|
5
5
|
type: TypeNode;
|
6
6
|
} ? Omit<T, 'loc' | 'type'> & {
|
7
7
|
gqlType: T['type'];
|
8
8
|
} : Omit<T, 'loc'>;
|
9
|
-
|
9
|
+
type Writeable<T> = {
|
10
10
|
-readonly [K in keyof T]: T[K];
|
11
11
|
};
|
12
|
-
export
|
12
|
+
export type TypeInformation = {
|
13
13
|
argument: ReturnType<TypeInfo['getArgument']>;
|
14
14
|
defaultValue: ReturnType<TypeInfo['getDefaultValue']>;
|
15
15
|
directive: ReturnType<TypeInfo['getDirective']>;
|
@@ -20,10 +20,10 @@ export declare type TypeInformation = {
|
|
20
20
|
parentType: ReturnType<TypeInfo['getParentType']>;
|
21
21
|
gqlType: ReturnType<TypeInfo['getType']>;
|
22
22
|
};
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
23
|
+
type NodeWithName = TypeDefinitionNode | TypeExtensionNode | ExecutableDefinitionNode | DirectiveDefinitionNode | FieldDefinitionNode | EnumValueDefinitionNode | FieldNode | FragmentSpreadNode | VariableNode | ArgumentNode | NamedTypeNode;
|
24
|
+
type NodeWithType = FieldDefinitionNode | InputValueDefinitionNode | OperationTypeDefinitionNode | NonNullTypeNode | ListTypeNode | VariableDefinitionNode;
|
25
|
+
type ParentNode<T> = T extends DocumentNode ? AST.Program : T extends DefinitionNode ? DocumentNode : T extends EnumValueDefinitionNode ? EnumTypeDefinitionNode | EnumTypeExtensionNode : T extends InputValueDefinitionNode ? InputObjectTypeDefinitionNode | InputObjectTypeExtensionNode | FieldDefinitionNode | DirectiveDefinitionNode : T extends FieldDefinitionNode ? ObjectTypeDefinitionNode | ObjectTypeExtensionNode | InterfaceTypeDefinitionNode | InterfaceTypeExtensionNode : T extends SelectionSetNode ? ExecutableDefinitionNode | FieldNode | InlineFragmentNode : T extends SelectionNode ? SelectionSetNode : T extends TypeNode ? NodeWithType : T extends NameNode ? NodeWithName : unknown;
|
26
|
+
type Node<T extends ASTNode, WithTypeInfo extends boolean> = Writeable<SafeGraphQLType<T>> & {
|
27
27
|
type: T['kind'];
|
28
28
|
loc: SourceLocation;
|
29
29
|
range: AST.Range;
|
@@ -32,7 +32,7 @@ declare type Node<T extends ASTNode, WithTypeInfo extends boolean> = Writeable<S
|
|
32
32
|
rawNode: () => T;
|
33
33
|
parent: ParentNode<T>;
|
34
34
|
};
|
35
|
-
export
|
35
|
+
export type GraphQLESTreeNode<T, W extends boolean = false> = T extends ASTNode ? {
|
36
36
|
[K in keyof Node<T, W>]: Node<T, W>[K] extends ReadonlyArray<infer ArrayItem> ? GraphQLESTreeNode<ArrayItem, W>[] : GraphQLESTreeNode<Node<T, W>[K], W>;
|
37
37
|
} : T extends AST.Program ? T & {
|
38
38
|
parent: null;
|
@@ -3,7 +3,7 @@ import type { Comment, SourceLocation } from 'estree';
|
|
3
3
|
import type { AST } from 'eslint';
|
4
4
|
export declare const valueFromNode: (valueNode: import("graphql").ValueNode, variables?: import("graphql/jsutils/ObjMap").ObjMap<unknown>) => any;
|
5
5
|
export declare function getBaseType(type: GraphQLOutputType): GraphQLNamedType;
|
6
|
-
|
6
|
+
type TokenKindValue = '<SOF>' | '!' | '$' | '&' | '(' | ')' | '...' | ':' | '=' | '@' | '[' | ']' | '{' | '|' | '}' | 'Name' | 'Int' | 'Float' | 'String' | 'BlockString' | 'Comment';
|
7
7
|
export declare function convertToken<T extends 'Line' | 'Block' | TokenKindValue>(token: Token, type: T): Omit<AST.Token, 'type'> & {
|
8
8
|
type: T;
|
9
9
|
};
|
package/index.js
CHANGED
@@ -221,7 +221,8 @@ const ARRAY_DEFAULT_OPTIONS = {
|
|
221
221
|
};
|
222
222
|
const englishJoinWords = words => new Intl.ListFormat('en-US', { type: 'disjunction' }).format(words);
|
223
223
|
|
224
|
-
function validateDocument(context, schema = null, documentNode, rule) {
|
224
|
+
function validateDocument({ context, schema = null, documentNode, rule, hasDidYouMeanSuggestions, }) {
|
225
|
+
var _a;
|
225
226
|
if (documentNode.definitions.length === 0) {
|
226
227
|
return;
|
227
228
|
}
|
@@ -245,9 +246,20 @@ function validateDocument(context, schema = null, documentNode, rule) {
|
|
245
246
|
? sourceCode.getNodeByRangeIndex(token.range[1] + 1).loc
|
246
247
|
: token.loc;
|
247
248
|
}
|
249
|
+
const didYouMeanContent = (_a = error.message.match(/Did you mean (?<content>.*)\?$/)) === null || _a === void 0 ? void 0 : _a.groups.content;
|
250
|
+
const matches = didYouMeanContent ? [...didYouMeanContent.matchAll(/"(?<name>[^"]*)"/g)] : [];
|
248
251
|
context.report({
|
249
252
|
loc,
|
250
253
|
message: error.message,
|
254
|
+
suggest: hasDidYouMeanSuggestions
|
255
|
+
? matches.map(match => {
|
256
|
+
const { name } = match.groups;
|
257
|
+
return {
|
258
|
+
desc: `Rename to \`${name}\``,
|
259
|
+
fix: fixer => fixer.replaceText(token, name),
|
260
|
+
};
|
261
|
+
})
|
262
|
+
: [],
|
251
263
|
});
|
252
264
|
}
|
253
265
|
}
|
@@ -301,7 +313,7 @@ const handleMissingFragments = ({ ruleId, context, node }) => {
|
|
301
313
|
}
|
302
314
|
return node;
|
303
315
|
};
|
304
|
-
const validationToRule = (ruleId, ruleName,
|
316
|
+
const validationToRule = ({ ruleId, ruleName, getDocumentNode, schema = [], hasDidYouMeanSuggestions, }, docs) => {
|
305
317
|
let ruleFn = null;
|
306
318
|
try {
|
307
319
|
ruleFn = require(`graphql/validation/rules/${ruleName}Rule`)[`${ruleName}Rule`];
|
@@ -325,6 +337,7 @@ const validationToRule = (ruleId, ruleName, docs, getDocumentNode, schema = [])
|
|
325
337
|
description: `${docs.description}\n\n> This rule is a wrapper around a \`graphql-js\` validation function.`,
|
326
338
|
},
|
327
339
|
schema,
|
340
|
+
hasSuggestions: hasDidYouMeanSuggestions,
|
328
341
|
},
|
329
342
|
create(context) {
|
330
343
|
if (!ruleFn) {
|
@@ -339,30 +352,79 @@ const validationToRule = (ruleId, ruleName, docs, getDocumentNode, schema = [])
|
|
339
352
|
const documentNode = getDocumentNode
|
340
353
|
? getDocumentNode({ ruleId, context, node: node.rawNode() })
|
341
354
|
: node.rawNode();
|
342
|
-
validateDocument(
|
355
|
+
validateDocument({
|
356
|
+
context,
|
357
|
+
schema,
|
358
|
+
documentNode,
|
359
|
+
rule: ruleFn,
|
360
|
+
hasDidYouMeanSuggestions,
|
361
|
+
});
|
343
362
|
},
|
344
363
|
};
|
345
364
|
},
|
346
365
|
},
|
347
366
|
};
|
348
367
|
};
|
349
|
-
const GRAPHQL_JS_VALIDATIONS = Object.assign({}, validationToRule(
|
368
|
+
const GRAPHQL_JS_VALIDATIONS = Object.assign({}, validationToRule({
|
369
|
+
ruleId: 'executable-definitions',
|
370
|
+
ruleName: 'ExecutableDefinitions',
|
371
|
+
}, {
|
350
372
|
category: 'Operations',
|
351
373
|
description: 'A GraphQL document is only valid for execution if all definitions are either operation or fragment definitions.',
|
352
374
|
requiresSchema: true,
|
353
|
-
}), validationToRule(
|
375
|
+
}), validationToRule({
|
376
|
+
ruleId: 'fields-on-correct-type',
|
377
|
+
ruleName: 'FieldsOnCorrectType',
|
378
|
+
hasDidYouMeanSuggestions: true,
|
379
|
+
}, {
|
354
380
|
category: 'Operations',
|
355
381
|
description: 'A GraphQL document is only valid if all fields selected are defined by the parent type, or are an allowed meta field such as `__typename`.',
|
356
382
|
requiresSchema: true,
|
357
|
-
}), validationToRule(
|
383
|
+
}), validationToRule({
|
384
|
+
ruleId: 'fragments-on-composite-type',
|
385
|
+
ruleName: 'FragmentsOnCompositeTypes',
|
386
|
+
}, {
|
358
387
|
category: 'Operations',
|
359
388
|
description: 'Fragments use a type condition to determine if they apply, since fragments can only be spread into a composite type (object, interface, or union), the type condition must also be a composite type.',
|
360
389
|
requiresSchema: true,
|
361
|
-
}), validationToRule(
|
390
|
+
}), validationToRule({
|
391
|
+
ruleId: 'known-argument-names',
|
392
|
+
ruleName: 'KnownArgumentNames',
|
393
|
+
hasDidYouMeanSuggestions: true,
|
394
|
+
}, {
|
362
395
|
category: ['Schema', 'Operations'],
|
363
396
|
description: 'A GraphQL field is only valid if all supplied arguments are defined by that field.',
|
364
397
|
requiresSchema: true,
|
365
|
-
}), validationToRule(
|
398
|
+
}), validationToRule({
|
399
|
+
ruleId: 'known-directives',
|
400
|
+
ruleName: 'KnownDirectives',
|
401
|
+
getDocumentNode({ context, node: documentNode }) {
|
402
|
+
const { ignoreClientDirectives = [] } = context.options[0] || {};
|
403
|
+
if (ignoreClientDirectives.length === 0) {
|
404
|
+
return documentNode;
|
405
|
+
}
|
406
|
+
const filterDirectives = (node) => ({
|
407
|
+
...node,
|
408
|
+
directives: node.directives.filter(directive => !ignoreClientDirectives.includes(directive.name.value)),
|
409
|
+
});
|
410
|
+
return graphql.visit(documentNode, {
|
411
|
+
Field: filterDirectives,
|
412
|
+
OperationDefinition: filterDirectives,
|
413
|
+
});
|
414
|
+
},
|
415
|
+
schema: {
|
416
|
+
type: 'array',
|
417
|
+
maxItems: 1,
|
418
|
+
items: {
|
419
|
+
type: 'object',
|
420
|
+
additionalProperties: false,
|
421
|
+
required: ['ignoreClientDirectives'],
|
422
|
+
properties: {
|
423
|
+
ignoreClientDirectives: ARRAY_DEFAULT_OPTIONS,
|
424
|
+
},
|
425
|
+
},
|
426
|
+
},
|
427
|
+
}, {
|
366
428
|
category: ['Schema', 'Operations'],
|
367
429
|
description: 'A GraphQL document is only valid if all `@directive`s are known by the schema and legally positioned.',
|
368
430
|
requiresSchema: true,
|
@@ -379,31 +441,11 @@ const GRAPHQL_JS_VALIDATIONS = Object.assign({}, validationToRule('executable-de
|
|
379
441
|
`,
|
380
442
|
},
|
381
443
|
],
|
382
|
-
}, ({
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
}
|
387
|
-
const filterDirectives = (node) => ({
|
388
|
-
...node,
|
389
|
-
directives: node.directives.filter(directive => !ignoreClientDirectives.includes(directive.name.value)),
|
390
|
-
});
|
391
|
-
return graphql.visit(documentNode, {
|
392
|
-
Field: filterDirectives,
|
393
|
-
OperationDefinition: filterDirectives,
|
394
|
-
});
|
444
|
+
}), validationToRule({
|
445
|
+
ruleId: 'known-fragment-names',
|
446
|
+
ruleName: 'KnownFragmentNames',
|
447
|
+
getDocumentNode: handleMissingFragments,
|
395
448
|
}, {
|
396
|
-
type: 'array',
|
397
|
-
maxItems: 1,
|
398
|
-
items: {
|
399
|
-
type: 'object',
|
400
|
-
additionalProperties: false,
|
401
|
-
required: ['ignoreClientDirectives'],
|
402
|
-
properties: {
|
403
|
-
ignoreClientDirectives: ARRAY_DEFAULT_OPTIONS,
|
404
|
-
},
|
405
|
-
},
|
406
|
-
}), validationToRule('known-fragment-names', 'KnownFragmentNames', {
|
407
449
|
category: 'Operations',
|
408
450
|
description: 'A GraphQL document is only valid if all `...Fragment` fragment spreads refer to fragments defined in the same document.',
|
409
451
|
requiresSchema: true,
|
@@ -454,138 +496,220 @@ const GRAPHQL_JS_VALIDATIONS = Object.assign({}, validationToRule('executable-de
|
|
454
496
|
`,
|
455
497
|
},
|
456
498
|
],
|
457
|
-
}
|
499
|
+
}), validationToRule({
|
500
|
+
ruleId: 'known-type-names',
|
501
|
+
ruleName: 'KnownTypeNames',
|
502
|
+
hasDidYouMeanSuggestions: true,
|
503
|
+
}, {
|
458
504
|
category: ['Schema', 'Operations'],
|
459
505
|
description: 'A GraphQL document is only valid if referenced types (specifically variable definitions and fragment conditions) are defined by the type schema.',
|
460
506
|
requiresSchema: true,
|
461
|
-
}), validationToRule(
|
507
|
+
}), validationToRule({
|
508
|
+
ruleId: 'lone-anonymous-operation',
|
509
|
+
ruleName: 'LoneAnonymousOperation',
|
510
|
+
}, {
|
462
511
|
category: 'Operations',
|
463
512
|
description: 'A GraphQL document is only valid if when it contains an anonymous operation (the query short-hand) that it contains only that one operation definition.',
|
464
513
|
requiresSchema: true,
|
465
|
-
}), validationToRule(
|
514
|
+
}), validationToRule({
|
515
|
+
ruleId: 'lone-schema-definition',
|
516
|
+
ruleName: 'LoneSchemaDefinition',
|
517
|
+
}, {
|
466
518
|
category: 'Schema',
|
467
519
|
description: 'A GraphQL document is only valid if it contains only one schema definition.',
|
468
|
-
}), validationToRule(
|
520
|
+
}), validationToRule({
|
521
|
+
ruleId: 'no-fragment-cycles',
|
522
|
+
ruleName: 'NoFragmentCycles',
|
523
|
+
}, {
|
469
524
|
category: 'Operations',
|
470
525
|
description: 'A GraphQL fragment is only valid when it does not have cycles in fragments usage.',
|
471
526
|
requiresSchema: true,
|
472
|
-
}), validationToRule(
|
527
|
+
}), validationToRule({
|
528
|
+
ruleId: 'no-undefined-variables',
|
529
|
+
ruleName: 'NoUndefinedVariables',
|
530
|
+
getDocumentNode: handleMissingFragments,
|
531
|
+
}, {
|
473
532
|
category: 'Operations',
|
474
533
|
description: 'A GraphQL operation is only valid if all variables encountered, both directly and via fragment spreads, are defined by that operation.',
|
475
534
|
requiresSchema: true,
|
476
535
|
requiresSiblings: true,
|
477
|
-
}
|
536
|
+
}), validationToRule({
|
537
|
+
ruleId: 'no-unused-fragments',
|
538
|
+
ruleName: 'NoUnusedFragments',
|
539
|
+
getDocumentNode: ({ ruleId, context, node }) => {
|
540
|
+
const siblings = requireSiblingsOperations(ruleId, context);
|
541
|
+
const FilePathToDocumentsMap = [
|
542
|
+
...siblings.getOperations(),
|
543
|
+
...siblings.getFragments(),
|
544
|
+
].reduce((map, { filePath, document }) => {
|
545
|
+
var _a;
|
546
|
+
(_a = map[filePath]) !== null && _a !== void 0 ? _a : (map[filePath] = []);
|
547
|
+
map[filePath].push(document);
|
548
|
+
return map;
|
549
|
+
}, Object.create(null));
|
550
|
+
const getParentNode = (currentFilePath, node) => {
|
551
|
+
const { fragmentDefs } = getFragmentDefsAndFragmentSpreads(node);
|
552
|
+
if (fragmentDefs.size === 0) {
|
553
|
+
return node;
|
554
|
+
}
|
555
|
+
// skip iteration over documents for current filepath
|
556
|
+
delete FilePathToDocumentsMap[currentFilePath];
|
557
|
+
for (const [filePath, documents] of Object.entries(FilePathToDocumentsMap)) {
|
558
|
+
const missingFragments = getMissingFragments({
|
559
|
+
kind: graphql.Kind.DOCUMENT,
|
560
|
+
definitions: documents,
|
561
|
+
});
|
562
|
+
const isCurrentFileImportFragment = missingFragments.some(fragment => fragmentDefs.has(fragment));
|
563
|
+
if (isCurrentFileImportFragment) {
|
564
|
+
return getParentNode(filePath, {
|
565
|
+
kind: graphql.Kind.DOCUMENT,
|
566
|
+
definitions: [...node.definitions, ...documents],
|
567
|
+
});
|
568
|
+
}
|
569
|
+
}
|
570
|
+
return node;
|
571
|
+
};
|
572
|
+
return getParentNode(context.getFilename(), node);
|
573
|
+
},
|
574
|
+
}, {
|
478
575
|
category: 'Operations',
|
479
576
|
description: 'A GraphQL document is only valid if all fragment definitions are spread within operations, or spread within other fragments spread within operations.',
|
480
577
|
requiresSchema: true,
|
481
578
|
requiresSiblings: true,
|
482
|
-
}, ({
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
].reduce((map, { filePath, document }) => {
|
488
|
-
var _a;
|
489
|
-
(_a = map[filePath]) !== null && _a !== void 0 ? _a : (map[filePath] = []);
|
490
|
-
map[filePath].push(document);
|
491
|
-
return map;
|
492
|
-
}, Object.create(null));
|
493
|
-
const getParentNode = (currentFilePath, node) => {
|
494
|
-
const { fragmentDefs } = getFragmentDefsAndFragmentSpreads(node);
|
495
|
-
if (fragmentDefs.size === 0) {
|
496
|
-
return node;
|
497
|
-
}
|
498
|
-
// skip iteration over documents for current filepath
|
499
|
-
delete FilePathToDocumentsMap[currentFilePath];
|
500
|
-
for (const [filePath, documents] of Object.entries(FilePathToDocumentsMap)) {
|
501
|
-
const missingFragments = getMissingFragments({
|
502
|
-
kind: graphql.Kind.DOCUMENT,
|
503
|
-
definitions: documents,
|
504
|
-
});
|
505
|
-
const isCurrentFileImportFragment = missingFragments.some(fragment => fragmentDefs.has(fragment));
|
506
|
-
if (isCurrentFileImportFragment) {
|
507
|
-
return getParentNode(filePath, {
|
508
|
-
kind: graphql.Kind.DOCUMENT,
|
509
|
-
definitions: [...node.definitions, ...documents],
|
510
|
-
});
|
511
|
-
}
|
512
|
-
}
|
513
|
-
return node;
|
514
|
-
};
|
515
|
-
return getParentNode(context.getFilename(), node);
|
516
|
-
}), validationToRule('no-unused-variables', 'NoUnusedVariables', {
|
579
|
+
}), validationToRule({
|
580
|
+
ruleId: 'no-unused-variables',
|
581
|
+
ruleName: 'NoUnusedVariables',
|
582
|
+
getDocumentNode: handleMissingFragments,
|
583
|
+
}, {
|
517
584
|
category: 'Operations',
|
518
585
|
description: 'A GraphQL operation is only valid if all variables defined by an operation are used, either directly or within a spread fragment.',
|
519
586
|
requiresSchema: true,
|
520
587
|
requiresSiblings: true,
|
521
|
-
}
|
588
|
+
}), validationToRule({
|
589
|
+
ruleId: 'overlapping-fields-can-be-merged',
|
590
|
+
ruleName: 'OverlappingFieldsCanBeMerged',
|
591
|
+
}, {
|
522
592
|
category: 'Operations',
|
523
593
|
description: 'A selection set is only valid if all fields (including spreading any fragments) either correspond to distinct response names or can be merged without ambiguity.',
|
524
594
|
requiresSchema: true,
|
525
|
-
}), validationToRule(
|
595
|
+
}), validationToRule({
|
596
|
+
ruleId: 'possible-fragment-spread',
|
597
|
+
ruleName: 'PossibleFragmentSpreads',
|
598
|
+
}, {
|
526
599
|
category: 'Operations',
|
527
600
|
description: 'A fragment spread is only valid if the type condition could ever possibly be true: if there is a non-empty intersection of the possible parent types, and possible types which pass the type condition.',
|
528
601
|
requiresSchema: true,
|
529
|
-
}), validationToRule(
|
602
|
+
}), validationToRule({
|
603
|
+
ruleId: 'possible-type-extension',
|
604
|
+
ruleName: 'PossibleTypeExtensions',
|
605
|
+
hasDidYouMeanSuggestions: true,
|
606
|
+
}, {
|
530
607
|
category: 'Schema',
|
531
608
|
description: 'A type extension is only valid if the type is defined and has the same kind.',
|
532
609
|
// TODO: add in graphql-eslint v4
|
533
610
|
recommended: false,
|
534
611
|
requiresSchema: true,
|
535
612
|
isDisabledForAllConfig: true,
|
536
|
-
}), validationToRule(
|
613
|
+
}), validationToRule({
|
614
|
+
ruleId: 'provided-required-arguments',
|
615
|
+
ruleName: 'ProvidedRequiredArguments',
|
616
|
+
}, {
|
537
617
|
category: ['Schema', 'Operations'],
|
538
618
|
description: 'A field or directive is only valid if all required (non-null without a default value) field arguments have been provided.',
|
539
619
|
requiresSchema: true,
|
540
|
-
}), validationToRule(
|
620
|
+
}), validationToRule({
|
621
|
+
ruleId: 'scalar-leafs',
|
622
|
+
ruleName: 'ScalarLeafs',
|
623
|
+
hasDidYouMeanSuggestions: true,
|
624
|
+
}, {
|
541
625
|
category: 'Operations',
|
542
626
|
description: 'A GraphQL document is valid only if all leaf fields (fields without sub selections) are of scalar or enum types.',
|
543
627
|
requiresSchema: true,
|
544
|
-
}), validationToRule(
|
628
|
+
}), validationToRule({
|
629
|
+
ruleId: 'one-field-subscriptions',
|
630
|
+
ruleName: 'SingleFieldSubscriptions',
|
631
|
+
}, {
|
545
632
|
category: 'Operations',
|
546
633
|
description: 'A GraphQL subscription is valid only if it contains a single root field.',
|
547
634
|
requiresSchema: true,
|
548
|
-
}), validationToRule(
|
635
|
+
}), validationToRule({
|
636
|
+
ruleId: 'unique-argument-names',
|
637
|
+
ruleName: 'UniqueArgumentNames',
|
638
|
+
}, {
|
549
639
|
category: 'Operations',
|
550
640
|
description: 'A GraphQL field or directive is only valid if all supplied arguments are uniquely named.',
|
551
641
|
requiresSchema: true,
|
552
|
-
}), validationToRule(
|
642
|
+
}), validationToRule({
|
643
|
+
ruleId: 'unique-directive-names',
|
644
|
+
ruleName: 'UniqueDirectiveNames',
|
645
|
+
}, {
|
553
646
|
category: 'Schema',
|
554
647
|
description: 'A GraphQL document is only valid if all defined directives have unique names.',
|
555
|
-
}), validationToRule(
|
648
|
+
}), validationToRule({
|
649
|
+
ruleId: 'unique-directive-names-per-location',
|
650
|
+
ruleName: 'UniqueDirectivesPerLocation',
|
651
|
+
}, {
|
556
652
|
category: ['Schema', 'Operations'],
|
557
653
|
description: 'A GraphQL document is only valid if all non-repeatable directives at a given location are uniquely named.',
|
558
654
|
requiresSchema: true,
|
559
|
-
}), validationToRule(
|
655
|
+
}), validationToRule({
|
656
|
+
ruleId: 'unique-enum-value-names',
|
657
|
+
ruleName: 'UniqueEnumValueNames',
|
658
|
+
}, {
|
560
659
|
category: 'Schema',
|
561
660
|
description: 'A GraphQL enum type is only valid if all its values are uniquely named.',
|
562
661
|
recommended: false,
|
563
662
|
isDisabledForAllConfig: true,
|
564
|
-
}), validationToRule(
|
663
|
+
}), validationToRule({
|
664
|
+
ruleId: 'unique-field-definition-names',
|
665
|
+
ruleName: 'UniqueFieldDefinitionNames',
|
666
|
+
}, {
|
565
667
|
category: 'Schema',
|
566
668
|
description: 'A GraphQL complex type is only valid if all its fields are uniquely named.',
|
567
|
-
}), validationToRule(
|
669
|
+
}), validationToRule({
|
670
|
+
ruleId: 'unique-input-field-names',
|
671
|
+
ruleName: 'UniqueInputFieldNames',
|
672
|
+
}, {
|
568
673
|
category: 'Operations',
|
569
674
|
description: 'A GraphQL input object value is only valid if all supplied fields are uniquely named.',
|
570
|
-
}), validationToRule(
|
675
|
+
}), validationToRule({
|
676
|
+
ruleId: 'unique-operation-types',
|
677
|
+
ruleName: 'UniqueOperationTypes',
|
678
|
+
}, {
|
571
679
|
category: 'Schema',
|
572
680
|
description: 'A GraphQL document is only valid if it has only one type per operation.',
|
573
|
-
}), validationToRule(
|
681
|
+
}), validationToRule({
|
682
|
+
ruleId: 'unique-type-names',
|
683
|
+
ruleName: 'UniqueTypeNames',
|
684
|
+
}, {
|
574
685
|
category: 'Schema',
|
575
686
|
description: 'A GraphQL document is only valid if all defined types have unique names.',
|
576
|
-
}), validationToRule(
|
687
|
+
}), validationToRule({
|
688
|
+
ruleId: 'unique-variable-names',
|
689
|
+
ruleName: 'UniqueVariableNames',
|
690
|
+
}, {
|
577
691
|
category: 'Operations',
|
578
692
|
description: 'A GraphQL operation is only valid if all its variables are uniquely named.',
|
579
693
|
requiresSchema: true,
|
580
|
-
}), validationToRule(
|
694
|
+
}), validationToRule({
|
695
|
+
ruleId: 'value-literals-of-correct-type',
|
696
|
+
ruleName: 'ValuesOfCorrectType',
|
697
|
+
hasDidYouMeanSuggestions: true,
|
698
|
+
}, {
|
581
699
|
category: 'Operations',
|
582
700
|
description: 'A GraphQL document is only valid if all value literals are of the type expected at their position.',
|
583
701
|
requiresSchema: true,
|
584
|
-
}), validationToRule(
|
702
|
+
}), validationToRule({
|
703
|
+
ruleId: 'variables-are-input-types',
|
704
|
+
ruleName: 'VariablesAreInputTypes',
|
705
|
+
}, {
|
585
706
|
category: 'Operations',
|
586
707
|
description: 'A GraphQL operation is only valid if all the variables it defines are of input types (scalar, enum, or input object).',
|
587
708
|
requiresSchema: true,
|
588
|
-
}), validationToRule(
|
709
|
+
}), validationToRule({
|
710
|
+
ruleId: 'variables-in-allowed-position',
|
711
|
+
ruleName: 'VariablesInAllowedPosition',
|
712
|
+
}, {
|
589
713
|
category: 'Operations',
|
590
714
|
description: 'Variables passed to field arguments conform to type.',
|
591
715
|
requiresSchema: true,
|