@graphql-eslint/eslint-plugin 3.0.0-alpha-0a66b90.0 → 3.0.0-alpha-2918431.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/README.md +19 -8
  2. package/configs/base.d.ts +5 -0
  3. package/configs/index.d.ts +78 -121
  4. package/configs/operations-all.d.ts +19 -0
  5. package/configs/operations-recommended.d.ts +51 -0
  6. package/configs/schema-all.d.ts +21 -0
  7. package/configs/schema-recommended.d.ts +42 -0
  8. package/docs/README.md +10 -18
  9. package/docs/deprecated-rules.md +21 -0
  10. package/docs/rules/alphabetize.md +1 -1
  11. package/docs/rules/description-style.md +5 -3
  12. package/docs/rules/executable-definitions.md +2 -2
  13. package/docs/rules/fields-on-correct-type.md +2 -2
  14. package/docs/rules/fragments-on-composite-type.md +2 -2
  15. package/docs/rules/input-name.md +1 -1
  16. package/docs/rules/known-argument-names.md +2 -2
  17. package/docs/rules/known-directives.md +2 -2
  18. package/docs/rules/known-fragment-names.md +2 -2
  19. package/docs/rules/known-type-names.md +2 -2
  20. package/docs/rules/lone-anonymous-operation.md +2 -2
  21. package/docs/rules/lone-schema-definition.md +2 -2
  22. package/docs/rules/match-document-filename.md +6 -4
  23. package/docs/rules/naming-convention.md +140 -41
  24. package/docs/rules/no-anonymous-operations.md +2 -2
  25. package/docs/rules/no-case-insensitive-enum-values-duplicates.md +2 -2
  26. package/docs/rules/no-deprecated.md +3 -1
  27. package/docs/rules/{avoid-duplicate-fields.md → no-duplicate-fields.md} +10 -8
  28. package/docs/rules/no-fragment-cycles.md +2 -2
  29. package/docs/rules/no-hashtag-description.md +3 -1
  30. package/docs/rules/no-root-type.md +9 -14
  31. package/docs/rules/{avoid-scalar-result-type-on-mutation.md → no-scalar-result-type-on-mutation.md} +7 -7
  32. package/docs/rules/no-typename-prefix.md +37 -0
  33. package/docs/rules/no-undefined-variables.md +2 -2
  34. package/docs/rules/no-unreachable-types.md +3 -1
  35. package/docs/rules/no-unused-fields.md +1 -1
  36. package/docs/rules/no-unused-fragments.md +2 -2
  37. package/docs/rules/no-unused-variables.md +2 -2
  38. package/docs/rules/one-field-subscriptions.md +2 -2
  39. package/docs/rules/overlapping-fields-can-be-merged.md +2 -2
  40. package/docs/rules/possible-fragment-spread.md +2 -2
  41. package/docs/rules/possible-type-extension.md +2 -2
  42. package/docs/rules/provided-required-arguments.md +2 -2
  43. package/docs/rules/require-deprecation-date.md +1 -1
  44. package/docs/rules/require-deprecation-reason.md +2 -2
  45. package/docs/rules/require-description.md +36 -22
  46. package/docs/rules/require-field-of-type-query-in-mutation-result.md +1 -1
  47. package/docs/rules/require-id-when-available.md +3 -1
  48. package/docs/rules/scalar-leafs.md +2 -2
  49. package/docs/rules/selection-set-depth.md +9 -2
  50. package/docs/rules/strict-id-in-types.md +16 -10
  51. package/docs/rules/unique-argument-names.md +2 -2
  52. package/docs/rules/unique-directive-names-per-location.md +2 -2
  53. package/docs/rules/unique-directive-names.md +2 -2
  54. package/docs/rules/unique-enum-value-names.md +2 -2
  55. package/docs/rules/unique-field-definition-names.md +2 -2
  56. package/docs/rules/unique-fragment-name.md +1 -1
  57. package/docs/rules/unique-input-field-names.md +2 -2
  58. package/docs/rules/unique-operation-name.md +1 -1
  59. package/docs/rules/unique-operation-types.md +2 -2
  60. package/docs/rules/unique-type-names.md +2 -2
  61. package/docs/rules/unique-variable-names.md +2 -2
  62. package/docs/rules/value-literals-of-correct-type.md +2 -2
  63. package/docs/rules/variables-are-input-types.md +2 -2
  64. package/docs/rules/variables-in-allowed-position.md +2 -2
  65. package/index.js +651 -724
  66. package/index.mjs +652 -725
  67. package/package.json +1 -1
  68. package/rules/alphabetize.d.ts +8 -10
  69. package/rules/description-style.d.ts +4 -6
  70. package/rules/index.d.ts +115 -119
  71. package/rules/input-name.d.ts +1 -1
  72. package/rules/match-document-filename.d.ts +8 -10
  73. package/rules/naming-convention.d.ts +3 -4
  74. package/rules/{avoid-duplicate-fields.d.ts → no-duplicate-fields.d.ts} +0 -0
  75. package/rules/no-root-type.d.ts +1 -1
  76. package/rules/{avoid-scalar-result-type-on-mutation.d.ts → no-scalar-result-type-on-mutation.d.ts} +0 -0
  77. package/rules/{avoid-typename-prefix.d.ts → no-typename-prefix.d.ts} +0 -0
  78. package/rules/require-description.d.ts +2 -3
  79. package/rules/require-id-when-available.d.ts +3 -3
  80. package/rules/selection-set-depth.d.ts +3 -3
  81. package/rules/strict-id-in-types.d.ts +6 -8
  82. package/types.d.ts +9 -5
  83. package/configs/all.d.ts +0 -104
  84. package/configs/recommended.d.ts +0 -72
  85. package/docs/rules/avoid-operation-name-prefix.md +0 -50
  86. package/docs/rules/avoid-typename-prefix.md +0 -37
  87. package/docs/rules/no-operation-name-suffix.md +0 -38
  88. package/rules/avoid-operation-name-prefix.d.ts +0 -9
  89. package/rules/no-operation-name-suffix.d.ts +0 -3
package/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { Kind, validate, isScalarType, TokenKind, isNonNullType, isListType, isObjectType as isObjectType$1, visit, visitWithTypeInfo, GraphQLObjectType, GraphQLInterfaceType, TypeInfo, isInterfaceType, Source, GraphQLError } from 'graphql';
1
+ import { Kind, validate, TokenKind, isScalarType, isNonNullType, isListType, isObjectType as isObjectType$1, visit, visitWithTypeInfo, GraphQLObjectType, GraphQLInterfaceType, TypeInfo, isInterfaceType, Source, GraphQLError } from 'graphql';
2
2
  import { validateSDL } from 'graphql/validation/validate';
3
3
  import { processImport, parseImportLine } from '@graphql-tools/import';
4
4
  import { statSync, existsSync, readFileSync } from 'fs';
@@ -12,14 +12,89 @@ import { CodeFileLoader } from '@graphql-tools/code-file-loader';
12
12
  import { RuleTester, Linter } from 'eslint';
13
13
  import { codeFrameColumns } from '@babel/code-frame';
14
14
 
15
+ const base = {
16
+ parser: '@graphql-eslint/eslint-plugin',
17
+ plugins: ['@graphql-eslint'],
18
+ };
19
+
15
20
  /*
16
21
  * 🚨 IMPORTANT! Do not manually modify this file. Run: `yarn generate-configs`
17
22
  */
18
- const recommendedConfig = {
19
- parser: '@graphql-eslint/eslint-plugin',
20
- plugins: ['@graphql-eslint'],
23
+ const schemaRecommendedConfig = {
24
+ extends: ['plugin:@graphql-eslint/base'],
25
+ rules: {
26
+ '@graphql-eslint/description-style': 'error',
27
+ '@graphql-eslint/known-argument-names': 'error',
28
+ '@graphql-eslint/known-directives': 'error',
29
+ '@graphql-eslint/known-type-names': 'error',
30
+ '@graphql-eslint/lone-schema-definition': 'error',
31
+ '@graphql-eslint/naming-convention': [
32
+ 'error',
33
+ {
34
+ types: 'PascalCase',
35
+ fields: 'camelCase',
36
+ EnumValueDefinition: 'UPPER_CASE',
37
+ 'FieldDefinition[parent.name.value=Query]': {
38
+ forbiddenPrefixes: ['query', 'get'],
39
+ forbiddenSuffixes: ['Query'],
40
+ },
41
+ 'FieldDefinition[parent.name.value=Mutation]': {
42
+ forbiddenPrefixes: ['mutation'],
43
+ forbiddenSuffixes: ['Mutation'],
44
+ },
45
+ 'FieldDefinition[parent.name.value=Subscription]': {
46
+ forbiddenPrefixes: ['subscription'],
47
+ forbiddenSuffixes: ['Subscription'],
48
+ },
49
+ },
50
+ ],
51
+ '@graphql-eslint/no-case-insensitive-enum-values-duplicates': 'error',
52
+ '@graphql-eslint/no-hashtag-description': 'error',
53
+ '@graphql-eslint/no-typename-prefix': 'error',
54
+ '@graphql-eslint/no-unreachable-types': 'error',
55
+ '@graphql-eslint/possible-type-extension': 'error',
56
+ '@graphql-eslint/provided-required-arguments': 'error',
57
+ '@graphql-eslint/require-deprecation-reason': 'error',
58
+ '@graphql-eslint/strict-id-in-types': 'error',
59
+ '@graphql-eslint/unique-directive-names': 'error',
60
+ '@graphql-eslint/unique-directive-names-per-location': 'error',
61
+ '@graphql-eslint/unique-enum-value-names': 'error',
62
+ '@graphql-eslint/unique-field-definition-names': 'error',
63
+ '@graphql-eslint/unique-operation-types': 'error',
64
+ '@graphql-eslint/unique-type-names': 'error',
65
+ },
66
+ };
67
+
68
+ /*
69
+ * 🚨 IMPORTANT! Do not manually modify this file. Run: `yarn generate-configs`
70
+ */
71
+ const schemaAllConfig = {
72
+ extends: ['plugin:@graphql-eslint/base', 'plugin:@graphql-eslint/schema-recommended'],
73
+ rules: {
74
+ '@graphql-eslint/alphabetize': [
75
+ 'error',
76
+ {
77
+ fields: ['ObjectTypeDefinition', 'InterfaceTypeDefinition', 'InputObjectTypeDefinition'],
78
+ values: ['EnumTypeDefinition'],
79
+ arguments: ['FieldDefinition', 'Field', 'DirectiveDefinition', 'Directive'],
80
+ },
81
+ ],
82
+ '@graphql-eslint/input-name': 'error',
83
+ '@graphql-eslint/no-root-type': 'off',
84
+ '@graphql-eslint/no-scalar-result-type-on-mutation': 'error',
85
+ '@graphql-eslint/no-unused-fields': 'off',
86
+ '@graphql-eslint/require-deprecation-date': 'error',
87
+ '@graphql-eslint/require-description': ['error', { types: true, DirectiveDefinition: true }],
88
+ '@graphql-eslint/require-field-of-type-query-in-mutation-result': 'error',
89
+ },
90
+ };
91
+
92
+ /*
93
+ * 🚨 IMPORTANT! Do not manually modify this file. Run: `yarn generate-configs`
94
+ */
95
+ const operationsRecommendedConfig = {
96
+ extends: ['plugin:@graphql-eslint/base'],
21
97
  rules: {
22
- '@graphql-eslint/avoid-typename-prefix': 'error',
23
98
  '@graphql-eslint/executable-definitions': 'error',
24
99
  '@graphql-eslint/fields-on-correct-type': 'error',
25
100
  '@graphql-eslint/fragments-on-composite-type': 'error',
@@ -28,58 +103,36 @@ const recommendedConfig = {
28
103
  '@graphql-eslint/known-fragment-names': 'error',
29
104
  '@graphql-eslint/known-type-names': 'error',
30
105
  '@graphql-eslint/lone-anonymous-operation': 'error',
31
- '@graphql-eslint/lone-schema-definition': 'error',
32
106
  '@graphql-eslint/naming-convention': [
33
107
  'error',
34
108
  {
35
- types: 'PascalCase',
36
- fields: 'camelCase',
37
- overrides: {
38
- EnumValueDefinition: 'UPPER_CASE',
39
- OperationDefinition: {
40
- style: 'PascalCase',
41
- forbiddenPrefixes: ['Query', 'Mutation', 'Subscription', 'Get'],
42
- forbiddenSuffixes: ['Query', 'Mutation', 'Subscription'],
43
- },
44
- FragmentDefinition: { style: 'PascalCase', forbiddenPrefixes: ['Fragment'], forbiddenSuffixes: ['Fragment'] },
45
- 'FieldDefinition[parent.name.value=Query]': {
46
- forbiddenPrefixes: ['query', 'get'],
47
- forbiddenSuffixes: ['Query'],
48
- },
49
- 'FieldDefinition[parent.name.value=Mutation]': {
50
- forbiddenPrefixes: ['mutation'],
51
- forbiddenSuffixes: ['Mutation'],
52
- },
53
- 'FieldDefinition[parent.name.value=Subscription]': {
54
- forbiddenPrefixes: ['subscription'],
55
- forbiddenSuffixes: ['Subscription'],
56
- },
57
- },
109
+ Argument: 'camelCase',
110
+ VariableDefinition: 'camelCase',
111
+ OperationDefinition: {
112
+ style: 'PascalCase',
113
+ forbiddenPrefixes: ['Query', 'Mutation', 'Subscription', 'Get'],
114
+ forbiddenSuffixes: ['Query', 'Mutation', 'Subscription'],
115
+ },
116
+ FragmentDefinition: { style: 'PascalCase', forbiddenPrefixes: ['Fragment'], forbiddenSuffixes: ['Fragment'] },
58
117
  },
59
118
  ],
60
119
  '@graphql-eslint/no-anonymous-operations': 'error',
61
- '@graphql-eslint/no-case-insensitive-enum-values-duplicates': 'error',
120
+ '@graphql-eslint/no-deprecated': 'error',
121
+ '@graphql-eslint/no-duplicate-fields': 'error',
62
122
  '@graphql-eslint/no-fragment-cycles': 'error',
63
- '@graphql-eslint/no-operation-name-suffix': 'error',
64
123
  '@graphql-eslint/no-undefined-variables': 'error',
65
124
  '@graphql-eslint/no-unused-fragments': 'error',
66
125
  '@graphql-eslint/no-unused-variables': 'error',
67
126
  '@graphql-eslint/one-field-subscriptions': 'error',
68
127
  '@graphql-eslint/overlapping-fields-can-be-merged': 'error',
69
128
  '@graphql-eslint/possible-fragment-spread': 'error',
70
- '@graphql-eslint/possible-type-extension': 'error',
71
129
  '@graphql-eslint/provided-required-arguments': 'error',
72
- '@graphql-eslint/require-deprecation-reason': 'error',
130
+ '@graphql-eslint/require-id-when-available': 'error',
73
131
  '@graphql-eslint/scalar-leafs': 'error',
74
- '@graphql-eslint/strict-id-in-types': 'error',
132
+ '@graphql-eslint/selection-set-depth': ['error', { maxDepth: 7 }],
75
133
  '@graphql-eslint/unique-argument-names': 'error',
76
- '@graphql-eslint/unique-directive-names': 'error',
77
134
  '@graphql-eslint/unique-directive-names-per-location': 'error',
78
- '@graphql-eslint/unique-enum-value-names': 'error',
79
- '@graphql-eslint/unique-field-definition-names': 'error',
80
135
  '@graphql-eslint/unique-input-field-names': 'error',
81
- '@graphql-eslint/unique-operation-types': 'error',
82
- '@graphql-eslint/unique-type-names': 'error',
83
136
  '@graphql-eslint/unique-variable-names': 'error',
84
137
  '@graphql-eslint/value-literals-of-correct-type': 'error',
85
138
  '@graphql-eslint/variables-are-input-types': 'error',
@@ -90,44 +143,32 @@ const recommendedConfig = {
90
143
  /*
91
144
  * 🚨 IMPORTANT! Do not manually modify this file. Run: `yarn generate-configs`
92
145
  */
93
- const allConfig = {
94
- ...recommendedConfig,
146
+ const operationsAllConfig = {
147
+ extends: ['plugin:@graphql-eslint/base', 'plugin:@graphql-eslint/operations-recommended'],
95
148
  rules: {
96
- ...recommendedConfig.rules,
97
149
  '@graphql-eslint/alphabetize': [
98
150
  'error',
99
151
  {
100
- fields: ['ObjectTypeDefinition', 'InterfaceTypeDefinition', 'InputObjectTypeDefinition'],
101
- values: ['EnumTypeDefinition'],
102
152
  selections: ['OperationDefinition', 'FragmentDefinition'],
103
153
  variables: ['OperationDefinition'],
104
- arguments: ['FieldDefinition', 'Field', 'DirectiveDefinition', 'Directive'],
154
+ arguments: ['Field', 'Directive'],
105
155
  },
106
156
  ],
107
- '@graphql-eslint/avoid-duplicate-fields': 'error',
108
- '@graphql-eslint/avoid-operation-name-prefix': 'error',
109
- '@graphql-eslint/avoid-scalar-result-type-on-mutation': 'error',
110
- '@graphql-eslint/description-style': 'error',
111
- '@graphql-eslint/input-name': 'error',
112
- '@graphql-eslint/match-document-filename': 'error',
113
- '@graphql-eslint/no-deprecated': 'error',
114
- '@graphql-eslint/no-hashtag-description': 'error',
115
- '@graphql-eslint/no-root-type': ['error', { disallow: ['subscription'] }],
116
- '@graphql-eslint/no-unreachable-types': 'error',
117
- '@graphql-eslint/no-unused-fields': 'error',
118
- '@graphql-eslint/require-deprecation-date': 'error',
119
- '@graphql-eslint/require-description': ['error', { types: true, overrides: { DirectiveDefinition: true } }],
120
- '@graphql-eslint/require-field-of-type-query-in-mutation-result': 'error',
121
- '@graphql-eslint/require-id-when-available': 'error',
122
- '@graphql-eslint/selection-set-depth': 'error',
157
+ '@graphql-eslint/match-document-filename': [
158
+ 'error',
159
+ { query: 'kebab-case', mutation: 'kebab-case', subscription: 'kebab-case', fragment: 'kebab-case' },
160
+ ],
123
161
  '@graphql-eslint/unique-fragment-name': 'error',
124
162
  '@graphql-eslint/unique-operation-name': 'error',
125
163
  },
126
164
  };
127
165
 
128
166
  const configs = {
129
- all: allConfig,
130
- recommended: recommendedConfig,
167
+ base,
168
+ 'schema-recommended': schemaRecommendedConfig,
169
+ 'schema-all': schemaAllConfig,
170
+ 'operations-recommended': operationsRecommendedConfig,
171
+ 'operations-all': operationsAllConfig,
131
172
  };
132
173
 
133
174
  function requireSiblingsOperations(ruleName, context) {
@@ -340,7 +381,6 @@ const validationToRule = (name, ruleName, docs, getDocumentNode) => {
340
381
  docs: {
341
382
  ...docs,
342
383
  graphQLJSRuleName: ruleName,
343
- category: 'Validation',
344
384
  recommended: true,
345
385
  requiresSchema,
346
386
  url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${name}.md`,
@@ -377,16 +417,22 @@ const importFiles = (context) => {
377
417
  return processImport(context.getFilename());
378
418
  };
379
419
  const GRAPHQL_JS_VALIDATIONS = Object.assign({}, validationToRule('executable-definitions', 'ExecutableDefinitions', {
420
+ category: 'Operations',
380
421
  description: `A GraphQL document is only valid for execution if all definitions are either operation or fragment definitions.`,
381
422
  }), validationToRule('fields-on-correct-type', 'FieldsOnCorrectType', {
423
+ category: 'Operations',
382
424
  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`.',
383
425
  }), validationToRule('fragments-on-composite-type', 'FragmentsOnCompositeTypes', {
426
+ category: 'Operations',
384
427
  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.`,
385
428
  }), validationToRule('known-argument-names', 'KnownArgumentNames', {
429
+ category: ['Schema', 'Operations'],
386
430
  description: `A GraphQL field is only valid if all supplied arguments are defined by that field.`,
387
431
  }), validationToRule('known-directives', 'KnownDirectives', {
432
+ category: ['Schema', 'Operations'],
388
433
  description: `A GraphQL document is only valid if all \`@directives\` are known by the schema and legally positioned.`,
389
434
  }), validationToRule('known-fragment-names', 'KnownFragmentNames', {
435
+ category: 'Operations',
390
436
  description: `A GraphQL document is only valid if all \`...Fragment\` fragment spreads refer to fragments defined in the same document.`,
391
437
  examples: [
392
438
  {
@@ -453,17 +499,23 @@ const GRAPHQL_JS_VALIDATIONS = Object.assign({}, validationToRule('executable-de
453
499
  },
454
500
  ],
455
501
  }, importFiles), validationToRule('known-type-names', 'KnownTypeNames', {
502
+ category: ['Schema', 'Operations'],
456
503
  description: `A GraphQL document is only valid if referenced types (specifically variable definitions and fragment conditions) are defined by the type schema.`,
457
504
  }), validationToRule('lone-anonymous-operation', 'LoneAnonymousOperation', {
505
+ category: 'Operations',
458
506
  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.`,
459
507
  }), validationToRule('lone-schema-definition', 'LoneSchemaDefinition', {
508
+ category: 'Schema',
460
509
  description: `A GraphQL document is only valid if it contains only one schema definition.`,
461
510
  requiresSchema: false,
462
511
  }), validationToRule('no-fragment-cycles', 'NoFragmentCycles', {
512
+ category: 'Operations',
463
513
  description: `A GraphQL fragment is only valid when it does not have cycles in fragments usage.`,
464
514
  }), validationToRule('no-undefined-variables', 'NoUndefinedVariables', {
515
+ category: 'Operations',
465
516
  description: `A GraphQL operation is only valid if all variables encountered, both directly and via fragment spreads, are defined by that operation.`,
466
517
  }, importFiles), validationToRule('no-unused-fragments', 'NoUnusedFragments', {
518
+ category: 'Operations',
467
519
  description: `A GraphQL document is only valid if all fragment definitions are spread within operations, or spread within other fragments spread within operations.`,
468
520
  requiresSiblings: true,
469
521
  }, context => {
@@ -493,49 +545,68 @@ const GRAPHQL_JS_VALIDATIONS = Object.assign({}, validationToRule('executable-de
493
545
  };
494
546
  return getParentNode(context.getFilename());
495
547
  }), validationToRule('no-unused-variables', 'NoUnusedVariables', {
548
+ category: 'Operations',
496
549
  description: `A GraphQL operation is only valid if all variables defined by an operation are used, either directly or within a spread fragment.`,
497
550
  }, importFiles), validationToRule('overlapping-fields-can-be-merged', 'OverlappingFieldsCanBeMerged', {
551
+ category: 'Operations',
498
552
  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.`,
499
553
  }), validationToRule('possible-fragment-spread', 'PossibleFragmentSpreads', {
554
+ category: 'Operations',
500
555
  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.`,
501
556
  }), validationToRule('possible-type-extension', 'PossibleTypeExtensions', {
557
+ category: 'Schema',
502
558
  description: `A type extension is only valid if the type is defined and has the same kind.`,
503
559
  requiresSchema: false,
504
560
  }), validationToRule('provided-required-arguments', 'ProvidedRequiredArguments', {
561
+ category: ['Schema', 'Operations'],
505
562
  description: `A field or directive is only valid if all required (non-null without a default value) field arguments have been provided.`,
506
563
  }), validationToRule('scalar-leafs', 'ScalarLeafs', {
564
+ category: 'Operations',
507
565
  description: `A GraphQL document is valid only if all leaf fields (fields without sub selections) are of scalar or enum types.`,
508
566
  }), validationToRule('one-field-subscriptions', 'SingleFieldSubscriptions', {
567
+ category: 'Operations',
509
568
  description: `A GraphQL subscription is valid only if it contains a single root field.`,
510
569
  }), validationToRule('unique-argument-names', 'UniqueArgumentNames', {
570
+ category: 'Operations',
511
571
  description: `A GraphQL field or directive is only valid if all supplied arguments are uniquely named.`,
512
572
  }), validationToRule('unique-directive-names', 'UniqueDirectiveNames', {
573
+ category: 'Schema',
513
574
  description: `A GraphQL document is only valid if all defined directives have unique names.`,
514
575
  requiresSchema: false,
515
576
  }), validationToRule('unique-directive-names-per-location', 'UniqueDirectivesPerLocation', {
577
+ category: ['Schema', 'Operations'],
516
578
  description: `A GraphQL document is only valid if all non-repeatable directives at a given location are uniquely named.`,
517
579
  }), validationToRule('unique-enum-value-names', 'UniqueEnumValueNames', {
580
+ category: 'Schema',
518
581
  description: `A GraphQL enum type is only valid if all its values are uniquely named.`,
519
582
  requiresSchema: false,
520
583
  }), validationToRule('unique-field-definition-names', 'UniqueFieldDefinitionNames', {
584
+ category: 'Schema',
521
585
  description: `A GraphQL complex type is only valid if all its fields are uniquely named.`,
522
586
  requiresSchema: false,
523
587
  }), validationToRule('unique-input-field-names', 'UniqueInputFieldNames', {
588
+ category: 'Operations',
524
589
  description: `A GraphQL input object value is only valid if all supplied fields are uniquely named.`,
525
590
  requiresSchema: false,
526
591
  }), validationToRule('unique-operation-types', 'UniqueOperationTypes', {
592
+ category: 'Schema',
527
593
  description: `A GraphQL document is only valid if it has only one type per operation.`,
528
594
  requiresSchema: false,
529
595
  }), validationToRule('unique-type-names', 'UniqueTypeNames', {
596
+ category: 'Schema',
530
597
  description: `A GraphQL document is only valid if all defined types have unique names.`,
531
598
  requiresSchema: false,
532
599
  }), validationToRule('unique-variable-names', 'UniqueVariableNames', {
600
+ category: 'Operations',
533
601
  description: `A GraphQL operation is only valid if all its variables are uniquely named.`,
534
602
  }), validationToRule('value-literals-of-correct-type', 'ValuesOfCorrectType', {
603
+ category: 'Operations',
535
604
  description: `A GraphQL document is only valid if all value literals are of the type expected at their position.`,
536
605
  }), validationToRule('variables-are-input-types', 'VariablesAreInputTypes', {
606
+ category: 'Operations',
537
607
  description: `A GraphQL operation is only valid if all the variables it defines are of input types (scalar, enum, or input object).`,
538
608
  }), validationToRule('variables-in-allowed-position', 'VariablesInAllowedPosition', {
609
+ category: 'Operations',
539
610
  description: `Variables passed to field arguments conform to type.`,
540
611
  }));
541
612
 
@@ -561,7 +632,7 @@ const rule = {
561
632
  meta: {
562
633
  type: 'suggestion',
563
634
  docs: {
564
- category: 'Best Practices',
635
+ category: ['Schema', 'Operations'],
565
636
  description: 'Enforce arrange in alphabetical order for type fields, enum values, input object fields, operation selections and more.',
566
637
  url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/alphabetize.md',
567
638
  examples: [
@@ -640,15 +711,22 @@ const rule = {
640
711
  `,
641
712
  },
642
713
  ],
643
- optionsForConfig: [
644
- {
645
- fields: fieldsEnum,
646
- values: valuesEnum,
647
- selections: selectionsEnum,
648
- variables: variablesEnum,
649
- arguments: argumentsEnum,
650
- },
651
- ],
714
+ configOptions: {
715
+ schema: [
716
+ {
717
+ fields: fieldsEnum,
718
+ values: valuesEnum,
719
+ arguments: argumentsEnum,
720
+ },
721
+ ],
722
+ operations: [
723
+ {
724
+ selections: selectionsEnum,
725
+ variables: variablesEnum,
726
+ arguments: [Kind.FIELD, Kind.DIRECTIVE],
727
+ },
728
+ ],
729
+ },
652
730
  },
653
731
  messages: {
654
732
  [ALPHABETIZE]: '"{{ currName }}" should be before "{{ prevName }}"',
@@ -786,430 +864,128 @@ const rule = {
786
864
  },
787
865
  };
788
866
 
789
- const AVOID_DUPLICATE_FIELDS = 'AVOID_DUPLICATE_FIELDS';
790
867
  const rule$1 = {
791
868
  meta: {
792
869
  type: 'suggestion',
793
870
  docs: {
794
- description: `Checks for duplicate fields in selection set, variables in operation definition, or in arguments set of a field.`,
795
- category: 'Stylistic Issues',
796
- url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/avoid-duplicate-fields.md',
797
871
  examples: [
798
872
  {
799
873
  title: 'Incorrect',
874
+ usage: [{ style: 'inline' }],
800
875
  code: /* GraphQL */ `
801
- query {
802
- user {
803
- name
804
- email
805
- name # duplicate field
806
- }
807
- }
808
- `,
809
- },
810
- {
811
- title: 'Incorrect',
812
- code: /* GraphQL */ `
813
- query {
814
- users(
815
- first: 100
816
- skip: 50
817
- after: "cji629tngfgou0b73kt7vi5jo"
818
- first: 100 # duplicate argument
819
- ) {
820
- id
821
- }
876
+ """ Description """
877
+ type someTypeName {
878
+ # ...
822
879
  }
823
880
  `,
824
881
  },
825
882
  {
826
- title: 'Incorrect',
883
+ title: 'Correct',
884
+ usage: [{ style: 'inline' }],
827
885
  code: /* GraphQL */ `
828
- query (
829
- $first: Int!
830
- $first: Int! # duplicate variable
831
- ) {
832
- users(first: $first, skip: 50) {
833
- id
834
- }
886
+ " Description "
887
+ type someTypeName {
888
+ # ...
835
889
  }
836
890
  `,
837
891
  },
838
892
  ],
893
+ description: 'Require all comments to follow the same style (either block or inline).',
894
+ category: 'Schema',
895
+ url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/description-style.md',
896
+ recommended: true,
839
897
  },
840
- messages: {
841
- [AVOID_DUPLICATE_FIELDS]: `{{ type }} "{{ fieldName }}" defined multiple times`,
842
- },
843
- schema: [],
898
+ schema: [
899
+ {
900
+ type: 'object',
901
+ additionalProperties: false,
902
+ properties: {
903
+ style: {
904
+ enum: ['block', 'inline'],
905
+ default: 'block',
906
+ },
907
+ },
908
+ },
909
+ ],
844
910
  },
845
911
  create(context) {
846
- function checkNode(usedFields, fieldName, type, node) {
847
- if (usedFields.has(fieldName)) {
912
+ const { style = 'block' } = context.options[0] || {};
913
+ const isBlock = style === 'block';
914
+ return {
915
+ [`.description[type=StringValue][block!=${isBlock}]`](node) {
848
916
  context.report({
849
- loc: getLocation((node.kind === Kind.FIELD && node.alias ? node.alias : node).loc, fieldName, {
850
- offsetEnd: node.kind === Kind.VARIABLE_DEFINITION ? 0 : 1,
851
- }),
852
- messageId: AVOID_DUPLICATE_FIELDS,
853
- data: {
854
- type,
855
- fieldName,
856
- },
917
+ loc: getLocation(node.loc),
918
+ message: `Unexpected ${isBlock ? 'inline' : 'block'} description`,
857
919
  });
858
- }
859
- else {
860
- usedFields.add(fieldName);
861
- }
862
- }
863
- return {
864
- OperationDefinition(node) {
865
- const set = new Set();
866
- for (const varDef of node.variableDefinitions) {
867
- checkNode(set, varDef.variable.name.value, 'Operation variable', varDef);
868
- }
869
- },
870
- Field(node) {
871
- const set = new Set();
872
- for (const arg of node.arguments) {
873
- checkNode(set, arg.name.value, 'Field argument', arg);
874
- }
875
- },
876
- SelectionSet(node) {
877
- var _a;
878
- const set = new Set();
879
- for (const selection of node.selections) {
880
- if (selection.kind === Kind.FIELD) {
881
- checkNode(set, ((_a = selection.alias) === null || _a === void 0 ? void 0 : _a.value) || selection.name.value, 'Field', selection);
882
- }
883
- }
884
920
  },
885
921
  };
886
922
  },
887
923
  };
888
924
 
889
- const AVOID_OPERATION_NAME_PREFIX = 'AVOID_OPERATION_NAME_PREFIX';
925
+ const isObjectType = (node) => [Kind.OBJECT_TYPE_DEFINITION, Kind.OBJECT_TYPE_EXTENSION].includes(node.type);
926
+ const isQueryType = (node) => isObjectType(node) && node.name.value === 'Query';
927
+ const isMutationType = (node) => isObjectType(node) && node.name.value === 'Mutation';
890
928
  const rule$2 = {
891
929
  meta: {
892
930
  type: 'suggestion',
893
931
  docs: {
894
- description: 'Enforce/avoid operation name prefix, useful if you wish to avoid prefix in your root fields, or avoid using REST terminology in your schema.',
895
- category: 'Stylistic Issues',
896
- url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/avoid-operation-name-prefix.md',
932
+ description: 'Require mutation argument to be always called "input" and input type to be called Mutation name + "Input".\nUsing the same name for all input parameters will make your schemas easier to consume and more predictable. Using the same name as mutation for InputType will make it easier to find mutations that InputType belongs to.',
933
+ category: 'Schema',
934
+ url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/input-name.md',
897
935
  examples: [
898
936
  {
899
937
  title: 'Incorrect',
900
- usage: [{ keywords: ['get'] }],
938
+ usage: [{ checkInputType: true }],
901
939
  code: /* GraphQL */ `
902
- query getUserDetails {
903
- # ...
904
- }`,
940
+ type Mutation {
941
+ SetMessage(message: InputMessage): String
942
+ }
943
+ `,
905
944
  },
906
945
  {
907
- title: 'Correct',
908
- usage: [{ keywords: ['get'] }],
946
+ title: 'Correct (with checkInputType)',
947
+ usage: [{ checkInputType: true }],
909
948
  code: /* GraphQL */ `
910
- query userDetails {
911
- # ...
912
- }`,
949
+ type Mutation {
950
+ SetMessage(input: SetMessageInput): String
951
+ }
952
+ `,
953
+ },
954
+ {
955
+ title: 'Correct (without checkInputType)',
956
+ usage: [{ checkInputType: false }],
957
+ code: /* GraphQL */ `
958
+ type Mutation {
959
+ SetMessage(input: AnyInputTypeName): String
960
+ }
961
+ `,
913
962
  },
914
963
  ],
915
964
  },
916
- messages: {
917
- [AVOID_OPERATION_NAME_PREFIX]: `Forbidden operation name prefix: "{{ invalidPrefix }}"`,
918
- },
919
965
  schema: [
920
966
  {
921
- additionalProperties: false,
922
967
  type: 'object',
923
- required: ['keywords'],
968
+ additionalProperties: false,
924
969
  properties: {
925
- caseSensitive: {
970
+ checkInputType: {
971
+ type: 'boolean',
926
972
  default: false,
973
+ description: 'Check that the input type name follows the convention <mutationName>Input',
974
+ },
975
+ caseSensitiveInputType: {
927
976
  type: 'boolean',
977
+ default: true,
978
+ description: 'Allow for case discrepancies in the input type name',
928
979
  },
929
- keywords: {
930
- additionalItems: false,
931
- type: 'array',
932
- minItems: 1,
933
- items: {
934
- type: 'string',
935
- },
936
- },
937
- },
938
- },
939
- ],
940
- },
941
- create(context) {
942
- return {
943
- 'OperationDefinition, FragmentDefinition'(node) {
944
- const config = context.options[0] || { keywords: [], caseSensitive: false };
945
- const caseSensitive = config.caseSensitive;
946
- const keywords = config.keywords || [];
947
- if (node && node.name && node.name.value !== '') {
948
- for (const keyword of keywords) {
949
- const testKeyword = caseSensitive ? keyword : keyword.toLowerCase();
950
- const testName = caseSensitive ? node.name.value : node.name.value.toLowerCase();
951
- if (testName.startsWith(testKeyword)) {
952
- const { start } = node.name.loc;
953
- context.report({
954
- loc: {
955
- start: {
956
- line: start.line,
957
- column: start.column - 1,
958
- },
959
- end: {
960
- line: start.line,
961
- column: start.column - 1 + testKeyword.length,
962
- },
963
- },
964
- data: {
965
- invalidPrefix: keyword,
966
- },
967
- messageId: AVOID_OPERATION_NAME_PREFIX,
968
- });
969
- }
970
- }
971
- }
972
- },
973
- };
974
- },
975
- };
976
-
977
- const rule$3 = {
978
- meta: {
979
- type: 'suggestion',
980
- docs: {
981
- category: 'Best Practices',
982
- description: 'Avoid scalar result type on mutation type to make sure to return a valid state.',
983
- url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/avoid-scalar-result-type-on-mutation.md',
984
- requiresSchema: true,
985
- examples: [
986
- {
987
- title: 'Incorrect',
988
- code: /* GraphQL */ `
989
- type Mutation {
990
- createUser: Boolean
991
- }
992
- `,
993
- },
994
- {
995
- title: 'Correct',
996
- code: /* GraphQL */ `
997
- type Mutation {
998
- createUser: User!
999
- }
1000
- `,
1001
- },
1002
- ],
1003
- },
1004
- schema: [],
1005
- },
1006
- create(context) {
1007
- const schema = requireGraphQLSchemaFromContext('avoid-scalar-result-type-on-mutation', context);
1008
- const mutationType = schema.getMutationType();
1009
- if (!mutationType) {
1010
- return {};
1011
- }
1012
- const selector = [
1013
- `:matches(${Kind.OBJECT_TYPE_DEFINITION}, ${Kind.OBJECT_TYPE_EXTENSION})[name.value=${mutationType.name}]`,
1014
- '>',
1015
- Kind.FIELD_DEFINITION,
1016
- Kind.NAMED_TYPE,
1017
- ].join(' ');
1018
- return {
1019
- [selector](node) {
1020
- const typeName = node.name.value;
1021
- const graphQLType = schema.getType(typeName);
1022
- if (isScalarType(graphQLType)) {
1023
- context.report({
1024
- loc: getLocation(node.loc, typeName),
1025
- message: `Unexpected scalar result type "${typeName}"`,
1026
- });
1027
- }
1028
- },
1029
- };
1030
- },
1031
- };
1032
-
1033
- const AVOID_TYPENAME_PREFIX = 'AVOID_TYPENAME_PREFIX';
1034
- const rule$4 = {
1035
- meta: {
1036
- type: 'suggestion',
1037
- docs: {
1038
- category: 'Best Practices',
1039
- description: 'Enforces users to avoid using the type name in a field name while defining your schema.',
1040
- recommended: true,
1041
- url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/avoid-typename-prefix.md',
1042
- examples: [
1043
- {
1044
- title: 'Incorrect',
1045
- code: /* GraphQL */ `
1046
- type User {
1047
- userId: ID!
1048
- }
1049
- `,
1050
- },
1051
- {
1052
- title: 'Correct',
1053
- code: /* GraphQL */ `
1054
- type User {
1055
- id: ID!
1056
- }
1057
- `,
1058
- },
1059
- ],
1060
- },
1061
- messages: {
1062
- [AVOID_TYPENAME_PREFIX]: `Field "{{ fieldName }}" starts with the name of the parent type "{{ typeName }}"`,
1063
- },
1064
- schema: [],
1065
- },
1066
- create(context) {
1067
- return {
1068
- 'ObjectTypeDefinition, ObjectTypeExtension, InterfaceTypeDefinition, InterfaceTypeExtension'(node) {
1069
- const typeName = node.name.value;
1070
- const lowerTypeName = typeName.toLowerCase();
1071
- for (const field of node.fields) {
1072
- const fieldName = field.name.value;
1073
- if (fieldName.toLowerCase().startsWith(lowerTypeName)) {
1074
- context.report({
1075
- data: {
1076
- fieldName,
1077
- typeName,
1078
- },
1079
- messageId: AVOID_TYPENAME_PREFIX,
1080
- loc: getLocation(field.loc, lowerTypeName),
1081
- });
1082
- }
1083
- }
1084
- },
1085
- };
1086
- },
1087
- };
1088
-
1089
- const rule$5 = {
1090
- meta: {
1091
- type: 'suggestion',
1092
- docs: {
1093
- examples: [
1094
- {
1095
- title: 'Incorrect',
1096
- usage: [{ style: 'inline' }],
1097
- code: /* GraphQL */ `
1098
- """ Description """
1099
- type someTypeName {
1100
- # ...
1101
- }
1102
- `,
1103
- },
1104
- {
1105
- title: 'Correct',
1106
- usage: [{ style: 'inline' }],
1107
- code: /* GraphQL */ `
1108
- " Description "
1109
- type someTypeName {
1110
- # ...
1111
- }
1112
- `,
1113
- },
1114
- ],
1115
- description: 'Require all comments to follow the same style (either block or inline).',
1116
- category: 'Stylistic Issues',
1117
- url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/description-style.md',
1118
- },
1119
- schema: [
1120
- {
1121
- type: 'object',
1122
- properties: {
1123
- style: {
1124
- type: 'string',
1125
- enum: ['block', 'inline'],
1126
- default: 'inline',
1127
- },
1128
- },
1129
- additionalProperties: false,
1130
- },
1131
- ],
1132
- },
1133
- create(context) {
1134
- const { style } = context.options[0] || { style: 'inline' };
1135
- const wrongDescriptionType = style === 'block' ? 'inline' : 'block';
1136
- return {
1137
- '[description.type="StringValue"]': node => {
1138
- if (node.description.block !== (style === 'block')) {
1139
- context.report({
1140
- loc: getLocation(node.description.loc),
1141
- message: `Unexpected ${wrongDescriptionType} description`,
1142
- });
1143
- }
1144
- },
1145
- };
1146
- },
1147
- };
1148
-
1149
- const isObjectType = (node) => [Kind.OBJECT_TYPE_DEFINITION, Kind.OBJECT_TYPE_EXTENSION].includes(node.type);
1150
- const isQueryType = (node) => isObjectType(node) && node.name.value === 'Query';
1151
- const isMutationType = (node) => isObjectType(node) && node.name.value === 'Mutation';
1152
- const rule$6 = {
1153
- meta: {
1154
- type: 'suggestion',
1155
- docs: {
1156
- description: 'Require mutation argument to be always called "input" and input type to be called Mutation name + "Input".\nUsing the same name for all input parameters will make your schemas easier to consume and more predictable. Using the same name as mutation for InputType will make it easier to find mutations that InputType belongs to.',
1157
- category: 'Stylistic Issues',
1158
- url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/input-name.md',
1159
- examples: [
1160
- {
1161
- title: 'Incorrect',
1162
- usage: [{ checkInputType: true }],
1163
- code: /* GraphQL */ `
1164
- type Mutation {
1165
- SetMessage(message: InputMessage): String
1166
- }
1167
- `,
1168
- },
1169
- {
1170
- title: 'Correct (with checkInputType)',
1171
- usage: [{ checkInputType: true }],
1172
- code: /* GraphQL */ `
1173
- type Mutation {
1174
- SetMessage(input: SetMessageInput): String
1175
- }
1176
- `,
1177
- },
1178
- {
1179
- title: 'Correct (without checkInputType)',
1180
- usage: [{ checkInputType: false }],
1181
- code: /* GraphQL */ `
1182
- type Mutation {
1183
- SetMessage(input: AnyInputTypeName): String
1184
- }
1185
- `,
1186
- },
1187
- ],
1188
- },
1189
- schema: [
1190
- {
1191
- type: 'object',
1192
- additionalProperties: false,
1193
- properties: {
1194
- checkInputType: {
1195
- type: 'boolean',
1196
- default: false,
1197
- description: 'Check that the input type name follows the convention <mutationName>Input',
1198
- },
1199
- caseSensitiveInputType: {
1200
- type: 'boolean',
1201
- default: true,
1202
- description: 'Allow for case discrepancies in the input type name',
1203
- },
1204
- checkQueries: {
1205
- type: 'boolean',
1206
- default: false,
1207
- description: 'Apply the rule to Queries',
1208
- },
1209
- checkMutations: {
1210
- type: 'boolean',
1211
- default: true,
1212
- description: 'Apply the rule to Mutations',
980
+ checkQueries: {
981
+ type: 'boolean',
982
+ default: false,
983
+ description: 'Apply the rule to Queries',
984
+ },
985
+ checkMutations: {
986
+ type: 'boolean',
987
+ default: true,
988
+ description: 'Apply the rule to Mutations',
1213
989
  },
1214
990
  },
1215
991
  },
@@ -1275,11 +1051,11 @@ const CASE_STYLES = [
1275
1051
  const schemaOption = {
1276
1052
  oneOf: [{ $ref: '#/definitions/asString' }, { $ref: '#/definitions/asObject' }],
1277
1053
  };
1278
- const rule$7 = {
1054
+ const rule$3 = {
1279
1055
  meta: {
1280
1056
  type: 'suggestion',
1281
1057
  docs: {
1282
- category: 'Best Practices',
1058
+ category: 'Operations',
1283
1059
  description: 'This rule allows you to enforce that the file name should match the operation name.',
1284
1060
  url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/match-document-filename.md`,
1285
1061
  examples: [
@@ -1353,6 +1129,14 @@ const rule$7 = {
1353
1129
  `,
1354
1130
  },
1355
1131
  ],
1132
+ configOptions: [
1133
+ {
1134
+ query: CaseStyle.kebabCase,
1135
+ mutation: CaseStyle.kebabCase,
1136
+ subscription: CaseStyle.kebabCase,
1137
+ fragment: CaseStyle.kebabCase,
1138
+ },
1139
+ ],
1356
1140
  },
1357
1141
  messages: {
1358
1142
  [MATCH_EXTENSION]: `File extension "{{ fileExtension }}" don't match extension "{{ expectedFileExtension }}"`,
@@ -1361,27 +1145,29 @@ const rule$7 = {
1361
1145
  schema: {
1362
1146
  definitions: {
1363
1147
  asString: {
1364
- type: 'string',
1365
- description: `One of: ${CASE_STYLES.map(t => `\`${t}\``).join(', ')}`,
1366
1148
  enum: CASE_STYLES,
1149
+ description: `One of: ${CASE_STYLES.map(t => `\`${t}\``).join(', ')}`,
1367
1150
  },
1368
1151
  asObject: {
1369
1152
  type: 'object',
1153
+ additionalProperties: false,
1370
1154
  properties: {
1371
1155
  style: {
1372
- type: 'string',
1373
1156
  enum: CASE_STYLES,
1374
1157
  },
1158
+ suffix: {
1159
+ type: 'string',
1160
+ },
1375
1161
  },
1376
1162
  },
1377
1163
  },
1378
- $schema: 'http://json-schema.org/draft-04/schema#',
1379
1164
  type: 'array',
1165
+ maxItems: 1,
1380
1166
  items: {
1381
1167
  type: 'object',
1168
+ additionalProperties: false,
1382
1169
  properties: {
1383
1170
  fileExtension: {
1384
- type: 'string',
1385
1171
  enum: ACCEPTED_EXTENSIONS,
1386
1172
  },
1387
1173
  query: schemaOption,
@@ -1456,13 +1242,7 @@ const rule$7 = {
1456
1242
  },
1457
1243
  };
1458
1244
 
1459
- const FIELDS_KINDS = [
1460
- Kind.FIELD_DEFINITION,
1461
- Kind.INPUT_VALUE_DEFINITION,
1462
- Kind.VARIABLE_DEFINITION,
1463
- Kind.ARGUMENT,
1464
- Kind.DIRECTIVE_DEFINITION,
1465
- ];
1245
+ const FIELDS_KINDS = [Kind.FIELD_DEFINITION, Kind.INPUT_VALUE_DEFINITION, Kind.ARGUMENT, Kind.DIRECTIVE_DEFINITION];
1466
1246
  const KindToDisplayName = {
1467
1247
  // types
1468
1248
  [Kind.OBJECT_TYPE_DEFINITION]: 'Type',
@@ -1474,13 +1254,13 @@ const KindToDisplayName = {
1474
1254
  // fields
1475
1255
  [Kind.FIELD_DEFINITION]: 'Field',
1476
1256
  [Kind.INPUT_VALUE_DEFINITION]: 'Input property',
1477
- [Kind.VARIABLE_DEFINITION]: 'Variable',
1478
1257
  [Kind.ARGUMENT]: 'Argument',
1479
1258
  [Kind.DIRECTIVE_DEFINITION]: 'Directive',
1480
1259
  // rest
1481
1260
  [Kind.ENUM_VALUE_DEFINITION]: 'Enumeration value',
1482
1261
  [Kind.OPERATION_DEFINITION]: 'Operation',
1483
1262
  [Kind.FRAGMENT_DEFINITION]: 'Fragment',
1263
+ [Kind.VARIABLE_DEFINITION]: 'Variable',
1484
1264
  };
1485
1265
  const StyleToRegex = {
1486
1266
  camelCase: /^[a-z][\dA-Za-z]*$/,
@@ -1493,12 +1273,12 @@ const ALLOWED_STYLES = Object.keys(StyleToRegex);
1493
1273
  const schemaOption$1 = {
1494
1274
  oneOf: [{ $ref: '#/definitions/asString' }, { $ref: '#/definitions/asObject' }],
1495
1275
  };
1496
- const rule$8 = {
1276
+ const rule$4 = {
1497
1277
  meta: {
1498
1278
  type: 'suggestion',
1499
1279
  docs: {
1500
1280
  description: 'Require names to follow specified conventions.',
1501
- category: 'Best Practices',
1281
+ category: ['Schema', 'Operations'],
1502
1282
  recommended: true,
1503
1283
  url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/naming-convention.md',
1504
1284
  examples: [
@@ -1521,22 +1301,12 @@ const rule$8 = {
1521
1301
  `,
1522
1302
  },
1523
1303
  ],
1524
- optionsForConfig: [
1525
- {
1526
- types: 'PascalCase',
1527
- fields: 'camelCase',
1528
- overrides: {
1304
+ configOptions: {
1305
+ schema: [
1306
+ {
1307
+ types: 'PascalCase',
1308
+ fields: 'camelCase',
1529
1309
  EnumValueDefinition: 'UPPER_CASE',
1530
- OperationDefinition: {
1531
- style: 'PascalCase',
1532
- forbiddenPrefixes: ['Query', 'Mutation', 'Subscription', 'Get'],
1533
- forbiddenSuffixes: ['Query', 'Mutation', 'Subscription'],
1534
- },
1535
- FragmentDefinition: {
1536
- style: 'PascalCase',
1537
- forbiddenPrefixes: ['Fragment'],
1538
- forbiddenSuffixes: ['Fragment'],
1539
- },
1540
1310
  'FieldDefinition[parent.name.value=Query]': {
1541
1311
  forbiddenPrefixes: ['query', 'get'],
1542
1312
  forbiddenSuffixes: ['Query'],
@@ -1550,8 +1320,24 @@ const rule$8 = {
1550
1320
  forbiddenSuffixes: ['Subscription'],
1551
1321
  },
1552
1322
  },
1553
- },
1554
- ],
1323
+ ],
1324
+ operations: [
1325
+ {
1326
+ Argument: 'camelCase',
1327
+ VariableDefinition: 'camelCase',
1328
+ OperationDefinition: {
1329
+ style: 'PascalCase',
1330
+ forbiddenPrefixes: ['Query', 'Mutation', 'Subscription', 'Get'],
1331
+ forbiddenSuffixes: ['Query', 'Mutation', 'Subscription'],
1332
+ },
1333
+ FragmentDefinition: {
1334
+ style: 'PascalCase',
1335
+ forbiddenPrefixes: ['Fragment'],
1336
+ forbiddenSuffixes: ['Fragment'],
1337
+ },
1338
+ },
1339
+ ],
1340
+ },
1555
1341
  },
1556
1342
  schema: {
1557
1343
  definitions: {
@@ -1587,14 +1373,6 @@ const rule$8 = {
1587
1373
  type: 'object',
1588
1374
  additionalProperties: false,
1589
1375
  properties: {
1590
- types: {
1591
- ...schemaOption$1,
1592
- description: `Includes:\n\n${TYPES_KINDS.map(kind => `- [${kind}](https://spec.graphql.org/October2021/#${kind})`).join('\n')}`,
1593
- },
1594
- fields: {
1595
- ...schemaOption$1,
1596
- description: `Includes:\n\n${FIELDS_KINDS.map(kind => `- [${kind}](https://spec.graphql.org/October2021/#${kind})`).join('\n')}`,
1597
- },
1598
1376
  allowLeadingUnderscore: {
1599
1377
  type: 'boolean',
1600
1378
  default: false,
@@ -1603,35 +1381,42 @@ const rule$8 = {
1603
1381
  type: 'boolean',
1604
1382
  default: false,
1605
1383
  },
1606
- overrides: {
1607
- type: 'object',
1608
- additionalProperties: false,
1609
- description: [
1610
- 'May contain the following `ASTNode` names:',
1611
- '',
1612
- ...ALLOWED_KINDS.map(kind => `- [${kind}](https://spec.graphql.org/October2021/#${kind})`),
1613
- '',
1614
- "> It's also possible to use a [`selector`](https://eslint.org/docs/developer-guide/selectors) that starts with `ASTNode` name",
1615
- '>',
1616
- '> Example: pattern property `FieldDefinition[parent.name.value=Query]` will match only fields for type `Query`',
1617
- ].join('\n'),
1618
- patternProperties: {
1619
- [`^(${ALLOWED_KINDS.join('|')})(.+)?$`]: schemaOption$1,
1620
- },
1384
+ types: {
1385
+ ...schemaOption$1,
1386
+ description: `Includes:\n\n${TYPES_KINDS.map(kind => `- \`${kind}\``).join('\n')}`,
1621
1387
  },
1622
- },
1388
+ fields: {
1389
+ ...schemaOption$1,
1390
+ description: `Includes:\n\n${FIELDS_KINDS.map(kind => `- \`${kind}\``).join('\n')}`,
1391
+ },
1392
+ ...Object.fromEntries(ALLOWED_KINDS.map(kind => [
1393
+ kind,
1394
+ {
1395
+ ...schemaOption$1,
1396
+ description: `Read more about this kind on [spec.graphql.org](https://spec.graphql.org/October2021/#${kind}).`,
1397
+ },
1398
+ ])),
1399
+ },
1400
+ patternProperties: {
1401
+ [`^(${ALLOWED_KINDS.join('|')})(.+)?$`]: schemaOption$1,
1402
+ },
1403
+ description: [
1404
+ "> It's possible to use a [`selector`](https://eslint.org/docs/developer-guide/selectors) that starts with allowed `ASTNode` names which are described below.",
1405
+ '>',
1406
+ '> Paste or drop code into the editor in [ASTExplorer](https://astexplorer.net) and inspect the generated AST to compose your selector.',
1407
+ '>',
1408
+ '> Example: pattern property `FieldDefinition[parent.name.value=Query]` will match only fields for type `Query`.',
1409
+ ].join('\n'),
1623
1410
  },
1624
1411
  },
1625
1412
  },
1626
1413
  create(context) {
1627
- const options = {
1628
- overrides: {},
1629
- ...context.options[0],
1630
- };
1414
+ const options = context.options[0] || {};
1415
+ const { allowLeadingUnderscore, allowTrailingUnderscore, types, fields, ...restOptions } = options;
1631
1416
  function normalisePropertyOption(kind) {
1632
- let style = options.overrides[kind];
1417
+ let style = options[kind];
1633
1418
  if (!style) {
1634
- style = TYPES_KINDS.includes(kind) ? options.types : options.fields;
1419
+ style = TYPES_KINDS.includes(kind) ? types : fields;
1635
1420
  }
1636
1421
  return typeof style === 'object' ? style : { style };
1637
1422
  }
@@ -1652,10 +1437,10 @@ const rule$8 = {
1652
1437
  }
1653
1438
  function getErrorMessage() {
1654
1439
  let name = nodeName;
1655
- if (options.allowLeadingUnderscore) {
1440
+ if (allowLeadingUnderscore) {
1656
1441
  name = name.replace(/^_*/, '');
1657
1442
  }
1658
- if (options.allowTrailingUnderscore) {
1443
+ if (allowTrailingUnderscore) {
1659
1444
  name = name.replace(/_*$/, '');
1660
1445
  }
1661
1446
  if (prefix && !name.startsWith(prefix)) {
@@ -1689,15 +1474,13 @@ const rule$8 = {
1689
1474
  });
1690
1475
  };
1691
1476
  const listeners = {};
1692
- if (!options.allowLeadingUnderscore) {
1477
+ if (!allowLeadingUnderscore) {
1693
1478
  listeners['Name[value=/^_/]:matches([parent.kind!=Field], [parent.kind=Field][parent.alias])'] = checkUnderscore;
1694
1479
  }
1695
- if (!options.allowTrailingUnderscore) {
1480
+ if (!allowTrailingUnderscore) {
1696
1481
  listeners['Name[value=/_$/]:matches([parent.kind!=Field], [parent.kind=Field][parent.alias])'] = checkUnderscore;
1697
1482
  }
1698
- const selectors = new Set([options.types && TYPES_KINDS, options.fields && FIELDS_KINDS, Object.keys(options.overrides)]
1699
- .flat()
1700
- .filter(Boolean));
1483
+ const selectors = new Set([types && TYPES_KINDS, fields && FIELDS_KINDS, Object.keys(restOptions)].flat().filter(Boolean));
1701
1484
  for (const selector of selectors) {
1702
1485
  listeners[selector] = checkNode(selector);
1703
1486
  }
@@ -1706,11 +1489,11 @@ const rule$8 = {
1706
1489
  };
1707
1490
 
1708
1491
  const NO_ANONYMOUS_OPERATIONS = 'NO_ANONYMOUS_OPERATIONS';
1709
- const rule$9 = {
1492
+ const rule$5 = {
1710
1493
  meta: {
1711
1494
  type: 'suggestion',
1712
1495
  docs: {
1713
- category: 'Best Practices',
1496
+ category: 'Operations',
1714
1497
  description: 'Require name for your GraphQL operations. This is useful since most GraphQL client libraries are using the operation name for caching purposes.',
1715
1498
  recommended: true,
1716
1499
  url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/no-anonymous-operations.md',
@@ -1754,12 +1537,12 @@ const rule$9 = {
1754
1537
  };
1755
1538
 
1756
1539
  const ERROR_MESSAGE_ID = 'NO_CASE_INSENSITIVE_ENUM_VALUES_DUPLICATES';
1757
- const rule$a = {
1540
+ const rule$6 = {
1758
1541
  meta: {
1759
1542
  type: 'suggestion',
1760
1543
  docs: {
1761
1544
  url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/no-case-insensitive-enum-values-duplicates.md',
1762
- category: 'Best Practices',
1545
+ category: 'Schema',
1763
1546
  recommended: true,
1764
1547
  description: 'Disallow case-insensitive enum values duplicates.',
1765
1548
  examples: [
@@ -1810,11 +1593,11 @@ const rule$a = {
1810
1593
  };
1811
1594
 
1812
1595
  const NO_DEPRECATED = 'NO_DEPRECATED';
1813
- const rule$b = {
1596
+ const rule$7 = {
1814
1597
  meta: {
1815
1598
  type: 'suggestion',
1816
1599
  docs: {
1817
- category: 'Best Practices',
1600
+ category: 'Operations',
1818
1601
  description: `Enforce that deprecated fields or enum values are not in use by operations.`,
1819
1602
  url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/no-deprecated.md`,
1820
1603
  requiresSchema: true,
@@ -1877,48 +1660,150 @@ const rule$b = {
1877
1660
  fullName
1878
1661
  }
1879
1662
  }
1880
- `,
1881
- },
1882
- ],
1883
- },
1884
- messages: {
1885
- [NO_DEPRECATED]: `This {{ type }} is marked as deprecated in your GraphQL schema {{ reason }}`,
1886
- },
1887
- schema: [],
1888
- },
1889
- create(context) {
1663
+ `,
1664
+ },
1665
+ ],
1666
+ recommended: true,
1667
+ },
1668
+ messages: {
1669
+ [NO_DEPRECATED]: `This {{ type }} is marked as deprecated in your GraphQL schema {{ reason }}`,
1670
+ },
1671
+ schema: [],
1672
+ },
1673
+ create(context) {
1674
+ return {
1675
+ EnumValue(node) {
1676
+ requireGraphQLSchemaFromContext('no-deprecated', context);
1677
+ const typeInfo = node.typeInfo();
1678
+ if (typeInfo && typeInfo.enumValue) {
1679
+ if (typeInfo.enumValue.deprecationReason) {
1680
+ const enumValueName = node.value;
1681
+ context.report({
1682
+ loc: getLocation(node.loc, enumValueName),
1683
+ messageId: NO_DEPRECATED,
1684
+ data: {
1685
+ type: 'enum value',
1686
+ reason: typeInfo.enumValue.deprecationReason ? `(reason: ${typeInfo.enumValue.deprecationReason})` : '',
1687
+ },
1688
+ });
1689
+ }
1690
+ }
1691
+ },
1692
+ Field(node) {
1693
+ requireGraphQLSchemaFromContext('no-deprecated', context);
1694
+ const typeInfo = node.typeInfo();
1695
+ if (typeInfo && typeInfo.fieldDef) {
1696
+ if (typeInfo.fieldDef.deprecationReason) {
1697
+ const fieldName = node.name.value;
1698
+ context.report({
1699
+ loc: getLocation(node.loc, fieldName),
1700
+ messageId: NO_DEPRECATED,
1701
+ data: {
1702
+ type: 'field',
1703
+ reason: typeInfo.fieldDef.deprecationReason ? `(reason: ${typeInfo.fieldDef.deprecationReason})` : '',
1704
+ },
1705
+ });
1706
+ }
1707
+ }
1708
+ },
1709
+ };
1710
+ },
1711
+ };
1712
+
1713
+ const NO_DUPLICATE_FIELDS = 'NO_DUPLICATE_FIELDS';
1714
+ const rule$8 = {
1715
+ meta: {
1716
+ type: 'suggestion',
1717
+ docs: {
1718
+ description: `Checks for duplicate fields in selection set, variables in operation definition, or in arguments set of a field.`,
1719
+ category: 'Operations',
1720
+ url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/no-duplicate-fields.md',
1721
+ recommended: true,
1722
+ examples: [
1723
+ {
1724
+ title: 'Incorrect',
1725
+ code: /* GraphQL */ `
1726
+ query {
1727
+ user {
1728
+ name
1729
+ email
1730
+ name # duplicate field
1731
+ }
1732
+ }
1733
+ `,
1734
+ },
1735
+ {
1736
+ title: 'Incorrect',
1737
+ code: /* GraphQL */ `
1738
+ query {
1739
+ users(
1740
+ first: 100
1741
+ skip: 50
1742
+ after: "cji629tngfgou0b73kt7vi5jo"
1743
+ first: 100 # duplicate argument
1744
+ ) {
1745
+ id
1746
+ }
1747
+ }
1748
+ `,
1749
+ },
1750
+ {
1751
+ title: 'Incorrect',
1752
+ code: /* GraphQL */ `
1753
+ query (
1754
+ $first: Int!
1755
+ $first: Int! # duplicate variable
1756
+ ) {
1757
+ users(first: $first, skip: 50) {
1758
+ id
1759
+ }
1760
+ }
1761
+ `,
1762
+ },
1763
+ ],
1764
+ },
1765
+ messages: {
1766
+ [NO_DUPLICATE_FIELDS]: `{{ type }} "{{ fieldName }}" defined multiple times`,
1767
+ },
1768
+ schema: [],
1769
+ },
1770
+ create(context) {
1771
+ function checkNode(usedFields, fieldName, type, node) {
1772
+ if (usedFields.has(fieldName)) {
1773
+ context.report({
1774
+ loc: getLocation((node.kind === Kind.FIELD && node.alias ? node.alias : node).loc, fieldName, {
1775
+ offsetEnd: node.kind === Kind.VARIABLE_DEFINITION ? 0 : 1,
1776
+ }),
1777
+ messageId: NO_DUPLICATE_FIELDS,
1778
+ data: {
1779
+ type,
1780
+ fieldName,
1781
+ },
1782
+ });
1783
+ }
1784
+ else {
1785
+ usedFields.add(fieldName);
1786
+ }
1787
+ }
1890
1788
  return {
1891
- EnumValue(node) {
1892
- requireGraphQLSchemaFromContext('no-deprecated', context);
1893
- const typeInfo = node.typeInfo();
1894
- if (typeInfo && typeInfo.enumValue) {
1895
- if (typeInfo.enumValue.deprecationReason) {
1896
- const enumValueName = node.value;
1897
- context.report({
1898
- loc: getLocation(node.loc, enumValueName),
1899
- messageId: NO_DEPRECATED,
1900
- data: {
1901
- type: 'enum value',
1902
- reason: typeInfo.enumValue.deprecationReason ? `(reason: ${typeInfo.enumValue.deprecationReason})` : '',
1903
- },
1904
- });
1905
- }
1789
+ OperationDefinition(node) {
1790
+ const set = new Set();
1791
+ for (const varDef of node.variableDefinitions) {
1792
+ checkNode(set, varDef.variable.name.value, 'Operation variable', varDef);
1906
1793
  }
1907
1794
  },
1908
1795
  Field(node) {
1909
- requireGraphQLSchemaFromContext('no-deprecated', context);
1910
- const typeInfo = node.typeInfo();
1911
- if (typeInfo && typeInfo.fieldDef) {
1912
- if (typeInfo.fieldDef.deprecationReason) {
1913
- const fieldName = node.name.value;
1914
- context.report({
1915
- loc: getLocation(node.loc, fieldName),
1916
- messageId: NO_DEPRECATED,
1917
- data: {
1918
- type: 'field',
1919
- reason: typeInfo.fieldDef.deprecationReason ? `(reason: ${typeInfo.fieldDef.deprecationReason})` : '',
1920
- },
1921
- });
1796
+ const set = new Set();
1797
+ for (const arg of node.arguments) {
1798
+ checkNode(set, arg.name.value, 'Field argument', arg);
1799
+ }
1800
+ },
1801
+ SelectionSet(node) {
1802
+ var _a;
1803
+ const set = new Set();
1804
+ for (const selection of node.selections) {
1805
+ if (selection.kind === Kind.FIELD) {
1806
+ checkNode(set, ((_a = selection.alias) === null || _a === void 0 ? void 0 : _a.value) || selection.name.value, 'Field', selection);
1922
1807
  }
1923
1808
  }
1924
1809
  },
@@ -1927,14 +1812,14 @@ const rule$b = {
1927
1812
  };
1928
1813
 
1929
1814
  const HASHTAG_COMMENT = 'HASHTAG_COMMENT';
1930
- const rule$c = {
1815
+ const rule$9 = {
1931
1816
  meta: {
1932
1817
  messages: {
1933
1818
  [HASHTAG_COMMENT]: 'Using hashtag (#) for adding GraphQL descriptions is not allowed. Prefer using """ for multiline, or " for a single line description.',
1934
1819
  },
1935
1820
  docs: {
1936
1821
  description: 'Requires to use `"""` or `"` for adding a GraphQL description instead of `#`.\nAllows to use hashtag for comments, as long as it\'s not attached to an AST definition.',
1937
- category: 'Best Practices',
1822
+ category: 'Schema',
1938
1823
  url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/no-hashtag-description.md',
1939
1824
  examples: [
1940
1825
  {
@@ -1971,6 +1856,7 @@ const rule$c = {
1971
1856
  `,
1972
1857
  },
1973
1858
  ],
1859
+ recommended: true,
1974
1860
  },
1975
1861
  type: 'suggestion',
1976
1862
  schema: [],
@@ -1999,85 +1885,18 @@ const rule$c = {
1999
1885
  },
2000
1886
  };
2001
1887
 
2002
- const NO_OPERATION_NAME_SUFFIX = 'NO_OPERATION_NAME_SUFFIX';
2003
- const rule$d = {
2004
- meta: {
2005
- fixable: 'code',
2006
- type: 'suggestion',
2007
- docs: {
2008
- category: 'Stylistic Issues',
2009
- recommended: true,
2010
- description: `Makes sure you are not adding the operation type to the name of the operation.`,
2011
- url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/no-operation-name-suffix.md`,
2012
- examples: [
2013
- {
2014
- title: 'Incorrect',
2015
- code: /* GraphQL */ `
2016
- query userQuery {
2017
- # ...
2018
- }
2019
- `,
2020
- },
2021
- {
2022
- title: 'Correct',
2023
- code: /* GraphQL */ `
2024
- query user {
2025
- # ...
2026
- }
2027
- `,
2028
- },
2029
- ],
2030
- },
2031
- messages: {
2032
- [NO_OPERATION_NAME_SUFFIX]: `Unnecessary "{{ invalidSuffix }}" suffix in your operation name!`,
2033
- },
2034
- schema: [],
2035
- },
2036
- create(context) {
2037
- return {
2038
- 'OperationDefinition, FragmentDefinition'(node) {
2039
- var _a;
2040
- const name = ((_a = node.name) === null || _a === void 0 ? void 0 : _a.value) || '';
2041
- if (name.length > 0) {
2042
- const invalidSuffix = 'operation' in node ? node.operation : 'fragment';
2043
- if (name.toLowerCase().endsWith(invalidSuffix)) {
2044
- const { start, end } = node.name.loc;
2045
- context.report({
2046
- loc: {
2047
- start: {
2048
- column: start.column - 1 + name.length - invalidSuffix.length,
2049
- line: start.line,
2050
- },
2051
- end: {
2052
- column: end.column - 1 + name.length,
2053
- line: end.line,
2054
- },
2055
- },
2056
- data: {
2057
- invalidSuffix,
2058
- },
2059
- fix: fixer => fixer.removeRange([node.name.range[1] - invalidSuffix.length, node.name.range[1]]),
2060
- messageId: NO_OPERATION_NAME_SUFFIX,
2061
- });
2062
- }
2063
- }
2064
- },
2065
- };
2066
- },
2067
- };
2068
-
2069
- const ROOT_TYPES = ['query', 'mutation', 'subscription'];
2070
- const rule$e = {
1888
+ const ROOT_TYPES = ['mutation', 'subscription'];
1889
+ const rule$a = {
2071
1890
  meta: {
2072
1891
  type: 'suggestion',
2073
1892
  docs: {
2074
- category: 'Validation',
2075
- description: 'Disallow using root types for `read-only` or `write-only` schemas.',
1893
+ category: 'Schema',
1894
+ description: 'Disallow using root types `mutation` and/or `subscription`.',
2076
1895
  url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/no-root-type.md',
2077
1896
  requiresSchema: true,
2078
1897
  examples: [
2079
1898
  {
2080
- title: 'Incorrect (`read-only` schema)',
1899
+ title: 'Incorrect',
2081
1900
  usage: [{ disallow: ['mutation', 'subscription'] }],
2082
1901
  code: /* GraphQL */ `
2083
1902
  type Mutation {
@@ -2086,16 +1905,7 @@ const rule$e = {
2086
1905
  `,
2087
1906
  },
2088
1907
  {
2089
- title: 'Incorrect (`write-only` schema)',
2090
- usage: [{ disallow: ['query'] }],
2091
- code: /* GraphQL */ `
2092
- type Query {
2093
- users: [User!]!
2094
- }
2095
- `,
2096
- },
2097
- {
2098
- title: 'Correct (`read-only` schema)',
1908
+ title: 'Correct',
2099
1909
  usage: [{ disallow: ['mutation', 'subscription'] }],
2100
1910
  code: /* GraphQL */ `
2101
1911
  type Query {
@@ -2104,7 +1914,6 @@ const rule$e = {
2104
1914
  `,
2105
1915
  },
2106
1916
  ],
2107
- optionsForConfig: [{ disallow: ['subscription'] }],
2108
1917
  },
2109
1918
  schema: {
2110
1919
  type: 'array',
@@ -2131,7 +1940,6 @@ const rule$e = {
2131
1940
  const schema = requireGraphQLSchemaFromContext('no-root-type', context);
2132
1941
  const disallow = new Set(context.options[0].disallow);
2133
1942
  const rootTypeNames = [
2134
- disallow.has('query') && schema.getQueryType(),
2135
1943
  disallow.has('mutation') && schema.getMutationType(),
2136
1944
  disallow.has('subscription') && schema.getSubscriptionType(),
2137
1945
  ]
@@ -2157,16 +1965,128 @@ const rule$e = {
2157
1965
  },
2158
1966
  };
2159
1967
 
1968
+ const rule$b = {
1969
+ meta: {
1970
+ type: 'suggestion',
1971
+ docs: {
1972
+ category: 'Schema',
1973
+ description: 'Avoid scalar result type on mutation type to make sure to return a valid state.',
1974
+ url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/no-scalar-result-type-on-mutation.md',
1975
+ requiresSchema: true,
1976
+ examples: [
1977
+ {
1978
+ title: 'Incorrect',
1979
+ code: /* GraphQL */ `
1980
+ type Mutation {
1981
+ createUser: Boolean
1982
+ }
1983
+ `,
1984
+ },
1985
+ {
1986
+ title: 'Correct',
1987
+ code: /* GraphQL */ `
1988
+ type Mutation {
1989
+ createUser: User!
1990
+ }
1991
+ `,
1992
+ },
1993
+ ],
1994
+ },
1995
+ schema: [],
1996
+ },
1997
+ create(context) {
1998
+ const schema = requireGraphQLSchemaFromContext('no-scalar-result-type-on-mutation', context);
1999
+ const mutationType = schema.getMutationType();
2000
+ if (!mutationType) {
2001
+ return {};
2002
+ }
2003
+ const selector = [
2004
+ `:matches(${Kind.OBJECT_TYPE_DEFINITION}, ${Kind.OBJECT_TYPE_EXTENSION})[name.value=${mutationType.name}]`,
2005
+ '>',
2006
+ Kind.FIELD_DEFINITION,
2007
+ Kind.NAMED_TYPE,
2008
+ ].join(' ');
2009
+ return {
2010
+ [selector](node) {
2011
+ const typeName = node.name.value;
2012
+ const graphQLType = schema.getType(typeName);
2013
+ if (isScalarType(graphQLType)) {
2014
+ context.report({
2015
+ loc: getLocation(node.loc, typeName),
2016
+ message: `Unexpected scalar result type "${typeName}"`,
2017
+ });
2018
+ }
2019
+ },
2020
+ };
2021
+ },
2022
+ };
2023
+
2024
+ const NO_TYPENAME_PREFIX = 'NO_TYPENAME_PREFIX';
2025
+ const rule$c = {
2026
+ meta: {
2027
+ type: 'suggestion',
2028
+ docs: {
2029
+ category: 'Schema',
2030
+ description: 'Enforces users to avoid using the type name in a field name while defining your schema.',
2031
+ recommended: true,
2032
+ url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/no-typename-prefix.md',
2033
+ examples: [
2034
+ {
2035
+ title: 'Incorrect',
2036
+ code: /* GraphQL */ `
2037
+ type User {
2038
+ userId: ID!
2039
+ }
2040
+ `,
2041
+ },
2042
+ {
2043
+ title: 'Correct',
2044
+ code: /* GraphQL */ `
2045
+ type User {
2046
+ id: ID!
2047
+ }
2048
+ `,
2049
+ },
2050
+ ],
2051
+ },
2052
+ messages: {
2053
+ [NO_TYPENAME_PREFIX]: `Field "{{ fieldName }}" starts with the name of the parent type "{{ typeName }}"`,
2054
+ },
2055
+ schema: [],
2056
+ },
2057
+ create(context) {
2058
+ return {
2059
+ 'ObjectTypeDefinition, ObjectTypeExtension, InterfaceTypeDefinition, InterfaceTypeExtension'(node) {
2060
+ const typeName = node.name.value;
2061
+ const lowerTypeName = typeName.toLowerCase();
2062
+ for (const field of node.fields) {
2063
+ const fieldName = field.name.value;
2064
+ if (fieldName.toLowerCase().startsWith(lowerTypeName)) {
2065
+ context.report({
2066
+ data: {
2067
+ fieldName,
2068
+ typeName,
2069
+ },
2070
+ messageId: NO_TYPENAME_PREFIX,
2071
+ loc: getLocation(field.loc, lowerTypeName),
2072
+ });
2073
+ }
2074
+ }
2075
+ },
2076
+ };
2077
+ },
2078
+ };
2079
+
2160
2080
  const UNREACHABLE_TYPE = 'UNREACHABLE_TYPE';
2161
2081
  const RULE_NAME = 'no-unreachable-types';
2162
- const rule$f = {
2082
+ const rule$d = {
2163
2083
  meta: {
2164
2084
  messages: {
2165
- [UNREACHABLE_TYPE]: `Type "{{ typeName }}" is unreachable`,
2085
+ [UNREACHABLE_TYPE]: 'Type "{{ typeName }}" is unreachable',
2166
2086
  },
2167
2087
  docs: {
2168
2088
  description: `Requires all types to be reachable at some level by root level fields.`,
2169
- category: 'Best Practices',
2089
+ category: 'Schema',
2170
2090
  url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_NAME}.md`,
2171
2091
  requiresSchema: true,
2172
2092
  examples: [
@@ -2197,6 +2117,7 @@ const rule$f = {
2197
2117
  `,
2198
2118
  },
2199
2119
  ],
2120
+ recommended: true,
2200
2121
  },
2201
2122
  fixable: 'code',
2202
2123
  type: 'suggestion',
@@ -2235,14 +2156,14 @@ const rule$f = {
2235
2156
 
2236
2157
  const UNUSED_FIELD = 'UNUSED_FIELD';
2237
2158
  const RULE_NAME$1 = 'no-unused-fields';
2238
- const rule$g = {
2159
+ const rule$e = {
2239
2160
  meta: {
2240
2161
  messages: {
2241
2162
  [UNUSED_FIELD]: `Field "{{fieldName}}" is unused`,
2242
2163
  },
2243
2164
  docs: {
2244
2165
  description: `Requires all fields to be used at some level by siblings operations.`,
2245
- category: 'Best Practices',
2166
+ category: 'Schema',
2246
2167
  url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_NAME$1}.md`,
2247
2168
  requiresSiblings: true,
2248
2169
  requiresSchema: true,
@@ -2424,11 +2345,11 @@ const MESSAGE_REQUIRE_DATE = 'MESSAGE_REQUIRE_DATE';
2424
2345
  const MESSAGE_INVALID_FORMAT = 'MESSAGE_INVALID_FORMAT';
2425
2346
  const MESSAGE_INVALID_DATE = 'MESSAGE_INVALID_DATE';
2426
2347
  const MESSAGE_CAN_BE_REMOVED = 'MESSAGE_CAN_BE_REMOVED';
2427
- const rule$h = {
2348
+ const rule$f = {
2428
2349
  meta: {
2429
2350
  type: 'suggestion',
2430
2351
  docs: {
2431
- category: 'Best Practices',
2352
+ category: 'Schema',
2432
2353
  description: 'Require deletion date on `@deprecated` directive. Suggest removing deprecated things after deprecated date.',
2433
2354
  url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/require-deprecation-date.md',
2434
2355
  examples: [
@@ -2527,11 +2448,11 @@ const rule$h = {
2527
2448
  },
2528
2449
  };
2529
2450
 
2530
- const rule$i = {
2451
+ const rule$g = {
2531
2452
  meta: {
2532
2453
  docs: {
2533
2454
  description: `Require all deprecation directives to specify a reason.`,
2534
- category: 'Best Practices',
2455
+ category: 'Schema',
2535
2456
  url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/require-deprecation-reason.md`,
2536
2457
  recommended: true,
2537
2458
  examples: [
@@ -2589,16 +2510,16 @@ const ALLOWED_KINDS$1 = [
2589
2510
  Kind.ENUM_VALUE_DEFINITION,
2590
2511
  Kind.DIRECTIVE_DEFINITION,
2591
2512
  ];
2592
- const rule$j = {
2513
+ const rule$h = {
2593
2514
  meta: {
2594
2515
  docs: {
2595
- category: 'Best Practices',
2516
+ category: 'Schema',
2596
2517
  description: 'Enforce descriptions in your type definitions.',
2597
2518
  url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/require-description.md',
2598
2519
  examples: [
2599
2520
  {
2600
2521
  title: 'Incorrect',
2601
- usage: [{ types: true, overrides: { FieldDefinition: true } }],
2522
+ usage: [{ types: true, FieldDefinition: true }],
2602
2523
  code: /* GraphQL */ `
2603
2524
  type someTypeName {
2604
2525
  name: String
@@ -2607,7 +2528,7 @@ const rule$j = {
2607
2528
  },
2608
2529
  {
2609
2530
  title: 'Correct',
2610
- usage: [{ types: true, overrides: { FieldDefinition: true } }],
2531
+ usage: [{ types: true, FieldDefinition: true }],
2611
2532
  code: /* GraphQL */ `
2612
2533
  """
2613
2534
  Some type description
@@ -2621,12 +2542,10 @@ const rule$j = {
2621
2542
  `,
2622
2543
  },
2623
2544
  ],
2624
- optionsForConfig: [
2545
+ configOptions: [
2625
2546
  {
2626
2547
  types: true,
2627
- overrides: {
2628
- [Kind.DIRECTIVE_DEFINITION]: true,
2629
- },
2548
+ [Kind.DIRECTIVE_DEFINITION]: true,
2630
2549
  },
2631
2550
  ],
2632
2551
  },
@@ -2645,22 +2564,23 @@ const rule$j = {
2645
2564
  properties: {
2646
2565
  types: {
2647
2566
  type: 'boolean',
2648
- description: `Includes:\n\n${TYPES_KINDS.map(kind => `- [${kind}](https://spec.graphql.org/October2021/#${kind})`).join('\n')}`,
2649
- },
2650
- overrides: {
2651
- type: 'object',
2652
- description: 'Configuration for precise `ASTNode`',
2653
- additionalProperties: false,
2654
- properties: Object.fromEntries(ALLOWED_KINDS$1.map(kind => [kind, { type: 'boolean' }])),
2567
+ description: `Includes:\n\n${TYPES_KINDS.map(kind => `- \`${kind}\``).join('\n')}`,
2655
2568
  },
2569
+ ...Object.fromEntries([...ALLOWED_KINDS$1].sort().map(kind => [
2570
+ kind,
2571
+ {
2572
+ type: 'boolean',
2573
+ description: `Read more about this kind on [spec.graphql.org](https://spec.graphql.org/October2021/#${kind}).`,
2574
+ },
2575
+ ])),
2656
2576
  },
2657
2577
  },
2658
2578
  },
2659
2579
  },
2660
2580
  create(context) {
2661
- const { types, overrides = {} } = context.options[0];
2581
+ const { types, ...restOptions } = context.options[0];
2662
2582
  const kinds = new Set(types ? TYPES_KINDS : []);
2663
- for (const [kind, isEnabled] of Object.entries(overrides)) {
2583
+ for (const [kind, isEnabled] of Object.entries(restOptions)) {
2664
2584
  if (isEnabled) {
2665
2585
  kinds.add(kind);
2666
2586
  }
@@ -2688,11 +2608,11 @@ const rule$j = {
2688
2608
  };
2689
2609
 
2690
2610
  const RULE_NAME$2 = 'require-field-of-type-query-in-mutation-result';
2691
- const rule$k = {
2611
+ const rule$i = {
2692
2612
  meta: {
2693
2613
  type: 'suggestion',
2694
2614
  docs: {
2695
- category: 'Best Practices',
2615
+ category: 'Schema',
2696
2616
  description: 'Allow the client in one round-trip not only to call mutation but also to get a wagon of data to update their application.\n> Currently, no errors are reported for result type `union`, `interface` and `scalar`.',
2697
2617
  url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_NAME$2}.md`,
2698
2618
  requiresSchema: true,
@@ -2856,13 +2776,13 @@ const convertNode = (typeInfo) => (node, key, parent) => {
2856
2776
 
2857
2777
  const REQUIRE_ID_WHEN_AVAILABLE = 'REQUIRE_ID_WHEN_AVAILABLE';
2858
2778
  const DEFAULT_ID_FIELD_NAME = 'id';
2859
- const rule$l = {
2779
+ const rule$j = {
2860
2780
  meta: {
2861
2781
  type: 'suggestion',
2862
2782
  docs: {
2863
- category: 'Best Practices',
2864
- description: `Enforce selecting specific fields when they are available on the GraphQL type.`,
2865
- url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/require-id-when-available.md`,
2783
+ category: 'Operations',
2784
+ description: 'Enforce selecting specific fields when they are available on the GraphQL type.',
2785
+ url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/require-id-when-available.md',
2866
2786
  requiresSchema: true,
2867
2787
  requiresSiblings: true,
2868
2788
  examples: [
@@ -2902,17 +2822,17 @@ const rule$l = {
2902
2822
  `,
2903
2823
  },
2904
2824
  ],
2825
+ recommended: true,
2905
2826
  },
2906
2827
  messages: {
2907
- [REQUIRE_ID_WHEN_AVAILABLE]: `Field "{{ fieldName }}" must be selected when it's available on a type. Please make sure to include it in your selection set!\nIf you are using fragments, make sure that all used fragments {{checkedFragments}} specifies the field "{{ fieldName }}".`,
2828
+ [REQUIRE_ID_WHEN_AVAILABLE]: `Field "{{ fieldName }}" must be selected when it's available on a type. Please make sure to include it in your selection set!\nIf you are using fragments, make sure that all used fragments {{ checkedFragments }} specifies the field "{{ fieldName }}".`,
2908
2829
  },
2909
2830
  schema: {
2910
2831
  type: 'array',
2911
- additionalItems: false,
2912
- minItems: 0,
2913
2832
  maxItems: 1,
2914
2833
  items: {
2915
2834
  type: 'object',
2835
+ additionalProperties: false,
2916
2836
  properties: {
2917
2837
  fieldName: {
2918
2838
  type: 'string',
@@ -2992,12 +2912,12 @@ const rule$l = {
2992
2912
  },
2993
2913
  };
2994
2914
 
2995
- const rule$m = {
2915
+ const rule$k = {
2996
2916
  meta: {
2997
2917
  docs: {
2998
- category: 'Best Practices',
2918
+ category: 'Operations',
2999
2919
  description: `Limit the complexity of the GraphQL operations solely by their depth. Based on [graphql-depth-limit](https://github.com/stems/graphql-depth-limit).`,
3000
- url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/selection-set-depth.md`,
2920
+ url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/selection-set-depth.md',
3001
2921
  requiresSiblings: true,
3002
2922
  examples: [
3003
2923
  {
@@ -3040,22 +2960,26 @@ const rule$m = {
3040
2960
  `,
3041
2961
  },
3042
2962
  ],
2963
+ recommended: true,
2964
+ configOptions: [{ maxDepth: 7 }],
3043
2965
  },
3044
2966
  type: 'suggestion',
3045
2967
  schema: {
3046
2968
  type: 'array',
3047
- additionalItems: false,
3048
2969
  minItems: 1,
3049
2970
  maxItems: 1,
3050
2971
  items: {
3051
2972
  type: 'object',
3052
- require: ['maxDepth'],
2973
+ additionalProperties: false,
2974
+ required: ['maxDepth'],
3053
2975
  properties: {
3054
2976
  maxDepth: {
3055
2977
  type: 'number',
3056
2978
  },
3057
2979
  ignore: {
3058
2980
  type: 'array',
2981
+ uniqueItems: true,
2982
+ minItems: 1,
3059
2983
  items: {
3060
2984
  type: 'string',
3061
2985
  },
@@ -3114,12 +3038,12 @@ const shouldIgnoreNode = ({ node, exceptions }) => {
3114
3038
  }
3115
3039
  return false;
3116
3040
  };
3117
- const rule$n = {
3041
+ const rule$l = {
3118
3042
  meta: {
3119
3043
  type: 'suggestion',
3120
3044
  docs: {
3121
3045
  description: 'Requires output types to have one unique identifier unless they do not have a logical one. Exceptions can be used to ignore output types that do not have unique identifiers.',
3122
- category: 'Best Practices',
3046
+ category: 'Schema',
3123
3047
  recommended: true,
3124
3048
  url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/strict-id-in-types.md',
3125
3049
  examples: [
@@ -3179,13 +3103,15 @@ const rule$n = {
3179
3103
  ],
3180
3104
  },
3181
3105
  schema: {
3182
- $schema: 'http://json-schema.org/draft-04/schema#',
3183
3106
  type: 'array',
3107
+ maxItems: 1,
3184
3108
  items: {
3185
3109
  type: 'object',
3110
+ additionalProperties: false,
3186
3111
  properties: {
3187
3112
  acceptedIdNames: {
3188
3113
  type: 'array',
3114
+ uniqueItems: true,
3189
3115
  items: {
3190
3116
  type: 'string',
3191
3117
  },
@@ -3193,6 +3119,7 @@ const rule$n = {
3193
3119
  },
3194
3120
  acceptedIdTypes: {
3195
3121
  type: 'array',
3122
+ uniqueItems: true,
3196
3123
  items: {
3197
3124
  type: 'string',
3198
3125
  },
@@ -3203,19 +3130,21 @@ const rule$n = {
3203
3130
  properties: {
3204
3131
  types: {
3205
3132
  type: 'array',
3133
+ uniqueItems: true,
3134
+ minItems: 1,
3206
3135
  description: 'This is used to exclude types with names that match one of the specified values.',
3207
3136
  items: {
3208
3137
  type: 'string',
3209
3138
  },
3210
- default: [],
3211
3139
  },
3212
3140
  suffixes: {
3213
3141
  type: 'array',
3142
+ uniqueItems: true,
3143
+ minItems: 1,
3214
3144
  description: 'This is used to exclude types with names with suffixes that match one of the specified values.',
3215
3145
  items: {
3216
3146
  type: 'string',
3217
3147
  },
3218
- default: [],
3219
3148
  },
3220
3149
  },
3221
3150
  },
@@ -3291,11 +3220,11 @@ const checkNode = (context, node, ruleName, messageId) => {
3291
3220
  });
3292
3221
  }
3293
3222
  };
3294
- const rule$o = {
3223
+ const rule$m = {
3295
3224
  meta: {
3296
3225
  type: 'suggestion',
3297
3226
  docs: {
3298
- category: 'Best Practices',
3227
+ category: 'Operations',
3299
3228
  description: `Enforce unique fragment names across your project.`,
3300
3229
  url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_NAME$3}.md`,
3301
3230
  requiresSiblings: true,
@@ -3350,11 +3279,11 @@ const rule$o = {
3350
3279
 
3351
3280
  const RULE_NAME$4 = 'unique-operation-name';
3352
3281
  const UNIQUE_OPERATION_NAME = 'UNIQUE_OPERATION_NAME';
3353
- const rule$p = {
3282
+ const rule$n = {
3354
3283
  meta: {
3355
3284
  type: 'suggestion',
3356
3285
  docs: {
3357
- category: 'Best Practices',
3286
+ category: 'Operations',
3358
3287
  description: `Enforce unique operation names across your project.`,
3359
3288
  url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_NAME$4}.md`,
3360
3289
  requiresSiblings: true,
@@ -3417,31 +3346,29 @@ const rule$p = {
3417
3346
  const rules = {
3418
3347
  ...GRAPHQL_JS_VALIDATIONS,
3419
3348
  alphabetize: rule,
3420
- 'avoid-duplicate-fields': rule$1,
3421
- 'avoid-operation-name-prefix': rule$2,
3422
- 'avoid-scalar-result-type-on-mutation': rule$3,
3423
- 'avoid-typename-prefix': rule$4,
3424
- 'description-style': rule$5,
3425
- 'input-name': rule$6,
3426
- 'match-document-filename': rule$7,
3427
- 'naming-convention': rule$8,
3428
- 'no-anonymous-operations': rule$9,
3429
- 'no-case-insensitive-enum-values-duplicates': rule$a,
3430
- 'no-deprecated': rule$b,
3431
- 'no-hashtag-description': rule$c,
3432
- 'no-operation-name-suffix': rule$d,
3433
- 'no-root-type': rule$e,
3434
- 'no-unreachable-types': rule$f,
3435
- 'no-unused-fields': rule$g,
3436
- 'require-deprecation-date': rule$h,
3437
- 'require-deprecation-reason': rule$i,
3438
- 'require-description': rule$j,
3439
- 'require-field-of-type-query-in-mutation-result': rule$k,
3440
- 'require-id-when-available': rule$l,
3441
- 'selection-set-depth': rule$m,
3442
- 'strict-id-in-types': rule$n,
3443
- 'unique-fragment-name': rule$o,
3444
- 'unique-operation-name': rule$p,
3349
+ 'description-style': rule$1,
3350
+ 'input-name': rule$2,
3351
+ 'match-document-filename': rule$3,
3352
+ 'naming-convention': rule$4,
3353
+ 'no-anonymous-operations': rule$5,
3354
+ 'no-case-insensitive-enum-values-duplicates': rule$6,
3355
+ 'no-deprecated': rule$7,
3356
+ 'no-duplicate-fields': rule$8,
3357
+ 'no-hashtag-description': rule$9,
3358
+ 'no-root-type': rule$a,
3359
+ 'no-scalar-result-type-on-mutation': rule$b,
3360
+ 'no-typename-prefix': rule$c,
3361
+ 'no-unreachable-types': rule$d,
3362
+ 'no-unused-fields': rule$e,
3363
+ 'require-deprecation-date': rule$f,
3364
+ 'require-deprecation-reason': rule$g,
3365
+ 'require-description': rule$h,
3366
+ 'require-field-of-type-query-in-mutation-result': rule$i,
3367
+ 'require-id-when-available': rule$j,
3368
+ 'selection-set-depth': rule$k,
3369
+ 'strict-id-in-types': rule$l,
3370
+ 'unique-fragment-name': rule$m,
3371
+ 'unique-operation-name': rule$n,
3445
3372
  };
3446
3373
 
3447
3374
  const RELEVANT_KEYWORDS = ['gql', 'graphql', '/* GraphQL */'];