@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.js CHANGED
@@ -18,14 +18,89 @@ const codeFileLoader = require('@graphql-tools/code-file-loader');
18
18
  const eslint = require('eslint');
19
19
  const codeFrame = require('@babel/code-frame');
20
20
 
21
+ const base = {
22
+ parser: '@graphql-eslint/eslint-plugin',
23
+ plugins: ['@graphql-eslint'],
24
+ };
25
+
21
26
  /*
22
27
  * 🚨 IMPORTANT! Do not manually modify this file. Run: `yarn generate-configs`
23
28
  */
24
- const recommendedConfig = {
25
- parser: '@graphql-eslint/eslint-plugin',
26
- plugins: ['@graphql-eslint'],
29
+ const schemaRecommendedConfig = {
30
+ extends: ['plugin:@graphql-eslint/base'],
31
+ rules: {
32
+ '@graphql-eslint/description-style': 'error',
33
+ '@graphql-eslint/known-argument-names': 'error',
34
+ '@graphql-eslint/known-directives': 'error',
35
+ '@graphql-eslint/known-type-names': 'error',
36
+ '@graphql-eslint/lone-schema-definition': 'error',
37
+ '@graphql-eslint/naming-convention': [
38
+ 'error',
39
+ {
40
+ types: 'PascalCase',
41
+ fields: 'camelCase',
42
+ EnumValueDefinition: 'UPPER_CASE',
43
+ 'FieldDefinition[parent.name.value=Query]': {
44
+ forbiddenPrefixes: ['query', 'get'],
45
+ forbiddenSuffixes: ['Query'],
46
+ },
47
+ 'FieldDefinition[parent.name.value=Mutation]': {
48
+ forbiddenPrefixes: ['mutation'],
49
+ forbiddenSuffixes: ['Mutation'],
50
+ },
51
+ 'FieldDefinition[parent.name.value=Subscription]': {
52
+ forbiddenPrefixes: ['subscription'],
53
+ forbiddenSuffixes: ['Subscription'],
54
+ },
55
+ },
56
+ ],
57
+ '@graphql-eslint/no-case-insensitive-enum-values-duplicates': 'error',
58
+ '@graphql-eslint/no-hashtag-description': 'error',
59
+ '@graphql-eslint/no-typename-prefix': 'error',
60
+ '@graphql-eslint/no-unreachable-types': 'error',
61
+ '@graphql-eslint/possible-type-extension': 'error',
62
+ '@graphql-eslint/provided-required-arguments': 'error',
63
+ '@graphql-eslint/require-deprecation-reason': 'error',
64
+ '@graphql-eslint/strict-id-in-types': 'error',
65
+ '@graphql-eslint/unique-directive-names': 'error',
66
+ '@graphql-eslint/unique-directive-names-per-location': 'error',
67
+ '@graphql-eslint/unique-enum-value-names': 'error',
68
+ '@graphql-eslint/unique-field-definition-names': 'error',
69
+ '@graphql-eslint/unique-operation-types': 'error',
70
+ '@graphql-eslint/unique-type-names': 'error',
71
+ },
72
+ };
73
+
74
+ /*
75
+ * 🚨 IMPORTANT! Do not manually modify this file. Run: `yarn generate-configs`
76
+ */
77
+ const schemaAllConfig = {
78
+ extends: ['plugin:@graphql-eslint/base', 'plugin:@graphql-eslint/schema-recommended'],
79
+ rules: {
80
+ '@graphql-eslint/alphabetize': [
81
+ 'error',
82
+ {
83
+ fields: ['ObjectTypeDefinition', 'InterfaceTypeDefinition', 'InputObjectTypeDefinition'],
84
+ values: ['EnumTypeDefinition'],
85
+ arguments: ['FieldDefinition', 'Field', 'DirectiveDefinition', 'Directive'],
86
+ },
87
+ ],
88
+ '@graphql-eslint/input-name': 'error',
89
+ '@graphql-eslint/no-root-type': 'off',
90
+ '@graphql-eslint/no-scalar-result-type-on-mutation': 'error',
91
+ '@graphql-eslint/no-unused-fields': 'off',
92
+ '@graphql-eslint/require-deprecation-date': 'error',
93
+ '@graphql-eslint/require-description': ['error', { types: true, DirectiveDefinition: true }],
94
+ '@graphql-eslint/require-field-of-type-query-in-mutation-result': 'error',
95
+ },
96
+ };
97
+
98
+ /*
99
+ * 🚨 IMPORTANT! Do not manually modify this file. Run: `yarn generate-configs`
100
+ */
101
+ const operationsRecommendedConfig = {
102
+ extends: ['plugin:@graphql-eslint/base'],
27
103
  rules: {
28
- '@graphql-eslint/avoid-typename-prefix': 'error',
29
104
  '@graphql-eslint/executable-definitions': 'error',
30
105
  '@graphql-eslint/fields-on-correct-type': 'error',
31
106
  '@graphql-eslint/fragments-on-composite-type': 'error',
@@ -34,58 +109,36 @@ const recommendedConfig = {
34
109
  '@graphql-eslint/known-fragment-names': 'error',
35
110
  '@graphql-eslint/known-type-names': 'error',
36
111
  '@graphql-eslint/lone-anonymous-operation': 'error',
37
- '@graphql-eslint/lone-schema-definition': 'error',
38
112
  '@graphql-eslint/naming-convention': [
39
113
  'error',
40
114
  {
41
- types: 'PascalCase',
42
- fields: 'camelCase',
43
- overrides: {
44
- EnumValueDefinition: 'UPPER_CASE',
45
- OperationDefinition: {
46
- style: 'PascalCase',
47
- forbiddenPrefixes: ['Query', 'Mutation', 'Subscription', 'Get'],
48
- forbiddenSuffixes: ['Query', 'Mutation', 'Subscription'],
49
- },
50
- FragmentDefinition: { style: 'PascalCase', forbiddenPrefixes: ['Fragment'], forbiddenSuffixes: ['Fragment'] },
51
- 'FieldDefinition[parent.name.value=Query]': {
52
- forbiddenPrefixes: ['query', 'get'],
53
- forbiddenSuffixes: ['Query'],
54
- },
55
- 'FieldDefinition[parent.name.value=Mutation]': {
56
- forbiddenPrefixes: ['mutation'],
57
- forbiddenSuffixes: ['Mutation'],
58
- },
59
- 'FieldDefinition[parent.name.value=Subscription]': {
60
- forbiddenPrefixes: ['subscription'],
61
- forbiddenSuffixes: ['Subscription'],
62
- },
63
- },
115
+ Argument: 'camelCase',
116
+ VariableDefinition: 'camelCase',
117
+ OperationDefinition: {
118
+ style: 'PascalCase',
119
+ forbiddenPrefixes: ['Query', 'Mutation', 'Subscription', 'Get'],
120
+ forbiddenSuffixes: ['Query', 'Mutation', 'Subscription'],
121
+ },
122
+ FragmentDefinition: { style: 'PascalCase', forbiddenPrefixes: ['Fragment'], forbiddenSuffixes: ['Fragment'] },
64
123
  },
65
124
  ],
66
125
  '@graphql-eslint/no-anonymous-operations': 'error',
67
- '@graphql-eslint/no-case-insensitive-enum-values-duplicates': 'error',
126
+ '@graphql-eslint/no-deprecated': 'error',
127
+ '@graphql-eslint/no-duplicate-fields': 'error',
68
128
  '@graphql-eslint/no-fragment-cycles': 'error',
69
- '@graphql-eslint/no-operation-name-suffix': 'error',
70
129
  '@graphql-eslint/no-undefined-variables': 'error',
71
130
  '@graphql-eslint/no-unused-fragments': 'error',
72
131
  '@graphql-eslint/no-unused-variables': 'error',
73
132
  '@graphql-eslint/one-field-subscriptions': 'error',
74
133
  '@graphql-eslint/overlapping-fields-can-be-merged': 'error',
75
134
  '@graphql-eslint/possible-fragment-spread': 'error',
76
- '@graphql-eslint/possible-type-extension': 'error',
77
135
  '@graphql-eslint/provided-required-arguments': 'error',
78
- '@graphql-eslint/require-deprecation-reason': 'error',
136
+ '@graphql-eslint/require-id-when-available': 'error',
79
137
  '@graphql-eslint/scalar-leafs': 'error',
80
- '@graphql-eslint/strict-id-in-types': 'error',
138
+ '@graphql-eslint/selection-set-depth': ['error', { maxDepth: 7 }],
81
139
  '@graphql-eslint/unique-argument-names': 'error',
82
- '@graphql-eslint/unique-directive-names': 'error',
83
140
  '@graphql-eslint/unique-directive-names-per-location': 'error',
84
- '@graphql-eslint/unique-enum-value-names': 'error',
85
- '@graphql-eslint/unique-field-definition-names': 'error',
86
141
  '@graphql-eslint/unique-input-field-names': 'error',
87
- '@graphql-eslint/unique-operation-types': 'error',
88
- '@graphql-eslint/unique-type-names': 'error',
89
142
  '@graphql-eslint/unique-variable-names': 'error',
90
143
  '@graphql-eslint/value-literals-of-correct-type': 'error',
91
144
  '@graphql-eslint/variables-are-input-types': 'error',
@@ -96,44 +149,32 @@ const recommendedConfig = {
96
149
  /*
97
150
  * 🚨 IMPORTANT! Do not manually modify this file. Run: `yarn generate-configs`
98
151
  */
99
- const allConfig = {
100
- ...recommendedConfig,
152
+ const operationsAllConfig = {
153
+ extends: ['plugin:@graphql-eslint/base', 'plugin:@graphql-eslint/operations-recommended'],
101
154
  rules: {
102
- ...recommendedConfig.rules,
103
155
  '@graphql-eslint/alphabetize': [
104
156
  'error',
105
157
  {
106
- fields: ['ObjectTypeDefinition', 'InterfaceTypeDefinition', 'InputObjectTypeDefinition'],
107
- values: ['EnumTypeDefinition'],
108
158
  selections: ['OperationDefinition', 'FragmentDefinition'],
109
159
  variables: ['OperationDefinition'],
110
- arguments: ['FieldDefinition', 'Field', 'DirectiveDefinition', 'Directive'],
160
+ arguments: ['Field', 'Directive'],
111
161
  },
112
162
  ],
113
- '@graphql-eslint/avoid-duplicate-fields': 'error',
114
- '@graphql-eslint/avoid-operation-name-prefix': 'error',
115
- '@graphql-eslint/avoid-scalar-result-type-on-mutation': 'error',
116
- '@graphql-eslint/description-style': 'error',
117
- '@graphql-eslint/input-name': 'error',
118
- '@graphql-eslint/match-document-filename': 'error',
119
- '@graphql-eslint/no-deprecated': 'error',
120
- '@graphql-eslint/no-hashtag-description': 'error',
121
- '@graphql-eslint/no-root-type': ['error', { disallow: ['subscription'] }],
122
- '@graphql-eslint/no-unreachable-types': 'error',
123
- '@graphql-eslint/no-unused-fields': 'error',
124
- '@graphql-eslint/require-deprecation-date': 'error',
125
- '@graphql-eslint/require-description': ['error', { types: true, overrides: { DirectiveDefinition: true } }],
126
- '@graphql-eslint/require-field-of-type-query-in-mutation-result': 'error',
127
- '@graphql-eslint/require-id-when-available': 'error',
128
- '@graphql-eslint/selection-set-depth': 'error',
163
+ '@graphql-eslint/match-document-filename': [
164
+ 'error',
165
+ { query: 'kebab-case', mutation: 'kebab-case', subscription: 'kebab-case', fragment: 'kebab-case' },
166
+ ],
129
167
  '@graphql-eslint/unique-fragment-name': 'error',
130
168
  '@graphql-eslint/unique-operation-name': 'error',
131
169
  },
132
170
  };
133
171
 
134
172
  const configs = {
135
- all: allConfig,
136
- recommended: recommendedConfig,
173
+ base,
174
+ 'schema-recommended': schemaRecommendedConfig,
175
+ 'schema-all': schemaAllConfig,
176
+ 'operations-recommended': operationsRecommendedConfig,
177
+ 'operations-all': operationsAllConfig,
137
178
  };
138
179
 
139
180
  function requireSiblingsOperations(ruleName, context) {
@@ -346,7 +387,6 @@ const validationToRule = (name, ruleName, docs, getDocumentNode) => {
346
387
  docs: {
347
388
  ...docs,
348
389
  graphQLJSRuleName: ruleName,
349
- category: 'Validation',
350
390
  recommended: true,
351
391
  requiresSchema,
352
392
  url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${name}.md`,
@@ -383,16 +423,22 @@ const importFiles = (context) => {
383
423
  return _import.processImport(context.getFilename());
384
424
  };
385
425
  const GRAPHQL_JS_VALIDATIONS = Object.assign({}, validationToRule('executable-definitions', 'ExecutableDefinitions', {
426
+ category: 'Operations',
386
427
  description: `A GraphQL document is only valid for execution if all definitions are either operation or fragment definitions.`,
387
428
  }), validationToRule('fields-on-correct-type', 'FieldsOnCorrectType', {
429
+ category: 'Operations',
388
430
  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`.',
389
431
  }), validationToRule('fragments-on-composite-type', 'FragmentsOnCompositeTypes', {
432
+ category: 'Operations',
390
433
  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.`,
391
434
  }), validationToRule('known-argument-names', 'KnownArgumentNames', {
435
+ category: ['Schema', 'Operations'],
392
436
  description: `A GraphQL field is only valid if all supplied arguments are defined by that field.`,
393
437
  }), validationToRule('known-directives', 'KnownDirectives', {
438
+ category: ['Schema', 'Operations'],
394
439
  description: `A GraphQL document is only valid if all \`@directives\` are known by the schema and legally positioned.`,
395
440
  }), validationToRule('known-fragment-names', 'KnownFragmentNames', {
441
+ category: 'Operations',
396
442
  description: `A GraphQL document is only valid if all \`...Fragment\` fragment spreads refer to fragments defined in the same document.`,
397
443
  examples: [
398
444
  {
@@ -459,17 +505,23 @@ const GRAPHQL_JS_VALIDATIONS = Object.assign({}, validationToRule('executable-de
459
505
  },
460
506
  ],
461
507
  }, importFiles), validationToRule('known-type-names', 'KnownTypeNames', {
508
+ category: ['Schema', 'Operations'],
462
509
  description: `A GraphQL document is only valid if referenced types (specifically variable definitions and fragment conditions) are defined by the type schema.`,
463
510
  }), validationToRule('lone-anonymous-operation', 'LoneAnonymousOperation', {
511
+ category: 'Operations',
464
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.`,
465
513
  }), validationToRule('lone-schema-definition', 'LoneSchemaDefinition', {
514
+ category: 'Schema',
466
515
  description: `A GraphQL document is only valid if it contains only one schema definition.`,
467
516
  requiresSchema: false,
468
517
  }), validationToRule('no-fragment-cycles', 'NoFragmentCycles', {
518
+ category: 'Operations',
469
519
  description: `A GraphQL fragment is only valid when it does not have cycles in fragments usage.`,
470
520
  }), validationToRule('no-undefined-variables', 'NoUndefinedVariables', {
521
+ category: 'Operations',
471
522
  description: `A GraphQL operation is only valid if all variables encountered, both directly and via fragment spreads, are defined by that operation.`,
472
523
  }, importFiles), validationToRule('no-unused-fragments', 'NoUnusedFragments', {
524
+ category: 'Operations',
473
525
  description: `A GraphQL document is only valid if all fragment definitions are spread within operations, or spread within other fragments spread within operations.`,
474
526
  requiresSiblings: true,
475
527
  }, context => {
@@ -499,49 +551,68 @@ const GRAPHQL_JS_VALIDATIONS = Object.assign({}, validationToRule('executable-de
499
551
  };
500
552
  return getParentNode(context.getFilename());
501
553
  }), validationToRule('no-unused-variables', 'NoUnusedVariables', {
554
+ category: 'Operations',
502
555
  description: `A GraphQL operation is only valid if all variables defined by an operation are used, either directly or within a spread fragment.`,
503
556
  }, importFiles), validationToRule('overlapping-fields-can-be-merged', 'OverlappingFieldsCanBeMerged', {
557
+ category: 'Operations',
504
558
  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.`,
505
559
  }), validationToRule('possible-fragment-spread', 'PossibleFragmentSpreads', {
560
+ category: 'Operations',
506
561
  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.`,
507
562
  }), validationToRule('possible-type-extension', 'PossibleTypeExtensions', {
563
+ category: 'Schema',
508
564
  description: `A type extension is only valid if the type is defined and has the same kind.`,
509
565
  requiresSchema: false,
510
566
  }), validationToRule('provided-required-arguments', 'ProvidedRequiredArguments', {
567
+ category: ['Schema', 'Operations'],
511
568
  description: `A field or directive is only valid if all required (non-null without a default value) field arguments have been provided.`,
512
569
  }), validationToRule('scalar-leafs', 'ScalarLeafs', {
570
+ category: 'Operations',
513
571
  description: `A GraphQL document is valid only if all leaf fields (fields without sub selections) are of scalar or enum types.`,
514
572
  }), validationToRule('one-field-subscriptions', 'SingleFieldSubscriptions', {
573
+ category: 'Operations',
515
574
  description: `A GraphQL subscription is valid only if it contains a single root field.`,
516
575
  }), validationToRule('unique-argument-names', 'UniqueArgumentNames', {
576
+ category: 'Operations',
517
577
  description: `A GraphQL field or directive is only valid if all supplied arguments are uniquely named.`,
518
578
  }), validationToRule('unique-directive-names', 'UniqueDirectiveNames', {
579
+ category: 'Schema',
519
580
  description: `A GraphQL document is only valid if all defined directives have unique names.`,
520
581
  requiresSchema: false,
521
582
  }), validationToRule('unique-directive-names-per-location', 'UniqueDirectivesPerLocation', {
583
+ category: ['Schema', 'Operations'],
522
584
  description: `A GraphQL document is only valid if all non-repeatable directives at a given location are uniquely named.`,
523
585
  }), validationToRule('unique-enum-value-names', 'UniqueEnumValueNames', {
586
+ category: 'Schema',
524
587
  description: `A GraphQL enum type is only valid if all its values are uniquely named.`,
525
588
  requiresSchema: false,
526
589
  }), validationToRule('unique-field-definition-names', 'UniqueFieldDefinitionNames', {
590
+ category: 'Schema',
527
591
  description: `A GraphQL complex type is only valid if all its fields are uniquely named.`,
528
592
  requiresSchema: false,
529
593
  }), validationToRule('unique-input-field-names', 'UniqueInputFieldNames', {
594
+ category: 'Operations',
530
595
  description: `A GraphQL input object value is only valid if all supplied fields are uniquely named.`,
531
596
  requiresSchema: false,
532
597
  }), validationToRule('unique-operation-types', 'UniqueOperationTypes', {
598
+ category: 'Schema',
533
599
  description: `A GraphQL document is only valid if it has only one type per operation.`,
534
600
  requiresSchema: false,
535
601
  }), validationToRule('unique-type-names', 'UniqueTypeNames', {
602
+ category: 'Schema',
536
603
  description: `A GraphQL document is only valid if all defined types have unique names.`,
537
604
  requiresSchema: false,
538
605
  }), validationToRule('unique-variable-names', 'UniqueVariableNames', {
606
+ category: 'Operations',
539
607
  description: `A GraphQL operation is only valid if all its variables are uniquely named.`,
540
608
  }), validationToRule('value-literals-of-correct-type', 'ValuesOfCorrectType', {
609
+ category: 'Operations',
541
610
  description: `A GraphQL document is only valid if all value literals are of the type expected at their position.`,
542
611
  }), validationToRule('variables-are-input-types', 'VariablesAreInputTypes', {
612
+ category: 'Operations',
543
613
  description: `A GraphQL operation is only valid if all the variables it defines are of input types (scalar, enum, or input object).`,
544
614
  }), validationToRule('variables-in-allowed-position', 'VariablesInAllowedPosition', {
615
+ category: 'Operations',
545
616
  description: `Variables passed to field arguments conform to type.`,
546
617
  }));
547
618
 
@@ -567,7 +638,7 @@ const rule = {
567
638
  meta: {
568
639
  type: 'suggestion',
569
640
  docs: {
570
- category: 'Best Practices',
641
+ category: ['Schema', 'Operations'],
571
642
  description: 'Enforce arrange in alphabetical order for type fields, enum values, input object fields, operation selections and more.',
572
643
  url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/alphabetize.md',
573
644
  examples: [
@@ -646,15 +717,22 @@ const rule = {
646
717
  `,
647
718
  },
648
719
  ],
649
- optionsForConfig: [
650
- {
651
- fields: fieldsEnum,
652
- values: valuesEnum,
653
- selections: selectionsEnum,
654
- variables: variablesEnum,
655
- arguments: argumentsEnum,
656
- },
657
- ],
720
+ configOptions: {
721
+ schema: [
722
+ {
723
+ fields: fieldsEnum,
724
+ values: valuesEnum,
725
+ arguments: argumentsEnum,
726
+ },
727
+ ],
728
+ operations: [
729
+ {
730
+ selections: selectionsEnum,
731
+ variables: variablesEnum,
732
+ arguments: [graphql.Kind.FIELD, graphql.Kind.DIRECTIVE],
733
+ },
734
+ ],
735
+ },
658
736
  },
659
737
  messages: {
660
738
  [ALPHABETIZE]: '"{{ currName }}" should be before "{{ prevName }}"',
@@ -792,430 +870,128 @@ const rule = {
792
870
  },
793
871
  };
794
872
 
795
- const AVOID_DUPLICATE_FIELDS = 'AVOID_DUPLICATE_FIELDS';
796
873
  const rule$1 = {
797
874
  meta: {
798
875
  type: 'suggestion',
799
876
  docs: {
800
- description: `Checks for duplicate fields in selection set, variables in operation definition, or in arguments set of a field.`,
801
- category: 'Stylistic Issues',
802
- url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/avoid-duplicate-fields.md',
803
877
  examples: [
804
878
  {
805
879
  title: 'Incorrect',
880
+ usage: [{ style: 'inline' }],
806
881
  code: /* GraphQL */ `
807
- query {
808
- user {
809
- name
810
- email
811
- name # duplicate field
812
- }
813
- }
814
- `,
815
- },
816
- {
817
- title: 'Incorrect',
818
- code: /* GraphQL */ `
819
- query {
820
- users(
821
- first: 100
822
- skip: 50
823
- after: "cji629tngfgou0b73kt7vi5jo"
824
- first: 100 # duplicate argument
825
- ) {
826
- id
827
- }
882
+ """ Description """
883
+ type someTypeName {
884
+ # ...
828
885
  }
829
886
  `,
830
887
  },
831
888
  {
832
- title: 'Incorrect',
889
+ title: 'Correct',
890
+ usage: [{ style: 'inline' }],
833
891
  code: /* GraphQL */ `
834
- query (
835
- $first: Int!
836
- $first: Int! # duplicate variable
837
- ) {
838
- users(first: $first, skip: 50) {
839
- id
840
- }
892
+ " Description "
893
+ type someTypeName {
894
+ # ...
841
895
  }
842
896
  `,
843
897
  },
844
898
  ],
899
+ description: 'Require all comments to follow the same style (either block or inline).',
900
+ category: 'Schema',
901
+ url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/description-style.md',
902
+ recommended: true,
845
903
  },
846
- messages: {
847
- [AVOID_DUPLICATE_FIELDS]: `{{ type }} "{{ fieldName }}" defined multiple times`,
848
- },
849
- schema: [],
904
+ schema: [
905
+ {
906
+ type: 'object',
907
+ additionalProperties: false,
908
+ properties: {
909
+ style: {
910
+ enum: ['block', 'inline'],
911
+ default: 'block',
912
+ },
913
+ },
914
+ },
915
+ ],
850
916
  },
851
917
  create(context) {
852
- function checkNode(usedFields, fieldName, type, node) {
853
- if (usedFields.has(fieldName)) {
918
+ const { style = 'block' } = context.options[0] || {};
919
+ const isBlock = style === 'block';
920
+ return {
921
+ [`.description[type=StringValue][block!=${isBlock}]`](node) {
854
922
  context.report({
855
- loc: getLocation((node.kind === graphql.Kind.FIELD && node.alias ? node.alias : node).loc, fieldName, {
856
- offsetEnd: node.kind === graphql.Kind.VARIABLE_DEFINITION ? 0 : 1,
857
- }),
858
- messageId: AVOID_DUPLICATE_FIELDS,
859
- data: {
860
- type,
861
- fieldName,
862
- },
923
+ loc: getLocation(node.loc),
924
+ message: `Unexpected ${isBlock ? 'inline' : 'block'} description`,
863
925
  });
864
- }
865
- else {
866
- usedFields.add(fieldName);
867
- }
868
- }
869
- return {
870
- OperationDefinition(node) {
871
- const set = new Set();
872
- for (const varDef of node.variableDefinitions) {
873
- checkNode(set, varDef.variable.name.value, 'Operation variable', varDef);
874
- }
875
- },
876
- Field(node) {
877
- const set = new Set();
878
- for (const arg of node.arguments) {
879
- checkNode(set, arg.name.value, 'Field argument', arg);
880
- }
881
- },
882
- SelectionSet(node) {
883
- var _a;
884
- const set = new Set();
885
- for (const selection of node.selections) {
886
- if (selection.kind === graphql.Kind.FIELD) {
887
- checkNode(set, ((_a = selection.alias) === null || _a === void 0 ? void 0 : _a.value) || selection.name.value, 'Field', selection);
888
- }
889
- }
890
926
  },
891
927
  };
892
928
  },
893
929
  };
894
930
 
895
- const AVOID_OPERATION_NAME_PREFIX = 'AVOID_OPERATION_NAME_PREFIX';
931
+ const isObjectType = (node) => [graphql.Kind.OBJECT_TYPE_DEFINITION, graphql.Kind.OBJECT_TYPE_EXTENSION].includes(node.type);
932
+ const isQueryType = (node) => isObjectType(node) && node.name.value === 'Query';
933
+ const isMutationType = (node) => isObjectType(node) && node.name.value === 'Mutation';
896
934
  const rule$2 = {
897
935
  meta: {
898
936
  type: 'suggestion',
899
937
  docs: {
900
- 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.',
901
- category: 'Stylistic Issues',
902
- url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/avoid-operation-name-prefix.md',
938
+ 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.',
939
+ category: 'Schema',
940
+ url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/input-name.md',
903
941
  examples: [
904
942
  {
905
943
  title: 'Incorrect',
906
- usage: [{ keywords: ['get'] }],
944
+ usage: [{ checkInputType: true }],
907
945
  code: /* GraphQL */ `
908
- query getUserDetails {
909
- # ...
910
- }`,
946
+ type Mutation {
947
+ SetMessage(message: InputMessage): String
948
+ }
949
+ `,
911
950
  },
912
951
  {
913
- title: 'Correct',
914
- usage: [{ keywords: ['get'] }],
952
+ title: 'Correct (with checkInputType)',
953
+ usage: [{ checkInputType: true }],
915
954
  code: /* GraphQL */ `
916
- query userDetails {
917
- # ...
918
- }`,
955
+ type Mutation {
956
+ SetMessage(input: SetMessageInput): String
957
+ }
958
+ `,
959
+ },
960
+ {
961
+ title: 'Correct (without checkInputType)',
962
+ usage: [{ checkInputType: false }],
963
+ code: /* GraphQL */ `
964
+ type Mutation {
965
+ SetMessage(input: AnyInputTypeName): String
966
+ }
967
+ `,
919
968
  },
920
969
  ],
921
970
  },
922
- messages: {
923
- [AVOID_OPERATION_NAME_PREFIX]: `Forbidden operation name prefix: "{{ invalidPrefix }}"`,
924
- },
925
971
  schema: [
926
972
  {
927
- additionalProperties: false,
928
973
  type: 'object',
929
- required: ['keywords'],
974
+ additionalProperties: false,
930
975
  properties: {
931
- caseSensitive: {
976
+ checkInputType: {
977
+ type: 'boolean',
932
978
  default: false,
979
+ description: 'Check that the input type name follows the convention <mutationName>Input',
980
+ },
981
+ caseSensitiveInputType: {
933
982
  type: 'boolean',
983
+ default: true,
984
+ description: 'Allow for case discrepancies in the input type name',
934
985
  },
935
- keywords: {
936
- additionalItems: false,
937
- type: 'array',
938
- minItems: 1,
939
- items: {
940
- type: 'string',
941
- },
942
- },
943
- },
944
- },
945
- ],
946
- },
947
- create(context) {
948
- return {
949
- 'OperationDefinition, FragmentDefinition'(node) {
950
- const config = context.options[0] || { keywords: [], caseSensitive: false };
951
- const caseSensitive = config.caseSensitive;
952
- const keywords = config.keywords || [];
953
- if (node && node.name && node.name.value !== '') {
954
- for (const keyword of keywords) {
955
- const testKeyword = caseSensitive ? keyword : keyword.toLowerCase();
956
- const testName = caseSensitive ? node.name.value : node.name.value.toLowerCase();
957
- if (testName.startsWith(testKeyword)) {
958
- const { start } = node.name.loc;
959
- context.report({
960
- loc: {
961
- start: {
962
- line: start.line,
963
- column: start.column - 1,
964
- },
965
- end: {
966
- line: start.line,
967
- column: start.column - 1 + testKeyword.length,
968
- },
969
- },
970
- data: {
971
- invalidPrefix: keyword,
972
- },
973
- messageId: AVOID_OPERATION_NAME_PREFIX,
974
- });
975
- }
976
- }
977
- }
978
- },
979
- };
980
- },
981
- };
982
-
983
- const rule$3 = {
984
- meta: {
985
- type: 'suggestion',
986
- docs: {
987
- category: 'Best Practices',
988
- description: 'Avoid scalar result type on mutation type to make sure to return a valid state.',
989
- url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/avoid-scalar-result-type-on-mutation.md',
990
- requiresSchema: true,
991
- examples: [
992
- {
993
- title: 'Incorrect',
994
- code: /* GraphQL */ `
995
- type Mutation {
996
- createUser: Boolean
997
- }
998
- `,
999
- },
1000
- {
1001
- title: 'Correct',
1002
- code: /* GraphQL */ `
1003
- type Mutation {
1004
- createUser: User!
1005
- }
1006
- `,
1007
- },
1008
- ],
1009
- },
1010
- schema: [],
1011
- },
1012
- create(context) {
1013
- const schema = requireGraphQLSchemaFromContext('avoid-scalar-result-type-on-mutation', context);
1014
- const mutationType = schema.getMutationType();
1015
- if (!mutationType) {
1016
- return {};
1017
- }
1018
- const selector = [
1019
- `:matches(${graphql.Kind.OBJECT_TYPE_DEFINITION}, ${graphql.Kind.OBJECT_TYPE_EXTENSION})[name.value=${mutationType.name}]`,
1020
- '>',
1021
- graphql.Kind.FIELD_DEFINITION,
1022
- graphql.Kind.NAMED_TYPE,
1023
- ].join(' ');
1024
- return {
1025
- [selector](node) {
1026
- const typeName = node.name.value;
1027
- const graphQLType = schema.getType(typeName);
1028
- if (graphql.isScalarType(graphQLType)) {
1029
- context.report({
1030
- loc: getLocation(node.loc, typeName),
1031
- message: `Unexpected scalar result type "${typeName}"`,
1032
- });
1033
- }
1034
- },
1035
- };
1036
- },
1037
- };
1038
-
1039
- const AVOID_TYPENAME_PREFIX = 'AVOID_TYPENAME_PREFIX';
1040
- const rule$4 = {
1041
- meta: {
1042
- type: 'suggestion',
1043
- docs: {
1044
- category: 'Best Practices',
1045
- description: 'Enforces users to avoid using the type name in a field name while defining your schema.',
1046
- recommended: true,
1047
- url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/avoid-typename-prefix.md',
1048
- examples: [
1049
- {
1050
- title: 'Incorrect',
1051
- code: /* GraphQL */ `
1052
- type User {
1053
- userId: ID!
1054
- }
1055
- `,
1056
- },
1057
- {
1058
- title: 'Correct',
1059
- code: /* GraphQL */ `
1060
- type User {
1061
- id: ID!
1062
- }
1063
- `,
1064
- },
1065
- ],
1066
- },
1067
- messages: {
1068
- [AVOID_TYPENAME_PREFIX]: `Field "{{ fieldName }}" starts with the name of the parent type "{{ typeName }}"`,
1069
- },
1070
- schema: [],
1071
- },
1072
- create(context) {
1073
- return {
1074
- 'ObjectTypeDefinition, ObjectTypeExtension, InterfaceTypeDefinition, InterfaceTypeExtension'(node) {
1075
- const typeName = node.name.value;
1076
- const lowerTypeName = typeName.toLowerCase();
1077
- for (const field of node.fields) {
1078
- const fieldName = field.name.value;
1079
- if (fieldName.toLowerCase().startsWith(lowerTypeName)) {
1080
- context.report({
1081
- data: {
1082
- fieldName,
1083
- typeName,
1084
- },
1085
- messageId: AVOID_TYPENAME_PREFIX,
1086
- loc: getLocation(field.loc, lowerTypeName),
1087
- });
1088
- }
1089
- }
1090
- },
1091
- };
1092
- },
1093
- };
1094
-
1095
- const rule$5 = {
1096
- meta: {
1097
- type: 'suggestion',
1098
- docs: {
1099
- examples: [
1100
- {
1101
- title: 'Incorrect',
1102
- usage: [{ style: 'inline' }],
1103
- code: /* GraphQL */ `
1104
- """ Description """
1105
- type someTypeName {
1106
- # ...
1107
- }
1108
- `,
1109
- },
1110
- {
1111
- title: 'Correct',
1112
- usage: [{ style: 'inline' }],
1113
- code: /* GraphQL */ `
1114
- " Description "
1115
- type someTypeName {
1116
- # ...
1117
- }
1118
- `,
1119
- },
1120
- ],
1121
- description: 'Require all comments to follow the same style (either block or inline).',
1122
- category: 'Stylistic Issues',
1123
- url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/description-style.md',
1124
- },
1125
- schema: [
1126
- {
1127
- type: 'object',
1128
- properties: {
1129
- style: {
1130
- type: 'string',
1131
- enum: ['block', 'inline'],
1132
- default: 'inline',
1133
- },
1134
- },
1135
- additionalProperties: false,
1136
- },
1137
- ],
1138
- },
1139
- create(context) {
1140
- const { style } = context.options[0] || { style: 'inline' };
1141
- const wrongDescriptionType = style === 'block' ? 'inline' : 'block';
1142
- return {
1143
- '[description.type="StringValue"]': node => {
1144
- if (node.description.block !== (style === 'block')) {
1145
- context.report({
1146
- loc: getLocation(node.description.loc),
1147
- message: `Unexpected ${wrongDescriptionType} description`,
1148
- });
1149
- }
1150
- },
1151
- };
1152
- },
1153
- };
1154
-
1155
- const isObjectType = (node) => [graphql.Kind.OBJECT_TYPE_DEFINITION, graphql.Kind.OBJECT_TYPE_EXTENSION].includes(node.type);
1156
- const isQueryType = (node) => isObjectType(node) && node.name.value === 'Query';
1157
- const isMutationType = (node) => isObjectType(node) && node.name.value === 'Mutation';
1158
- const rule$6 = {
1159
- meta: {
1160
- type: 'suggestion',
1161
- docs: {
1162
- 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.',
1163
- category: 'Stylistic Issues',
1164
- url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/input-name.md',
1165
- examples: [
1166
- {
1167
- title: 'Incorrect',
1168
- usage: [{ checkInputType: true }],
1169
- code: /* GraphQL */ `
1170
- type Mutation {
1171
- SetMessage(message: InputMessage): String
1172
- }
1173
- `,
1174
- },
1175
- {
1176
- title: 'Correct (with checkInputType)',
1177
- usage: [{ checkInputType: true }],
1178
- code: /* GraphQL */ `
1179
- type Mutation {
1180
- SetMessage(input: SetMessageInput): String
1181
- }
1182
- `,
1183
- },
1184
- {
1185
- title: 'Correct (without checkInputType)',
1186
- usage: [{ checkInputType: false }],
1187
- code: /* GraphQL */ `
1188
- type Mutation {
1189
- SetMessage(input: AnyInputTypeName): String
1190
- }
1191
- `,
1192
- },
1193
- ],
1194
- },
1195
- schema: [
1196
- {
1197
- type: 'object',
1198
- additionalProperties: false,
1199
- properties: {
1200
- checkInputType: {
1201
- type: 'boolean',
1202
- default: false,
1203
- description: 'Check that the input type name follows the convention <mutationName>Input',
1204
- },
1205
- caseSensitiveInputType: {
1206
- type: 'boolean',
1207
- default: true,
1208
- description: 'Allow for case discrepancies in the input type name',
1209
- },
1210
- checkQueries: {
1211
- type: 'boolean',
1212
- default: false,
1213
- description: 'Apply the rule to Queries',
1214
- },
1215
- checkMutations: {
1216
- type: 'boolean',
1217
- default: true,
1218
- description: 'Apply the rule to Mutations',
986
+ checkQueries: {
987
+ type: 'boolean',
988
+ default: false,
989
+ description: 'Apply the rule to Queries',
990
+ },
991
+ checkMutations: {
992
+ type: 'boolean',
993
+ default: true,
994
+ description: 'Apply the rule to Mutations',
1219
995
  },
1220
996
  },
1221
997
  },
@@ -1281,11 +1057,11 @@ const CASE_STYLES = [
1281
1057
  const schemaOption = {
1282
1058
  oneOf: [{ $ref: '#/definitions/asString' }, { $ref: '#/definitions/asObject' }],
1283
1059
  };
1284
- const rule$7 = {
1060
+ const rule$3 = {
1285
1061
  meta: {
1286
1062
  type: 'suggestion',
1287
1063
  docs: {
1288
- category: 'Best Practices',
1064
+ category: 'Operations',
1289
1065
  description: 'This rule allows you to enforce that the file name should match the operation name.',
1290
1066
  url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/match-document-filename.md`,
1291
1067
  examples: [
@@ -1359,6 +1135,14 @@ const rule$7 = {
1359
1135
  `,
1360
1136
  },
1361
1137
  ],
1138
+ configOptions: [
1139
+ {
1140
+ query: CaseStyle.kebabCase,
1141
+ mutation: CaseStyle.kebabCase,
1142
+ subscription: CaseStyle.kebabCase,
1143
+ fragment: CaseStyle.kebabCase,
1144
+ },
1145
+ ],
1362
1146
  },
1363
1147
  messages: {
1364
1148
  [MATCH_EXTENSION]: `File extension "{{ fileExtension }}" don't match extension "{{ expectedFileExtension }}"`,
@@ -1367,27 +1151,29 @@ const rule$7 = {
1367
1151
  schema: {
1368
1152
  definitions: {
1369
1153
  asString: {
1370
- type: 'string',
1371
- description: `One of: ${CASE_STYLES.map(t => `\`${t}\``).join(', ')}`,
1372
1154
  enum: CASE_STYLES,
1155
+ description: `One of: ${CASE_STYLES.map(t => `\`${t}\``).join(', ')}`,
1373
1156
  },
1374
1157
  asObject: {
1375
1158
  type: 'object',
1159
+ additionalProperties: false,
1376
1160
  properties: {
1377
1161
  style: {
1378
- type: 'string',
1379
1162
  enum: CASE_STYLES,
1380
1163
  },
1164
+ suffix: {
1165
+ type: 'string',
1166
+ },
1381
1167
  },
1382
1168
  },
1383
1169
  },
1384
- $schema: 'http://json-schema.org/draft-04/schema#',
1385
1170
  type: 'array',
1171
+ maxItems: 1,
1386
1172
  items: {
1387
1173
  type: 'object',
1174
+ additionalProperties: false,
1388
1175
  properties: {
1389
1176
  fileExtension: {
1390
- type: 'string',
1391
1177
  enum: ACCEPTED_EXTENSIONS,
1392
1178
  },
1393
1179
  query: schemaOption,
@@ -1462,13 +1248,7 @@ const rule$7 = {
1462
1248
  },
1463
1249
  };
1464
1250
 
1465
- const FIELDS_KINDS = [
1466
- graphql.Kind.FIELD_DEFINITION,
1467
- graphql.Kind.INPUT_VALUE_DEFINITION,
1468
- graphql.Kind.VARIABLE_DEFINITION,
1469
- graphql.Kind.ARGUMENT,
1470
- graphql.Kind.DIRECTIVE_DEFINITION,
1471
- ];
1251
+ const FIELDS_KINDS = [graphql.Kind.FIELD_DEFINITION, graphql.Kind.INPUT_VALUE_DEFINITION, graphql.Kind.ARGUMENT, graphql.Kind.DIRECTIVE_DEFINITION];
1472
1252
  const KindToDisplayName = {
1473
1253
  // types
1474
1254
  [graphql.Kind.OBJECT_TYPE_DEFINITION]: 'Type',
@@ -1480,13 +1260,13 @@ const KindToDisplayName = {
1480
1260
  // fields
1481
1261
  [graphql.Kind.FIELD_DEFINITION]: 'Field',
1482
1262
  [graphql.Kind.INPUT_VALUE_DEFINITION]: 'Input property',
1483
- [graphql.Kind.VARIABLE_DEFINITION]: 'Variable',
1484
1263
  [graphql.Kind.ARGUMENT]: 'Argument',
1485
1264
  [graphql.Kind.DIRECTIVE_DEFINITION]: 'Directive',
1486
1265
  // rest
1487
1266
  [graphql.Kind.ENUM_VALUE_DEFINITION]: 'Enumeration value',
1488
1267
  [graphql.Kind.OPERATION_DEFINITION]: 'Operation',
1489
1268
  [graphql.Kind.FRAGMENT_DEFINITION]: 'Fragment',
1269
+ [graphql.Kind.VARIABLE_DEFINITION]: 'Variable',
1490
1270
  };
1491
1271
  const StyleToRegex = {
1492
1272
  camelCase: /^[a-z][\dA-Za-z]*$/,
@@ -1499,12 +1279,12 @@ const ALLOWED_STYLES = Object.keys(StyleToRegex);
1499
1279
  const schemaOption$1 = {
1500
1280
  oneOf: [{ $ref: '#/definitions/asString' }, { $ref: '#/definitions/asObject' }],
1501
1281
  };
1502
- const rule$8 = {
1282
+ const rule$4 = {
1503
1283
  meta: {
1504
1284
  type: 'suggestion',
1505
1285
  docs: {
1506
1286
  description: 'Require names to follow specified conventions.',
1507
- category: 'Best Practices',
1287
+ category: ['Schema', 'Operations'],
1508
1288
  recommended: true,
1509
1289
  url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/naming-convention.md',
1510
1290
  examples: [
@@ -1527,22 +1307,12 @@ const rule$8 = {
1527
1307
  `,
1528
1308
  },
1529
1309
  ],
1530
- optionsForConfig: [
1531
- {
1532
- types: 'PascalCase',
1533
- fields: 'camelCase',
1534
- overrides: {
1310
+ configOptions: {
1311
+ schema: [
1312
+ {
1313
+ types: 'PascalCase',
1314
+ fields: 'camelCase',
1535
1315
  EnumValueDefinition: 'UPPER_CASE',
1536
- OperationDefinition: {
1537
- style: 'PascalCase',
1538
- forbiddenPrefixes: ['Query', 'Mutation', 'Subscription', 'Get'],
1539
- forbiddenSuffixes: ['Query', 'Mutation', 'Subscription'],
1540
- },
1541
- FragmentDefinition: {
1542
- style: 'PascalCase',
1543
- forbiddenPrefixes: ['Fragment'],
1544
- forbiddenSuffixes: ['Fragment'],
1545
- },
1546
1316
  'FieldDefinition[parent.name.value=Query]': {
1547
1317
  forbiddenPrefixes: ['query', 'get'],
1548
1318
  forbiddenSuffixes: ['Query'],
@@ -1556,8 +1326,24 @@ const rule$8 = {
1556
1326
  forbiddenSuffixes: ['Subscription'],
1557
1327
  },
1558
1328
  },
1559
- },
1560
- ],
1329
+ ],
1330
+ operations: [
1331
+ {
1332
+ Argument: 'camelCase',
1333
+ VariableDefinition: 'camelCase',
1334
+ OperationDefinition: {
1335
+ style: 'PascalCase',
1336
+ forbiddenPrefixes: ['Query', 'Mutation', 'Subscription', 'Get'],
1337
+ forbiddenSuffixes: ['Query', 'Mutation', 'Subscription'],
1338
+ },
1339
+ FragmentDefinition: {
1340
+ style: 'PascalCase',
1341
+ forbiddenPrefixes: ['Fragment'],
1342
+ forbiddenSuffixes: ['Fragment'],
1343
+ },
1344
+ },
1345
+ ],
1346
+ },
1561
1347
  },
1562
1348
  schema: {
1563
1349
  definitions: {
@@ -1593,14 +1379,6 @@ const rule$8 = {
1593
1379
  type: 'object',
1594
1380
  additionalProperties: false,
1595
1381
  properties: {
1596
- types: {
1597
- ...schemaOption$1,
1598
- description: `Includes:\n\n${TYPES_KINDS.map(kind => `- [${kind}](https://spec.graphql.org/October2021/#${kind})`).join('\n')}`,
1599
- },
1600
- fields: {
1601
- ...schemaOption$1,
1602
- description: `Includes:\n\n${FIELDS_KINDS.map(kind => `- [${kind}](https://spec.graphql.org/October2021/#${kind})`).join('\n')}`,
1603
- },
1604
1382
  allowLeadingUnderscore: {
1605
1383
  type: 'boolean',
1606
1384
  default: false,
@@ -1609,35 +1387,42 @@ const rule$8 = {
1609
1387
  type: 'boolean',
1610
1388
  default: false,
1611
1389
  },
1612
- overrides: {
1613
- type: 'object',
1614
- additionalProperties: false,
1615
- description: [
1616
- 'May contain the following `ASTNode` names:',
1617
- '',
1618
- ...ALLOWED_KINDS.map(kind => `- [${kind}](https://spec.graphql.org/October2021/#${kind})`),
1619
- '',
1620
- "> It's also possible to use a [`selector`](https://eslint.org/docs/developer-guide/selectors) that starts with `ASTNode` name",
1621
- '>',
1622
- '> Example: pattern property `FieldDefinition[parent.name.value=Query]` will match only fields for type `Query`',
1623
- ].join('\n'),
1624
- patternProperties: {
1625
- [`^(${ALLOWED_KINDS.join('|')})(.+)?$`]: schemaOption$1,
1626
- },
1390
+ types: {
1391
+ ...schemaOption$1,
1392
+ description: `Includes:\n\n${TYPES_KINDS.map(kind => `- \`${kind}\``).join('\n')}`,
1627
1393
  },
1628
- },
1394
+ fields: {
1395
+ ...schemaOption$1,
1396
+ description: `Includes:\n\n${FIELDS_KINDS.map(kind => `- \`${kind}\``).join('\n')}`,
1397
+ },
1398
+ ...Object.fromEntries(ALLOWED_KINDS.map(kind => [
1399
+ kind,
1400
+ {
1401
+ ...schemaOption$1,
1402
+ description: `Read more about this kind on [spec.graphql.org](https://spec.graphql.org/October2021/#${kind}).`,
1403
+ },
1404
+ ])),
1405
+ },
1406
+ patternProperties: {
1407
+ [`^(${ALLOWED_KINDS.join('|')})(.+)?$`]: schemaOption$1,
1408
+ },
1409
+ description: [
1410
+ "> It's possible to use a [`selector`](https://eslint.org/docs/developer-guide/selectors) that starts with allowed `ASTNode` names which are described below.",
1411
+ '>',
1412
+ '> Paste or drop code into the editor in [ASTExplorer](https://astexplorer.net) and inspect the generated AST to compose your selector.',
1413
+ '>',
1414
+ '> Example: pattern property `FieldDefinition[parent.name.value=Query]` will match only fields for type `Query`.',
1415
+ ].join('\n'),
1629
1416
  },
1630
1417
  },
1631
1418
  },
1632
1419
  create(context) {
1633
- const options = {
1634
- overrides: {},
1635
- ...context.options[0],
1636
- };
1420
+ const options = context.options[0] || {};
1421
+ const { allowLeadingUnderscore, allowTrailingUnderscore, types, fields, ...restOptions } = options;
1637
1422
  function normalisePropertyOption(kind) {
1638
- let style = options.overrides[kind];
1423
+ let style = options[kind];
1639
1424
  if (!style) {
1640
- style = TYPES_KINDS.includes(kind) ? options.types : options.fields;
1425
+ style = TYPES_KINDS.includes(kind) ? types : fields;
1641
1426
  }
1642
1427
  return typeof style === 'object' ? style : { style };
1643
1428
  }
@@ -1658,10 +1443,10 @@ const rule$8 = {
1658
1443
  }
1659
1444
  function getErrorMessage() {
1660
1445
  let name = nodeName;
1661
- if (options.allowLeadingUnderscore) {
1446
+ if (allowLeadingUnderscore) {
1662
1447
  name = name.replace(/^_*/, '');
1663
1448
  }
1664
- if (options.allowTrailingUnderscore) {
1449
+ if (allowTrailingUnderscore) {
1665
1450
  name = name.replace(/_*$/, '');
1666
1451
  }
1667
1452
  if (prefix && !name.startsWith(prefix)) {
@@ -1695,15 +1480,13 @@ const rule$8 = {
1695
1480
  });
1696
1481
  };
1697
1482
  const listeners = {};
1698
- if (!options.allowLeadingUnderscore) {
1483
+ if (!allowLeadingUnderscore) {
1699
1484
  listeners['Name[value=/^_/]:matches([parent.kind!=Field], [parent.kind=Field][parent.alias])'] = checkUnderscore;
1700
1485
  }
1701
- if (!options.allowTrailingUnderscore) {
1486
+ if (!allowTrailingUnderscore) {
1702
1487
  listeners['Name[value=/_$/]:matches([parent.kind!=Field], [parent.kind=Field][parent.alias])'] = checkUnderscore;
1703
1488
  }
1704
- const selectors = new Set([options.types && TYPES_KINDS, options.fields && FIELDS_KINDS, Object.keys(options.overrides)]
1705
- .flat()
1706
- .filter(Boolean));
1489
+ const selectors = new Set([types && TYPES_KINDS, fields && FIELDS_KINDS, Object.keys(restOptions)].flat().filter(Boolean));
1707
1490
  for (const selector of selectors) {
1708
1491
  listeners[selector] = checkNode(selector);
1709
1492
  }
@@ -1712,11 +1495,11 @@ const rule$8 = {
1712
1495
  };
1713
1496
 
1714
1497
  const NO_ANONYMOUS_OPERATIONS = 'NO_ANONYMOUS_OPERATIONS';
1715
- const rule$9 = {
1498
+ const rule$5 = {
1716
1499
  meta: {
1717
1500
  type: 'suggestion',
1718
1501
  docs: {
1719
- category: 'Best Practices',
1502
+ category: 'Operations',
1720
1503
  description: 'Require name for your GraphQL operations. This is useful since most GraphQL client libraries are using the operation name for caching purposes.',
1721
1504
  recommended: true,
1722
1505
  url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/no-anonymous-operations.md',
@@ -1760,12 +1543,12 @@ const rule$9 = {
1760
1543
  };
1761
1544
 
1762
1545
  const ERROR_MESSAGE_ID = 'NO_CASE_INSENSITIVE_ENUM_VALUES_DUPLICATES';
1763
- const rule$a = {
1546
+ const rule$6 = {
1764
1547
  meta: {
1765
1548
  type: 'suggestion',
1766
1549
  docs: {
1767
1550
  url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/no-case-insensitive-enum-values-duplicates.md',
1768
- category: 'Best Practices',
1551
+ category: 'Schema',
1769
1552
  recommended: true,
1770
1553
  description: 'Disallow case-insensitive enum values duplicates.',
1771
1554
  examples: [
@@ -1816,11 +1599,11 @@ const rule$a = {
1816
1599
  };
1817
1600
 
1818
1601
  const NO_DEPRECATED = 'NO_DEPRECATED';
1819
- const rule$b = {
1602
+ const rule$7 = {
1820
1603
  meta: {
1821
1604
  type: 'suggestion',
1822
1605
  docs: {
1823
- category: 'Best Practices',
1606
+ category: 'Operations',
1824
1607
  description: `Enforce that deprecated fields or enum values are not in use by operations.`,
1825
1608
  url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/no-deprecated.md`,
1826
1609
  requiresSchema: true,
@@ -1883,48 +1666,150 @@ const rule$b = {
1883
1666
  fullName
1884
1667
  }
1885
1668
  }
1886
- `,
1887
- },
1888
- ],
1889
- },
1890
- messages: {
1891
- [NO_DEPRECATED]: `This {{ type }} is marked as deprecated in your GraphQL schema {{ reason }}`,
1892
- },
1893
- schema: [],
1894
- },
1895
- create(context) {
1669
+ `,
1670
+ },
1671
+ ],
1672
+ recommended: true,
1673
+ },
1674
+ messages: {
1675
+ [NO_DEPRECATED]: `This {{ type }} is marked as deprecated in your GraphQL schema {{ reason }}`,
1676
+ },
1677
+ schema: [],
1678
+ },
1679
+ create(context) {
1680
+ return {
1681
+ EnumValue(node) {
1682
+ requireGraphQLSchemaFromContext('no-deprecated', context);
1683
+ const typeInfo = node.typeInfo();
1684
+ if (typeInfo && typeInfo.enumValue) {
1685
+ if (typeInfo.enumValue.deprecationReason) {
1686
+ const enumValueName = node.value;
1687
+ context.report({
1688
+ loc: getLocation(node.loc, enumValueName),
1689
+ messageId: NO_DEPRECATED,
1690
+ data: {
1691
+ type: 'enum value',
1692
+ reason: typeInfo.enumValue.deprecationReason ? `(reason: ${typeInfo.enumValue.deprecationReason})` : '',
1693
+ },
1694
+ });
1695
+ }
1696
+ }
1697
+ },
1698
+ Field(node) {
1699
+ requireGraphQLSchemaFromContext('no-deprecated', context);
1700
+ const typeInfo = node.typeInfo();
1701
+ if (typeInfo && typeInfo.fieldDef) {
1702
+ if (typeInfo.fieldDef.deprecationReason) {
1703
+ const fieldName = node.name.value;
1704
+ context.report({
1705
+ loc: getLocation(node.loc, fieldName),
1706
+ messageId: NO_DEPRECATED,
1707
+ data: {
1708
+ type: 'field',
1709
+ reason: typeInfo.fieldDef.deprecationReason ? `(reason: ${typeInfo.fieldDef.deprecationReason})` : '',
1710
+ },
1711
+ });
1712
+ }
1713
+ }
1714
+ },
1715
+ };
1716
+ },
1717
+ };
1718
+
1719
+ const NO_DUPLICATE_FIELDS = 'NO_DUPLICATE_FIELDS';
1720
+ const rule$8 = {
1721
+ meta: {
1722
+ type: 'suggestion',
1723
+ docs: {
1724
+ description: `Checks for duplicate fields in selection set, variables in operation definition, or in arguments set of a field.`,
1725
+ category: 'Operations',
1726
+ url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/no-duplicate-fields.md',
1727
+ recommended: true,
1728
+ examples: [
1729
+ {
1730
+ title: 'Incorrect',
1731
+ code: /* GraphQL */ `
1732
+ query {
1733
+ user {
1734
+ name
1735
+ email
1736
+ name # duplicate field
1737
+ }
1738
+ }
1739
+ `,
1740
+ },
1741
+ {
1742
+ title: 'Incorrect',
1743
+ code: /* GraphQL */ `
1744
+ query {
1745
+ users(
1746
+ first: 100
1747
+ skip: 50
1748
+ after: "cji629tngfgou0b73kt7vi5jo"
1749
+ first: 100 # duplicate argument
1750
+ ) {
1751
+ id
1752
+ }
1753
+ }
1754
+ `,
1755
+ },
1756
+ {
1757
+ title: 'Incorrect',
1758
+ code: /* GraphQL */ `
1759
+ query (
1760
+ $first: Int!
1761
+ $first: Int! # duplicate variable
1762
+ ) {
1763
+ users(first: $first, skip: 50) {
1764
+ id
1765
+ }
1766
+ }
1767
+ `,
1768
+ },
1769
+ ],
1770
+ },
1771
+ messages: {
1772
+ [NO_DUPLICATE_FIELDS]: `{{ type }} "{{ fieldName }}" defined multiple times`,
1773
+ },
1774
+ schema: [],
1775
+ },
1776
+ create(context) {
1777
+ function checkNode(usedFields, fieldName, type, node) {
1778
+ if (usedFields.has(fieldName)) {
1779
+ context.report({
1780
+ loc: getLocation((node.kind === graphql.Kind.FIELD && node.alias ? node.alias : node).loc, fieldName, {
1781
+ offsetEnd: node.kind === graphql.Kind.VARIABLE_DEFINITION ? 0 : 1,
1782
+ }),
1783
+ messageId: NO_DUPLICATE_FIELDS,
1784
+ data: {
1785
+ type,
1786
+ fieldName,
1787
+ },
1788
+ });
1789
+ }
1790
+ else {
1791
+ usedFields.add(fieldName);
1792
+ }
1793
+ }
1896
1794
  return {
1897
- EnumValue(node) {
1898
- requireGraphQLSchemaFromContext('no-deprecated', context);
1899
- const typeInfo = node.typeInfo();
1900
- if (typeInfo && typeInfo.enumValue) {
1901
- if (typeInfo.enumValue.deprecationReason) {
1902
- const enumValueName = node.value;
1903
- context.report({
1904
- loc: getLocation(node.loc, enumValueName),
1905
- messageId: NO_DEPRECATED,
1906
- data: {
1907
- type: 'enum value',
1908
- reason: typeInfo.enumValue.deprecationReason ? `(reason: ${typeInfo.enumValue.deprecationReason})` : '',
1909
- },
1910
- });
1911
- }
1795
+ OperationDefinition(node) {
1796
+ const set = new Set();
1797
+ for (const varDef of node.variableDefinitions) {
1798
+ checkNode(set, varDef.variable.name.value, 'Operation variable', varDef);
1912
1799
  }
1913
1800
  },
1914
1801
  Field(node) {
1915
- requireGraphQLSchemaFromContext('no-deprecated', context);
1916
- const typeInfo = node.typeInfo();
1917
- if (typeInfo && typeInfo.fieldDef) {
1918
- if (typeInfo.fieldDef.deprecationReason) {
1919
- const fieldName = node.name.value;
1920
- context.report({
1921
- loc: getLocation(node.loc, fieldName),
1922
- messageId: NO_DEPRECATED,
1923
- data: {
1924
- type: 'field',
1925
- reason: typeInfo.fieldDef.deprecationReason ? `(reason: ${typeInfo.fieldDef.deprecationReason})` : '',
1926
- },
1927
- });
1802
+ const set = new Set();
1803
+ for (const arg of node.arguments) {
1804
+ checkNode(set, arg.name.value, 'Field argument', arg);
1805
+ }
1806
+ },
1807
+ SelectionSet(node) {
1808
+ var _a;
1809
+ const set = new Set();
1810
+ for (const selection of node.selections) {
1811
+ if (selection.kind === graphql.Kind.FIELD) {
1812
+ checkNode(set, ((_a = selection.alias) === null || _a === void 0 ? void 0 : _a.value) || selection.name.value, 'Field', selection);
1928
1813
  }
1929
1814
  }
1930
1815
  },
@@ -1933,14 +1818,14 @@ const rule$b = {
1933
1818
  };
1934
1819
 
1935
1820
  const HASHTAG_COMMENT = 'HASHTAG_COMMENT';
1936
- const rule$c = {
1821
+ const rule$9 = {
1937
1822
  meta: {
1938
1823
  messages: {
1939
1824
  [HASHTAG_COMMENT]: 'Using hashtag (#) for adding GraphQL descriptions is not allowed. Prefer using """ for multiline, or " for a single line description.',
1940
1825
  },
1941
1826
  docs: {
1942
1827
  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.',
1943
- category: 'Best Practices',
1828
+ category: 'Schema',
1944
1829
  url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/no-hashtag-description.md',
1945
1830
  examples: [
1946
1831
  {
@@ -1977,6 +1862,7 @@ const rule$c = {
1977
1862
  `,
1978
1863
  },
1979
1864
  ],
1865
+ recommended: true,
1980
1866
  },
1981
1867
  type: 'suggestion',
1982
1868
  schema: [],
@@ -2005,85 +1891,18 @@ const rule$c = {
2005
1891
  },
2006
1892
  };
2007
1893
 
2008
- const NO_OPERATION_NAME_SUFFIX = 'NO_OPERATION_NAME_SUFFIX';
2009
- const rule$d = {
2010
- meta: {
2011
- fixable: 'code',
2012
- type: 'suggestion',
2013
- docs: {
2014
- category: 'Stylistic Issues',
2015
- recommended: true,
2016
- description: `Makes sure you are not adding the operation type to the name of the operation.`,
2017
- url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/no-operation-name-suffix.md`,
2018
- examples: [
2019
- {
2020
- title: 'Incorrect',
2021
- code: /* GraphQL */ `
2022
- query userQuery {
2023
- # ...
2024
- }
2025
- `,
2026
- },
2027
- {
2028
- title: 'Correct',
2029
- code: /* GraphQL */ `
2030
- query user {
2031
- # ...
2032
- }
2033
- `,
2034
- },
2035
- ],
2036
- },
2037
- messages: {
2038
- [NO_OPERATION_NAME_SUFFIX]: `Unnecessary "{{ invalidSuffix }}" suffix in your operation name!`,
2039
- },
2040
- schema: [],
2041
- },
2042
- create(context) {
2043
- return {
2044
- 'OperationDefinition, FragmentDefinition'(node) {
2045
- var _a;
2046
- const name = ((_a = node.name) === null || _a === void 0 ? void 0 : _a.value) || '';
2047
- if (name.length > 0) {
2048
- const invalidSuffix = 'operation' in node ? node.operation : 'fragment';
2049
- if (name.toLowerCase().endsWith(invalidSuffix)) {
2050
- const { start, end } = node.name.loc;
2051
- context.report({
2052
- loc: {
2053
- start: {
2054
- column: start.column - 1 + name.length - invalidSuffix.length,
2055
- line: start.line,
2056
- },
2057
- end: {
2058
- column: end.column - 1 + name.length,
2059
- line: end.line,
2060
- },
2061
- },
2062
- data: {
2063
- invalidSuffix,
2064
- },
2065
- fix: fixer => fixer.removeRange([node.name.range[1] - invalidSuffix.length, node.name.range[1]]),
2066
- messageId: NO_OPERATION_NAME_SUFFIX,
2067
- });
2068
- }
2069
- }
2070
- },
2071
- };
2072
- },
2073
- };
2074
-
2075
- const ROOT_TYPES = ['query', 'mutation', 'subscription'];
2076
- const rule$e = {
1894
+ const ROOT_TYPES = ['mutation', 'subscription'];
1895
+ const rule$a = {
2077
1896
  meta: {
2078
1897
  type: 'suggestion',
2079
1898
  docs: {
2080
- category: 'Validation',
2081
- description: 'Disallow using root types for `read-only` or `write-only` schemas.',
1899
+ category: 'Schema',
1900
+ description: 'Disallow using root types `mutation` and/or `subscription`.',
2082
1901
  url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/no-root-type.md',
2083
1902
  requiresSchema: true,
2084
1903
  examples: [
2085
1904
  {
2086
- title: 'Incorrect (`read-only` schema)',
1905
+ title: 'Incorrect',
2087
1906
  usage: [{ disallow: ['mutation', 'subscription'] }],
2088
1907
  code: /* GraphQL */ `
2089
1908
  type Mutation {
@@ -2092,16 +1911,7 @@ const rule$e = {
2092
1911
  `,
2093
1912
  },
2094
1913
  {
2095
- title: 'Incorrect (`write-only` schema)',
2096
- usage: [{ disallow: ['query'] }],
2097
- code: /* GraphQL */ `
2098
- type Query {
2099
- users: [User!]!
2100
- }
2101
- `,
2102
- },
2103
- {
2104
- title: 'Correct (`read-only` schema)',
1914
+ title: 'Correct',
2105
1915
  usage: [{ disallow: ['mutation', 'subscription'] }],
2106
1916
  code: /* GraphQL */ `
2107
1917
  type Query {
@@ -2110,7 +1920,6 @@ const rule$e = {
2110
1920
  `,
2111
1921
  },
2112
1922
  ],
2113
- optionsForConfig: [{ disallow: ['subscription'] }],
2114
1923
  },
2115
1924
  schema: {
2116
1925
  type: 'array',
@@ -2137,7 +1946,6 @@ const rule$e = {
2137
1946
  const schema = requireGraphQLSchemaFromContext('no-root-type', context);
2138
1947
  const disallow = new Set(context.options[0].disallow);
2139
1948
  const rootTypeNames = [
2140
- disallow.has('query') && schema.getQueryType(),
2141
1949
  disallow.has('mutation') && schema.getMutationType(),
2142
1950
  disallow.has('subscription') && schema.getSubscriptionType(),
2143
1951
  ]
@@ -2163,16 +1971,128 @@ const rule$e = {
2163
1971
  },
2164
1972
  };
2165
1973
 
1974
+ const rule$b = {
1975
+ meta: {
1976
+ type: 'suggestion',
1977
+ docs: {
1978
+ category: 'Schema',
1979
+ description: 'Avoid scalar result type on mutation type to make sure to return a valid state.',
1980
+ url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/no-scalar-result-type-on-mutation.md',
1981
+ requiresSchema: true,
1982
+ examples: [
1983
+ {
1984
+ title: 'Incorrect',
1985
+ code: /* GraphQL */ `
1986
+ type Mutation {
1987
+ createUser: Boolean
1988
+ }
1989
+ `,
1990
+ },
1991
+ {
1992
+ title: 'Correct',
1993
+ code: /* GraphQL */ `
1994
+ type Mutation {
1995
+ createUser: User!
1996
+ }
1997
+ `,
1998
+ },
1999
+ ],
2000
+ },
2001
+ schema: [],
2002
+ },
2003
+ create(context) {
2004
+ const schema = requireGraphQLSchemaFromContext('no-scalar-result-type-on-mutation', context);
2005
+ const mutationType = schema.getMutationType();
2006
+ if (!mutationType) {
2007
+ return {};
2008
+ }
2009
+ const selector = [
2010
+ `:matches(${graphql.Kind.OBJECT_TYPE_DEFINITION}, ${graphql.Kind.OBJECT_TYPE_EXTENSION})[name.value=${mutationType.name}]`,
2011
+ '>',
2012
+ graphql.Kind.FIELD_DEFINITION,
2013
+ graphql.Kind.NAMED_TYPE,
2014
+ ].join(' ');
2015
+ return {
2016
+ [selector](node) {
2017
+ const typeName = node.name.value;
2018
+ const graphQLType = schema.getType(typeName);
2019
+ if (graphql.isScalarType(graphQLType)) {
2020
+ context.report({
2021
+ loc: getLocation(node.loc, typeName),
2022
+ message: `Unexpected scalar result type "${typeName}"`,
2023
+ });
2024
+ }
2025
+ },
2026
+ };
2027
+ },
2028
+ };
2029
+
2030
+ const NO_TYPENAME_PREFIX = 'NO_TYPENAME_PREFIX';
2031
+ const rule$c = {
2032
+ meta: {
2033
+ type: 'suggestion',
2034
+ docs: {
2035
+ category: 'Schema',
2036
+ description: 'Enforces users to avoid using the type name in a field name while defining your schema.',
2037
+ recommended: true,
2038
+ url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/no-typename-prefix.md',
2039
+ examples: [
2040
+ {
2041
+ title: 'Incorrect',
2042
+ code: /* GraphQL */ `
2043
+ type User {
2044
+ userId: ID!
2045
+ }
2046
+ `,
2047
+ },
2048
+ {
2049
+ title: 'Correct',
2050
+ code: /* GraphQL */ `
2051
+ type User {
2052
+ id: ID!
2053
+ }
2054
+ `,
2055
+ },
2056
+ ],
2057
+ },
2058
+ messages: {
2059
+ [NO_TYPENAME_PREFIX]: `Field "{{ fieldName }}" starts with the name of the parent type "{{ typeName }}"`,
2060
+ },
2061
+ schema: [],
2062
+ },
2063
+ create(context) {
2064
+ return {
2065
+ 'ObjectTypeDefinition, ObjectTypeExtension, InterfaceTypeDefinition, InterfaceTypeExtension'(node) {
2066
+ const typeName = node.name.value;
2067
+ const lowerTypeName = typeName.toLowerCase();
2068
+ for (const field of node.fields) {
2069
+ const fieldName = field.name.value;
2070
+ if (fieldName.toLowerCase().startsWith(lowerTypeName)) {
2071
+ context.report({
2072
+ data: {
2073
+ fieldName,
2074
+ typeName,
2075
+ },
2076
+ messageId: NO_TYPENAME_PREFIX,
2077
+ loc: getLocation(field.loc, lowerTypeName),
2078
+ });
2079
+ }
2080
+ }
2081
+ },
2082
+ };
2083
+ },
2084
+ };
2085
+
2166
2086
  const UNREACHABLE_TYPE = 'UNREACHABLE_TYPE';
2167
2087
  const RULE_NAME = 'no-unreachable-types';
2168
- const rule$f = {
2088
+ const rule$d = {
2169
2089
  meta: {
2170
2090
  messages: {
2171
- [UNREACHABLE_TYPE]: `Type "{{ typeName }}" is unreachable`,
2091
+ [UNREACHABLE_TYPE]: 'Type "{{ typeName }}" is unreachable',
2172
2092
  },
2173
2093
  docs: {
2174
2094
  description: `Requires all types to be reachable at some level by root level fields.`,
2175
- category: 'Best Practices',
2095
+ category: 'Schema',
2176
2096
  url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_NAME}.md`,
2177
2097
  requiresSchema: true,
2178
2098
  examples: [
@@ -2203,6 +2123,7 @@ const rule$f = {
2203
2123
  `,
2204
2124
  },
2205
2125
  ],
2126
+ recommended: true,
2206
2127
  },
2207
2128
  fixable: 'code',
2208
2129
  type: 'suggestion',
@@ -2241,14 +2162,14 @@ const rule$f = {
2241
2162
 
2242
2163
  const UNUSED_FIELD = 'UNUSED_FIELD';
2243
2164
  const RULE_NAME$1 = 'no-unused-fields';
2244
- const rule$g = {
2165
+ const rule$e = {
2245
2166
  meta: {
2246
2167
  messages: {
2247
2168
  [UNUSED_FIELD]: `Field "{{fieldName}}" is unused`,
2248
2169
  },
2249
2170
  docs: {
2250
2171
  description: `Requires all fields to be used at some level by siblings operations.`,
2251
- category: 'Best Practices',
2172
+ category: 'Schema',
2252
2173
  url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_NAME$1}.md`,
2253
2174
  requiresSiblings: true,
2254
2175
  requiresSchema: true,
@@ -2430,11 +2351,11 @@ const MESSAGE_REQUIRE_DATE = 'MESSAGE_REQUIRE_DATE';
2430
2351
  const MESSAGE_INVALID_FORMAT = 'MESSAGE_INVALID_FORMAT';
2431
2352
  const MESSAGE_INVALID_DATE = 'MESSAGE_INVALID_DATE';
2432
2353
  const MESSAGE_CAN_BE_REMOVED = 'MESSAGE_CAN_BE_REMOVED';
2433
- const rule$h = {
2354
+ const rule$f = {
2434
2355
  meta: {
2435
2356
  type: 'suggestion',
2436
2357
  docs: {
2437
- category: 'Best Practices',
2358
+ category: 'Schema',
2438
2359
  description: 'Require deletion date on `@deprecated` directive. Suggest removing deprecated things after deprecated date.',
2439
2360
  url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/require-deprecation-date.md',
2440
2361
  examples: [
@@ -2533,11 +2454,11 @@ const rule$h = {
2533
2454
  },
2534
2455
  };
2535
2456
 
2536
- const rule$i = {
2457
+ const rule$g = {
2537
2458
  meta: {
2538
2459
  docs: {
2539
2460
  description: `Require all deprecation directives to specify a reason.`,
2540
- category: 'Best Practices',
2461
+ category: 'Schema',
2541
2462
  url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/require-deprecation-reason.md`,
2542
2463
  recommended: true,
2543
2464
  examples: [
@@ -2595,16 +2516,16 @@ const ALLOWED_KINDS$1 = [
2595
2516
  graphql.Kind.ENUM_VALUE_DEFINITION,
2596
2517
  graphql.Kind.DIRECTIVE_DEFINITION,
2597
2518
  ];
2598
- const rule$j = {
2519
+ const rule$h = {
2599
2520
  meta: {
2600
2521
  docs: {
2601
- category: 'Best Practices',
2522
+ category: 'Schema',
2602
2523
  description: 'Enforce descriptions in your type definitions.',
2603
2524
  url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/require-description.md',
2604
2525
  examples: [
2605
2526
  {
2606
2527
  title: 'Incorrect',
2607
- usage: [{ types: true, overrides: { FieldDefinition: true } }],
2528
+ usage: [{ types: true, FieldDefinition: true }],
2608
2529
  code: /* GraphQL */ `
2609
2530
  type someTypeName {
2610
2531
  name: String
@@ -2613,7 +2534,7 @@ const rule$j = {
2613
2534
  },
2614
2535
  {
2615
2536
  title: 'Correct',
2616
- usage: [{ types: true, overrides: { FieldDefinition: true } }],
2537
+ usage: [{ types: true, FieldDefinition: true }],
2617
2538
  code: /* GraphQL */ `
2618
2539
  """
2619
2540
  Some type description
@@ -2627,12 +2548,10 @@ const rule$j = {
2627
2548
  `,
2628
2549
  },
2629
2550
  ],
2630
- optionsForConfig: [
2551
+ configOptions: [
2631
2552
  {
2632
2553
  types: true,
2633
- overrides: {
2634
- [graphql.Kind.DIRECTIVE_DEFINITION]: true,
2635
- },
2554
+ [graphql.Kind.DIRECTIVE_DEFINITION]: true,
2636
2555
  },
2637
2556
  ],
2638
2557
  },
@@ -2651,22 +2570,23 @@ const rule$j = {
2651
2570
  properties: {
2652
2571
  types: {
2653
2572
  type: 'boolean',
2654
- description: `Includes:\n\n${TYPES_KINDS.map(kind => `- [${kind}](https://spec.graphql.org/October2021/#${kind})`).join('\n')}`,
2655
- },
2656
- overrides: {
2657
- type: 'object',
2658
- description: 'Configuration for precise `ASTNode`',
2659
- additionalProperties: false,
2660
- properties: Object.fromEntries(ALLOWED_KINDS$1.map(kind => [kind, { type: 'boolean' }])),
2573
+ description: `Includes:\n\n${TYPES_KINDS.map(kind => `- \`${kind}\``).join('\n')}`,
2661
2574
  },
2575
+ ...Object.fromEntries([...ALLOWED_KINDS$1].sort().map(kind => [
2576
+ kind,
2577
+ {
2578
+ type: 'boolean',
2579
+ description: `Read more about this kind on [spec.graphql.org](https://spec.graphql.org/October2021/#${kind}).`,
2580
+ },
2581
+ ])),
2662
2582
  },
2663
2583
  },
2664
2584
  },
2665
2585
  },
2666
2586
  create(context) {
2667
- const { types, overrides = {} } = context.options[0];
2587
+ const { types, ...restOptions } = context.options[0];
2668
2588
  const kinds = new Set(types ? TYPES_KINDS : []);
2669
- for (const [kind, isEnabled] of Object.entries(overrides)) {
2589
+ for (const [kind, isEnabled] of Object.entries(restOptions)) {
2670
2590
  if (isEnabled) {
2671
2591
  kinds.add(kind);
2672
2592
  }
@@ -2694,11 +2614,11 @@ const rule$j = {
2694
2614
  };
2695
2615
 
2696
2616
  const RULE_NAME$2 = 'require-field-of-type-query-in-mutation-result';
2697
- const rule$k = {
2617
+ const rule$i = {
2698
2618
  meta: {
2699
2619
  type: 'suggestion',
2700
2620
  docs: {
2701
- category: 'Best Practices',
2621
+ category: 'Schema',
2702
2622
  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`.',
2703
2623
  url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_NAME$2}.md`,
2704
2624
  requiresSchema: true,
@@ -2862,13 +2782,13 @@ const convertNode = (typeInfo) => (node, key, parent) => {
2862
2782
 
2863
2783
  const REQUIRE_ID_WHEN_AVAILABLE = 'REQUIRE_ID_WHEN_AVAILABLE';
2864
2784
  const DEFAULT_ID_FIELD_NAME = 'id';
2865
- const rule$l = {
2785
+ const rule$j = {
2866
2786
  meta: {
2867
2787
  type: 'suggestion',
2868
2788
  docs: {
2869
- category: 'Best Practices',
2870
- description: `Enforce selecting specific fields when they are available on the GraphQL type.`,
2871
- url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/require-id-when-available.md`,
2789
+ category: 'Operations',
2790
+ description: 'Enforce selecting specific fields when they are available on the GraphQL type.',
2791
+ url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/require-id-when-available.md',
2872
2792
  requiresSchema: true,
2873
2793
  requiresSiblings: true,
2874
2794
  examples: [
@@ -2908,17 +2828,17 @@ const rule$l = {
2908
2828
  `,
2909
2829
  },
2910
2830
  ],
2831
+ recommended: true,
2911
2832
  },
2912
2833
  messages: {
2913
- [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 }}".`,
2834
+ [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 }}".`,
2914
2835
  },
2915
2836
  schema: {
2916
2837
  type: 'array',
2917
- additionalItems: false,
2918
- minItems: 0,
2919
2838
  maxItems: 1,
2920
2839
  items: {
2921
2840
  type: 'object',
2841
+ additionalProperties: false,
2922
2842
  properties: {
2923
2843
  fieldName: {
2924
2844
  type: 'string',
@@ -2998,12 +2918,12 @@ const rule$l = {
2998
2918
  },
2999
2919
  };
3000
2920
 
3001
- const rule$m = {
2921
+ const rule$k = {
3002
2922
  meta: {
3003
2923
  docs: {
3004
- category: 'Best Practices',
2924
+ category: 'Operations',
3005
2925
  description: `Limit the complexity of the GraphQL operations solely by their depth. Based on [graphql-depth-limit](https://github.com/stems/graphql-depth-limit).`,
3006
- url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/selection-set-depth.md`,
2926
+ url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/selection-set-depth.md',
3007
2927
  requiresSiblings: true,
3008
2928
  examples: [
3009
2929
  {
@@ -3046,22 +2966,26 @@ const rule$m = {
3046
2966
  `,
3047
2967
  },
3048
2968
  ],
2969
+ recommended: true,
2970
+ configOptions: [{ maxDepth: 7 }],
3049
2971
  },
3050
2972
  type: 'suggestion',
3051
2973
  schema: {
3052
2974
  type: 'array',
3053
- additionalItems: false,
3054
2975
  minItems: 1,
3055
2976
  maxItems: 1,
3056
2977
  items: {
3057
2978
  type: 'object',
3058
- require: ['maxDepth'],
2979
+ additionalProperties: false,
2980
+ required: ['maxDepth'],
3059
2981
  properties: {
3060
2982
  maxDepth: {
3061
2983
  type: 'number',
3062
2984
  },
3063
2985
  ignore: {
3064
2986
  type: 'array',
2987
+ uniqueItems: true,
2988
+ minItems: 1,
3065
2989
  items: {
3066
2990
  type: 'string',
3067
2991
  },
@@ -3120,12 +3044,12 @@ const shouldIgnoreNode = ({ node, exceptions }) => {
3120
3044
  }
3121
3045
  return false;
3122
3046
  };
3123
- const rule$n = {
3047
+ const rule$l = {
3124
3048
  meta: {
3125
3049
  type: 'suggestion',
3126
3050
  docs: {
3127
3051
  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.',
3128
- category: 'Best Practices',
3052
+ category: 'Schema',
3129
3053
  recommended: true,
3130
3054
  url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/strict-id-in-types.md',
3131
3055
  examples: [
@@ -3185,13 +3109,15 @@ const rule$n = {
3185
3109
  ],
3186
3110
  },
3187
3111
  schema: {
3188
- $schema: 'http://json-schema.org/draft-04/schema#',
3189
3112
  type: 'array',
3113
+ maxItems: 1,
3190
3114
  items: {
3191
3115
  type: 'object',
3116
+ additionalProperties: false,
3192
3117
  properties: {
3193
3118
  acceptedIdNames: {
3194
3119
  type: 'array',
3120
+ uniqueItems: true,
3195
3121
  items: {
3196
3122
  type: 'string',
3197
3123
  },
@@ -3199,6 +3125,7 @@ const rule$n = {
3199
3125
  },
3200
3126
  acceptedIdTypes: {
3201
3127
  type: 'array',
3128
+ uniqueItems: true,
3202
3129
  items: {
3203
3130
  type: 'string',
3204
3131
  },
@@ -3209,19 +3136,21 @@ const rule$n = {
3209
3136
  properties: {
3210
3137
  types: {
3211
3138
  type: 'array',
3139
+ uniqueItems: true,
3140
+ minItems: 1,
3212
3141
  description: 'This is used to exclude types with names that match one of the specified values.',
3213
3142
  items: {
3214
3143
  type: 'string',
3215
3144
  },
3216
- default: [],
3217
3145
  },
3218
3146
  suffixes: {
3219
3147
  type: 'array',
3148
+ uniqueItems: true,
3149
+ minItems: 1,
3220
3150
  description: 'This is used to exclude types with names with suffixes that match one of the specified values.',
3221
3151
  items: {
3222
3152
  type: 'string',
3223
3153
  },
3224
- default: [],
3225
3154
  },
3226
3155
  },
3227
3156
  },
@@ -3297,11 +3226,11 @@ const checkNode = (context, node, ruleName, messageId) => {
3297
3226
  });
3298
3227
  }
3299
3228
  };
3300
- const rule$o = {
3229
+ const rule$m = {
3301
3230
  meta: {
3302
3231
  type: 'suggestion',
3303
3232
  docs: {
3304
- category: 'Best Practices',
3233
+ category: 'Operations',
3305
3234
  description: `Enforce unique fragment names across your project.`,
3306
3235
  url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_NAME$3}.md`,
3307
3236
  requiresSiblings: true,
@@ -3356,11 +3285,11 @@ const rule$o = {
3356
3285
 
3357
3286
  const RULE_NAME$4 = 'unique-operation-name';
3358
3287
  const UNIQUE_OPERATION_NAME = 'UNIQUE_OPERATION_NAME';
3359
- const rule$p = {
3288
+ const rule$n = {
3360
3289
  meta: {
3361
3290
  type: 'suggestion',
3362
3291
  docs: {
3363
- category: 'Best Practices',
3292
+ category: 'Operations',
3364
3293
  description: `Enforce unique operation names across your project.`,
3365
3294
  url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/${RULE_NAME$4}.md`,
3366
3295
  requiresSiblings: true,
@@ -3423,31 +3352,29 @@ const rule$p = {
3423
3352
  const rules = {
3424
3353
  ...GRAPHQL_JS_VALIDATIONS,
3425
3354
  alphabetize: rule,
3426
- 'avoid-duplicate-fields': rule$1,
3427
- 'avoid-operation-name-prefix': rule$2,
3428
- 'avoid-scalar-result-type-on-mutation': rule$3,
3429
- 'avoid-typename-prefix': rule$4,
3430
- 'description-style': rule$5,
3431
- 'input-name': rule$6,
3432
- 'match-document-filename': rule$7,
3433
- 'naming-convention': rule$8,
3434
- 'no-anonymous-operations': rule$9,
3435
- 'no-case-insensitive-enum-values-duplicates': rule$a,
3436
- 'no-deprecated': rule$b,
3437
- 'no-hashtag-description': rule$c,
3438
- 'no-operation-name-suffix': rule$d,
3439
- 'no-root-type': rule$e,
3440
- 'no-unreachable-types': rule$f,
3441
- 'no-unused-fields': rule$g,
3442
- 'require-deprecation-date': rule$h,
3443
- 'require-deprecation-reason': rule$i,
3444
- 'require-description': rule$j,
3445
- 'require-field-of-type-query-in-mutation-result': rule$k,
3446
- 'require-id-when-available': rule$l,
3447
- 'selection-set-depth': rule$m,
3448
- 'strict-id-in-types': rule$n,
3449
- 'unique-fragment-name': rule$o,
3450
- 'unique-operation-name': rule$p,
3355
+ 'description-style': rule$1,
3356
+ 'input-name': rule$2,
3357
+ 'match-document-filename': rule$3,
3358
+ 'naming-convention': rule$4,
3359
+ 'no-anonymous-operations': rule$5,
3360
+ 'no-case-insensitive-enum-values-duplicates': rule$6,
3361
+ 'no-deprecated': rule$7,
3362
+ 'no-duplicate-fields': rule$8,
3363
+ 'no-hashtag-description': rule$9,
3364
+ 'no-root-type': rule$a,
3365
+ 'no-scalar-result-type-on-mutation': rule$b,
3366
+ 'no-typename-prefix': rule$c,
3367
+ 'no-unreachable-types': rule$d,
3368
+ 'no-unused-fields': rule$e,
3369
+ 'require-deprecation-date': rule$f,
3370
+ 'require-deprecation-reason': rule$g,
3371
+ 'require-description': rule$h,
3372
+ 'require-field-of-type-query-in-mutation-result': rule$i,
3373
+ 'require-id-when-available': rule$j,
3374
+ 'selection-set-depth': rule$k,
3375
+ 'strict-id-in-types': rule$l,
3376
+ 'unique-fragment-name': rule$m,
3377
+ 'unique-operation-name': rule$n,
3451
3378
  };
3452
3379
 
3453
3380
  const RELEVANT_KEYWORDS = ['gql', 'graphql', '/* GraphQL */'];