@graphql-eslint/eslint-plugin 3.14.0-alpha-20221221142641-4e1a924 → 3.14.0-alpha-20221222012949-5caade4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (231) hide show
  1. package/README.md +309 -0
  2. package/cjs/cache.js +30 -0
  3. package/cjs/configs/base.js +7 -0
  4. package/cjs/configs/index.js +16 -0
  5. package/cjs/configs/operations-all.js +31 -0
  6. package/cjs/configs/operations-recommended.js +56 -0
  7. package/cjs/configs/relay.js +12 -0
  8. package/cjs/configs/schema-all.js +23 -0
  9. package/cjs/configs/schema-recommended.js +52 -0
  10. package/cjs/documents.js +149 -0
  11. package/cjs/estree-converter/converter.js +62 -0
  12. package/cjs/estree-converter/index.js +6 -0
  13. package/cjs/estree-converter/types.js +2 -0
  14. package/cjs/estree-converter/utils.js +109 -0
  15. package/cjs/graphql-config.js +55 -0
  16. package/cjs/index.js +17 -0
  17. package/cjs/parser.js +61 -0
  18. package/cjs/processor.js +78 -0
  19. package/cjs/rules/alphabetize.js +348 -0
  20. package/cjs/rules/description-style.js +78 -0
  21. package/cjs/rules/graphql-js-validation.js +499 -0
  22. package/cjs/rules/index.js +68 -0
  23. package/cjs/rules/input-name.js +136 -0
  24. package/cjs/rules/lone-executable-definition.js +88 -0
  25. package/cjs/rules/match-document-filename.js +235 -0
  26. package/cjs/rules/naming-convention.js +310 -0
  27. package/cjs/rules/no-anonymous-operations.js +67 -0
  28. package/cjs/rules/no-case-insensitive-enum-values-duplicates.js +61 -0
  29. package/cjs/rules/no-deprecated.js +124 -0
  30. package/cjs/rules/no-duplicate-fields.js +112 -0
  31. package/cjs/rules/no-hashtag-description.js +89 -0
  32. package/cjs/rules/no-root-type.js +86 -0
  33. package/cjs/rules/no-scalar-result-type-on-mutation.js +66 -0
  34. package/cjs/rules/no-typename-prefix.js +65 -0
  35. package/cjs/rules/no-unreachable-types.js +158 -0
  36. package/cjs/rules/no-unused-fields.js +130 -0
  37. package/cjs/rules/relay-arguments.js +121 -0
  38. package/cjs/rules/relay-connection-types.js +107 -0
  39. package/cjs/rules/relay-edge-types.js +189 -0
  40. package/cjs/rules/relay-page-info.js +100 -0
  41. package/cjs/rules/require-deprecation-date.js +123 -0
  42. package/cjs/rules/require-deprecation-reason.js +56 -0
  43. package/cjs/rules/require-description.js +193 -0
  44. package/cjs/rules/require-field-of-type-query-in-mutation-result.js +72 -0
  45. package/cjs/rules/require-id-when-available.js +199 -0
  46. package/cjs/rules/selection-set-depth.js +135 -0
  47. package/cjs/rules/strict-id-in-types.js +162 -0
  48. package/cjs/rules/unique-fragment-name.js +90 -0
  49. package/cjs/rules/unique-operation-name.js +65 -0
  50. package/cjs/schema.js +42 -0
  51. package/cjs/testkit.js +183 -0
  52. package/cjs/types.js +2 -0
  53. package/cjs/utils.js +96 -0
  54. package/docs/README.md +82 -0
  55. package/docs/custom-rules.md +184 -0
  56. package/docs/deprecated-rules.md +24 -0
  57. package/docs/parser-options.md +95 -0
  58. package/docs/parser.md +67 -0
  59. package/docs/rules/alphabetize.md +194 -0
  60. package/docs/rules/description-style.md +57 -0
  61. package/docs/rules/executable-definitions.md +20 -0
  62. package/docs/rules/fields-on-correct-type.md +23 -0
  63. package/docs/rules/fragments-on-composite-type.md +20 -0
  64. package/docs/rules/input-name.md +80 -0
  65. package/docs/rules/known-argument-names.md +23 -0
  66. package/docs/rules/known-directives.md +48 -0
  67. package/docs/rules/known-fragment-names.md +72 -0
  68. package/docs/rules/known-type-names.md +24 -0
  69. package/docs/rules/lone-anonymous-operation.md +20 -0
  70. package/docs/rules/lone-executable-definition.md +59 -0
  71. package/docs/rules/lone-schema-definition.md +19 -0
  72. package/docs/rules/match-document-filename.md +181 -0
  73. package/docs/rules/naming-convention.md +320 -0
  74. package/docs/rules/no-anonymous-operations.md +43 -0
  75. package/docs/rules/no-case-insensitive-enum-values-duplicates.md +46 -0
  76. package/docs/rules/no-deprecated.md +88 -0
  77. package/docs/rules/no-duplicate-fields.md +69 -0
  78. package/docs/rules/no-fragment-cycles.md +19 -0
  79. package/docs/rules/no-hashtag-description.md +62 -0
  80. package/docs/rules/no-root-type.md +55 -0
  81. package/docs/rules/no-scalar-result-type-on-mutation.md +39 -0
  82. package/docs/rules/no-typename-prefix.md +42 -0
  83. package/docs/rules/no-undefined-variables.md +20 -0
  84. package/docs/rules/no-unreachable-types.md +52 -0
  85. package/docs/rules/no-unused-fields.md +64 -0
  86. package/docs/rules/no-unused-fragments.md +20 -0
  87. package/docs/rules/no-unused-variables.md +20 -0
  88. package/docs/rules/one-field-subscriptions.md +19 -0
  89. package/docs/rules/overlapping-fields-can-be-merged.md +20 -0
  90. package/docs/rules/possible-fragment-spread.md +21 -0
  91. package/docs/rules/possible-type-extension.md +19 -0
  92. package/docs/rules/provided-required-arguments.md +21 -0
  93. package/docs/rules/relay-arguments.md +59 -0
  94. package/docs/rules/relay-connection-types.md +43 -0
  95. package/docs/rules/relay-edge-types.md +60 -0
  96. package/docs/rules/relay-page-info.md +34 -0
  97. package/docs/rules/require-deprecation-date.md +59 -0
  98. package/docs/rules/require-deprecation-reason.md +49 -0
  99. package/docs/rules/require-description.md +147 -0
  100. package/docs/rules/require-field-of-type-query-in-mutation-result.md +50 -0
  101. package/docs/rules/require-id-when-available.md +91 -0
  102. package/docs/rules/scalar-leafs.md +23 -0
  103. package/docs/rules/selection-set-depth.md +86 -0
  104. package/docs/rules/strict-id-in-types.md +129 -0
  105. package/docs/rules/unique-argument-names.md +19 -0
  106. package/docs/rules/unique-directive-names-per-location.md +21 -0
  107. package/docs/rules/unique-directive-names.md +19 -0
  108. package/docs/rules/unique-enum-value-names.md +16 -0
  109. package/docs/rules/unique-field-definition-names.md +19 -0
  110. package/docs/rules/unique-fragment-name.md +52 -0
  111. package/docs/rules/unique-input-field-names.md +19 -0
  112. package/docs/rules/unique-operation-name.md +56 -0
  113. package/docs/rules/unique-operation-types.md +19 -0
  114. package/docs/rules/unique-type-names.md +19 -0
  115. package/docs/rules/unique-variable-names.md +19 -0
  116. package/docs/rules/value-literals-of-correct-type.md +22 -0
  117. package/docs/rules/variables-are-input-types.md +20 -0
  118. package/docs/rules/variables-in-allowed-position.md +19 -0
  119. package/package.json +8 -11
  120. package/{cache.d.ts → typings/cache.d.cts} +0 -0
  121. package/typings/cache.d.ts +10 -0
  122. package/typings/configs/base.d.cts +5 -0
  123. package/typings/configs/base.d.ts +5 -0
  124. package/typings/configs/index.d.cts +139 -0
  125. package/typings/configs/index.d.ts +139 -0
  126. package/typings/configs/operations-all.d.cts +20 -0
  127. package/typings/configs/operations-all.d.ts +20 -0
  128. package/typings/configs/operations-recommended.d.cts +50 -0
  129. package/typings/configs/operations-recommended.d.ts +50 -0
  130. package/typings/configs/relay.d.cts +10 -0
  131. package/typings/configs/relay.d.ts +10 -0
  132. package/typings/configs/schema-all.d.cts +15 -0
  133. package/typings/configs/schema-all.d.ts +15 -0
  134. package/typings/configs/schema-recommended.d.cts +47 -0
  135. package/typings/configs/schema-recommended.d.ts +47 -0
  136. package/{documents.d.ts → typings/documents.d.cts} +0 -0
  137. package/typings/documents.d.ts +21 -0
  138. package/{estree-converter/converter.d.ts → typings/estree-converter/converter.d.cts} +0 -0
  139. package/typings/estree-converter/converter.d.ts +3 -0
  140. package/{estree-converter/index.d.ts → typings/estree-converter/index.d.cts} +0 -0
  141. package/typings/estree-converter/index.d.ts +3 -0
  142. package/{estree-converter/types.d.ts → typings/estree-converter/types.d.cts} +0 -0
  143. package/typings/estree-converter/types.d.ts +40 -0
  144. package/{estree-converter/utils.d.ts → typings/estree-converter/utils.d.cts} +0 -0
  145. package/typings/estree-converter/utils.d.ts +13 -0
  146. package/{graphql-config.d.ts → typings/graphql-config.d.cts} +0 -0
  147. package/typings/graphql-config.d.ts +4 -0
  148. package/typings/index.d.cts +9 -0
  149. package/{index.d.ts → typings/index.d.ts} +1 -5
  150. package/{parser.d.ts → typings/parser.d.cts} +0 -0
  151. package/typings/parser.d.ts +2 -0
  152. package/{processor.d.ts → typings/processor.d.cts} +0 -0
  153. package/typings/processor.d.ts +6 -0
  154. package/{rules/alphabetize.d.ts → typings/rules/alphabetize.d.cts} +0 -0
  155. package/typings/rules/alphabetize.d.ts +76 -0
  156. package/{rules/description-style.d.ts → typings/rules/description-style.d.cts} +0 -0
  157. package/typings/rules/description-style.d.ts +20 -0
  158. package/{rules/graphql-js-validation.d.ts → typings/rules/graphql-js-validation.d.cts} +0 -0
  159. package/typings/rules/graphql-js-validation.d.ts +2 -0
  160. package/{rules/index.d.ts → typings/rules/index.d.cts} +0 -0
  161. package/typings/rules/index.d.ts +104 -0
  162. package/{rules/input-name.d.ts → typings/rules/input-name.d.cts} +0 -0
  163. package/typings/rules/input-name.d.ts +35 -0
  164. package/{rules/lone-executable-definition.d.ts → typings/rules/lone-executable-definition.d.cts} +0 -0
  165. package/typings/rules/lone-executable-definition.d.ts +26 -0
  166. package/{rules/match-document-filename.d.ts → typings/rules/match-document-filename.d.cts} +0 -0
  167. package/typings/rules/match-document-filename.d.ts +72 -0
  168. package/{rules/naming-convention.d.ts → typings/rules/naming-convention.d.cts} +0 -0
  169. package/typings/rules/naming-convention.d.ts +83 -0
  170. package/{rules/no-anonymous-operations.d.ts → typings/rules/no-anonymous-operations.d.cts} +0 -0
  171. package/{rules/no-case-insensitive-enum-values-duplicates.d.ts → typings/rules/no-anonymous-operations.d.ts} +0 -0
  172. package/{rules/no-hashtag-description.d.ts → typings/rules/no-case-insensitive-enum-values-duplicates.d.cts} +0 -0
  173. package/{rules/no-scalar-result-type-on-mutation.d.ts → typings/rules/no-case-insensitive-enum-values-duplicates.d.ts} +0 -0
  174. package/{rules/no-deprecated.d.ts → typings/rules/no-deprecated.d.cts} +0 -0
  175. package/typings/rules/no-deprecated.d.ts +2 -0
  176. package/{rules/no-duplicate-fields.d.ts → typings/rules/no-duplicate-fields.d.cts} +0 -0
  177. package/{rules/relay-page-info.d.ts → typings/rules/no-duplicate-fields.d.ts} +0 -0
  178. package/{rules/no-typename-prefix.d.ts → typings/rules/no-hashtag-description.d.cts} +0 -0
  179. package/{rules/no-unreachable-types.d.ts → typings/rules/no-hashtag-description.d.ts} +0 -0
  180. package/{rules/no-root-type.d.ts → typings/rules/no-root-type.d.cts} +0 -0
  181. package/typings/rules/no-root-type.d.ts +25 -0
  182. package/{rules/no-unused-fields.d.ts → typings/rules/no-scalar-result-type-on-mutation.d.cts} +0 -0
  183. package/{rules/unique-operation-name.d.ts → typings/rules/no-scalar-result-type-on-mutation.d.ts} +0 -0
  184. package/typings/rules/no-typename-prefix.d.cts +2 -0
  185. package/typings/rules/no-typename-prefix.d.ts +2 -0
  186. package/typings/rules/no-unreachable-types.d.cts +2 -0
  187. package/typings/rules/no-unreachable-types.d.ts +2 -0
  188. package/typings/rules/no-unused-fields.d.cts +2 -0
  189. package/typings/rules/no-unused-fields.d.ts +2 -0
  190. package/{rules/relay-arguments.d.ts → typings/rules/relay-arguments.d.cts} +0 -0
  191. package/typings/rules/relay-arguments.d.ts +21 -0
  192. package/{rules/relay-connection-types.d.ts → typings/rules/relay-connection-types.d.cts} +0 -0
  193. package/typings/rules/relay-connection-types.d.ts +4 -0
  194. package/{rules/relay-edge-types.d.ts → typings/rules/relay-edge-types.d.cts} +0 -0
  195. package/typings/rules/relay-edge-types.d.ts +31 -0
  196. package/{rules/require-deprecation-reason.d.ts → typings/rules/relay-page-info.d.cts} +0 -0
  197. package/{rules/require-field-of-type-query-in-mutation-result.d.ts → typings/rules/relay-page-info.d.ts} +0 -0
  198. package/{rules/require-deprecation-date.d.ts → typings/rules/require-deprecation-date.d.cts} +0 -0
  199. package/typings/rules/require-deprecation-date.d.ts +18 -0
  200. package/typings/rules/require-deprecation-reason.d.cts +2 -0
  201. package/typings/rules/require-deprecation-reason.d.ts +2 -0
  202. package/{rules/require-description.d.ts → typings/rules/require-description.d.cts} +0 -0
  203. package/typings/rules/require-description.d.ts +14 -0
  204. package/typings/rules/require-field-of-type-query-in-mutation-result.d.cts +2 -0
  205. package/typings/rules/require-field-of-type-query-in-mutation-result.d.ts +2 -0
  206. package/{rules/require-id-when-available.d.ts → typings/rules/require-id-when-available.d.cts} +0 -0
  207. package/typings/rules/require-id-when-available.d.ts +36 -0
  208. package/{rules/selection-set-depth.d.ts → typings/rules/selection-set-depth.d.cts} +0 -0
  209. package/typings/rules/selection-set-depth.d.ts +28 -0
  210. package/{rules/strict-id-in-types.d.ts → typings/rules/strict-id-in-types.d.cts} +0 -0
  211. package/typings/rules/strict-id-in-types.d.ts +57 -0
  212. package/{rules/unique-fragment-name.d.ts → typings/rules/unique-fragment-name.d.cts} +0 -0
  213. package/typings/rules/unique-fragment-name.d.ts +5 -0
  214. package/typings/rules/unique-operation-name.d.cts +2 -0
  215. package/typings/rules/unique-operation-name.d.ts +2 -0
  216. package/{schema.d.ts → typings/schema.d.cts} +0 -0
  217. package/typings/schema.d.ts +3 -0
  218. package/{testkit.d.ts → typings/testkit.d.cts} +0 -0
  219. package/typings/testkit.d.ts +27 -0
  220. package/{types.d.ts → typings/types.d.cts} +0 -0
  221. package/typings/types.d.ts +81 -0
  222. package/{utils.d.ts → typings/utils.d.cts} +0 -0
  223. package/typings/utils.d.ts +34 -0
  224. package/configs/base.json +0 -4
  225. package/configs/operations-all.json +0 -25
  226. package/configs/operations-recommended.json +0 -50
  227. package/configs/relay.json +0 -9
  228. package/configs/schema-all.json +0 -17
  229. package/configs/schema-recommended.json +0 -49
  230. package/index.js +0 -4995
  231. package/index.mjs +0 -4983
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rule = void 0;
4
+ const estree_converter_1 = require("../estree-converter");
5
+ exports.rule = {
6
+ meta: {
7
+ docs: {
8
+ description: 'Require all deprecation directives to specify a reason.',
9
+ category: 'Schema',
10
+ url: 'https://github.com/B2o5T/graphql-eslint/blob/master/docs/rules/require-deprecation-reason.md',
11
+ recommended: true,
12
+ examples: [
13
+ {
14
+ title: 'Incorrect',
15
+ code: /* GraphQL */ `
16
+ type MyType {
17
+ name: String @deprecated
18
+ }
19
+ `,
20
+ },
21
+ {
22
+ title: 'Incorrect',
23
+ code: /* GraphQL */ `
24
+ type MyType {
25
+ name: String @deprecated(reason: "")
26
+ }
27
+ `,
28
+ },
29
+ {
30
+ title: 'Correct',
31
+ code: /* GraphQL */ `
32
+ type MyType {
33
+ name: String @deprecated(reason: "no longer relevant, please use fullName field")
34
+ }
35
+ `,
36
+ },
37
+ ],
38
+ },
39
+ type: 'suggestion',
40
+ schema: [],
41
+ },
42
+ create(context) {
43
+ return {
44
+ 'Directive[name.value=deprecated]'(node) {
45
+ const reasonArgument = node.arguments.find(arg => arg.name.value === 'reason');
46
+ const value = reasonArgument && String((0, estree_converter_1.valueFromNode)(reasonArgument.value)).trim();
47
+ if (!value) {
48
+ context.report({
49
+ node: node.name,
50
+ message: 'Directive "@deprecated" must have a reason!',
51
+ });
52
+ }
53
+ },
54
+ };
55
+ },
56
+ };
@@ -0,0 +1,193 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rule = void 0;
4
+ const graphql_1 = require("graphql");
5
+ const utils_1 = require("../utils");
6
+ const utils_2 = require("@graphql-tools/utils");
7
+ const RULE_ID = 'require-description';
8
+ const ALLOWED_KINDS = [
9
+ ...utils_1.TYPES_KINDS,
10
+ graphql_1.Kind.DIRECTIVE_DEFINITION,
11
+ graphql_1.Kind.FIELD_DEFINITION,
12
+ graphql_1.Kind.INPUT_VALUE_DEFINITION,
13
+ graphql_1.Kind.ENUM_VALUE_DEFINITION,
14
+ graphql_1.Kind.OPERATION_DEFINITION,
15
+ ];
16
+ function getNodeName(node) {
17
+ const DisplayNodeNameMap = {
18
+ [graphql_1.Kind.OBJECT_TYPE_DEFINITION]: 'type',
19
+ [graphql_1.Kind.INTERFACE_TYPE_DEFINITION]: 'interface',
20
+ [graphql_1.Kind.ENUM_TYPE_DEFINITION]: 'enum',
21
+ [graphql_1.Kind.SCALAR_TYPE_DEFINITION]: 'scalar',
22
+ [graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION]: 'input',
23
+ [graphql_1.Kind.UNION_TYPE_DEFINITION]: 'union',
24
+ [graphql_1.Kind.DIRECTIVE_DEFINITION]: 'directive',
25
+ };
26
+ switch (node.kind) {
27
+ case graphql_1.Kind.OBJECT_TYPE_DEFINITION:
28
+ case graphql_1.Kind.INTERFACE_TYPE_DEFINITION:
29
+ case graphql_1.Kind.ENUM_TYPE_DEFINITION:
30
+ case graphql_1.Kind.SCALAR_TYPE_DEFINITION:
31
+ case graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION:
32
+ case graphql_1.Kind.UNION_TYPE_DEFINITION:
33
+ return `${DisplayNodeNameMap[node.kind]} ${node.name.value}`;
34
+ case graphql_1.Kind.DIRECTIVE_DEFINITION:
35
+ return `${DisplayNodeNameMap[node.kind]} @${node.name.value}`;
36
+ case graphql_1.Kind.FIELD_DEFINITION:
37
+ case graphql_1.Kind.INPUT_VALUE_DEFINITION:
38
+ case graphql_1.Kind.ENUM_VALUE_DEFINITION:
39
+ return `${node.parent.name.value}.${node.name.value}`;
40
+ case graphql_1.Kind.OPERATION_DEFINITION:
41
+ return node.name ? `${node.operation} ${node.name.value}` : node.operation;
42
+ }
43
+ }
44
+ const schema = {
45
+ type: 'array',
46
+ minItems: 1,
47
+ maxItems: 1,
48
+ items: {
49
+ type: 'object',
50
+ additionalProperties: false,
51
+ minProperties: 1,
52
+ properties: {
53
+ types: {
54
+ type: 'boolean',
55
+ description: `Includes:\n${utils_1.TYPES_KINDS.map(kind => `- \`${kind}\``).join('\n')}`,
56
+ },
57
+ rootField: {
58
+ type: 'boolean',
59
+ description: 'Definitions within `Query`, `Mutation`, and `Subscription` root types.',
60
+ },
61
+ ...Object.fromEntries([...ALLOWED_KINDS].sort().map(kind => {
62
+ let description = `Read more about this kind on [spec.graphql.org](https://spec.graphql.org/October2021/#${kind}).`;
63
+ if (kind === graphql_1.Kind.OPERATION_DEFINITION) {
64
+ description +=
65
+ '\n> You must use only comment syntax `#` and not description syntax `"""` or `"`.';
66
+ }
67
+ return [kind, { type: 'boolean', description }];
68
+ })),
69
+ },
70
+ },
71
+ };
72
+ exports.rule = {
73
+ meta: {
74
+ docs: {
75
+ category: 'Schema',
76
+ description: 'Enforce descriptions in type definitions and operations.',
77
+ url: `https://github.com/B2o5T/graphql-eslint/blob/master/docs/rules/${RULE_ID}.md`,
78
+ examples: [
79
+ {
80
+ title: 'Incorrect',
81
+ usage: [{ types: true, FieldDefinition: true }],
82
+ code: /* GraphQL */ `
83
+ type someTypeName {
84
+ name: String
85
+ }
86
+ `,
87
+ },
88
+ {
89
+ title: 'Correct',
90
+ usage: [{ types: true, FieldDefinition: true }],
91
+ code: /* GraphQL */ `
92
+ """
93
+ Some type description
94
+ """
95
+ type someTypeName {
96
+ """
97
+ Name description
98
+ """
99
+ name: String
100
+ }
101
+ `,
102
+ },
103
+ {
104
+ title: 'Correct',
105
+ usage: [{ OperationDefinition: true }],
106
+ code: /* GraphQL */ `
107
+ # Create a new user
108
+ mutation createUser {
109
+ # ...
110
+ }
111
+ `,
112
+ },
113
+ {
114
+ title: 'Correct',
115
+ usage: [{ rootField: true }],
116
+ code: /* GraphQL */ `
117
+ type Mutation {
118
+ "Create a new user"
119
+ createUser: User
120
+ }
121
+
122
+ type User {
123
+ name: String
124
+ }
125
+ `,
126
+ },
127
+ ],
128
+ configOptions: [
129
+ {
130
+ types: true,
131
+ [graphql_1.Kind.DIRECTIVE_DEFINITION]: true,
132
+ // rootField: true TODO enable in graphql-eslint v4
133
+ },
134
+ ],
135
+ recommended: true,
136
+ },
137
+ type: 'suggestion',
138
+ messages: {
139
+ [RULE_ID]: 'Description is required for `{{ nodeName }}`.',
140
+ },
141
+ schema,
142
+ },
143
+ create(context) {
144
+ const { types, rootField, ...restOptions } = context.options[0] || {};
145
+ const kinds = new Set(types ? utils_1.TYPES_KINDS : []);
146
+ for (const [kind, isEnabled] of Object.entries(restOptions)) {
147
+ if (isEnabled) {
148
+ kinds.add(kind);
149
+ }
150
+ else {
151
+ kinds.delete(kind);
152
+ }
153
+ }
154
+ if (rootField) {
155
+ const schema = (0, utils_1.requireGraphQLSchemaFromContext)(RULE_ID, context);
156
+ const rootTypeNames = (0, utils_2.getRootTypeNames)(schema);
157
+ kinds.add(`:matches(ObjectTypeDefinition, ObjectTypeExtension)[name.value=/^(${[
158
+ ...rootTypeNames,
159
+ ].join(',')})$/] > FieldDefinition`);
160
+ }
161
+ const selector = [...kinds].join(',');
162
+ return {
163
+ [selector](node) {
164
+ var _a;
165
+ let description = '';
166
+ const isOperation = node.kind === graphql_1.Kind.OPERATION_DEFINITION;
167
+ if (isOperation) {
168
+ const rawNode = node.rawNode();
169
+ const { prev, line } = rawNode.loc.startToken;
170
+ if (prev.kind === graphql_1.TokenKind.COMMENT) {
171
+ const value = prev.value.trim();
172
+ const linesBefore = line - prev.line;
173
+ if (!value.startsWith('eslint') && linesBefore === 1) {
174
+ description = value;
175
+ }
176
+ }
177
+ }
178
+ else {
179
+ description = ((_a = node.description) === null || _a === void 0 ? void 0 : _a.value.trim()) || '';
180
+ }
181
+ if (description.length === 0) {
182
+ context.report({
183
+ loc: isOperation ? (0, utils_1.getLocation)(node.loc.start, node.operation) : node.name.loc,
184
+ messageId: RULE_ID,
185
+ data: {
186
+ nodeName: getNodeName(node),
187
+ },
188
+ });
189
+ }
190
+ },
191
+ };
192
+ },
193
+ };
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rule = void 0;
4
+ const graphql_1 = require("graphql");
5
+ const utils_1 = require("../utils");
6
+ const RULE_ID = 'require-field-of-type-query-in-mutation-result';
7
+ exports.rule = {
8
+ meta: {
9
+ type: 'suggestion',
10
+ docs: {
11
+ category: 'Schema',
12
+ description: 'Allow the client in one round-trip not only to call mutation but also to get a wagon of data to update their application.\n> Currently, no errors are reported for result type `union`, `interface` and `scalar`.',
13
+ url: `https://github.com/B2o5T/graphql-eslint/blob/master/docs/rules/${RULE_ID}.md`,
14
+ requiresSchema: true,
15
+ examples: [
16
+ {
17
+ title: 'Incorrect',
18
+ code: /* GraphQL */ `
19
+ type User { ... }
20
+
21
+ type Mutation {
22
+ createUser: User!
23
+ }
24
+ `,
25
+ },
26
+ {
27
+ title: 'Correct',
28
+ code: /* GraphQL */ `
29
+ type User { ... }
30
+
31
+ type Query { ... }
32
+
33
+ type CreateUserPayload {
34
+ user: User!
35
+ query: Query!
36
+ }
37
+
38
+ type Mutation {
39
+ createUser: CreateUserPayload!
40
+ }
41
+ `,
42
+ },
43
+ ],
44
+ },
45
+ schema: [],
46
+ },
47
+ create(context) {
48
+ const schema = (0, utils_1.requireGraphQLSchemaFromContext)(RULE_ID, context);
49
+ const mutationType = schema.getMutationType();
50
+ const queryType = schema.getQueryType();
51
+ if (!mutationType || !queryType) {
52
+ return {};
53
+ }
54
+ const selector = `:matches(ObjectTypeDefinition, ObjectTypeExtension)[name.value=${mutationType.name}] > FieldDefinition > .gqlType Name`;
55
+ return {
56
+ [selector](node) {
57
+ const typeName = node.value;
58
+ const graphQLType = schema.getType(typeName);
59
+ if ((0, graphql_1.isObjectType)(graphQLType)) {
60
+ const { fields } = graphQLType.astNode;
61
+ const hasQueryType = fields.some(field => (0, utils_1.getTypeName)(field) === queryType.name);
62
+ if (!hasQueryType) {
63
+ context.report({
64
+ node,
65
+ message: `Mutation result type "${graphQLType.name}" must contain field of type "${queryType.name}"`,
66
+ });
67
+ }
68
+ }
69
+ },
70
+ };
71
+ },
72
+ };
@@ -0,0 +1,199 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rule = void 0;
4
+ const graphql_1 = require("graphql");
5
+ const utils_1 = require("@graphql-tools/utils");
6
+ const utils_2 = require("../utils");
7
+ const estree_converter_1 = require("../estree-converter");
8
+ const RULE_ID = 'require-id-when-available';
9
+ const DEFAULT_ID_FIELD_NAME = 'id';
10
+ const schema = {
11
+ definitions: {
12
+ asString: {
13
+ type: 'string',
14
+ },
15
+ asArray: utils_2.ARRAY_DEFAULT_OPTIONS,
16
+ },
17
+ type: 'array',
18
+ maxItems: 1,
19
+ items: {
20
+ type: 'object',
21
+ additionalProperties: false,
22
+ properties: {
23
+ fieldName: {
24
+ oneOf: [{ $ref: '#/definitions/asString' }, { $ref: '#/definitions/asArray' }],
25
+ default: DEFAULT_ID_FIELD_NAME,
26
+ },
27
+ },
28
+ },
29
+ };
30
+ exports.rule = {
31
+ meta: {
32
+ type: 'suggestion',
33
+ hasSuggestions: true,
34
+ docs: {
35
+ category: 'Operations',
36
+ description: 'Enforce selecting specific fields when they are available on the GraphQL type.',
37
+ url: `https://github.com/B2o5T/graphql-eslint/blob/master/docs/rules/${RULE_ID}.md`,
38
+ requiresSchema: true,
39
+ requiresSiblings: true,
40
+ examples: [
41
+ {
42
+ title: 'Incorrect',
43
+ code: /* GraphQL */ `
44
+ # In your schema
45
+ type User {
46
+ id: ID!
47
+ name: String!
48
+ }
49
+
50
+ # Query
51
+ query {
52
+ user {
53
+ name
54
+ }
55
+ }
56
+ `,
57
+ },
58
+ {
59
+ title: 'Correct',
60
+ code: /* GraphQL */ `
61
+ # In your schema
62
+ type User {
63
+ id: ID!
64
+ name: String!
65
+ }
66
+
67
+ # Query
68
+ query {
69
+ user {
70
+ id
71
+ name
72
+ }
73
+ }
74
+
75
+ # Selecting \`id\` with an alias is also valid
76
+ query {
77
+ user {
78
+ id: name
79
+ }
80
+ }
81
+ `,
82
+ },
83
+ ],
84
+ recommended: true,
85
+ },
86
+ messages: {
87
+ [RULE_ID]: "Field{{ pluralSuffix }} {{ fieldName }} must be selected when it's available on a type.\nInclude it in your selection set{{ addition }}.",
88
+ },
89
+ schema,
90
+ },
91
+ create(context) {
92
+ const schema = (0, utils_2.requireGraphQLSchemaFromContext)(RULE_ID, context);
93
+ const siblings = (0, utils_2.requireSiblingsOperations)(RULE_ID, context);
94
+ const { fieldName = DEFAULT_ID_FIELD_NAME } = context.options[0] || {};
95
+ const idNames = (0, utils_1.asArray)(fieldName);
96
+ // Check selections only in OperationDefinition,
97
+ // skip selections of OperationDefinition and InlineFragment
98
+ const selector = 'OperationDefinition SelectionSet[parent.kind!=/(^OperationDefinition|InlineFragment)$/]';
99
+ const typeInfo = new graphql_1.TypeInfo(schema);
100
+ function checkFragments(node) {
101
+ for (const selection of node.selections) {
102
+ if (selection.kind !== graphql_1.Kind.FRAGMENT_SPREAD) {
103
+ continue;
104
+ }
105
+ const [foundSpread] = siblings.getFragment(selection.name.value);
106
+ if (!foundSpread) {
107
+ continue;
108
+ }
109
+ const checkedFragmentSpreads = new Set();
110
+ const visitor = (0, graphql_1.visitWithTypeInfo)(typeInfo, {
111
+ SelectionSet(node, key, parent) {
112
+ if (parent.kind === graphql_1.Kind.FRAGMENT_DEFINITION) {
113
+ checkedFragmentSpreads.add(parent.name.value);
114
+ }
115
+ else if (parent.kind !== graphql_1.Kind.INLINE_FRAGMENT) {
116
+ checkSelections(node, typeInfo.getType(), selection.loc.start, parent, checkedFragmentSpreads);
117
+ }
118
+ },
119
+ });
120
+ (0, graphql_1.visit)(foundSpread.document, visitor);
121
+ }
122
+ }
123
+ function checkSelections(node, type,
124
+ // Fragment can be placed in separate file
125
+ // Provide actual fragment spread location instead of location in fragment
126
+ loc,
127
+ // Can't access to node.parent in GraphQL AST.Node, so pass as argument
128
+ parent, checkedFragmentSpreads = new Set()) {
129
+ const rawType = (0, estree_converter_1.getBaseType)(type);
130
+ const isObjectType = rawType instanceof graphql_1.GraphQLObjectType;
131
+ const isInterfaceType = rawType instanceof graphql_1.GraphQLInterfaceType;
132
+ if (!isObjectType && !isInterfaceType) {
133
+ return;
134
+ }
135
+ const fields = rawType.getFields();
136
+ const hasIdFieldInType = idNames.some(name => fields[name]);
137
+ if (!hasIdFieldInType) {
138
+ return;
139
+ }
140
+ function hasIdField({ selections }) {
141
+ return selections.some(selection => {
142
+ if (selection.kind === graphql_1.Kind.FIELD) {
143
+ if (selection.alias && idNames.includes(selection.alias.value)) {
144
+ return true;
145
+ }
146
+ return idNames.includes(selection.name.value);
147
+ }
148
+ if (selection.kind === graphql_1.Kind.INLINE_FRAGMENT) {
149
+ return hasIdField(selection.selectionSet);
150
+ }
151
+ if (selection.kind === graphql_1.Kind.FRAGMENT_SPREAD) {
152
+ const [foundSpread] = siblings.getFragment(selection.name.value);
153
+ if (foundSpread) {
154
+ const fragmentSpread = foundSpread.document;
155
+ checkedFragmentSpreads.add(fragmentSpread.name.value);
156
+ return hasIdField(fragmentSpread.selectionSet);
157
+ }
158
+ }
159
+ return false;
160
+ });
161
+ }
162
+ const hasId = hasIdField(node);
163
+ checkFragments(node);
164
+ if (hasId) {
165
+ return;
166
+ }
167
+ const pluralSuffix = idNames.length > 1 ? 's' : '';
168
+ const fieldName = (0, utils_2.englishJoinWords)(idNames.map(name => `\`${(parent.alias || parent.name).value}.${name}\``));
169
+ const addition = checkedFragmentSpreads.size === 0
170
+ ? ''
171
+ : ` or add to used fragment${checkedFragmentSpreads.size > 1 ? 's' : ''} ${(0, utils_2.englishJoinWords)([...checkedFragmentSpreads].map(name => `\`${name}\``))}`;
172
+ const problem = {
173
+ loc,
174
+ messageId: RULE_ID,
175
+ data: {
176
+ pluralSuffix,
177
+ fieldName,
178
+ addition,
179
+ },
180
+ };
181
+ // Don't provide suggestions for selections in fragments as fragment can be in a separate file
182
+ if ('type' in node) {
183
+ problem.suggest = idNames.map(idName => ({
184
+ desc: `Add \`${idName}\` selection`,
185
+ fix: fixer => fixer.insertTextBefore(node.selections[0], `${idName} `),
186
+ }));
187
+ }
188
+ context.report(problem);
189
+ }
190
+ return {
191
+ [selector](node) {
192
+ const typeInfo = node.typeInfo();
193
+ if (typeInfo.gqlType) {
194
+ checkSelections(node, typeInfo.gqlType, node.loc.start, node.parent);
195
+ }
196
+ },
197
+ };
198
+ },
199
+ };
@@ -0,0 +1,135 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rule = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const graphql_depth_limit_1 = tslib_1.__importDefault(require("graphql-depth-limit"));
6
+ const graphql_1 = require("graphql");
7
+ const utils_1 = require("../utils");
8
+ const RULE_ID = 'selection-set-depth';
9
+ const schema = {
10
+ type: 'array',
11
+ minItems: 1,
12
+ maxItems: 1,
13
+ items: {
14
+ type: 'object',
15
+ additionalProperties: false,
16
+ required: ['maxDepth'],
17
+ properties: {
18
+ maxDepth: {
19
+ type: 'number',
20
+ },
21
+ ignore: utils_1.ARRAY_DEFAULT_OPTIONS,
22
+ },
23
+ },
24
+ };
25
+ exports.rule = {
26
+ meta: {
27
+ type: 'suggestion',
28
+ hasSuggestions: true,
29
+ docs: {
30
+ category: 'Operations',
31
+ description: 'Limit the complexity of the GraphQL operations solely by their depth. Based on [graphql-depth-limit](https://npmjs.com/package/graphql-depth-limit).',
32
+ url: `https://github.com/B2o5T/graphql-eslint/blob/master/docs/rules/${RULE_ID}.md`,
33
+ requiresSiblings: true,
34
+ examples: [
35
+ {
36
+ title: 'Incorrect',
37
+ usage: [{ maxDepth: 1 }],
38
+ code: `
39
+ query deep2 {
40
+ viewer { # Level 0
41
+ albums { # Level 1
42
+ title # Level 2
43
+ }
44
+ }
45
+ }
46
+ `,
47
+ },
48
+ {
49
+ title: 'Correct',
50
+ usage: [{ maxDepth: 4 }],
51
+ code: `
52
+ query deep2 {
53
+ viewer { # Level 0
54
+ albums { # Level 1
55
+ title # Level 2
56
+ }
57
+ }
58
+ }
59
+ `,
60
+ },
61
+ {
62
+ title: 'Correct (ignored field)',
63
+ usage: [{ maxDepth: 1, ignore: ['albums'] }],
64
+ code: `
65
+ query deep2 {
66
+ viewer { # Level 0
67
+ albums { # Level 1
68
+ title # Level 2
69
+ }
70
+ }
71
+ }
72
+ `,
73
+ },
74
+ ],
75
+ recommended: true,
76
+ configOptions: [{ maxDepth: 7 }],
77
+ },
78
+ schema,
79
+ },
80
+ create(context) {
81
+ let siblings = null;
82
+ try {
83
+ siblings = (0, utils_1.requireSiblingsOperations)(RULE_ID, context);
84
+ }
85
+ catch (_a) {
86
+ utils_1.logger.warn(`Rule "${RULE_ID}" works best with siblings operations loaded. For more info: https://bit.ly/graphql-eslint-operations`);
87
+ }
88
+ const { maxDepth, ignore = [] } = context.options[0];
89
+ const checkFn = (0, graphql_depth_limit_1.default)(maxDepth, { ignore });
90
+ return {
91
+ 'OperationDefinition, FragmentDefinition'(node) {
92
+ try {
93
+ const rawNode = node.rawNode();
94
+ const fragmentsInUse = siblings ? siblings.getFragmentsInUse(rawNode) : [];
95
+ const document = {
96
+ kind: graphql_1.Kind.DOCUMENT,
97
+ definitions: [rawNode, ...fragmentsInUse],
98
+ };
99
+ checkFn({
100
+ getDocument: () => document,
101
+ reportError(error) {
102
+ const { line, column } = error.locations[0];
103
+ const ancestors = context.getAncestors();
104
+ const token = ancestors[0].tokens.find(token => token.loc.start.line === line && token.loc.start.column === column - 1);
105
+ context.report({
106
+ loc: {
107
+ line,
108
+ column: column - 1,
109
+ },
110
+ message: error.message,
111
+ // Don't provide suggestions for fragment that can be in a separate file
112
+ ...(token && {
113
+ suggest: [
114
+ {
115
+ desc: 'Remove selections',
116
+ fix(fixer) {
117
+ const sourceCode = context.getSourceCode();
118
+ const foundNode = sourceCode.getNodeByRangeIndex(token.range[0]);
119
+ const parentNode = foundNode.parent.parent;
120
+ return fixer.remove(foundNode.kind === 'Name' ? parentNode.parent : parentNode);
121
+ },
122
+ },
123
+ ],
124
+ }),
125
+ });
126
+ },
127
+ });
128
+ }
129
+ catch (e) {
130
+ utils_1.logger.warn(`Rule "${RULE_ID}" check failed due to a missing siblings operations. For more info: https://bit.ly/graphql-eslint-operations`, e);
131
+ }
132
+ },
133
+ };
134
+ },
135
+ };