@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.js CHANGED
@@ -25,7 +25,6 @@ const recommendedConfig = {
25
25
  parser: '@graphql-eslint/eslint-plugin',
26
26
  plugins: ['@graphql-eslint'],
27
27
  rules: {
28
- '@graphql-eslint/avoid-typename-prefix': 'error',
29
28
  '@graphql-eslint/executable-definitions': 'error',
30
29
  '@graphql-eslint/fields-on-correct-type': 'error',
31
30
  '@graphql-eslint/fragments-on-composite-type': 'error',
@@ -66,7 +65,7 @@ const recommendedConfig = {
66
65
  '@graphql-eslint/no-anonymous-operations': 'error',
67
66
  '@graphql-eslint/no-case-insensitive-enum-values-duplicates': 'error',
68
67
  '@graphql-eslint/no-fragment-cycles': 'error',
69
- '@graphql-eslint/no-operation-name-suffix': 'error',
68
+ '@graphql-eslint/no-typename-prefix': 'error',
70
69
  '@graphql-eslint/no-undefined-variables': 'error',
71
70
  '@graphql-eslint/no-unused-fragments': 'error',
72
71
  '@graphql-eslint/no-unused-variables': 'error',
@@ -110,19 +109,18 @@ const allConfig = {
110
109
  arguments: ['FieldDefinition', 'Field', 'DirectiveDefinition', 'Directive'],
111
110
  },
112
111
  ],
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
112
  '@graphql-eslint/description-style': 'error',
117
113
  '@graphql-eslint/input-name': 'error',
118
114
  '@graphql-eslint/match-document-filename': 'error',
119
115
  '@graphql-eslint/no-deprecated': 'error',
116
+ '@graphql-eslint/no-duplicate-fields': 'error',
120
117
  '@graphql-eslint/no-hashtag-description': 'error',
121
118
  '@graphql-eslint/no-root-type': ['error', { disallow: ['subscription'] }],
119
+ '@graphql-eslint/no-scalar-result-type-on-mutation': 'error',
122
120
  '@graphql-eslint/no-unreachable-types': 'error',
123
121
  '@graphql-eslint/no-unused-fields': 'error',
124
122
  '@graphql-eslint/require-deprecation-date': 'error',
125
- '@graphql-eslint/require-description': 'error',
123
+ '@graphql-eslint/require-description': ['error', { types: true, overrides: { DirectiveDefinition: true } }],
126
124
  '@graphql-eslint/require-field-of-type-query-in-mutation-result': 'error',
127
125
  '@graphql-eslint/require-id-when-available': 'error',
128
126
  '@graphql-eslint/selection-set-depth': 'error',
@@ -546,11 +544,23 @@ const GRAPHQL_JS_VALIDATIONS = Object.assign({}, validationToRule('executable-de
546
544
  }));
547
545
 
548
546
  const ALPHABETIZE = 'ALPHABETIZE';
549
- const fieldsEnum = [graphql.Kind.OBJECT_TYPE_DEFINITION, graphql.Kind.INTERFACE_TYPE_DEFINITION, graphql.Kind.INPUT_OBJECT_TYPE_DEFINITION];
547
+ const fieldsEnum = [
548
+ graphql.Kind.OBJECT_TYPE_DEFINITION,
549
+ graphql.Kind.INTERFACE_TYPE_DEFINITION,
550
+ graphql.Kind.INPUT_OBJECT_TYPE_DEFINITION,
551
+ ];
550
552
  const valuesEnum = [graphql.Kind.ENUM_TYPE_DEFINITION];
551
- const selectionsEnum = [graphql.Kind.OPERATION_DEFINITION, graphql.Kind.FRAGMENT_DEFINITION];
553
+ const selectionsEnum = [
554
+ graphql.Kind.OPERATION_DEFINITION,
555
+ graphql.Kind.FRAGMENT_DEFINITION,
556
+ ];
552
557
  const variablesEnum = [graphql.Kind.OPERATION_DEFINITION];
553
- const argumentsEnum = [graphql.Kind.FIELD_DEFINITION, graphql.Kind.FIELD, graphql.Kind.DIRECTIVE_DEFINITION, graphql.Kind.DIRECTIVE];
558
+ const argumentsEnum = [
559
+ graphql.Kind.FIELD_DEFINITION,
560
+ graphql.Kind.FIELD,
561
+ graphql.Kind.DIRECTIVE_DEFINITION,
562
+ graphql.Kind.DIRECTIVE,
563
+ ];
554
564
  const rule = {
555
565
  meta: {
556
566
  type: 'suggestion',
@@ -780,307 +790,7 @@ const rule = {
780
790
  },
781
791
  };
782
792
 
783
- const AVOID_DUPLICATE_FIELDS = 'AVOID_DUPLICATE_FIELDS';
784
793
  const rule$1 = {
785
- meta: {
786
- type: 'suggestion',
787
- docs: {
788
- description: `Checks for duplicate fields in selection set, variables in operation definition, or in arguments set of a field.`,
789
- category: 'Stylistic Issues',
790
- url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/avoid-duplicate-fields.md',
791
- examples: [
792
- {
793
- title: 'Incorrect',
794
- code: /* GraphQL */ `
795
- query {
796
- user {
797
- name
798
- email
799
- name # duplicate field
800
- }
801
- }
802
- `,
803
- },
804
- {
805
- title: 'Incorrect',
806
- code: /* GraphQL */ `
807
- query {
808
- users(
809
- first: 100
810
- skip: 50
811
- after: "cji629tngfgou0b73kt7vi5jo"
812
- first: 100 # duplicate argument
813
- ) {
814
- id
815
- }
816
- }
817
- `,
818
- },
819
- {
820
- title: 'Incorrect',
821
- code: /* GraphQL */ `
822
- query (
823
- $first: Int!
824
- $first: Int! # duplicate variable
825
- ) {
826
- users(first: $first, skip: 50) {
827
- id
828
- }
829
- }
830
- `,
831
- },
832
- ],
833
- },
834
- messages: {
835
- [AVOID_DUPLICATE_FIELDS]: `{{ type }} "{{ fieldName }}" defined multiple times`,
836
- },
837
- schema: [],
838
- },
839
- create(context) {
840
- function checkNode(usedFields, fieldName, type, node) {
841
- if (usedFields.has(fieldName)) {
842
- context.report({
843
- loc: getLocation((node.kind === graphql.Kind.FIELD && node.alias ? node.alias : node).loc, fieldName, {
844
- offsetEnd: node.kind === graphql.Kind.VARIABLE_DEFINITION ? 0 : 1,
845
- }),
846
- messageId: AVOID_DUPLICATE_FIELDS,
847
- data: {
848
- type,
849
- fieldName,
850
- },
851
- });
852
- }
853
- else {
854
- usedFields.add(fieldName);
855
- }
856
- }
857
- return {
858
- OperationDefinition(node) {
859
- const set = new Set();
860
- for (const varDef of node.variableDefinitions) {
861
- checkNode(set, varDef.variable.name.value, 'Operation variable', varDef);
862
- }
863
- },
864
- Field(node) {
865
- const set = new Set();
866
- for (const arg of node.arguments) {
867
- checkNode(set, arg.name.value, 'Field argument', arg);
868
- }
869
- },
870
- SelectionSet(node) {
871
- var _a;
872
- const set = new Set();
873
- for (const selection of node.selections) {
874
- if (selection.kind === graphql.Kind.FIELD) {
875
- checkNode(set, ((_a = selection.alias) === null || _a === void 0 ? void 0 : _a.value) || selection.name.value, 'Field', selection);
876
- }
877
- }
878
- },
879
- };
880
- },
881
- };
882
-
883
- const AVOID_OPERATION_NAME_PREFIX = 'AVOID_OPERATION_NAME_PREFIX';
884
- const rule$2 = {
885
- meta: {
886
- type: 'suggestion',
887
- docs: {
888
- 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.',
889
- category: 'Stylistic Issues',
890
- url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/avoid-operation-name-prefix.md',
891
- examples: [
892
- {
893
- title: 'Incorrect',
894
- usage: [{ keywords: ['get'] }],
895
- code: /* GraphQL */ `
896
- query getUserDetails {
897
- # ...
898
- }`,
899
- },
900
- {
901
- title: 'Correct',
902
- usage: [{ keywords: ['get'] }],
903
- code: /* GraphQL */ `
904
- query userDetails {
905
- # ...
906
- }`,
907
- },
908
- ],
909
- },
910
- messages: {
911
- [AVOID_OPERATION_NAME_PREFIX]: `Forbidden operation name prefix: "{{ invalidPrefix }}"`,
912
- },
913
- schema: [
914
- {
915
- additionalProperties: false,
916
- type: 'object',
917
- required: ['keywords'],
918
- properties: {
919
- caseSensitive: {
920
- default: false,
921
- type: 'boolean',
922
- },
923
- keywords: {
924
- additionalItems: false,
925
- type: 'array',
926
- minItems: 1,
927
- items: {
928
- type: 'string',
929
- },
930
- },
931
- },
932
- },
933
- ],
934
- },
935
- create(context) {
936
- return {
937
- 'OperationDefinition, FragmentDefinition'(node) {
938
- const config = context.options[0] || { keywords: [], caseSensitive: false };
939
- const caseSensitive = config.caseSensitive;
940
- const keywords = config.keywords || [];
941
- if (node && node.name && node.name.value !== '') {
942
- for (const keyword of keywords) {
943
- const testKeyword = caseSensitive ? keyword : keyword.toLowerCase();
944
- const testName = caseSensitive ? node.name.value : node.name.value.toLowerCase();
945
- if (testName.startsWith(testKeyword)) {
946
- const { start } = node.name.loc;
947
- context.report({
948
- loc: {
949
- start: {
950
- line: start.line,
951
- column: start.column - 1,
952
- },
953
- end: {
954
- line: start.line,
955
- column: start.column - 1 + testKeyword.length,
956
- },
957
- },
958
- data: {
959
- invalidPrefix: keyword,
960
- },
961
- messageId: AVOID_OPERATION_NAME_PREFIX,
962
- });
963
- }
964
- }
965
- }
966
- },
967
- };
968
- },
969
- };
970
-
971
- const rule$3 = {
972
- meta: {
973
- type: 'suggestion',
974
- docs: {
975
- category: 'Best Practices',
976
- description: 'Avoid scalar result type on mutation type to make sure to return a valid state.',
977
- url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/avoid-scalar-result-type-on-mutation.md',
978
- requiresSchema: true,
979
- examples: [
980
- {
981
- title: 'Incorrect',
982
- code: /* GraphQL */ `
983
- type Mutation {
984
- createUser: Boolean
985
- }
986
- `,
987
- },
988
- {
989
- title: 'Correct',
990
- code: /* GraphQL */ `
991
- type Mutation {
992
- createUser: User!
993
- }
994
- `,
995
- },
996
- ],
997
- },
998
- schema: [],
999
- },
1000
- create(context) {
1001
- const schema = requireGraphQLSchemaFromContext('avoid-scalar-result-type-on-mutation', context);
1002
- const mutationType = schema.getMutationType();
1003
- if (!mutationType) {
1004
- return {};
1005
- }
1006
- const selector = [
1007
- `:matches(${graphql.Kind.OBJECT_TYPE_DEFINITION}, ${graphql.Kind.OBJECT_TYPE_EXTENSION})[name.value=${mutationType.name}]`,
1008
- '>',
1009
- graphql.Kind.FIELD_DEFINITION,
1010
- graphql.Kind.NAMED_TYPE,
1011
- ].join(' ');
1012
- return {
1013
- [selector](node) {
1014
- const typeName = node.name.value;
1015
- const graphQLType = schema.getType(typeName);
1016
- if (graphql.isScalarType(graphQLType)) {
1017
- context.report({
1018
- loc: getLocation(node.loc, typeName),
1019
- message: `Unexpected scalar result type "${typeName}"`,
1020
- });
1021
- }
1022
- },
1023
- };
1024
- },
1025
- };
1026
-
1027
- const AVOID_TYPENAME_PREFIX = 'AVOID_TYPENAME_PREFIX';
1028
- const rule$4 = {
1029
- meta: {
1030
- type: 'suggestion',
1031
- docs: {
1032
- category: 'Best Practices',
1033
- description: 'Enforces users to avoid using the type name in a field name while defining your schema.',
1034
- recommended: true,
1035
- url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/avoid-typename-prefix.md',
1036
- examples: [
1037
- {
1038
- title: 'Incorrect',
1039
- code: /* GraphQL */ `
1040
- type User {
1041
- userId: ID!
1042
- }
1043
- `,
1044
- },
1045
- {
1046
- title: 'Correct',
1047
- code: /* GraphQL */ `
1048
- type User {
1049
- id: ID!
1050
- }
1051
- `,
1052
- },
1053
- ],
1054
- },
1055
- messages: {
1056
- [AVOID_TYPENAME_PREFIX]: `Field "{{ fieldName }}" starts with the name of the parent type "{{ typeName }}"`,
1057
- },
1058
- schema: [],
1059
- },
1060
- create(context) {
1061
- return {
1062
- 'ObjectTypeDefinition, ObjectTypeExtension, InterfaceTypeDefinition, InterfaceTypeExtension'(node) {
1063
- const typeName = node.name.value;
1064
- const lowerTypeName = typeName.toLowerCase();
1065
- for (const field of node.fields) {
1066
- const fieldName = field.name.value;
1067
- if (fieldName.toLowerCase().startsWith(lowerTypeName)) {
1068
- context.report({
1069
- data: {
1070
- fieldName,
1071
- typeName,
1072
- },
1073
- messageId: AVOID_TYPENAME_PREFIX,
1074
- loc: getLocation(field.loc, lowerTypeName),
1075
- });
1076
- }
1077
- }
1078
- },
1079
- };
1080
- },
1081
- };
1082
-
1083
- const rule$5 = {
1084
794
  meta: {
1085
795
  type: 'suggestion',
1086
796
  docs: {
@@ -1143,7 +853,7 @@ const rule$5 = {
1143
853
  const isObjectType = (node) => [graphql.Kind.OBJECT_TYPE_DEFINITION, graphql.Kind.OBJECT_TYPE_EXTENSION].includes(node.type);
1144
854
  const isQueryType = (node) => isObjectType(node) && node.name.value === 'Query';
1145
855
  const isMutationType = (node) => isObjectType(node) && node.name.value === 'Mutation';
1146
- const rule$6 = {
856
+ const rule$2 = {
1147
857
  meta: {
1148
858
  type: 'suggestion',
1149
859
  docs: {
@@ -1269,7 +979,7 @@ const CASE_STYLES = [
1269
979
  const schemaOption = {
1270
980
  oneOf: [{ $ref: '#/definitions/asString' }, { $ref: '#/definitions/asObject' }],
1271
981
  };
1272
- const rule$7 = {
982
+ const rule$3 = {
1273
983
  meta: {
1274
984
  type: 'suggestion',
1275
985
  docs: {
@@ -1487,7 +1197,7 @@ const ALLOWED_STYLES = Object.keys(StyleToRegex);
1487
1197
  const schemaOption$1 = {
1488
1198
  oneOf: [{ $ref: '#/definitions/asString' }, { $ref: '#/definitions/asObject' }],
1489
1199
  };
1490
- const rule$8 = {
1200
+ const rule$4 = {
1491
1201
  meta: {
1492
1202
  type: 'suggestion',
1493
1203
  docs: {
@@ -1583,11 +1293,11 @@ const rule$8 = {
1583
1293
  properties: {
1584
1294
  types: {
1585
1295
  ...schemaOption$1,
1586
- description: `Includes:\n\n${TYPES_KINDS.map(kind => `- \`${kind}\``).join('\n')}`,
1296
+ description: `Includes:\n\n${TYPES_KINDS.map(kind => `- [${kind}](https://spec.graphql.org/October2021/#${kind})`).join('\n')}`,
1587
1297
  },
1588
1298
  fields: {
1589
1299
  ...schemaOption$1,
1590
- description: `Includes:\n\n${FIELDS_KINDS.map(kind => `- \`${kind}\``).join('\n')}`,
1300
+ description: `Includes:\n\n${FIELDS_KINDS.map(kind => `- [${kind}](https://spec.graphql.org/October2021/#${kind})`).join('\n')}`,
1591
1301
  },
1592
1302
  allowLeadingUnderscore: {
1593
1303
  type: 'boolean',
@@ -1603,7 +1313,7 @@ const rule$8 = {
1603
1313
  description: [
1604
1314
  'May contain the following `ASTNode` names:',
1605
1315
  '',
1606
- ...ALLOWED_KINDS.map(kind => `- \`${kind}\``),
1316
+ ...ALLOWED_KINDS.map(kind => `- [${kind}](https://spec.graphql.org/October2021/#${kind})`),
1607
1317
  '',
1608
1318
  "> It's also possible to use a [`selector`](https://eslint.org/docs/developer-guide/selectors) that starts with `ASTNode` name",
1609
1319
  '>',
@@ -1700,7 +1410,7 @@ const rule$8 = {
1700
1410
  };
1701
1411
 
1702
1412
  const NO_ANONYMOUS_OPERATIONS = 'NO_ANONYMOUS_OPERATIONS';
1703
- const rule$9 = {
1413
+ const rule$5 = {
1704
1414
  meta: {
1705
1415
  type: 'suggestion',
1706
1416
  docs: {
@@ -1748,7 +1458,7 @@ const rule$9 = {
1748
1458
  };
1749
1459
 
1750
1460
  const ERROR_MESSAGE_ID = 'NO_CASE_INSENSITIVE_ENUM_VALUES_DUPLICATES';
1751
- const rule$a = {
1461
+ const rule$6 = {
1752
1462
  meta: {
1753
1463
  type: 'suggestion',
1754
1464
  docs: {
@@ -1804,7 +1514,7 @@ const rule$a = {
1804
1514
  };
1805
1515
 
1806
1516
  const NO_DEPRECATED = 'NO_DEPRECATED';
1807
- const rule$b = {
1517
+ const rule$7 = {
1808
1518
  meta: {
1809
1519
  type: 'suggestion',
1810
1520
  docs: {
@@ -1900,19 +1610,119 @@ const rule$b = {
1900
1610
  }
1901
1611
  },
1902
1612
  Field(node) {
1903
- requireGraphQLSchemaFromContext('no-deprecated', context);
1904
- const typeInfo = node.typeInfo();
1905
- if (typeInfo && typeInfo.fieldDef) {
1906
- if (typeInfo.fieldDef.deprecationReason) {
1907
- const fieldName = node.name.value;
1908
- context.report({
1909
- loc: getLocation(node.loc, fieldName),
1910
- messageId: NO_DEPRECATED,
1911
- data: {
1912
- type: 'field',
1913
- reason: typeInfo.fieldDef.deprecationReason ? `(reason: ${typeInfo.fieldDef.deprecationReason})` : '',
1914
- },
1915
- });
1613
+ requireGraphQLSchemaFromContext('no-deprecated', context);
1614
+ const typeInfo = node.typeInfo();
1615
+ if (typeInfo && typeInfo.fieldDef) {
1616
+ if (typeInfo.fieldDef.deprecationReason) {
1617
+ const fieldName = node.name.value;
1618
+ context.report({
1619
+ loc: getLocation(node.loc, fieldName),
1620
+ messageId: NO_DEPRECATED,
1621
+ data: {
1622
+ type: 'field',
1623
+ reason: typeInfo.fieldDef.deprecationReason ? `(reason: ${typeInfo.fieldDef.deprecationReason})` : '',
1624
+ },
1625
+ });
1626
+ }
1627
+ }
1628
+ },
1629
+ };
1630
+ },
1631
+ };
1632
+
1633
+ const NO_DUPLICATE_FIELDS = 'NO_DUPLICATE_FIELDS';
1634
+ const rule$8 = {
1635
+ meta: {
1636
+ type: 'suggestion',
1637
+ docs: {
1638
+ description: `Checks for duplicate fields in selection set, variables in operation definition, or in arguments set of a field.`,
1639
+ category: 'Stylistic Issues',
1640
+ url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/no-duplicate-fields.md',
1641
+ examples: [
1642
+ {
1643
+ title: 'Incorrect',
1644
+ code: /* GraphQL */ `
1645
+ query {
1646
+ user {
1647
+ name
1648
+ email
1649
+ name # duplicate field
1650
+ }
1651
+ }
1652
+ `,
1653
+ },
1654
+ {
1655
+ title: 'Incorrect',
1656
+ code: /* GraphQL */ `
1657
+ query {
1658
+ users(
1659
+ first: 100
1660
+ skip: 50
1661
+ after: "cji629tngfgou0b73kt7vi5jo"
1662
+ first: 100 # duplicate argument
1663
+ ) {
1664
+ id
1665
+ }
1666
+ }
1667
+ `,
1668
+ },
1669
+ {
1670
+ title: 'Incorrect',
1671
+ code: /* GraphQL */ `
1672
+ query (
1673
+ $first: Int!
1674
+ $first: Int! # duplicate variable
1675
+ ) {
1676
+ users(first: $first, skip: 50) {
1677
+ id
1678
+ }
1679
+ }
1680
+ `,
1681
+ },
1682
+ ],
1683
+ },
1684
+ messages: {
1685
+ [NO_DUPLICATE_FIELDS]: `{{ type }} "{{ fieldName }}" defined multiple times`,
1686
+ },
1687
+ schema: [],
1688
+ },
1689
+ create(context) {
1690
+ function checkNode(usedFields, fieldName, type, node) {
1691
+ if (usedFields.has(fieldName)) {
1692
+ context.report({
1693
+ loc: getLocation((node.kind === graphql.Kind.FIELD && node.alias ? node.alias : node).loc, fieldName, {
1694
+ offsetEnd: node.kind === graphql.Kind.VARIABLE_DEFINITION ? 0 : 1,
1695
+ }),
1696
+ messageId: NO_DUPLICATE_FIELDS,
1697
+ data: {
1698
+ type,
1699
+ fieldName,
1700
+ },
1701
+ });
1702
+ }
1703
+ else {
1704
+ usedFields.add(fieldName);
1705
+ }
1706
+ }
1707
+ return {
1708
+ OperationDefinition(node) {
1709
+ const set = new Set();
1710
+ for (const varDef of node.variableDefinitions) {
1711
+ checkNode(set, varDef.variable.name.value, 'Operation variable', varDef);
1712
+ }
1713
+ },
1714
+ Field(node) {
1715
+ const set = new Set();
1716
+ for (const arg of node.arguments) {
1717
+ checkNode(set, arg.name.value, 'Field argument', arg);
1718
+ }
1719
+ },
1720
+ SelectionSet(node) {
1721
+ var _a;
1722
+ const set = new Set();
1723
+ for (const selection of node.selections) {
1724
+ if (selection.kind === graphql.Kind.FIELD) {
1725
+ checkNode(set, ((_a = selection.alias) === null || _a === void 0 ? void 0 : _a.value) || selection.name.value, 'Field', selection);
1916
1726
  }
1917
1727
  }
1918
1728
  },
@@ -1921,7 +1731,7 @@ const rule$b = {
1921
1731
  };
1922
1732
 
1923
1733
  const HASHTAG_COMMENT = 'HASHTAG_COMMENT';
1924
- const rule$c = {
1734
+ const rule$9 = {
1925
1735
  meta: {
1926
1736
  messages: {
1927
1737
  [HASHTAG_COMMENT]: 'Using hashtag (#) for adding GraphQL descriptions is not allowed. Prefer using """ for multiline, or " for a single line description.',
@@ -1993,75 +1803,8 @@ const rule$c = {
1993
1803
  },
1994
1804
  };
1995
1805
 
1996
- const NO_OPERATION_NAME_SUFFIX = 'NO_OPERATION_NAME_SUFFIX';
1997
- const rule$d = {
1998
- meta: {
1999
- fixable: 'code',
2000
- type: 'suggestion',
2001
- docs: {
2002
- category: 'Stylistic Issues',
2003
- recommended: true,
2004
- description: `Makes sure you are not adding the operation type to the name of the operation.`,
2005
- url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/no-operation-name-suffix.md`,
2006
- examples: [
2007
- {
2008
- title: 'Incorrect',
2009
- code: /* GraphQL */ `
2010
- query userQuery {
2011
- # ...
2012
- }
2013
- `,
2014
- },
2015
- {
2016
- title: 'Correct',
2017
- code: /* GraphQL */ `
2018
- query user {
2019
- # ...
2020
- }
2021
- `,
2022
- },
2023
- ],
2024
- },
2025
- messages: {
2026
- [NO_OPERATION_NAME_SUFFIX]: `Unnecessary "{{ invalidSuffix }}" suffix in your operation name!`,
2027
- },
2028
- schema: [],
2029
- },
2030
- create(context) {
2031
- return {
2032
- 'OperationDefinition, FragmentDefinition'(node) {
2033
- var _a;
2034
- const name = ((_a = node.name) === null || _a === void 0 ? void 0 : _a.value) || '';
2035
- if (name.length > 0) {
2036
- const invalidSuffix = 'operation' in node ? node.operation : 'fragment';
2037
- if (name.toLowerCase().endsWith(invalidSuffix)) {
2038
- const { start, end } = node.name.loc;
2039
- context.report({
2040
- loc: {
2041
- start: {
2042
- column: start.column - 1 + name.length - invalidSuffix.length,
2043
- line: start.line,
2044
- },
2045
- end: {
2046
- column: end.column - 1 + name.length,
2047
- line: end.line,
2048
- },
2049
- },
2050
- data: {
2051
- invalidSuffix,
2052
- },
2053
- fix: fixer => fixer.removeRange([node.name.range[1] - invalidSuffix.length, node.name.range[1]]),
2054
- messageId: NO_OPERATION_NAME_SUFFIX,
2055
- });
2056
- }
2057
- }
2058
- },
2059
- };
2060
- },
2061
- };
2062
-
2063
1806
  const ROOT_TYPES = ['query', 'mutation', 'subscription'];
2064
- const rule$e = {
1807
+ const rule$a = {
2065
1808
  meta: {
2066
1809
  type: 'suggestion',
2067
1810
  docs: {
@@ -2151,9 +1894,121 @@ const rule$e = {
2151
1894
  },
2152
1895
  };
2153
1896
 
1897
+ const rule$b = {
1898
+ meta: {
1899
+ type: 'suggestion',
1900
+ docs: {
1901
+ category: 'Best Practices',
1902
+ description: 'Avoid scalar result type on mutation type to make sure to return a valid state.',
1903
+ url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/no-scalar-result-type-on-mutation.md',
1904
+ requiresSchema: true,
1905
+ examples: [
1906
+ {
1907
+ title: 'Incorrect',
1908
+ code: /* GraphQL */ `
1909
+ type Mutation {
1910
+ createUser: Boolean
1911
+ }
1912
+ `,
1913
+ },
1914
+ {
1915
+ title: 'Correct',
1916
+ code: /* GraphQL */ `
1917
+ type Mutation {
1918
+ createUser: User!
1919
+ }
1920
+ `,
1921
+ },
1922
+ ],
1923
+ },
1924
+ schema: [],
1925
+ },
1926
+ create(context) {
1927
+ const schema = requireGraphQLSchemaFromContext('no-scalar-result-type-on-mutation', context);
1928
+ const mutationType = schema.getMutationType();
1929
+ if (!mutationType) {
1930
+ return {};
1931
+ }
1932
+ const selector = [
1933
+ `:matches(${graphql.Kind.OBJECT_TYPE_DEFINITION}, ${graphql.Kind.OBJECT_TYPE_EXTENSION})[name.value=${mutationType.name}]`,
1934
+ '>',
1935
+ graphql.Kind.FIELD_DEFINITION,
1936
+ graphql.Kind.NAMED_TYPE,
1937
+ ].join(' ');
1938
+ return {
1939
+ [selector](node) {
1940
+ const typeName = node.name.value;
1941
+ const graphQLType = schema.getType(typeName);
1942
+ if (graphql.isScalarType(graphQLType)) {
1943
+ context.report({
1944
+ loc: getLocation(node.loc, typeName),
1945
+ message: `Unexpected scalar result type "${typeName}"`,
1946
+ });
1947
+ }
1948
+ },
1949
+ };
1950
+ },
1951
+ };
1952
+
1953
+ const NO_TYPENAME_PREFIX = 'NO_TYPENAME_PREFIX';
1954
+ const rule$c = {
1955
+ meta: {
1956
+ type: 'suggestion',
1957
+ docs: {
1958
+ category: 'Best Practices',
1959
+ description: 'Enforces users to avoid using the type name in a field name while defining your schema.',
1960
+ recommended: true,
1961
+ url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/no-typename-prefix.md',
1962
+ examples: [
1963
+ {
1964
+ title: 'Incorrect',
1965
+ code: /* GraphQL */ `
1966
+ type User {
1967
+ userId: ID!
1968
+ }
1969
+ `,
1970
+ },
1971
+ {
1972
+ title: 'Correct',
1973
+ code: /* GraphQL */ `
1974
+ type User {
1975
+ id: ID!
1976
+ }
1977
+ `,
1978
+ },
1979
+ ],
1980
+ },
1981
+ messages: {
1982
+ [NO_TYPENAME_PREFIX]: `Field "{{ fieldName }}" starts with the name of the parent type "{{ typeName }}"`,
1983
+ },
1984
+ schema: [],
1985
+ },
1986
+ create(context) {
1987
+ return {
1988
+ 'ObjectTypeDefinition, ObjectTypeExtension, InterfaceTypeDefinition, InterfaceTypeExtension'(node) {
1989
+ const typeName = node.name.value;
1990
+ const lowerTypeName = typeName.toLowerCase();
1991
+ for (const field of node.fields) {
1992
+ const fieldName = field.name.value;
1993
+ if (fieldName.toLowerCase().startsWith(lowerTypeName)) {
1994
+ context.report({
1995
+ data: {
1996
+ fieldName,
1997
+ typeName,
1998
+ },
1999
+ messageId: NO_TYPENAME_PREFIX,
2000
+ loc: getLocation(field.loc, lowerTypeName),
2001
+ });
2002
+ }
2003
+ }
2004
+ },
2005
+ };
2006
+ },
2007
+ };
2008
+
2154
2009
  const UNREACHABLE_TYPE = 'UNREACHABLE_TYPE';
2155
2010
  const RULE_NAME = 'no-unreachable-types';
2156
- const rule$f = {
2011
+ const rule$d = {
2157
2012
  meta: {
2158
2013
  messages: {
2159
2014
  [UNREACHABLE_TYPE]: `Type "{{ typeName }}" is unreachable`,
@@ -2229,7 +2084,7 @@ const rule$f = {
2229
2084
 
2230
2085
  const UNUSED_FIELD = 'UNUSED_FIELD';
2231
2086
  const RULE_NAME$1 = 'no-unused-fields';
2232
- const rule$g = {
2087
+ const rule$e = {
2233
2088
  meta: {
2234
2089
  messages: {
2235
2090
  [UNUSED_FIELD]: `Field "{{fieldName}}" is unused`,
@@ -2418,7 +2273,7 @@ const MESSAGE_REQUIRE_DATE = 'MESSAGE_REQUIRE_DATE';
2418
2273
  const MESSAGE_INVALID_FORMAT = 'MESSAGE_INVALID_FORMAT';
2419
2274
  const MESSAGE_INVALID_DATE = 'MESSAGE_INVALID_DATE';
2420
2275
  const MESSAGE_CAN_BE_REMOVED = 'MESSAGE_CAN_BE_REMOVED';
2421
- const rule$h = {
2276
+ const rule$f = {
2422
2277
  meta: {
2423
2278
  type: 'suggestion',
2424
2279
  docs: {
@@ -2521,7 +2376,7 @@ const rule$h = {
2521
2376
  },
2522
2377
  };
2523
2378
 
2524
- const rule$i = {
2379
+ const rule$g = {
2525
2380
  meta: {
2526
2381
  docs: {
2527
2382
  description: `Require all deprecation directives to specify a reason.`,
@@ -2576,41 +2431,23 @@ const rule$i = {
2576
2431
  };
2577
2432
 
2578
2433
  const REQUIRE_DESCRIPTION_ERROR = 'REQUIRE_DESCRIPTION_ERROR';
2579
- const DESCRIBABLE_NODES = [
2580
- graphql.Kind.SCHEMA_DEFINITION,
2581
- graphql.Kind.OBJECT_TYPE_DEFINITION,
2434
+ const ALLOWED_KINDS$1 = [
2435
+ ...TYPES_KINDS,
2582
2436
  graphql.Kind.FIELD_DEFINITION,
2583
2437
  graphql.Kind.INPUT_VALUE_DEFINITION,
2584
- graphql.Kind.INTERFACE_TYPE_DEFINITION,
2585
- graphql.Kind.UNION_TYPE_DEFINITION,
2586
- graphql.Kind.ENUM_TYPE_DEFINITION,
2587
2438
  graphql.Kind.ENUM_VALUE_DEFINITION,
2588
- graphql.Kind.INPUT_OBJECT_TYPE_DEFINITION,
2589
2439
  graphql.Kind.DIRECTIVE_DEFINITION,
2590
2440
  ];
2591
- function verifyRule(context, node) {
2592
- if (node) {
2593
- if (!node.description || !node.description.value || node.description.value.trim().length === 0) {
2594
- context.report({
2595
- loc: getLocation(('name' in node ? node.name : node).loc, 'name' in node ? node.name.value : 'schema'),
2596
- messageId: REQUIRE_DESCRIPTION_ERROR,
2597
- data: {
2598
- nodeType: node.kind,
2599
- },
2600
- });
2601
- }
2602
- }
2603
- }
2604
- const rule$j = {
2441
+ const rule$h = {
2605
2442
  meta: {
2606
2443
  docs: {
2607
2444
  category: 'Best Practices',
2608
- description: `Enforce descriptions in your type definitions.`,
2609
- url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/require-description.md`,
2445
+ description: 'Enforce descriptions in your type definitions.',
2446
+ url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/require-description.md',
2610
2447
  examples: [
2611
2448
  {
2612
2449
  title: 'Incorrect',
2613
- usage: [{ on: [graphql.Kind.OBJECT_TYPE_DEFINITION, graphql.Kind.FIELD_DEFINITION] }],
2450
+ usage: [{ types: true, overrides: { FieldDefinition: true } }],
2614
2451
  code: /* GraphQL */ `
2615
2452
  type someTypeName {
2616
2453
  name: String
@@ -2619,7 +2456,7 @@ const rule$j = {
2619
2456
  },
2620
2457
  {
2621
2458
  title: 'Correct',
2622
- usage: [{ on: [graphql.Kind.OBJECT_TYPE_DEFINITION, graphql.Kind.FIELD_DEFINITION] }],
2459
+ usage: [{ types: true, overrides: { FieldDefinition: true } }],
2623
2460
  code: /* GraphQL */ `
2624
2461
  """
2625
2462
  Some type description
@@ -2633,40 +2470,74 @@ const rule$j = {
2633
2470
  `,
2634
2471
  },
2635
2472
  ],
2473
+ optionsForConfig: [
2474
+ {
2475
+ types: true,
2476
+ overrides: {
2477
+ [graphql.Kind.DIRECTIVE_DEFINITION]: true,
2478
+ },
2479
+ },
2480
+ ],
2636
2481
  },
2637
2482
  type: 'suggestion',
2638
2483
  messages: {
2639
- [REQUIRE_DESCRIPTION_ERROR]: `Description is required for nodes of type "{{ nodeType }}"`,
2484
+ [REQUIRE_DESCRIPTION_ERROR]: 'Description is required for nodes of type "{{ nodeType }}"',
2640
2485
  },
2641
2486
  schema: {
2642
2487
  type: 'array',
2643
- additionalItems: false,
2644
2488
  minItems: 1,
2645
2489
  maxItems: 1,
2646
2490
  items: {
2647
2491
  type: 'object',
2648
- require: ['on'],
2492
+ additionalProperties: false,
2493
+ minProperties: 1,
2649
2494
  properties: {
2650
- on: {
2651
- type: 'array',
2652
- minItems: 1,
2653
- additionalItems: false,
2654
- items: {
2655
- type: 'string',
2656
- enum: DESCRIBABLE_NODES,
2657
- },
2495
+ types: {
2496
+ type: 'boolean',
2497
+ description: `Includes:\n\n${TYPES_KINDS.map(kind => `- [${kind}](https://spec.graphql.org/October2021/#${kind})`).join('\n')}`,
2498
+ },
2499
+ overrides: {
2500
+ type: 'object',
2501
+ description: 'Configuration for precise `ASTNode`',
2502
+ additionalProperties: false,
2503
+ properties: Object.fromEntries(ALLOWED_KINDS$1.map(kind => [kind, { type: 'boolean' }])),
2658
2504
  },
2659
2505
  },
2660
2506
  },
2661
2507
  },
2662
2508
  },
2663
2509
  create(context) {
2664
- return Object.fromEntries(context.options[0].on.map(optionKey => [optionKey, node => verifyRule(context, node)]));
2510
+ const { types, overrides = {} } = context.options[0];
2511
+ const kinds = new Set(types ? TYPES_KINDS : []);
2512
+ for (const [kind, isEnabled] of Object.entries(overrides)) {
2513
+ if (isEnabled) {
2514
+ kinds.add(kind);
2515
+ }
2516
+ else {
2517
+ kinds.delete(kind);
2518
+ }
2519
+ }
2520
+ const selector = [...kinds].join(',');
2521
+ return {
2522
+ [selector](node) {
2523
+ var _a;
2524
+ const description = ((_a = node.description) === null || _a === void 0 ? void 0 : _a.value) || '';
2525
+ if (description.trim().length === 0) {
2526
+ context.report({
2527
+ loc: getLocation(node.name.loc, node.name.value),
2528
+ messageId: REQUIRE_DESCRIPTION_ERROR,
2529
+ data: {
2530
+ nodeType: node.kind,
2531
+ },
2532
+ });
2533
+ }
2534
+ },
2535
+ };
2665
2536
  },
2666
2537
  };
2667
2538
 
2668
2539
  const RULE_NAME$2 = 'require-field-of-type-query-in-mutation-result';
2669
- const rule$k = {
2540
+ const rule$i = {
2670
2541
  meta: {
2671
2542
  type: 'suggestion',
2672
2543
  docs: {
@@ -2834,7 +2705,7 @@ const convertNode = (typeInfo) => (node, key, parent) => {
2834
2705
 
2835
2706
  const REQUIRE_ID_WHEN_AVAILABLE = 'REQUIRE_ID_WHEN_AVAILABLE';
2836
2707
  const DEFAULT_ID_FIELD_NAME = 'id';
2837
- const rule$l = {
2708
+ const rule$j = {
2838
2709
  meta: {
2839
2710
  type: 'suggestion',
2840
2711
  docs: {
@@ -2970,7 +2841,7 @@ const rule$l = {
2970
2841
  },
2971
2842
  };
2972
2843
 
2973
- const rule$m = {
2844
+ const rule$k = {
2974
2845
  meta: {
2975
2846
  docs: {
2976
2847
  category: 'Best Practices',
@@ -3092,7 +2963,7 @@ const shouldIgnoreNode = ({ node, exceptions }) => {
3092
2963
  }
3093
2964
  return false;
3094
2965
  };
3095
- const rule$n = {
2966
+ const rule$l = {
3096
2967
  meta: {
3097
2968
  type: 'suggestion',
3098
2969
  docs: {
@@ -3269,7 +3140,7 @@ const checkNode = (context, node, ruleName, messageId) => {
3269
3140
  });
3270
3141
  }
3271
3142
  };
3272
- const rule$o = {
3143
+ const rule$m = {
3273
3144
  meta: {
3274
3145
  type: 'suggestion',
3275
3146
  docs: {
@@ -3328,7 +3199,7 @@ const rule$o = {
3328
3199
 
3329
3200
  const RULE_NAME$4 = 'unique-operation-name';
3330
3201
  const UNIQUE_OPERATION_NAME = 'UNIQUE_OPERATION_NAME';
3331
- const rule$p = {
3202
+ const rule$n = {
3332
3203
  meta: {
3333
3204
  type: 'suggestion',
3334
3205
  docs: {
@@ -3395,31 +3266,29 @@ const rule$p = {
3395
3266
  const rules = {
3396
3267
  ...GRAPHQL_JS_VALIDATIONS,
3397
3268
  alphabetize: rule,
3398
- 'avoid-duplicate-fields': rule$1,
3399
- 'avoid-operation-name-prefix': rule$2,
3400
- 'avoid-scalar-result-type-on-mutation': rule$3,
3401
- 'avoid-typename-prefix': rule$4,
3402
- 'description-style': rule$5,
3403
- 'input-name': rule$6,
3404
- 'match-document-filename': rule$7,
3405
- 'naming-convention': rule$8,
3406
- 'no-anonymous-operations': rule$9,
3407
- 'no-case-insensitive-enum-values-duplicates': rule$a,
3408
- 'no-deprecated': rule$b,
3409
- 'no-hashtag-description': rule$c,
3410
- 'no-operation-name-suffix': rule$d,
3411
- 'no-root-type': rule$e,
3412
- 'no-unreachable-types': rule$f,
3413
- 'no-unused-fields': rule$g,
3414
- 'require-deprecation-date': rule$h,
3415
- 'require-deprecation-reason': rule$i,
3416
- 'require-description': rule$j,
3417
- 'require-field-of-type-query-in-mutation-result': rule$k,
3418
- 'require-id-when-available': rule$l,
3419
- 'selection-set-depth': rule$m,
3420
- 'strict-id-in-types': rule$n,
3421
- 'unique-fragment-name': rule$o,
3422
- 'unique-operation-name': rule$p,
3269
+ 'description-style': rule$1,
3270
+ 'input-name': rule$2,
3271
+ 'match-document-filename': rule$3,
3272
+ 'naming-convention': rule$4,
3273
+ 'no-anonymous-operations': rule$5,
3274
+ 'no-case-insensitive-enum-values-duplicates': rule$6,
3275
+ 'no-deprecated': rule$7,
3276
+ 'no-duplicate-fields': rule$8,
3277
+ 'no-hashtag-description': rule$9,
3278
+ 'no-root-type': rule$a,
3279
+ 'no-scalar-result-type-on-mutation': rule$b,
3280
+ 'no-typename-prefix': rule$c,
3281
+ 'no-unreachable-types': rule$d,
3282
+ 'no-unused-fields': rule$e,
3283
+ 'require-deprecation-date': rule$f,
3284
+ 'require-deprecation-reason': rule$g,
3285
+ 'require-description': rule$h,
3286
+ 'require-field-of-type-query-in-mutation-result': rule$i,
3287
+ 'require-id-when-available': rule$j,
3288
+ 'selection-set-depth': rule$k,
3289
+ 'strict-id-in-types': rule$l,
3290
+ 'unique-fragment-name': rule$m,
3291
+ 'unique-operation-name': rule$n,
3423
3292
  };
3424
3293
 
3425
3294
  const RELEVANT_KEYWORDS = ['gql', 'graphql', '/* GraphQL */'];