@graphql-eslint/eslint-plugin 3.0.0-alpha-580615a.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,15 +109,14 @@ 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',
@@ -792,307 +790,7 @@ const rule = {
792
790
  },
793
791
  };
794
792
 
795
- const AVOID_DUPLICATE_FIELDS = 'AVOID_DUPLICATE_FIELDS';
796
793
  const rule$1 = {
797
- meta: {
798
- type: 'suggestion',
799
- 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
- examples: [
804
- {
805
- title: 'Incorrect',
806
- 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
- }
828
- }
829
- `,
830
- },
831
- {
832
- title: 'Incorrect',
833
- code: /* GraphQL */ `
834
- query (
835
- $first: Int!
836
- $first: Int! # duplicate variable
837
- ) {
838
- users(first: $first, skip: 50) {
839
- id
840
- }
841
- }
842
- `,
843
- },
844
- ],
845
- },
846
- messages: {
847
- [AVOID_DUPLICATE_FIELDS]: `{{ type }} "{{ fieldName }}" defined multiple times`,
848
- },
849
- schema: [],
850
- },
851
- create(context) {
852
- function checkNode(usedFields, fieldName, type, node) {
853
- if (usedFields.has(fieldName)) {
854
- 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
- },
863
- });
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
- },
891
- };
892
- },
893
- };
894
-
895
- const AVOID_OPERATION_NAME_PREFIX = 'AVOID_OPERATION_NAME_PREFIX';
896
- const rule$2 = {
897
- meta: {
898
- type: 'suggestion',
899
- 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',
903
- examples: [
904
- {
905
- title: 'Incorrect',
906
- usage: [{ keywords: ['get'] }],
907
- code: /* GraphQL */ `
908
- query getUserDetails {
909
- # ...
910
- }`,
911
- },
912
- {
913
- title: 'Correct',
914
- usage: [{ keywords: ['get'] }],
915
- code: /* GraphQL */ `
916
- query userDetails {
917
- # ...
918
- }`,
919
- },
920
- ],
921
- },
922
- messages: {
923
- [AVOID_OPERATION_NAME_PREFIX]: `Forbidden operation name prefix: "{{ invalidPrefix }}"`,
924
- },
925
- schema: [
926
- {
927
- additionalProperties: false,
928
- type: 'object',
929
- required: ['keywords'],
930
- properties: {
931
- caseSensitive: {
932
- default: false,
933
- type: 'boolean',
934
- },
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
794
  meta: {
1097
795
  type: 'suggestion',
1098
796
  docs: {
@@ -1155,7 +853,7 @@ const rule$5 = {
1155
853
  const isObjectType = (node) => [graphql.Kind.OBJECT_TYPE_DEFINITION, graphql.Kind.OBJECT_TYPE_EXTENSION].includes(node.type);
1156
854
  const isQueryType = (node) => isObjectType(node) && node.name.value === 'Query';
1157
855
  const isMutationType = (node) => isObjectType(node) && node.name.value === 'Mutation';
1158
- const rule$6 = {
856
+ const rule$2 = {
1159
857
  meta: {
1160
858
  type: 'suggestion',
1161
859
  docs: {
@@ -1281,7 +979,7 @@ const CASE_STYLES = [
1281
979
  const schemaOption = {
1282
980
  oneOf: [{ $ref: '#/definitions/asString' }, { $ref: '#/definitions/asObject' }],
1283
981
  };
1284
- const rule$7 = {
982
+ const rule$3 = {
1285
983
  meta: {
1286
984
  type: 'suggestion',
1287
985
  docs: {
@@ -1499,7 +1197,7 @@ const ALLOWED_STYLES = Object.keys(StyleToRegex);
1499
1197
  const schemaOption$1 = {
1500
1198
  oneOf: [{ $ref: '#/definitions/asString' }, { $ref: '#/definitions/asObject' }],
1501
1199
  };
1502
- const rule$8 = {
1200
+ const rule$4 = {
1503
1201
  meta: {
1504
1202
  type: 'suggestion',
1505
1203
  docs: {
@@ -1712,7 +1410,7 @@ const rule$8 = {
1712
1410
  };
1713
1411
 
1714
1412
  const NO_ANONYMOUS_OPERATIONS = 'NO_ANONYMOUS_OPERATIONS';
1715
- const rule$9 = {
1413
+ const rule$5 = {
1716
1414
  meta: {
1717
1415
  type: 'suggestion',
1718
1416
  docs: {
@@ -1760,7 +1458,7 @@ const rule$9 = {
1760
1458
  };
1761
1459
 
1762
1460
  const ERROR_MESSAGE_ID = 'NO_CASE_INSENSITIVE_ENUM_VALUES_DUPLICATES';
1763
- const rule$a = {
1461
+ const rule$6 = {
1764
1462
  meta: {
1765
1463
  type: 'suggestion',
1766
1464
  docs: {
@@ -1816,7 +1514,7 @@ const rule$a = {
1816
1514
  };
1817
1515
 
1818
1516
  const NO_DEPRECATED = 'NO_DEPRECATED';
1819
- const rule$b = {
1517
+ const rule$7 = {
1820
1518
  meta: {
1821
1519
  type: 'suggestion',
1822
1520
  docs: {
@@ -1867,20 +1565,116 @@ const rule$b = {
1867
1565
  `,
1868
1566
  },
1869
1567
  {
1870
- title: 'Correct',
1568
+ title: 'Correct',
1569
+ code: /* GraphQL */ `
1570
+ # In your schema
1571
+ type User {
1572
+ id: ID!
1573
+ name: String! @deprecated(reason: "old field, please use fullName instead")
1574
+ fullName: String!
1575
+ }
1576
+
1577
+ # Query
1578
+ query user {
1579
+ user {
1580
+ id
1581
+ fullName
1582
+ }
1583
+ }
1584
+ `,
1585
+ },
1586
+ ],
1587
+ },
1588
+ messages: {
1589
+ [NO_DEPRECATED]: `This {{ type }} is marked as deprecated in your GraphQL schema {{ reason }}`,
1590
+ },
1591
+ schema: [],
1592
+ },
1593
+ create(context) {
1594
+ return {
1595
+ EnumValue(node) {
1596
+ requireGraphQLSchemaFromContext('no-deprecated', context);
1597
+ const typeInfo = node.typeInfo();
1598
+ if (typeInfo && typeInfo.enumValue) {
1599
+ if (typeInfo.enumValue.deprecationReason) {
1600
+ const enumValueName = node.value;
1601
+ context.report({
1602
+ loc: getLocation(node.loc, enumValueName),
1603
+ messageId: NO_DEPRECATED,
1604
+ data: {
1605
+ type: 'enum value',
1606
+ reason: typeInfo.enumValue.deprecationReason ? `(reason: ${typeInfo.enumValue.deprecationReason})` : '',
1607
+ },
1608
+ });
1609
+ }
1610
+ }
1611
+ },
1612
+ Field(node) {
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',
1871
1671
  code: /* GraphQL */ `
1872
- # In your schema
1873
- type User {
1874
- id: ID!
1875
- name: String! @deprecated(reason: "old field, please use fullName instead")
1876
- fullName: String!
1877
- }
1878
-
1879
- # Query
1880
- query user {
1881
- user {
1672
+ query (
1673
+ $first: Int!
1674
+ $first: Int! # duplicate variable
1675
+ ) {
1676
+ users(first: $first, skip: 50) {
1882
1677
  id
1883
- fullName
1884
1678
  }
1885
1679
  }
1886
1680
  `,
@@ -1888,43 +1682,47 @@ const rule$b = {
1888
1682
  ],
1889
1683
  },
1890
1684
  messages: {
1891
- [NO_DEPRECATED]: `This {{ type }} is marked as deprecated in your GraphQL schema {{ reason }}`,
1685
+ [NO_DUPLICATE_FIELDS]: `{{ type }} "{{ fieldName }}" defined multiple times`,
1892
1686
  },
1893
1687
  schema: [],
1894
1688
  },
1895
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
+ }
1896
1707
  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
- }
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);
1912
1712
  }
1913
1713
  },
1914
1714
  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
- });
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);
1928
1726
  }
1929
1727
  }
1930
1728
  },
@@ -1933,7 +1731,7 @@ const rule$b = {
1933
1731
  };
1934
1732
 
1935
1733
  const HASHTAG_COMMENT = 'HASHTAG_COMMENT';
1936
- const rule$c = {
1734
+ const rule$9 = {
1937
1735
  meta: {
1938
1736
  messages: {
1939
1737
  [HASHTAG_COMMENT]: 'Using hashtag (#) for adding GraphQL descriptions is not allowed. Prefer using """ for multiline, or " for a single line description.',
@@ -2005,75 +1803,8 @@ const rule$c = {
2005
1803
  },
2006
1804
  };
2007
1805
 
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
1806
  const ROOT_TYPES = ['query', 'mutation', 'subscription'];
2076
- const rule$e = {
1807
+ const rule$a = {
2077
1808
  meta: {
2078
1809
  type: 'suggestion',
2079
1810
  docs: {
@@ -2163,9 +1894,121 @@ const rule$e = {
2163
1894
  },
2164
1895
  };
2165
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
+
2166
2009
  const UNREACHABLE_TYPE = 'UNREACHABLE_TYPE';
2167
2010
  const RULE_NAME = 'no-unreachable-types';
2168
- const rule$f = {
2011
+ const rule$d = {
2169
2012
  meta: {
2170
2013
  messages: {
2171
2014
  [UNREACHABLE_TYPE]: `Type "{{ typeName }}" is unreachable`,
@@ -2241,7 +2084,7 @@ const rule$f = {
2241
2084
 
2242
2085
  const UNUSED_FIELD = 'UNUSED_FIELD';
2243
2086
  const RULE_NAME$1 = 'no-unused-fields';
2244
- const rule$g = {
2087
+ const rule$e = {
2245
2088
  meta: {
2246
2089
  messages: {
2247
2090
  [UNUSED_FIELD]: `Field "{{fieldName}}" is unused`,
@@ -2430,7 +2273,7 @@ const MESSAGE_REQUIRE_DATE = 'MESSAGE_REQUIRE_DATE';
2430
2273
  const MESSAGE_INVALID_FORMAT = 'MESSAGE_INVALID_FORMAT';
2431
2274
  const MESSAGE_INVALID_DATE = 'MESSAGE_INVALID_DATE';
2432
2275
  const MESSAGE_CAN_BE_REMOVED = 'MESSAGE_CAN_BE_REMOVED';
2433
- const rule$h = {
2276
+ const rule$f = {
2434
2277
  meta: {
2435
2278
  type: 'suggestion',
2436
2279
  docs: {
@@ -2533,7 +2376,7 @@ const rule$h = {
2533
2376
  },
2534
2377
  };
2535
2378
 
2536
- const rule$i = {
2379
+ const rule$g = {
2537
2380
  meta: {
2538
2381
  docs: {
2539
2382
  description: `Require all deprecation directives to specify a reason.`,
@@ -2595,7 +2438,7 @@ const ALLOWED_KINDS$1 = [
2595
2438
  graphql.Kind.ENUM_VALUE_DEFINITION,
2596
2439
  graphql.Kind.DIRECTIVE_DEFINITION,
2597
2440
  ];
2598
- const rule$j = {
2441
+ const rule$h = {
2599
2442
  meta: {
2600
2443
  docs: {
2601
2444
  category: 'Best Practices',
@@ -2694,7 +2537,7 @@ const rule$j = {
2694
2537
  };
2695
2538
 
2696
2539
  const RULE_NAME$2 = 'require-field-of-type-query-in-mutation-result';
2697
- const rule$k = {
2540
+ const rule$i = {
2698
2541
  meta: {
2699
2542
  type: 'suggestion',
2700
2543
  docs: {
@@ -2862,7 +2705,7 @@ const convertNode = (typeInfo) => (node, key, parent) => {
2862
2705
 
2863
2706
  const REQUIRE_ID_WHEN_AVAILABLE = 'REQUIRE_ID_WHEN_AVAILABLE';
2864
2707
  const DEFAULT_ID_FIELD_NAME = 'id';
2865
- const rule$l = {
2708
+ const rule$j = {
2866
2709
  meta: {
2867
2710
  type: 'suggestion',
2868
2711
  docs: {
@@ -2998,7 +2841,7 @@ const rule$l = {
2998
2841
  },
2999
2842
  };
3000
2843
 
3001
- const rule$m = {
2844
+ const rule$k = {
3002
2845
  meta: {
3003
2846
  docs: {
3004
2847
  category: 'Best Practices',
@@ -3120,7 +2963,7 @@ const shouldIgnoreNode = ({ node, exceptions }) => {
3120
2963
  }
3121
2964
  return false;
3122
2965
  };
3123
- const rule$n = {
2966
+ const rule$l = {
3124
2967
  meta: {
3125
2968
  type: 'suggestion',
3126
2969
  docs: {
@@ -3297,7 +3140,7 @@ const checkNode = (context, node, ruleName, messageId) => {
3297
3140
  });
3298
3141
  }
3299
3142
  };
3300
- const rule$o = {
3143
+ const rule$m = {
3301
3144
  meta: {
3302
3145
  type: 'suggestion',
3303
3146
  docs: {
@@ -3356,7 +3199,7 @@ const rule$o = {
3356
3199
 
3357
3200
  const RULE_NAME$4 = 'unique-operation-name';
3358
3201
  const UNIQUE_OPERATION_NAME = 'UNIQUE_OPERATION_NAME';
3359
- const rule$p = {
3202
+ const rule$n = {
3360
3203
  meta: {
3361
3204
  type: 'suggestion',
3362
3205
  docs: {
@@ -3423,31 +3266,29 @@ const rule$p = {
3423
3266
  const rules = {
3424
3267
  ...GRAPHQL_JS_VALIDATIONS,
3425
3268
  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,
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,
3451
3292
  };
3452
3293
 
3453
3294
  const RELEVANT_KEYWORDS = ['gql', 'graphql', '/* GraphQL */'];