@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.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,15 +103,14 @@ 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',
@@ -786,307 +784,7 @@ const rule = {
786
784
  },
787
785
  };
788
786
 
789
- const AVOID_DUPLICATE_FIELDS = 'AVOID_DUPLICATE_FIELDS';
790
787
  const rule$1 = {
791
- meta: {
792
- type: 'suggestion',
793
- docs: {
794
- description: `Checks for duplicate fields in selection set, variables in operation definition, or in arguments set of a field.`,
795
- category: 'Stylistic Issues',
796
- url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/avoid-duplicate-fields.md',
797
- examples: [
798
- {
799
- title: 'Incorrect',
800
- code: /* GraphQL */ `
801
- query {
802
- user {
803
- name
804
- email
805
- name # duplicate field
806
- }
807
- }
808
- `,
809
- },
810
- {
811
- title: 'Incorrect',
812
- code: /* GraphQL */ `
813
- query {
814
- users(
815
- first: 100
816
- skip: 50
817
- after: "cji629tngfgou0b73kt7vi5jo"
818
- first: 100 # duplicate argument
819
- ) {
820
- id
821
- }
822
- }
823
- `,
824
- },
825
- {
826
- title: 'Incorrect',
827
- code: /* GraphQL */ `
828
- query (
829
- $first: Int!
830
- $first: Int! # duplicate variable
831
- ) {
832
- users(first: $first, skip: 50) {
833
- id
834
- }
835
- }
836
- `,
837
- },
838
- ],
839
- },
840
- messages: {
841
- [AVOID_DUPLICATE_FIELDS]: `{{ type }} "{{ fieldName }}" defined multiple times`,
842
- },
843
- schema: [],
844
- },
845
- create(context) {
846
- function checkNode(usedFields, fieldName, type, node) {
847
- if (usedFields.has(fieldName)) {
848
- context.report({
849
- loc: getLocation((node.kind === Kind.FIELD && node.alias ? node.alias : node).loc, fieldName, {
850
- offsetEnd: node.kind === Kind.VARIABLE_DEFINITION ? 0 : 1,
851
- }),
852
- messageId: AVOID_DUPLICATE_FIELDS,
853
- data: {
854
- type,
855
- fieldName,
856
- },
857
- });
858
- }
859
- else {
860
- usedFields.add(fieldName);
861
- }
862
- }
863
- return {
864
- OperationDefinition(node) {
865
- const set = new Set();
866
- for (const varDef of node.variableDefinitions) {
867
- checkNode(set, varDef.variable.name.value, 'Operation variable', varDef);
868
- }
869
- },
870
- Field(node) {
871
- const set = new Set();
872
- for (const arg of node.arguments) {
873
- checkNode(set, arg.name.value, 'Field argument', arg);
874
- }
875
- },
876
- SelectionSet(node) {
877
- var _a;
878
- const set = new Set();
879
- for (const selection of node.selections) {
880
- if (selection.kind === Kind.FIELD) {
881
- checkNode(set, ((_a = selection.alias) === null || _a === void 0 ? void 0 : _a.value) || selection.name.value, 'Field', selection);
882
- }
883
- }
884
- },
885
- };
886
- },
887
- };
888
-
889
- const AVOID_OPERATION_NAME_PREFIX = 'AVOID_OPERATION_NAME_PREFIX';
890
- const rule$2 = {
891
- meta: {
892
- type: 'suggestion',
893
- docs: {
894
- description: 'Enforce/avoid operation name prefix, useful if you wish to avoid prefix in your root fields, or avoid using REST terminology in your schema.',
895
- category: 'Stylistic Issues',
896
- url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/avoid-operation-name-prefix.md',
897
- examples: [
898
- {
899
- title: 'Incorrect',
900
- usage: [{ keywords: ['get'] }],
901
- code: /* GraphQL */ `
902
- query getUserDetails {
903
- # ...
904
- }`,
905
- },
906
- {
907
- title: 'Correct',
908
- usage: [{ keywords: ['get'] }],
909
- code: /* GraphQL */ `
910
- query userDetails {
911
- # ...
912
- }`,
913
- },
914
- ],
915
- },
916
- messages: {
917
- [AVOID_OPERATION_NAME_PREFIX]: `Forbidden operation name prefix: "{{ invalidPrefix }}"`,
918
- },
919
- schema: [
920
- {
921
- additionalProperties: false,
922
- type: 'object',
923
- required: ['keywords'],
924
- properties: {
925
- caseSensitive: {
926
- default: false,
927
- type: 'boolean',
928
- },
929
- keywords: {
930
- additionalItems: false,
931
- type: 'array',
932
- minItems: 1,
933
- items: {
934
- type: 'string',
935
- },
936
- },
937
- },
938
- },
939
- ],
940
- },
941
- create(context) {
942
- return {
943
- 'OperationDefinition, FragmentDefinition'(node) {
944
- const config = context.options[0] || { keywords: [], caseSensitive: false };
945
- const caseSensitive = config.caseSensitive;
946
- const keywords = config.keywords || [];
947
- if (node && node.name && node.name.value !== '') {
948
- for (const keyword of keywords) {
949
- const testKeyword = caseSensitive ? keyword : keyword.toLowerCase();
950
- const testName = caseSensitive ? node.name.value : node.name.value.toLowerCase();
951
- if (testName.startsWith(testKeyword)) {
952
- const { start } = node.name.loc;
953
- context.report({
954
- loc: {
955
- start: {
956
- line: start.line,
957
- column: start.column - 1,
958
- },
959
- end: {
960
- line: start.line,
961
- column: start.column - 1 + testKeyword.length,
962
- },
963
- },
964
- data: {
965
- invalidPrefix: keyword,
966
- },
967
- messageId: AVOID_OPERATION_NAME_PREFIX,
968
- });
969
- }
970
- }
971
- }
972
- },
973
- };
974
- },
975
- };
976
-
977
- const rule$3 = {
978
- meta: {
979
- type: 'suggestion',
980
- docs: {
981
- category: 'Best Practices',
982
- description: 'Avoid scalar result type on mutation type to make sure to return a valid state.',
983
- url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/avoid-scalar-result-type-on-mutation.md',
984
- requiresSchema: true,
985
- examples: [
986
- {
987
- title: 'Incorrect',
988
- code: /* GraphQL */ `
989
- type Mutation {
990
- createUser: Boolean
991
- }
992
- `,
993
- },
994
- {
995
- title: 'Correct',
996
- code: /* GraphQL */ `
997
- type Mutation {
998
- createUser: User!
999
- }
1000
- `,
1001
- },
1002
- ],
1003
- },
1004
- schema: [],
1005
- },
1006
- create(context) {
1007
- const schema = requireGraphQLSchemaFromContext('avoid-scalar-result-type-on-mutation', context);
1008
- const mutationType = schema.getMutationType();
1009
- if (!mutationType) {
1010
- return {};
1011
- }
1012
- const selector = [
1013
- `:matches(${Kind.OBJECT_TYPE_DEFINITION}, ${Kind.OBJECT_TYPE_EXTENSION})[name.value=${mutationType.name}]`,
1014
- '>',
1015
- Kind.FIELD_DEFINITION,
1016
- Kind.NAMED_TYPE,
1017
- ].join(' ');
1018
- return {
1019
- [selector](node) {
1020
- const typeName = node.name.value;
1021
- const graphQLType = schema.getType(typeName);
1022
- if (isScalarType(graphQLType)) {
1023
- context.report({
1024
- loc: getLocation(node.loc, typeName),
1025
- message: `Unexpected scalar result type "${typeName}"`,
1026
- });
1027
- }
1028
- },
1029
- };
1030
- },
1031
- };
1032
-
1033
- const AVOID_TYPENAME_PREFIX = 'AVOID_TYPENAME_PREFIX';
1034
- const rule$4 = {
1035
- meta: {
1036
- type: 'suggestion',
1037
- docs: {
1038
- category: 'Best Practices',
1039
- description: 'Enforces users to avoid using the type name in a field name while defining your schema.',
1040
- recommended: true,
1041
- url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/avoid-typename-prefix.md',
1042
- examples: [
1043
- {
1044
- title: 'Incorrect',
1045
- code: /* GraphQL */ `
1046
- type User {
1047
- userId: ID!
1048
- }
1049
- `,
1050
- },
1051
- {
1052
- title: 'Correct',
1053
- code: /* GraphQL */ `
1054
- type User {
1055
- id: ID!
1056
- }
1057
- `,
1058
- },
1059
- ],
1060
- },
1061
- messages: {
1062
- [AVOID_TYPENAME_PREFIX]: `Field "{{ fieldName }}" starts with the name of the parent type "{{ typeName }}"`,
1063
- },
1064
- schema: [],
1065
- },
1066
- create(context) {
1067
- return {
1068
- 'ObjectTypeDefinition, ObjectTypeExtension, InterfaceTypeDefinition, InterfaceTypeExtension'(node) {
1069
- const typeName = node.name.value;
1070
- const lowerTypeName = typeName.toLowerCase();
1071
- for (const field of node.fields) {
1072
- const fieldName = field.name.value;
1073
- if (fieldName.toLowerCase().startsWith(lowerTypeName)) {
1074
- context.report({
1075
- data: {
1076
- fieldName,
1077
- typeName,
1078
- },
1079
- messageId: AVOID_TYPENAME_PREFIX,
1080
- loc: getLocation(field.loc, lowerTypeName),
1081
- });
1082
- }
1083
- }
1084
- },
1085
- };
1086
- },
1087
- };
1088
-
1089
- const rule$5 = {
1090
788
  meta: {
1091
789
  type: 'suggestion',
1092
790
  docs: {
@@ -1149,7 +847,7 @@ const rule$5 = {
1149
847
  const isObjectType = (node) => [Kind.OBJECT_TYPE_DEFINITION, Kind.OBJECT_TYPE_EXTENSION].includes(node.type);
1150
848
  const isQueryType = (node) => isObjectType(node) && node.name.value === 'Query';
1151
849
  const isMutationType = (node) => isObjectType(node) && node.name.value === 'Mutation';
1152
- const rule$6 = {
850
+ const rule$2 = {
1153
851
  meta: {
1154
852
  type: 'suggestion',
1155
853
  docs: {
@@ -1275,7 +973,7 @@ const CASE_STYLES = [
1275
973
  const schemaOption = {
1276
974
  oneOf: [{ $ref: '#/definitions/asString' }, { $ref: '#/definitions/asObject' }],
1277
975
  };
1278
- const rule$7 = {
976
+ const rule$3 = {
1279
977
  meta: {
1280
978
  type: 'suggestion',
1281
979
  docs: {
@@ -1493,7 +1191,7 @@ const ALLOWED_STYLES = Object.keys(StyleToRegex);
1493
1191
  const schemaOption$1 = {
1494
1192
  oneOf: [{ $ref: '#/definitions/asString' }, { $ref: '#/definitions/asObject' }],
1495
1193
  };
1496
- const rule$8 = {
1194
+ const rule$4 = {
1497
1195
  meta: {
1498
1196
  type: 'suggestion',
1499
1197
  docs: {
@@ -1706,7 +1404,7 @@ const rule$8 = {
1706
1404
  };
1707
1405
 
1708
1406
  const NO_ANONYMOUS_OPERATIONS = 'NO_ANONYMOUS_OPERATIONS';
1709
- const rule$9 = {
1407
+ const rule$5 = {
1710
1408
  meta: {
1711
1409
  type: 'suggestion',
1712
1410
  docs: {
@@ -1754,7 +1452,7 @@ const rule$9 = {
1754
1452
  };
1755
1453
 
1756
1454
  const ERROR_MESSAGE_ID = 'NO_CASE_INSENSITIVE_ENUM_VALUES_DUPLICATES';
1757
- const rule$a = {
1455
+ const rule$6 = {
1758
1456
  meta: {
1759
1457
  type: 'suggestion',
1760
1458
  docs: {
@@ -1810,7 +1508,7 @@ const rule$a = {
1810
1508
  };
1811
1509
 
1812
1510
  const NO_DEPRECATED = 'NO_DEPRECATED';
1813
- const rule$b = {
1511
+ const rule$7 = {
1814
1512
  meta: {
1815
1513
  type: 'suggestion',
1816
1514
  docs: {
@@ -1861,20 +1559,116 @@ const rule$b = {
1861
1559
  `,
1862
1560
  },
1863
1561
  {
1864
- title: 'Correct',
1562
+ title: 'Correct',
1563
+ code: /* GraphQL */ `
1564
+ # In your schema
1565
+ type User {
1566
+ id: ID!
1567
+ name: String! @deprecated(reason: "old field, please use fullName instead")
1568
+ fullName: String!
1569
+ }
1570
+
1571
+ # Query
1572
+ query user {
1573
+ user {
1574
+ id
1575
+ fullName
1576
+ }
1577
+ }
1578
+ `,
1579
+ },
1580
+ ],
1581
+ },
1582
+ messages: {
1583
+ [NO_DEPRECATED]: `This {{ type }} is marked as deprecated in your GraphQL schema {{ reason }}`,
1584
+ },
1585
+ schema: [],
1586
+ },
1587
+ create(context) {
1588
+ return {
1589
+ EnumValue(node) {
1590
+ requireGraphQLSchemaFromContext('no-deprecated', context);
1591
+ const typeInfo = node.typeInfo();
1592
+ if (typeInfo && typeInfo.enumValue) {
1593
+ if (typeInfo.enumValue.deprecationReason) {
1594
+ const enumValueName = node.value;
1595
+ context.report({
1596
+ loc: getLocation(node.loc, enumValueName),
1597
+ messageId: NO_DEPRECATED,
1598
+ data: {
1599
+ type: 'enum value',
1600
+ reason: typeInfo.enumValue.deprecationReason ? `(reason: ${typeInfo.enumValue.deprecationReason})` : '',
1601
+ },
1602
+ });
1603
+ }
1604
+ }
1605
+ },
1606
+ Field(node) {
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',
1865
1665
  code: /* GraphQL */ `
1866
- # In your schema
1867
- type User {
1868
- id: ID!
1869
- name: String! @deprecated(reason: "old field, please use fullName instead")
1870
- fullName: String!
1871
- }
1872
-
1873
- # Query
1874
- query user {
1875
- user {
1666
+ query (
1667
+ $first: Int!
1668
+ $first: Int! # duplicate variable
1669
+ ) {
1670
+ users(first: $first, skip: 50) {
1876
1671
  id
1877
- fullName
1878
1672
  }
1879
1673
  }
1880
1674
  `,
@@ -1882,43 +1676,47 @@ const rule$b = {
1882
1676
  ],
1883
1677
  },
1884
1678
  messages: {
1885
- [NO_DEPRECATED]: `This {{ type }} is marked as deprecated in your GraphQL schema {{ reason }}`,
1679
+ [NO_DUPLICATE_FIELDS]: `{{ type }} "{{ fieldName }}" defined multiple times`,
1886
1680
  },
1887
1681
  schema: [],
1888
1682
  },
1889
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
+ }
1890
1701
  return {
1891
- EnumValue(node) {
1892
- requireGraphQLSchemaFromContext('no-deprecated', context);
1893
- const typeInfo = node.typeInfo();
1894
- if (typeInfo && typeInfo.enumValue) {
1895
- if (typeInfo.enumValue.deprecationReason) {
1896
- const enumValueName = node.value;
1897
- context.report({
1898
- loc: getLocation(node.loc, enumValueName),
1899
- messageId: NO_DEPRECATED,
1900
- data: {
1901
- type: 'enum value',
1902
- reason: typeInfo.enumValue.deprecationReason ? `(reason: ${typeInfo.enumValue.deprecationReason})` : '',
1903
- },
1904
- });
1905
- }
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);
1906
1706
  }
1907
1707
  },
1908
1708
  Field(node) {
1909
- requireGraphQLSchemaFromContext('no-deprecated', context);
1910
- const typeInfo = node.typeInfo();
1911
- if (typeInfo && typeInfo.fieldDef) {
1912
- if (typeInfo.fieldDef.deprecationReason) {
1913
- const fieldName = node.name.value;
1914
- context.report({
1915
- loc: getLocation(node.loc, fieldName),
1916
- messageId: NO_DEPRECATED,
1917
- data: {
1918
- type: 'field',
1919
- reason: typeInfo.fieldDef.deprecationReason ? `(reason: ${typeInfo.fieldDef.deprecationReason})` : '',
1920
- },
1921
- });
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);
1922
1720
  }
1923
1721
  }
1924
1722
  },
@@ -1927,7 +1725,7 @@ const rule$b = {
1927
1725
  };
1928
1726
 
1929
1727
  const HASHTAG_COMMENT = 'HASHTAG_COMMENT';
1930
- const rule$c = {
1728
+ const rule$9 = {
1931
1729
  meta: {
1932
1730
  messages: {
1933
1731
  [HASHTAG_COMMENT]: 'Using hashtag (#) for adding GraphQL descriptions is not allowed. Prefer using """ for multiline, or " for a single line description.',
@@ -1999,75 +1797,8 @@ const rule$c = {
1999
1797
  },
2000
1798
  };
2001
1799
 
2002
- const NO_OPERATION_NAME_SUFFIX = 'NO_OPERATION_NAME_SUFFIX';
2003
- const rule$d = {
2004
- meta: {
2005
- fixable: 'code',
2006
- type: 'suggestion',
2007
- docs: {
2008
- category: 'Stylistic Issues',
2009
- recommended: true,
2010
- description: `Makes sure you are not adding the operation type to the name of the operation.`,
2011
- url: `https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/no-operation-name-suffix.md`,
2012
- examples: [
2013
- {
2014
- title: 'Incorrect',
2015
- code: /* GraphQL */ `
2016
- query userQuery {
2017
- # ...
2018
- }
2019
- `,
2020
- },
2021
- {
2022
- title: 'Correct',
2023
- code: /* GraphQL */ `
2024
- query user {
2025
- # ...
2026
- }
2027
- `,
2028
- },
2029
- ],
2030
- },
2031
- messages: {
2032
- [NO_OPERATION_NAME_SUFFIX]: `Unnecessary "{{ invalidSuffix }}" suffix in your operation name!`,
2033
- },
2034
- schema: [],
2035
- },
2036
- create(context) {
2037
- return {
2038
- 'OperationDefinition, FragmentDefinition'(node) {
2039
- var _a;
2040
- const name = ((_a = node.name) === null || _a === void 0 ? void 0 : _a.value) || '';
2041
- if (name.length > 0) {
2042
- const invalidSuffix = 'operation' in node ? node.operation : 'fragment';
2043
- if (name.toLowerCase().endsWith(invalidSuffix)) {
2044
- const { start, end } = node.name.loc;
2045
- context.report({
2046
- loc: {
2047
- start: {
2048
- column: start.column - 1 + name.length - invalidSuffix.length,
2049
- line: start.line,
2050
- },
2051
- end: {
2052
- column: end.column - 1 + name.length,
2053
- line: end.line,
2054
- },
2055
- },
2056
- data: {
2057
- invalidSuffix,
2058
- },
2059
- fix: fixer => fixer.removeRange([node.name.range[1] - invalidSuffix.length, node.name.range[1]]),
2060
- messageId: NO_OPERATION_NAME_SUFFIX,
2061
- });
2062
- }
2063
- }
2064
- },
2065
- };
2066
- },
2067
- };
2068
-
2069
1800
  const ROOT_TYPES = ['query', 'mutation', 'subscription'];
2070
- const rule$e = {
1801
+ const rule$a = {
2071
1802
  meta: {
2072
1803
  type: 'suggestion',
2073
1804
  docs: {
@@ -2157,9 +1888,121 @@ const rule$e = {
2157
1888
  },
2158
1889
  };
2159
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
+
2160
2003
  const UNREACHABLE_TYPE = 'UNREACHABLE_TYPE';
2161
2004
  const RULE_NAME = 'no-unreachable-types';
2162
- const rule$f = {
2005
+ const rule$d = {
2163
2006
  meta: {
2164
2007
  messages: {
2165
2008
  [UNREACHABLE_TYPE]: `Type "{{ typeName }}" is unreachable`,
@@ -2235,7 +2078,7 @@ const rule$f = {
2235
2078
 
2236
2079
  const UNUSED_FIELD = 'UNUSED_FIELD';
2237
2080
  const RULE_NAME$1 = 'no-unused-fields';
2238
- const rule$g = {
2081
+ const rule$e = {
2239
2082
  meta: {
2240
2083
  messages: {
2241
2084
  [UNUSED_FIELD]: `Field "{{fieldName}}" is unused`,
@@ -2424,7 +2267,7 @@ const MESSAGE_REQUIRE_DATE = 'MESSAGE_REQUIRE_DATE';
2424
2267
  const MESSAGE_INVALID_FORMAT = 'MESSAGE_INVALID_FORMAT';
2425
2268
  const MESSAGE_INVALID_DATE = 'MESSAGE_INVALID_DATE';
2426
2269
  const MESSAGE_CAN_BE_REMOVED = 'MESSAGE_CAN_BE_REMOVED';
2427
- const rule$h = {
2270
+ const rule$f = {
2428
2271
  meta: {
2429
2272
  type: 'suggestion',
2430
2273
  docs: {
@@ -2527,7 +2370,7 @@ const rule$h = {
2527
2370
  },
2528
2371
  };
2529
2372
 
2530
- const rule$i = {
2373
+ const rule$g = {
2531
2374
  meta: {
2532
2375
  docs: {
2533
2376
  description: `Require all deprecation directives to specify a reason.`,
@@ -2589,7 +2432,7 @@ const ALLOWED_KINDS$1 = [
2589
2432
  Kind.ENUM_VALUE_DEFINITION,
2590
2433
  Kind.DIRECTIVE_DEFINITION,
2591
2434
  ];
2592
- const rule$j = {
2435
+ const rule$h = {
2593
2436
  meta: {
2594
2437
  docs: {
2595
2438
  category: 'Best Practices',
@@ -2688,7 +2531,7 @@ const rule$j = {
2688
2531
  };
2689
2532
 
2690
2533
  const RULE_NAME$2 = 'require-field-of-type-query-in-mutation-result';
2691
- const rule$k = {
2534
+ const rule$i = {
2692
2535
  meta: {
2693
2536
  type: 'suggestion',
2694
2537
  docs: {
@@ -2856,7 +2699,7 @@ const convertNode = (typeInfo) => (node, key, parent) => {
2856
2699
 
2857
2700
  const REQUIRE_ID_WHEN_AVAILABLE = 'REQUIRE_ID_WHEN_AVAILABLE';
2858
2701
  const DEFAULT_ID_FIELD_NAME = 'id';
2859
- const rule$l = {
2702
+ const rule$j = {
2860
2703
  meta: {
2861
2704
  type: 'suggestion',
2862
2705
  docs: {
@@ -2992,7 +2835,7 @@ const rule$l = {
2992
2835
  },
2993
2836
  };
2994
2837
 
2995
- const rule$m = {
2838
+ const rule$k = {
2996
2839
  meta: {
2997
2840
  docs: {
2998
2841
  category: 'Best Practices',
@@ -3114,7 +2957,7 @@ const shouldIgnoreNode = ({ node, exceptions }) => {
3114
2957
  }
3115
2958
  return false;
3116
2959
  };
3117
- const rule$n = {
2960
+ const rule$l = {
3118
2961
  meta: {
3119
2962
  type: 'suggestion',
3120
2963
  docs: {
@@ -3291,7 +3134,7 @@ const checkNode = (context, node, ruleName, messageId) => {
3291
3134
  });
3292
3135
  }
3293
3136
  };
3294
- const rule$o = {
3137
+ const rule$m = {
3295
3138
  meta: {
3296
3139
  type: 'suggestion',
3297
3140
  docs: {
@@ -3350,7 +3193,7 @@ const rule$o = {
3350
3193
 
3351
3194
  const RULE_NAME$4 = 'unique-operation-name';
3352
3195
  const UNIQUE_OPERATION_NAME = 'UNIQUE_OPERATION_NAME';
3353
- const rule$p = {
3196
+ const rule$n = {
3354
3197
  meta: {
3355
3198
  type: 'suggestion',
3356
3199
  docs: {
@@ -3417,31 +3260,29 @@ const rule$p = {
3417
3260
  const rules = {
3418
3261
  ...GRAPHQL_JS_VALIDATIONS,
3419
3262
  alphabetize: rule,
3420
- 'avoid-duplicate-fields': rule$1,
3421
- 'avoid-operation-name-prefix': rule$2,
3422
- 'avoid-scalar-result-type-on-mutation': rule$3,
3423
- 'avoid-typename-prefix': rule$4,
3424
- 'description-style': rule$5,
3425
- 'input-name': rule$6,
3426
- 'match-document-filename': rule$7,
3427
- 'naming-convention': rule$8,
3428
- 'no-anonymous-operations': rule$9,
3429
- 'no-case-insensitive-enum-values-duplicates': rule$a,
3430
- 'no-deprecated': rule$b,
3431
- 'no-hashtag-description': rule$c,
3432
- 'no-operation-name-suffix': rule$d,
3433
- 'no-root-type': rule$e,
3434
- 'no-unreachable-types': rule$f,
3435
- 'no-unused-fields': rule$g,
3436
- 'require-deprecation-date': rule$h,
3437
- 'require-deprecation-reason': rule$i,
3438
- 'require-description': rule$j,
3439
- 'require-field-of-type-query-in-mutation-result': rule$k,
3440
- 'require-id-when-available': rule$l,
3441
- 'selection-set-depth': rule$m,
3442
- 'strict-id-in-types': rule$n,
3443
- 'unique-fragment-name': rule$o,
3444
- 'unique-operation-name': rule$p,
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,
3445
3286
  };
3446
3287
 
3447
3288
  const RELEVANT_KEYWORDS = ['gql', 'graphql', '/* GraphQL */'];