@graphql-eslint/eslint-plugin 3.0.0-alpha-3168a9b.0 → 3.0.0-alpha-7462f3d.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.
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';
@@ -19,7 +19,6 @@ const recommendedConfig = {
19
19
  parser: '@graphql-eslint/eslint-plugin',
20
20
  plugins: ['@graphql-eslint'],
21
21
  rules: {
22
- '@graphql-eslint/avoid-typename-prefix': 'error',
23
22
  '@graphql-eslint/executable-definitions': 'error',
24
23
  '@graphql-eslint/fields-on-correct-type': 'error',
25
24
  '@graphql-eslint/fragments-on-composite-type': 'error',
@@ -60,7 +59,7 @@ const recommendedConfig = {
60
59
  '@graphql-eslint/no-anonymous-operations': 'error',
61
60
  '@graphql-eslint/no-case-insensitive-enum-values-duplicates': 'error',
62
61
  '@graphql-eslint/no-fragment-cycles': 'error',
63
- '@graphql-eslint/no-operation-name-suffix': 'error',
62
+ '@graphql-eslint/no-typename-prefix': 'error',
64
63
  '@graphql-eslint/no-undefined-variables': 'error',
65
64
  '@graphql-eslint/no-unused-fragments': 'error',
66
65
  '@graphql-eslint/no-unused-variables': 'error',
@@ -104,19 +103,18 @@ const allConfig = {
104
103
  arguments: ['FieldDefinition', 'Field', 'DirectiveDefinition', 'Directive'],
105
104
  },
106
105
  ],
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
106
  '@graphql-eslint/description-style': 'error',
111
107
  '@graphql-eslint/input-name': 'error',
112
108
  '@graphql-eslint/match-document-filename': 'error',
113
109
  '@graphql-eslint/no-deprecated': 'error',
110
+ '@graphql-eslint/no-duplicate-fields': 'error',
114
111
  '@graphql-eslint/no-hashtag-description': 'error',
115
112
  '@graphql-eslint/no-root-type': ['error', { disallow: ['subscription'] }],
113
+ '@graphql-eslint/no-scalar-result-type-on-mutation': 'error',
116
114
  '@graphql-eslint/no-unreachable-types': 'error',
117
115
  '@graphql-eslint/no-unused-fields': 'error',
118
116
  '@graphql-eslint/require-deprecation-date': 'error',
119
- '@graphql-eslint/require-description': 'error',
117
+ '@graphql-eslint/require-description': ['error', { types: true, overrides: { DirectiveDefinition: true } }],
120
118
  '@graphql-eslint/require-field-of-type-query-in-mutation-result': 'error',
121
119
  '@graphql-eslint/require-id-when-available': 'error',
122
120
  '@graphql-eslint/selection-set-depth': 'error',
@@ -540,11 +538,23 @@ const GRAPHQL_JS_VALIDATIONS = Object.assign({}, validationToRule('executable-de
540
538
  }));
541
539
 
542
540
  const ALPHABETIZE = 'ALPHABETIZE';
543
- const fieldsEnum = [Kind.OBJECT_TYPE_DEFINITION, Kind.INTERFACE_TYPE_DEFINITION, Kind.INPUT_OBJECT_TYPE_DEFINITION];
541
+ const fieldsEnum = [
542
+ Kind.OBJECT_TYPE_DEFINITION,
543
+ Kind.INTERFACE_TYPE_DEFINITION,
544
+ Kind.INPUT_OBJECT_TYPE_DEFINITION,
545
+ ];
544
546
  const valuesEnum = [Kind.ENUM_TYPE_DEFINITION];
545
- const selectionsEnum = [Kind.OPERATION_DEFINITION, Kind.FRAGMENT_DEFINITION];
547
+ const selectionsEnum = [
548
+ Kind.OPERATION_DEFINITION,
549
+ Kind.FRAGMENT_DEFINITION,
550
+ ];
546
551
  const variablesEnum = [Kind.OPERATION_DEFINITION];
547
- const argumentsEnum = [Kind.FIELD_DEFINITION, Kind.FIELD, Kind.DIRECTIVE_DEFINITION, Kind.DIRECTIVE];
552
+ const argumentsEnum = [
553
+ Kind.FIELD_DEFINITION,
554
+ Kind.FIELD,
555
+ Kind.DIRECTIVE_DEFINITION,
556
+ Kind.DIRECTIVE,
557
+ ];
548
558
  const rule = {
549
559
  meta: {
550
560
  type: 'suggestion',
@@ -774,307 +784,7 @@ const rule = {
774
784
  },
775
785
  };
776
786
 
777
- const AVOID_DUPLICATE_FIELDS = 'AVOID_DUPLICATE_FIELDS';
778
787
  const rule$1 = {
779
- meta: {
780
- type: 'suggestion',
781
- docs: {
782
- description: `Checks for duplicate fields in selection set, variables in operation definition, or in arguments set of a field.`,
783
- category: 'Stylistic Issues',
784
- url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/avoid-duplicate-fields.md',
785
- examples: [
786
- {
787
- title: 'Incorrect',
788
- code: /* GraphQL */ `
789
- query {
790
- user {
791
- name
792
- email
793
- name # duplicate field
794
- }
795
- }
796
- `,
797
- },
798
- {
799
- title: 'Incorrect',
800
- code: /* GraphQL */ `
801
- query {
802
- users(
803
- first: 100
804
- skip: 50
805
- after: "cji629tngfgou0b73kt7vi5jo"
806
- first: 100 # duplicate argument
807
- ) {
808
- id
809
- }
810
- }
811
- `,
812
- },
813
- {
814
- title: 'Incorrect',
815
- code: /* GraphQL */ `
816
- query (
817
- $first: Int!
818
- $first: Int! # duplicate variable
819
- ) {
820
- users(first: $first, skip: 50) {
821
- id
822
- }
823
- }
824
- `,
825
- },
826
- ],
827
- },
828
- messages: {
829
- [AVOID_DUPLICATE_FIELDS]: `{{ type }} "{{ fieldName }}" defined multiple times`,
830
- },
831
- schema: [],
832
- },
833
- create(context) {
834
- function checkNode(usedFields, fieldName, type, node) {
835
- if (usedFields.has(fieldName)) {
836
- context.report({
837
- loc: getLocation((node.kind === Kind.FIELD && node.alias ? node.alias : node).loc, fieldName, {
838
- offsetEnd: node.kind === Kind.VARIABLE_DEFINITION ? 0 : 1,
839
- }),
840
- messageId: AVOID_DUPLICATE_FIELDS,
841
- data: {
842
- type,
843
- fieldName,
844
- },
845
- });
846
- }
847
- else {
848
- usedFields.add(fieldName);
849
- }
850
- }
851
- return {
852
- OperationDefinition(node) {
853
- const set = new Set();
854
- for (const varDef of node.variableDefinitions) {
855
- checkNode(set, varDef.variable.name.value, 'Operation variable', varDef);
856
- }
857
- },
858
- Field(node) {
859
- const set = new Set();
860
- for (const arg of node.arguments) {
861
- checkNode(set, arg.name.value, 'Field argument', arg);
862
- }
863
- },
864
- SelectionSet(node) {
865
- var _a;
866
- const set = new Set();
867
- for (const selection of node.selections) {
868
- if (selection.kind === Kind.FIELD) {
869
- checkNode(set, ((_a = selection.alias) === null || _a === void 0 ? void 0 : _a.value) || selection.name.value, 'Field', selection);
870
- }
871
- }
872
- },
873
- };
874
- },
875
- };
876
-
877
- const AVOID_OPERATION_NAME_PREFIX = 'AVOID_OPERATION_NAME_PREFIX';
878
- const rule$2 = {
879
- meta: {
880
- type: 'suggestion',
881
- docs: {
882
- 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.',
883
- category: 'Stylistic Issues',
884
- url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/avoid-operation-name-prefix.md',
885
- examples: [
886
- {
887
- title: 'Incorrect',
888
- usage: [{ keywords: ['get'] }],
889
- code: /* GraphQL */ `
890
- query getUserDetails {
891
- # ...
892
- }`,
893
- },
894
- {
895
- title: 'Correct',
896
- usage: [{ keywords: ['get'] }],
897
- code: /* GraphQL */ `
898
- query userDetails {
899
- # ...
900
- }`,
901
- },
902
- ],
903
- },
904
- messages: {
905
- [AVOID_OPERATION_NAME_PREFIX]: `Forbidden operation name prefix: "{{ invalidPrefix }}"`,
906
- },
907
- schema: [
908
- {
909
- additionalProperties: false,
910
- type: 'object',
911
- required: ['keywords'],
912
- properties: {
913
- caseSensitive: {
914
- default: false,
915
- type: 'boolean',
916
- },
917
- keywords: {
918
- additionalItems: false,
919
- type: 'array',
920
- minItems: 1,
921
- items: {
922
- type: 'string',
923
- },
924
- },
925
- },
926
- },
927
- ],
928
- },
929
- create(context) {
930
- return {
931
- 'OperationDefinition, FragmentDefinition'(node) {
932
- const config = context.options[0] || { keywords: [], caseSensitive: false };
933
- const caseSensitive = config.caseSensitive;
934
- const keywords = config.keywords || [];
935
- if (node && node.name && node.name.value !== '') {
936
- for (const keyword of keywords) {
937
- const testKeyword = caseSensitive ? keyword : keyword.toLowerCase();
938
- const testName = caseSensitive ? node.name.value : node.name.value.toLowerCase();
939
- if (testName.startsWith(testKeyword)) {
940
- const { start } = node.name.loc;
941
- context.report({
942
- loc: {
943
- start: {
944
- line: start.line,
945
- column: start.column - 1,
946
- },
947
- end: {
948
- line: start.line,
949
- column: start.column - 1 + testKeyword.length,
950
- },
951
- },
952
- data: {
953
- invalidPrefix: keyword,
954
- },
955
- messageId: AVOID_OPERATION_NAME_PREFIX,
956
- });
957
- }
958
- }
959
- }
960
- },
961
- };
962
- },
963
- };
964
-
965
- const rule$3 = {
966
- meta: {
967
- type: 'suggestion',
968
- docs: {
969
- category: 'Best Practices',
970
- description: 'Avoid scalar result type on mutation type to make sure to return a valid state.',
971
- url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/avoid-scalar-result-type-on-mutation.md',
972
- requiresSchema: true,
973
- examples: [
974
- {
975
- title: 'Incorrect',
976
- code: /* GraphQL */ `
977
- type Mutation {
978
- createUser: Boolean
979
- }
980
- `,
981
- },
982
- {
983
- title: 'Correct',
984
- code: /* GraphQL */ `
985
- type Mutation {
986
- createUser: User!
987
- }
988
- `,
989
- },
990
- ],
991
- },
992
- schema: [],
993
- },
994
- create(context) {
995
- const schema = requireGraphQLSchemaFromContext('avoid-scalar-result-type-on-mutation', context);
996
- const mutationType = schema.getMutationType();
997
- if (!mutationType) {
998
- return {};
999
- }
1000
- const selector = [
1001
- `:matches(${Kind.OBJECT_TYPE_DEFINITION}, ${Kind.OBJECT_TYPE_EXTENSION})[name.value=${mutationType.name}]`,
1002
- '>',
1003
- Kind.FIELD_DEFINITION,
1004
- Kind.NAMED_TYPE,
1005
- ].join(' ');
1006
- return {
1007
- [selector](node) {
1008
- const typeName = node.name.value;
1009
- const graphQLType = schema.getType(typeName);
1010
- if (isScalarType(graphQLType)) {
1011
- context.report({
1012
- loc: getLocation(node.loc, typeName),
1013
- message: `Unexpected scalar result type "${typeName}"`,
1014
- });
1015
- }
1016
- },
1017
- };
1018
- },
1019
- };
1020
-
1021
- const AVOID_TYPENAME_PREFIX = 'AVOID_TYPENAME_PREFIX';
1022
- const rule$4 = {
1023
- meta: {
1024
- type: 'suggestion',
1025
- docs: {
1026
- category: 'Best Practices',
1027
- description: 'Enforces users to avoid using the type name in a field name while defining your schema.',
1028
- recommended: true,
1029
- url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/avoid-typename-prefix.md',
1030
- examples: [
1031
- {
1032
- title: 'Incorrect',
1033
- code: /* GraphQL */ `
1034
- type User {
1035
- userId: ID!
1036
- }
1037
- `,
1038
- },
1039
- {
1040
- title: 'Correct',
1041
- code: /* GraphQL */ `
1042
- type User {
1043
- id: ID!
1044
- }
1045
- `,
1046
- },
1047
- ],
1048
- },
1049
- messages: {
1050
- [AVOID_TYPENAME_PREFIX]: `Field "{{ fieldName }}" starts with the name of the parent type "{{ typeName }}"`,
1051
- },
1052
- schema: [],
1053
- },
1054
- create(context) {
1055
- return {
1056
- 'ObjectTypeDefinition, ObjectTypeExtension, InterfaceTypeDefinition, InterfaceTypeExtension'(node) {
1057
- const typeName = node.name.value;
1058
- const lowerTypeName = typeName.toLowerCase();
1059
- for (const field of node.fields) {
1060
- const fieldName = field.name.value;
1061
- if (fieldName.toLowerCase().startsWith(lowerTypeName)) {
1062
- context.report({
1063
- data: {
1064
- fieldName,
1065
- typeName,
1066
- },
1067
- messageId: AVOID_TYPENAME_PREFIX,
1068
- loc: getLocation(field.loc, lowerTypeName),
1069
- });
1070
- }
1071
- }
1072
- },
1073
- };
1074
- },
1075
- };
1076
-
1077
- const rule$5 = {
1078
788
  meta: {
1079
789
  type: 'suggestion',
1080
790
  docs: {
@@ -1137,7 +847,7 @@ const rule$5 = {
1137
847
  const isObjectType = (node) => [Kind.OBJECT_TYPE_DEFINITION, Kind.OBJECT_TYPE_EXTENSION].includes(node.type);
1138
848
  const isQueryType = (node) => isObjectType(node) && node.name.value === 'Query';
1139
849
  const isMutationType = (node) => isObjectType(node) && node.name.value === 'Mutation';
1140
- const rule$6 = {
850
+ const rule$2 = {
1141
851
  meta: {
1142
852
  type: 'suggestion',
1143
853
  docs: {
@@ -1263,7 +973,7 @@ const CASE_STYLES = [
1263
973
  const schemaOption = {
1264
974
  oneOf: [{ $ref: '#/definitions/asString' }, { $ref: '#/definitions/asObject' }],
1265
975
  };
1266
- const rule$7 = {
976
+ const rule$3 = {
1267
977
  meta: {
1268
978
  type: 'suggestion',
1269
979
  docs: {
@@ -1481,7 +1191,7 @@ const ALLOWED_STYLES = Object.keys(StyleToRegex);
1481
1191
  const schemaOption$1 = {
1482
1192
  oneOf: [{ $ref: '#/definitions/asString' }, { $ref: '#/definitions/asObject' }],
1483
1193
  };
1484
- const rule$8 = {
1194
+ const rule$4 = {
1485
1195
  meta: {
1486
1196
  type: 'suggestion',
1487
1197
  docs: {
@@ -1577,11 +1287,11 @@ const rule$8 = {
1577
1287
  properties: {
1578
1288
  types: {
1579
1289
  ...schemaOption$1,
1580
- description: `Includes:\n\n${TYPES_KINDS.map(kind => `- \`${kind}\``).join('\n')}`,
1290
+ description: `Includes:\n\n${TYPES_KINDS.map(kind => `- [${kind}](https://spec.graphql.org/October2021/#${kind})`).join('\n')}`,
1581
1291
  },
1582
1292
  fields: {
1583
1293
  ...schemaOption$1,
1584
- description: `Includes:\n\n${FIELDS_KINDS.map(kind => `- \`${kind}\``).join('\n')}`,
1294
+ description: `Includes:\n\n${FIELDS_KINDS.map(kind => `- [${kind}](https://spec.graphql.org/October2021/#${kind})`).join('\n')}`,
1585
1295
  },
1586
1296
  allowLeadingUnderscore: {
1587
1297
  type: 'boolean',
@@ -1597,7 +1307,7 @@ const rule$8 = {
1597
1307
  description: [
1598
1308
  'May contain the following `ASTNode` names:',
1599
1309
  '',
1600
- ...ALLOWED_KINDS.map(kind => `- \`${kind}\``),
1310
+ ...ALLOWED_KINDS.map(kind => `- [${kind}](https://spec.graphql.org/October2021/#${kind})`),
1601
1311
  '',
1602
1312
  "> It's also possible to use a [`selector`](https://eslint.org/docs/developer-guide/selectors) that starts with `ASTNode` name",
1603
1313
  '>',
@@ -1694,7 +1404,7 @@ const rule$8 = {
1694
1404
  };
1695
1405
 
1696
1406
  const NO_ANONYMOUS_OPERATIONS = 'NO_ANONYMOUS_OPERATIONS';
1697
- const rule$9 = {
1407
+ const rule$5 = {
1698
1408
  meta: {
1699
1409
  type: 'suggestion',
1700
1410
  docs: {
@@ -1742,7 +1452,7 @@ const rule$9 = {
1742
1452
  };
1743
1453
 
1744
1454
  const ERROR_MESSAGE_ID = 'NO_CASE_INSENSITIVE_ENUM_VALUES_DUPLICATES';
1745
- const rule$a = {
1455
+ const rule$6 = {
1746
1456
  meta: {
1747
1457
  type: 'suggestion',
1748
1458
  docs: {
@@ -1798,7 +1508,7 @@ const rule$a = {
1798
1508
  };
1799
1509
 
1800
1510
  const NO_DEPRECATED = 'NO_DEPRECATED';
1801
- const rule$b = {
1511
+ const rule$7 = {
1802
1512
  meta: {
1803
1513
  type: 'suggestion',
1804
1514
  docs: {
@@ -1894,19 +1604,119 @@ const rule$b = {
1894
1604
  }
1895
1605
  },
1896
1606
  Field(node) {
1897
- requireGraphQLSchemaFromContext('no-deprecated', context);
1898
- const typeInfo = node.typeInfo();
1899
- if (typeInfo && typeInfo.fieldDef) {
1900
- if (typeInfo.fieldDef.deprecationReason) {
1901
- const fieldName = node.name.value;
1902
- context.report({
1903
- loc: getLocation(node.loc, fieldName),
1904
- messageId: NO_DEPRECATED,
1905
- data: {
1906
- type: 'field',
1907
- reason: typeInfo.fieldDef.deprecationReason ? `(reason: ${typeInfo.fieldDef.deprecationReason})` : '',
1908
- },
1909
- });
1607
+ requireGraphQLSchemaFromContext('no-deprecated', context);
1608
+ const typeInfo = node.typeInfo();
1609
+ if (typeInfo && typeInfo.fieldDef) {
1610
+ if (typeInfo.fieldDef.deprecationReason) {
1611
+ const fieldName = node.name.value;
1612
+ context.report({
1613
+ loc: getLocation(node.loc, fieldName),
1614
+ messageId: NO_DEPRECATED,
1615
+ data: {
1616
+ type: 'field',
1617
+ reason: typeInfo.fieldDef.deprecationReason ? `(reason: ${typeInfo.fieldDef.deprecationReason})` : '',
1618
+ },
1619
+ });
1620
+ }
1621
+ }
1622
+ },
1623
+ };
1624
+ },
1625
+ };
1626
+
1627
+ const NO_DUPLICATE_FIELDS = 'NO_DUPLICATE_FIELDS';
1628
+ const rule$8 = {
1629
+ meta: {
1630
+ type: 'suggestion',
1631
+ docs: {
1632
+ description: `Checks for duplicate fields in selection set, variables in operation definition, or in arguments set of a field.`,
1633
+ category: 'Stylistic Issues',
1634
+ url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/no-duplicate-fields.md',
1635
+ examples: [
1636
+ {
1637
+ title: 'Incorrect',
1638
+ code: /* GraphQL */ `
1639
+ query {
1640
+ user {
1641
+ name
1642
+ email
1643
+ name # duplicate field
1644
+ }
1645
+ }
1646
+ `,
1647
+ },
1648
+ {
1649
+ title: 'Incorrect',
1650
+ code: /* GraphQL */ `
1651
+ query {
1652
+ users(
1653
+ first: 100
1654
+ skip: 50
1655
+ after: "cji629tngfgou0b73kt7vi5jo"
1656
+ first: 100 # duplicate argument
1657
+ ) {
1658
+ id
1659
+ }
1660
+ }
1661
+ `,
1662
+ },
1663
+ {
1664
+ title: 'Incorrect',
1665
+ code: /* GraphQL */ `
1666
+ query (
1667
+ $first: Int!
1668
+ $first: Int! # duplicate variable
1669
+ ) {
1670
+ users(first: $first, skip: 50) {
1671
+ id
1672
+ }
1673
+ }
1674
+ `,
1675
+ },
1676
+ ],
1677
+ },
1678
+ messages: {
1679
+ [NO_DUPLICATE_FIELDS]: `{{ type }} "{{ fieldName }}" defined multiple times`,
1680
+ },
1681
+ schema: [],
1682
+ },
1683
+ create(context) {
1684
+ function checkNode(usedFields, fieldName, type, node) {
1685
+ if (usedFields.has(fieldName)) {
1686
+ context.report({
1687
+ loc: getLocation((node.kind === Kind.FIELD && node.alias ? node.alias : node).loc, fieldName, {
1688
+ offsetEnd: node.kind === Kind.VARIABLE_DEFINITION ? 0 : 1,
1689
+ }),
1690
+ messageId: NO_DUPLICATE_FIELDS,
1691
+ data: {
1692
+ type,
1693
+ fieldName,
1694
+ },
1695
+ });
1696
+ }
1697
+ else {
1698
+ usedFields.add(fieldName);
1699
+ }
1700
+ }
1701
+ return {
1702
+ OperationDefinition(node) {
1703
+ const set = new Set();
1704
+ for (const varDef of node.variableDefinitions) {
1705
+ checkNode(set, varDef.variable.name.value, 'Operation variable', varDef);
1706
+ }
1707
+ },
1708
+ Field(node) {
1709
+ const set = new Set();
1710
+ for (const arg of node.arguments) {
1711
+ checkNode(set, arg.name.value, 'Field argument', arg);
1712
+ }
1713
+ },
1714
+ SelectionSet(node) {
1715
+ var _a;
1716
+ const set = new Set();
1717
+ for (const selection of node.selections) {
1718
+ if (selection.kind === Kind.FIELD) {
1719
+ checkNode(set, ((_a = selection.alias) === null || _a === void 0 ? void 0 : _a.value) || selection.name.value, 'Field', selection);
1910
1720
  }
1911
1721
  }
1912
1722
  },
@@ -1915,7 +1725,7 @@ const rule$b = {
1915
1725
  };
1916
1726
 
1917
1727
  const HASHTAG_COMMENT = 'HASHTAG_COMMENT';
1918
- const rule$c = {
1728
+ const rule$9 = {
1919
1729
  meta: {
1920
1730
  messages: {
1921
1731
  [HASHTAG_COMMENT]: 'Using hashtag (#) for adding GraphQL descriptions is not allowed. Prefer using """ for multiline, or " for a single line description.',
@@ -1987,75 +1797,8 @@ const rule$c = {
1987
1797
  },
1988
1798
  };
1989
1799
 
1990
- const NO_OPERATION_NAME_SUFFIX = 'NO_OPERATION_NAME_SUFFIX';
1991
- const rule$d = {
1992
- meta: {
1993
- fixable: 'code',
1994
- type: 'suggestion',
1995
- docs: {
1996
- category: 'Stylistic Issues',
1997
- recommended: true,
1998
- description: `Makes sure you are not adding the operation type to the name of the operation.`,
1999
- url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/no-operation-name-suffix.md`,
2000
- examples: [
2001
- {
2002
- title: 'Incorrect',
2003
- code: /* GraphQL */ `
2004
- query userQuery {
2005
- # ...
2006
- }
2007
- `,
2008
- },
2009
- {
2010
- title: 'Correct',
2011
- code: /* GraphQL */ `
2012
- query user {
2013
- # ...
2014
- }
2015
- `,
2016
- },
2017
- ],
2018
- },
2019
- messages: {
2020
- [NO_OPERATION_NAME_SUFFIX]: `Unnecessary "{{ invalidSuffix }}" suffix in your operation name!`,
2021
- },
2022
- schema: [],
2023
- },
2024
- create(context) {
2025
- return {
2026
- 'OperationDefinition, FragmentDefinition'(node) {
2027
- var _a;
2028
- const name = ((_a = node.name) === null || _a === void 0 ? void 0 : _a.value) || '';
2029
- if (name.length > 0) {
2030
- const invalidSuffix = 'operation' in node ? node.operation : 'fragment';
2031
- if (name.toLowerCase().endsWith(invalidSuffix)) {
2032
- const { start, end } = node.name.loc;
2033
- context.report({
2034
- loc: {
2035
- start: {
2036
- column: start.column - 1 + name.length - invalidSuffix.length,
2037
- line: start.line,
2038
- },
2039
- end: {
2040
- column: end.column - 1 + name.length,
2041
- line: end.line,
2042
- },
2043
- },
2044
- data: {
2045
- invalidSuffix,
2046
- },
2047
- fix: fixer => fixer.removeRange([node.name.range[1] - invalidSuffix.length, node.name.range[1]]),
2048
- messageId: NO_OPERATION_NAME_SUFFIX,
2049
- });
2050
- }
2051
- }
2052
- },
2053
- };
2054
- },
2055
- };
2056
-
2057
1800
  const ROOT_TYPES = ['query', 'mutation', 'subscription'];
2058
- const rule$e = {
1801
+ const rule$a = {
2059
1802
  meta: {
2060
1803
  type: 'suggestion',
2061
1804
  docs: {
@@ -2145,9 +1888,121 @@ const rule$e = {
2145
1888
  },
2146
1889
  };
2147
1890
 
1891
+ const rule$b = {
1892
+ meta: {
1893
+ type: 'suggestion',
1894
+ docs: {
1895
+ category: 'Best Practices',
1896
+ description: 'Avoid scalar result type on mutation type to make sure to return a valid state.',
1897
+ url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/no-scalar-result-type-on-mutation.md',
1898
+ requiresSchema: true,
1899
+ examples: [
1900
+ {
1901
+ title: 'Incorrect',
1902
+ code: /* GraphQL */ `
1903
+ type Mutation {
1904
+ createUser: Boolean
1905
+ }
1906
+ `,
1907
+ },
1908
+ {
1909
+ title: 'Correct',
1910
+ code: /* GraphQL */ `
1911
+ type Mutation {
1912
+ createUser: User!
1913
+ }
1914
+ `,
1915
+ },
1916
+ ],
1917
+ },
1918
+ schema: [],
1919
+ },
1920
+ create(context) {
1921
+ const schema = requireGraphQLSchemaFromContext('no-scalar-result-type-on-mutation', context);
1922
+ const mutationType = schema.getMutationType();
1923
+ if (!mutationType) {
1924
+ return {};
1925
+ }
1926
+ const selector = [
1927
+ `:matches(${Kind.OBJECT_TYPE_DEFINITION}, ${Kind.OBJECT_TYPE_EXTENSION})[name.value=${mutationType.name}]`,
1928
+ '>',
1929
+ Kind.FIELD_DEFINITION,
1930
+ Kind.NAMED_TYPE,
1931
+ ].join(' ');
1932
+ return {
1933
+ [selector](node) {
1934
+ const typeName = node.name.value;
1935
+ const graphQLType = schema.getType(typeName);
1936
+ if (isScalarType(graphQLType)) {
1937
+ context.report({
1938
+ loc: getLocation(node.loc, typeName),
1939
+ message: `Unexpected scalar result type "${typeName}"`,
1940
+ });
1941
+ }
1942
+ },
1943
+ };
1944
+ },
1945
+ };
1946
+
1947
+ const NO_TYPENAME_PREFIX = 'NO_TYPENAME_PREFIX';
1948
+ const rule$c = {
1949
+ meta: {
1950
+ type: 'suggestion',
1951
+ docs: {
1952
+ category: 'Best Practices',
1953
+ description: 'Enforces users to avoid using the type name in a field name while defining your schema.',
1954
+ recommended: true,
1955
+ url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/no-typename-prefix.md',
1956
+ examples: [
1957
+ {
1958
+ title: 'Incorrect',
1959
+ code: /* GraphQL */ `
1960
+ type User {
1961
+ userId: ID!
1962
+ }
1963
+ `,
1964
+ },
1965
+ {
1966
+ title: 'Correct',
1967
+ code: /* GraphQL */ `
1968
+ type User {
1969
+ id: ID!
1970
+ }
1971
+ `,
1972
+ },
1973
+ ],
1974
+ },
1975
+ messages: {
1976
+ [NO_TYPENAME_PREFIX]: `Field "{{ fieldName }}" starts with the name of the parent type "{{ typeName }}"`,
1977
+ },
1978
+ schema: [],
1979
+ },
1980
+ create(context) {
1981
+ return {
1982
+ 'ObjectTypeDefinition, ObjectTypeExtension, InterfaceTypeDefinition, InterfaceTypeExtension'(node) {
1983
+ const typeName = node.name.value;
1984
+ const lowerTypeName = typeName.toLowerCase();
1985
+ for (const field of node.fields) {
1986
+ const fieldName = field.name.value;
1987
+ if (fieldName.toLowerCase().startsWith(lowerTypeName)) {
1988
+ context.report({
1989
+ data: {
1990
+ fieldName,
1991
+ typeName,
1992
+ },
1993
+ messageId: NO_TYPENAME_PREFIX,
1994
+ loc: getLocation(field.loc, lowerTypeName),
1995
+ });
1996
+ }
1997
+ }
1998
+ },
1999
+ };
2000
+ },
2001
+ };
2002
+
2148
2003
  const UNREACHABLE_TYPE = 'UNREACHABLE_TYPE';
2149
2004
  const RULE_NAME = 'no-unreachable-types';
2150
- const rule$f = {
2005
+ const rule$d = {
2151
2006
  meta: {
2152
2007
  messages: {
2153
2008
  [UNREACHABLE_TYPE]: `Type "{{ typeName }}" is unreachable`,
@@ -2223,7 +2078,7 @@ const rule$f = {
2223
2078
 
2224
2079
  const UNUSED_FIELD = 'UNUSED_FIELD';
2225
2080
  const RULE_NAME$1 = 'no-unused-fields';
2226
- const rule$g = {
2081
+ const rule$e = {
2227
2082
  meta: {
2228
2083
  messages: {
2229
2084
  [UNUSED_FIELD]: `Field "{{fieldName}}" is unused`,
@@ -2412,7 +2267,7 @@ const MESSAGE_REQUIRE_DATE = 'MESSAGE_REQUIRE_DATE';
2412
2267
  const MESSAGE_INVALID_FORMAT = 'MESSAGE_INVALID_FORMAT';
2413
2268
  const MESSAGE_INVALID_DATE = 'MESSAGE_INVALID_DATE';
2414
2269
  const MESSAGE_CAN_BE_REMOVED = 'MESSAGE_CAN_BE_REMOVED';
2415
- const rule$h = {
2270
+ const rule$f = {
2416
2271
  meta: {
2417
2272
  type: 'suggestion',
2418
2273
  docs: {
@@ -2515,7 +2370,7 @@ const rule$h = {
2515
2370
  },
2516
2371
  };
2517
2372
 
2518
- const rule$i = {
2373
+ const rule$g = {
2519
2374
  meta: {
2520
2375
  docs: {
2521
2376
  description: `Require all deprecation directives to specify a reason.`,
@@ -2570,41 +2425,23 @@ const rule$i = {
2570
2425
  };
2571
2426
 
2572
2427
  const REQUIRE_DESCRIPTION_ERROR = 'REQUIRE_DESCRIPTION_ERROR';
2573
- const DESCRIBABLE_NODES = [
2574
- Kind.SCHEMA_DEFINITION,
2575
- Kind.OBJECT_TYPE_DEFINITION,
2428
+ const ALLOWED_KINDS$1 = [
2429
+ ...TYPES_KINDS,
2576
2430
  Kind.FIELD_DEFINITION,
2577
2431
  Kind.INPUT_VALUE_DEFINITION,
2578
- Kind.INTERFACE_TYPE_DEFINITION,
2579
- Kind.UNION_TYPE_DEFINITION,
2580
- Kind.ENUM_TYPE_DEFINITION,
2581
2432
  Kind.ENUM_VALUE_DEFINITION,
2582
- Kind.INPUT_OBJECT_TYPE_DEFINITION,
2583
2433
  Kind.DIRECTIVE_DEFINITION,
2584
2434
  ];
2585
- function verifyRule(context, node) {
2586
- if (node) {
2587
- if (!node.description || !node.description.value || node.description.value.trim().length === 0) {
2588
- context.report({
2589
- loc: getLocation(('name' in node ? node.name : node).loc, 'name' in node ? node.name.value : 'schema'),
2590
- messageId: REQUIRE_DESCRIPTION_ERROR,
2591
- data: {
2592
- nodeType: node.kind,
2593
- },
2594
- });
2595
- }
2596
- }
2597
- }
2598
- const rule$j = {
2435
+ const rule$h = {
2599
2436
  meta: {
2600
2437
  docs: {
2601
2438
  category: 'Best Practices',
2602
- description: `Enforce descriptions in your type definitions.`,
2603
- url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/require-description.md`,
2439
+ description: 'Enforce descriptions in your type definitions.',
2440
+ url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/require-description.md',
2604
2441
  examples: [
2605
2442
  {
2606
2443
  title: 'Incorrect',
2607
- usage: [{ on: [Kind.OBJECT_TYPE_DEFINITION, Kind.FIELD_DEFINITION] }],
2444
+ usage: [{ types: true, overrides: { FieldDefinition: true } }],
2608
2445
  code: /* GraphQL */ `
2609
2446
  type someTypeName {
2610
2447
  name: String
@@ -2613,7 +2450,7 @@ const rule$j = {
2613
2450
  },
2614
2451
  {
2615
2452
  title: 'Correct',
2616
- usage: [{ on: [Kind.OBJECT_TYPE_DEFINITION, Kind.FIELD_DEFINITION] }],
2453
+ usage: [{ types: true, overrides: { FieldDefinition: true } }],
2617
2454
  code: /* GraphQL */ `
2618
2455
  """
2619
2456
  Some type description
@@ -2627,40 +2464,74 @@ const rule$j = {
2627
2464
  `,
2628
2465
  },
2629
2466
  ],
2467
+ optionsForConfig: [
2468
+ {
2469
+ types: true,
2470
+ overrides: {
2471
+ [Kind.DIRECTIVE_DEFINITION]: true,
2472
+ },
2473
+ },
2474
+ ],
2630
2475
  },
2631
2476
  type: 'suggestion',
2632
2477
  messages: {
2633
- [REQUIRE_DESCRIPTION_ERROR]: `Description is required for nodes of type "{{ nodeType }}"`,
2478
+ [REQUIRE_DESCRIPTION_ERROR]: 'Description is required for nodes of type "{{ nodeType }}"',
2634
2479
  },
2635
2480
  schema: {
2636
2481
  type: 'array',
2637
- additionalItems: false,
2638
2482
  minItems: 1,
2639
2483
  maxItems: 1,
2640
2484
  items: {
2641
2485
  type: 'object',
2642
- require: ['on'],
2486
+ additionalProperties: false,
2487
+ minProperties: 1,
2643
2488
  properties: {
2644
- on: {
2645
- type: 'array',
2646
- minItems: 1,
2647
- additionalItems: false,
2648
- items: {
2649
- type: 'string',
2650
- enum: DESCRIBABLE_NODES,
2651
- },
2489
+ types: {
2490
+ type: 'boolean',
2491
+ description: `Includes:\n\n${TYPES_KINDS.map(kind => `- [${kind}](https://spec.graphql.org/October2021/#${kind})`).join('\n')}`,
2492
+ },
2493
+ overrides: {
2494
+ type: 'object',
2495
+ description: 'Configuration for precise `ASTNode`',
2496
+ additionalProperties: false,
2497
+ properties: Object.fromEntries(ALLOWED_KINDS$1.map(kind => [kind, { type: 'boolean' }])),
2652
2498
  },
2653
2499
  },
2654
2500
  },
2655
2501
  },
2656
2502
  },
2657
2503
  create(context) {
2658
- return Object.fromEntries(context.options[0].on.map(optionKey => [optionKey, node => verifyRule(context, node)]));
2504
+ const { types, overrides = {} } = context.options[0];
2505
+ const kinds = new Set(types ? TYPES_KINDS : []);
2506
+ for (const [kind, isEnabled] of Object.entries(overrides)) {
2507
+ if (isEnabled) {
2508
+ kinds.add(kind);
2509
+ }
2510
+ else {
2511
+ kinds.delete(kind);
2512
+ }
2513
+ }
2514
+ const selector = [...kinds].join(',');
2515
+ return {
2516
+ [selector](node) {
2517
+ var _a;
2518
+ const description = ((_a = node.description) === null || _a === void 0 ? void 0 : _a.value) || '';
2519
+ if (description.trim().length === 0) {
2520
+ context.report({
2521
+ loc: getLocation(node.name.loc, node.name.value),
2522
+ messageId: REQUIRE_DESCRIPTION_ERROR,
2523
+ data: {
2524
+ nodeType: node.kind,
2525
+ },
2526
+ });
2527
+ }
2528
+ },
2529
+ };
2659
2530
  },
2660
2531
  };
2661
2532
 
2662
2533
  const RULE_NAME$2 = 'require-field-of-type-query-in-mutation-result';
2663
- const rule$k = {
2534
+ const rule$i = {
2664
2535
  meta: {
2665
2536
  type: 'suggestion',
2666
2537
  docs: {
@@ -2828,7 +2699,7 @@ const convertNode = (typeInfo) => (node, key, parent) => {
2828
2699
 
2829
2700
  const REQUIRE_ID_WHEN_AVAILABLE = 'REQUIRE_ID_WHEN_AVAILABLE';
2830
2701
  const DEFAULT_ID_FIELD_NAME = 'id';
2831
- const rule$l = {
2702
+ const rule$j = {
2832
2703
  meta: {
2833
2704
  type: 'suggestion',
2834
2705
  docs: {
@@ -2964,7 +2835,7 @@ const rule$l = {
2964
2835
  },
2965
2836
  };
2966
2837
 
2967
- const rule$m = {
2838
+ const rule$k = {
2968
2839
  meta: {
2969
2840
  docs: {
2970
2841
  category: 'Best Practices',
@@ -3086,7 +2957,7 @@ const shouldIgnoreNode = ({ node, exceptions }) => {
3086
2957
  }
3087
2958
  return false;
3088
2959
  };
3089
- const rule$n = {
2960
+ const rule$l = {
3090
2961
  meta: {
3091
2962
  type: 'suggestion',
3092
2963
  docs: {
@@ -3263,7 +3134,7 @@ const checkNode = (context, node, ruleName, messageId) => {
3263
3134
  });
3264
3135
  }
3265
3136
  };
3266
- const rule$o = {
3137
+ const rule$m = {
3267
3138
  meta: {
3268
3139
  type: 'suggestion',
3269
3140
  docs: {
@@ -3322,7 +3193,7 @@ const rule$o = {
3322
3193
 
3323
3194
  const RULE_NAME$4 = 'unique-operation-name';
3324
3195
  const UNIQUE_OPERATION_NAME = 'UNIQUE_OPERATION_NAME';
3325
- const rule$p = {
3196
+ const rule$n = {
3326
3197
  meta: {
3327
3198
  type: 'suggestion',
3328
3199
  docs: {
@@ -3389,31 +3260,29 @@ const rule$p = {
3389
3260
  const rules = {
3390
3261
  ...GRAPHQL_JS_VALIDATIONS,
3391
3262
  alphabetize: rule,
3392
- 'avoid-duplicate-fields': rule$1,
3393
- 'avoid-operation-name-prefix': rule$2,
3394
- 'avoid-scalar-result-type-on-mutation': rule$3,
3395
- 'avoid-typename-prefix': rule$4,
3396
- 'description-style': rule$5,
3397
- 'input-name': rule$6,
3398
- 'match-document-filename': rule$7,
3399
- 'naming-convention': rule$8,
3400
- 'no-anonymous-operations': rule$9,
3401
- 'no-case-insensitive-enum-values-duplicates': rule$a,
3402
- 'no-deprecated': rule$b,
3403
- 'no-hashtag-description': rule$c,
3404
- 'no-operation-name-suffix': rule$d,
3405
- 'no-root-type': rule$e,
3406
- 'no-unreachable-types': rule$f,
3407
- 'no-unused-fields': rule$g,
3408
- 'require-deprecation-date': rule$h,
3409
- 'require-deprecation-reason': rule$i,
3410
- 'require-description': rule$j,
3411
- 'require-field-of-type-query-in-mutation-result': rule$k,
3412
- 'require-id-when-available': rule$l,
3413
- 'selection-set-depth': rule$m,
3414
- 'strict-id-in-types': rule$n,
3415
- 'unique-fragment-name': rule$o,
3416
- 'unique-operation-name': rule$p,
3263
+ 'description-style': rule$1,
3264
+ 'input-name': rule$2,
3265
+ 'match-document-filename': rule$3,
3266
+ 'naming-convention': rule$4,
3267
+ 'no-anonymous-operations': rule$5,
3268
+ 'no-case-insensitive-enum-values-duplicates': rule$6,
3269
+ 'no-deprecated': rule$7,
3270
+ 'no-duplicate-fields': rule$8,
3271
+ 'no-hashtag-description': rule$9,
3272
+ 'no-root-type': rule$a,
3273
+ 'no-scalar-result-type-on-mutation': rule$b,
3274
+ 'no-typename-prefix': rule$c,
3275
+ 'no-unreachable-types': rule$d,
3276
+ 'no-unused-fields': rule$e,
3277
+ 'require-deprecation-date': rule$f,
3278
+ 'require-deprecation-reason': rule$g,
3279
+ 'require-description': rule$h,
3280
+ 'require-field-of-type-query-in-mutation-result': rule$i,
3281
+ 'require-id-when-available': rule$j,
3282
+ 'selection-set-depth': rule$k,
3283
+ 'strict-id-in-types': rule$l,
3284
+ 'unique-fragment-name': rule$m,
3285
+ 'unique-operation-name': rule$n,
3417
3286
  };
3418
3287
 
3419
3288
  const RELEVANT_KEYWORDS = ['gql', 'graphql', '/* GraphQL */'];