@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,162 @@
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 = 'strict-id-in-types';
7
+ const schema = {
8
+ type: 'array',
9
+ maxItems: 1,
10
+ items: {
11
+ type: 'object',
12
+ additionalProperties: false,
13
+ properties: {
14
+ acceptedIdNames: {
15
+ ...utils_1.ARRAY_DEFAULT_OPTIONS,
16
+ default: ['id'],
17
+ },
18
+ acceptedIdTypes: {
19
+ ...utils_1.ARRAY_DEFAULT_OPTIONS,
20
+ default: ['ID'],
21
+ },
22
+ exceptions: {
23
+ type: 'object',
24
+ additionalProperties: false,
25
+ properties: {
26
+ types: {
27
+ ...utils_1.ARRAY_DEFAULT_OPTIONS,
28
+ description: 'This is used to exclude types with names that match one of the specified values.',
29
+ },
30
+ suffixes: {
31
+ ...utils_1.ARRAY_DEFAULT_OPTIONS,
32
+ description: 'This is used to exclude types with names with suffixes that match one of the specified values.',
33
+ },
34
+ },
35
+ },
36
+ },
37
+ },
38
+ };
39
+ exports.rule = {
40
+ meta: {
41
+ type: 'suggestion',
42
+ docs: {
43
+ description: 'Requires output types to have one unique identifier unless they do not have a logical one. Exceptions can be used to ignore output types that do not have unique identifiers.',
44
+ category: 'Schema',
45
+ recommended: true,
46
+ url: `https://github.com/B2o5T/graphql-eslint/blob/master/docs/rules/${RULE_ID}.md`,
47
+ requiresSchema: true,
48
+ examples: [
49
+ {
50
+ title: 'Incorrect',
51
+ usage: [
52
+ {
53
+ acceptedIdNames: ['id', '_id'],
54
+ acceptedIdTypes: ['ID'],
55
+ exceptions: { suffixes: ['Payload'] },
56
+ },
57
+ ],
58
+ code: /* GraphQL */ `
59
+ # Incorrect field name
60
+ type InvalidFieldName {
61
+ key: ID!
62
+ }
63
+
64
+ # Incorrect field type
65
+ type InvalidFieldType {
66
+ id: String!
67
+ }
68
+
69
+ # Incorrect exception suffix
70
+ type InvalidSuffixResult {
71
+ data: String!
72
+ }
73
+
74
+ # Too many unique identifiers. Must only contain one.
75
+ type InvalidFieldName {
76
+ id: ID!
77
+ _id: ID!
78
+ }
79
+ `,
80
+ },
81
+ {
82
+ title: 'Correct',
83
+ usage: [
84
+ {
85
+ acceptedIdNames: ['id', '_id'],
86
+ acceptedIdTypes: ['ID'],
87
+ exceptions: { types: ['Error'], suffixes: ['Payload'] },
88
+ },
89
+ ],
90
+ code: /* GraphQL */ `
91
+ type User {
92
+ id: ID!
93
+ }
94
+
95
+ type Post {
96
+ _id: ID!
97
+ }
98
+
99
+ type CreateUserPayload {
100
+ data: String!
101
+ }
102
+
103
+ type Error {
104
+ message: String!
105
+ }
106
+ `,
107
+ },
108
+ ],
109
+ },
110
+ schema,
111
+ },
112
+ create(context) {
113
+ const options = {
114
+ acceptedIdNames: ['id'],
115
+ acceptedIdTypes: ['ID'],
116
+ exceptions: {},
117
+ ...context.options[0],
118
+ };
119
+ const schema = (0, utils_1.requireGraphQLSchemaFromContext)(RULE_ID, context);
120
+ const rootTypeNames = [
121
+ schema.getQueryType(),
122
+ schema.getMutationType(),
123
+ schema.getSubscriptionType(),
124
+ ]
125
+ .filter(Boolean)
126
+ .map(type => type.name);
127
+ const selector = `ObjectTypeDefinition[name.value!=/^(${rootTypeNames.join('|')})$/]`;
128
+ return {
129
+ [selector](node) {
130
+ var _a, _b;
131
+ const typeName = node.name.value;
132
+ const shouldIgnoreNode = ((_a = options.exceptions.types) === null || _a === void 0 ? void 0 : _a.includes(typeName)) ||
133
+ ((_b = options.exceptions.suffixes) === null || _b === void 0 ? void 0 : _b.some(suffix => typeName.endsWith(suffix)));
134
+ if (shouldIgnoreNode) {
135
+ return;
136
+ }
137
+ const validIds = node.fields.filter(field => {
138
+ const fieldNode = field.rawNode();
139
+ const isValidIdName = options.acceptedIdNames.includes(fieldNode.name.value);
140
+ // To be a valid type, it must be non-null and one of the accepted types.
141
+ let isValidIdType = false;
142
+ if (fieldNode.type.kind === graphql_1.Kind.NON_NULL_TYPE &&
143
+ fieldNode.type.type.kind === graphql_1.Kind.NAMED_TYPE) {
144
+ isValidIdType = options.acceptedIdTypes.includes(fieldNode.type.type.name.value);
145
+ }
146
+ return isValidIdName && isValidIdType;
147
+ });
148
+ // Usually, there should be only one unique identifier field per type.
149
+ // Some clients allow multiple fields to be used. If more people need this,
150
+ // we can extend this rule later.
151
+ if (validIds.length !== 1) {
152
+ const pluralNamesSuffix = options.acceptedIdNames.length > 1 ? 's' : '';
153
+ const pluralTypesSuffix = options.acceptedIdTypes.length > 1 ? 's' : '';
154
+ context.report({
155
+ node: node.name,
156
+ message: `${typeName} must have exactly one non-nullable unique identifier. Accepted name${pluralNamesSuffix}: ${(0, utils_1.englishJoinWords)(options.acceptedIdNames)}. Accepted type${pluralTypesSuffix}: ${(0, utils_1.englishJoinWords)(options.acceptedIdTypes)}.`,
157
+ });
158
+ }
159
+ },
160
+ };
161
+ },
162
+ };
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rule = exports.checkNode = void 0;
4
+ const path_1 = require("path");
5
+ const graphql_1 = require("graphql");
6
+ const utils_1 = require("../utils");
7
+ const RULE_ID = 'unique-fragment-name';
8
+ const checkNode = (context, node, ruleId) => {
9
+ const documentName = node.name.value;
10
+ const siblings = (0, utils_1.requireSiblingsOperations)(ruleId, context);
11
+ const siblingDocuments = node.kind === graphql_1.Kind.FRAGMENT_DEFINITION
12
+ ? siblings.getFragment(documentName)
13
+ : siblings.getOperation(documentName);
14
+ const filepath = context.getFilename();
15
+ const conflictingDocuments = siblingDocuments.filter(f => {
16
+ var _a;
17
+ const isSameName = ((_a = f.document.name) === null || _a === void 0 ? void 0 : _a.value) === documentName;
18
+ const isSamePath = (0, utils_1.normalizePath)(f.filePath) === (0, utils_1.normalizePath)(filepath);
19
+ return isSameName && !isSamePath;
20
+ });
21
+ if (conflictingDocuments.length > 0) {
22
+ context.report({
23
+ messageId: ruleId,
24
+ data: {
25
+ documentName,
26
+ summary: conflictingDocuments
27
+ .map(f => `\t${(0, path_1.relative)(utils_1.CWD, f.filePath.replace(utils_1.VIRTUAL_DOCUMENT_REGEX, ''))}`)
28
+ .join('\n'),
29
+ },
30
+ node: node.name,
31
+ });
32
+ }
33
+ };
34
+ exports.checkNode = checkNode;
35
+ exports.rule = {
36
+ meta: {
37
+ type: 'suggestion',
38
+ docs: {
39
+ category: 'Operations',
40
+ description: 'Enforce unique fragment names across your project.',
41
+ url: `https://github.com/B2o5T/graphql-eslint/blob/master/docs/rules/${RULE_ID}.md`,
42
+ requiresSiblings: true,
43
+ examples: [
44
+ {
45
+ title: 'Incorrect',
46
+ code: /* GraphQL */ `
47
+ # user.fragment.graphql
48
+ fragment UserFields on User {
49
+ id
50
+ name
51
+ fullName
52
+ }
53
+
54
+ # user-fields.graphql
55
+ fragment UserFields on User {
56
+ id
57
+ }
58
+ `,
59
+ },
60
+ {
61
+ title: 'Correct',
62
+ code: /* GraphQL */ `
63
+ # user.fragment.graphql
64
+ fragment AllUserFields on User {
65
+ id
66
+ name
67
+ fullName
68
+ }
69
+
70
+ # user-fields.graphql
71
+ fragment UserFields on User {
72
+ id
73
+ }
74
+ `,
75
+ },
76
+ ],
77
+ },
78
+ messages: {
79
+ [RULE_ID]: 'Fragment named "{{ documentName }}" already defined in:\n{{ summary }}',
80
+ },
81
+ schema: [],
82
+ },
83
+ create(context) {
84
+ return {
85
+ FragmentDefinition(node) {
86
+ (0, exports.checkNode)(context, node, RULE_ID);
87
+ },
88
+ };
89
+ },
90
+ };
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rule = void 0;
4
+ const unique_fragment_name_1 = require("./unique-fragment-name");
5
+ const RULE_ID = 'unique-operation-name';
6
+ exports.rule = {
7
+ meta: {
8
+ type: 'suggestion',
9
+ docs: {
10
+ category: 'Operations',
11
+ description: 'Enforce unique operation names across your project.',
12
+ url: `https://github.com/B2o5T/graphql-eslint/blob/master/docs/rules/${RULE_ID}.md`,
13
+ requiresSiblings: true,
14
+ examples: [
15
+ {
16
+ title: 'Incorrect',
17
+ code: /* GraphQL */ `
18
+ # foo.query.graphql
19
+ query user {
20
+ user {
21
+ id
22
+ }
23
+ }
24
+
25
+ # bar.query.graphql
26
+ query user {
27
+ me {
28
+ id
29
+ }
30
+ }
31
+ `,
32
+ },
33
+ {
34
+ title: 'Correct',
35
+ code: /* GraphQL */ `
36
+ # foo.query.graphql
37
+ query user {
38
+ user {
39
+ id
40
+ }
41
+ }
42
+
43
+ # bar.query.graphql
44
+ query me {
45
+ me {
46
+ id
47
+ }
48
+ }
49
+ `,
50
+ },
51
+ ],
52
+ },
53
+ messages: {
54
+ [RULE_ID]: 'Operation named "{{ documentName }}" already defined in:\n{{ summary }}',
55
+ },
56
+ schema: [],
57
+ },
58
+ create(context) {
59
+ return {
60
+ 'OperationDefinition[name!=undefined]'(node) {
61
+ (0, unique_fragment_name_1.checkNode)(context, node, RULE_ID);
62
+ },
63
+ };
64
+ },
65
+ };
package/cjs/schema.js ADDED
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getSchema = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const graphql_1 = require("graphql");
6
+ const debug_1 = tslib_1.__importDefault(require("debug"));
7
+ const fast_glob_1 = tslib_1.__importDefault(require("fast-glob"));
8
+ const chalk_1 = tslib_1.__importDefault(require("chalk"));
9
+ const cache_1 = require("./cache");
10
+ const schemaCache = new cache_1.ModuleCache();
11
+ const debug = (0, debug_1.default)('graphql-eslint:schema');
12
+ function getSchema(project, schemaOptions) {
13
+ const schemaKey = project.schema;
14
+ if (!schemaKey) {
15
+ return null;
16
+ }
17
+ const cache = schemaCache.get(schemaKey);
18
+ if (cache) {
19
+ return cache;
20
+ }
21
+ let schema;
22
+ try {
23
+ debug('Loading schema from %o', project.schema);
24
+ schema = project.loadSchemaSync(project.schema, 'GraphQLSchema', {
25
+ ...schemaOptions,
26
+ pluckConfig: project.extensions.pluckConfig,
27
+ });
28
+ if (debug.enabled) {
29
+ debug('Schema loaded: %o', schema instanceof graphql_1.GraphQLSchema);
30
+ const schemaPaths = fast_glob_1.default.sync(project.schema, { absolute: true });
31
+ debug('Schema pointers %O', schemaPaths);
32
+ }
33
+ // Do not set error to cache, since cache reload will be done after some `lifetime` seconds
34
+ schemaCache.set(schemaKey, schema);
35
+ }
36
+ catch (error) {
37
+ error.message = chalk_1.default.red(`Error while loading schema: ${error.message}`);
38
+ schema = error;
39
+ }
40
+ return schema;
41
+ }
42
+ exports.getSchema = getSchema;
package/cjs/testkit.js ADDED
@@ -0,0 +1,183 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GraphQLRuleTester = void 0;
4
+ /* eslint-env jest */
5
+ const fs_1 = require("fs");
6
+ const path_1 = require("path");
7
+ const eslint_1 = require("eslint");
8
+ const code_frame_1 = require("@babel/code-frame");
9
+ function indentCode(code, indent = 4) {
10
+ return code.replace(/^/gm, ' '.repeat(indent));
11
+ }
12
+ // A simple version of `SourceCodeFixer.applyFixes`
13
+ // https://github.com/eslint/eslint/issues/14936#issuecomment-906746754
14
+ function applyFix(code, { range, text }) {
15
+ return [code.slice(0, range[0]), text, code.slice(range[1])].join('');
16
+ }
17
+ class GraphQLRuleTester extends eslint_1.RuleTester {
18
+ constructor(parserOptions = {}) {
19
+ const config = {
20
+ parser: require.resolve('@graphql-eslint/eslint-plugin'),
21
+ parserOptions: {
22
+ ...parserOptions,
23
+ skipGraphQLConfig: true,
24
+ },
25
+ };
26
+ super(config);
27
+ this.config = config;
28
+ }
29
+ fromMockFile(path) {
30
+ return (0, fs_1.readFileSync)((0, path_1.resolve)(__dirname, `../tests/mocks/${path}`), 'utf-8');
31
+ }
32
+ runGraphQLTests(ruleId, rule, tests) {
33
+ const ruleTests = eslint_1.Linter.version.startsWith('8')
34
+ ? tests
35
+ : {
36
+ valid: tests.valid.map(test => {
37
+ if (typeof test === 'string') {
38
+ return test;
39
+ }
40
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
41
+ const { name, ...testCaseOptions } = test;
42
+ return testCaseOptions;
43
+ }),
44
+ invalid: tests.invalid.map(test => {
45
+ // ESLint 7 throws an error on CI - Unexpected top-level property "name"
46
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
47
+ const { name, ...testCaseOptions } = test;
48
+ return testCaseOptions;
49
+ }),
50
+ };
51
+ super.run(ruleId, rule, ruleTests);
52
+ const linter = new eslint_1.Linter();
53
+ linter.defineRule(ruleId, rule);
54
+ const hasOnlyTest = [...tests.valid, ...tests.invalid].some(t => typeof t !== 'string' && t.only);
55
+ // for (const [index, testCase] of tests.valid.entries()) {
56
+ // const { name, code, filename, only }: RuleTester.ValidTestCase =
57
+ // typeof testCase === 'string' ? { code: testCase } : testCase;
58
+ //
59
+ // if (hasOnlyTest && !only) {
60
+ // continue;
61
+ // }
62
+ //
63
+ // const verifyConfig = getVerifyConfig(ruleId, this.config, testCase);
64
+ // defineParser(linter, verifyConfig.parser);
65
+ //
66
+ // const messages = linter.verify(code, verifyConfig, { filename });
67
+ // const codeFrame = printCode(code, { line: 0, column: 0 });
68
+ //
69
+ // it(name || `Valid #${index + 1}\n${codeFrame}`, () => {
70
+ // expect(messages).toEqual([]);
71
+ // });
72
+ // }
73
+ for (const [idx, testCase] of tests.invalid.entries()) {
74
+ const { only, filename, options, name } = testCase;
75
+ if (hasOnlyTest && !only) {
76
+ continue;
77
+ }
78
+ const code = removeTrailingBlankLines(testCase.code);
79
+ const verifyConfig = getVerifyConfig(ruleId, this.config, testCase);
80
+ defineParser(linter, verifyConfig.parser);
81
+ const messages = linter.verify(code, verifyConfig, filename);
82
+ if (messages.length === 0) {
83
+ throw new Error('Invalid case should have at least one error.');
84
+ }
85
+ const codeFrame = indentCode(printCode(code, { line: 0, column: 0 }));
86
+ const messageForSnapshot = ['#### ⌨️ Code', codeFrame];
87
+ if (options) {
88
+ const opts = JSON.stringify(options, null, 2).slice(1, -1);
89
+ messageForSnapshot.push('#### ⚙️ Options', indentCode(removeTrailingBlankLines(opts), 2));
90
+ }
91
+ for (const [index, message] of messages.entries()) {
92
+ if (message.fatal) {
93
+ throw new Error(message.message);
94
+ }
95
+ const codeWithMessage = printCode(code, message, 1);
96
+ messageForSnapshot.push(printWithIndex('#### ❌ Error', index, messages.length), indentCode(codeWithMessage));
97
+ const { suggestions } = message;
98
+ // Don't print suggestions in snapshots for too big codes
99
+ if (suggestions && (code.match(/\n/g) || '').length < 1000) {
100
+ for (const [i, suggestion] of message.suggestions.entries()) {
101
+ const title = printWithIndex('#### 💡 Suggestion', i, suggestions.length, suggestion.desc);
102
+ const output = applyFix(code, suggestion.fix);
103
+ const codeFrame = printCode(output, { line: 0, column: 0 });
104
+ messageForSnapshot.push(title, indentCode(codeFrame, 2));
105
+ }
106
+ }
107
+ }
108
+ if (rule.meta.fixable) {
109
+ const { fixed, output } = linter.verifyAndFix(code, verifyConfig, filename);
110
+ if (fixed) {
111
+ messageForSnapshot.push('#### 🔧 Autofix output', indentCode(printCode(output)));
112
+ }
113
+ }
114
+ // @ts-expect-error -- we should import `vitest` but somebody could use globals from `jest`
115
+ it(name || `Invalid #${idx + 1}`, () => {
116
+ // @ts-expect-error -- ^ same
117
+ expect(messageForSnapshot.join('\n\n')).toMatchSnapshot();
118
+ });
119
+ }
120
+ }
121
+ }
122
+ exports.GraphQLRuleTester = GraphQLRuleTester;
123
+ function removeTrailingBlankLines(text) {
124
+ return text.replace(/^\s*\n/, '').trimEnd();
125
+ }
126
+ function printWithIndex(title, index, total, description) {
127
+ if (total > 1) {
128
+ title += ` ${index + 1}/${total}`;
129
+ }
130
+ if (description) {
131
+ title += `: ${description}`;
132
+ }
133
+ return title;
134
+ }
135
+ function getVerifyConfig(ruleId, testerConfig, testCase) {
136
+ const { parser = testerConfig.parser, parserOptions, options } = testCase;
137
+ return {
138
+ ...testerConfig,
139
+ parser,
140
+ parserOptions: {
141
+ ...testerConfig.parserOptions,
142
+ ...parserOptions,
143
+ },
144
+ rules: {
145
+ [ruleId]: Array.isArray(options) ? ['error', ...options] : 'error',
146
+ },
147
+ };
148
+ }
149
+ const parsers = new WeakMap();
150
+ function defineParser(linter, parser) {
151
+ if (!parser) {
152
+ return;
153
+ }
154
+ if (!parsers.has(linter)) {
155
+ parsers.set(linter, new Set());
156
+ }
157
+ const defined = parsers.get(linter);
158
+ if (!defined.has(parser)) {
159
+ defined.add(parser);
160
+ linter.defineParser(parser, require(parser));
161
+ }
162
+ }
163
+ function printCode(code, result = {}, linesOffset = Number.POSITIVE_INFINITY) {
164
+ const { line, column, endLine, endColumn, message } = result;
165
+ const location = {};
166
+ if (typeof line === 'number' && typeof column === 'number') {
167
+ location.start = {
168
+ line,
169
+ column,
170
+ };
171
+ }
172
+ if (typeof endLine === 'number' && typeof endColumn === 'number') {
173
+ location.end = {
174
+ line: endLine,
175
+ column: endColumn,
176
+ };
177
+ }
178
+ return (0, code_frame_1.codeFrameColumns)(code, location, {
179
+ linesAbove: linesOffset,
180
+ linesBelow: linesOffset,
181
+ message,
182
+ });
183
+ }
package/cjs/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/cjs/utils.js ADDED
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.englishJoinWords = exports.ARRAY_DEFAULT_OPTIONS = exports.REPORT_ON_FIRST_CHARACTER = exports.getLocation = exports.convertCase = exports.camelCase = exports.pascalCase = exports.TYPES_KINDS = exports.getTypeName = exports.CWD = exports.VIRTUAL_DOCUMENT_REGEX = exports.normalizePath = exports.logger = exports.requireGraphQLSchemaFromContext = exports.requireSiblingsOperations = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const graphql_1 = require("graphql");
6
+ const lodash_lowercase_1 = tslib_1.__importDefault(require("lodash.lowercase"));
7
+ const chalk_1 = tslib_1.__importDefault(require("chalk"));
8
+ function requireSiblingsOperations(ruleId, context) {
9
+ const { siblingOperations } = context.parserServices;
10
+ if (!siblingOperations.available) {
11
+ throw new Error(`Rule \`${ruleId}\` requires \`parserOptions.operations\` to be set and loaded. See https://bit.ly/graphql-eslint-operations for more info`);
12
+ }
13
+ return siblingOperations;
14
+ }
15
+ exports.requireSiblingsOperations = requireSiblingsOperations;
16
+ function requireGraphQLSchemaFromContext(ruleId, context) {
17
+ const { schema } = context.parserServices;
18
+ if (!schema) {
19
+ throw new Error(`Rule \`${ruleId}\` requires \`parserOptions.schema\` to be set and loaded. See https://bit.ly/graphql-eslint-schema for more info`);
20
+ }
21
+ else if (schema instanceof Error) {
22
+ throw schema;
23
+ }
24
+ return schema;
25
+ }
26
+ exports.requireGraphQLSchemaFromContext = requireGraphQLSchemaFromContext;
27
+ exports.logger = {
28
+ // eslint-disable-next-line no-console
29
+ error: (...args) => console.error(chalk_1.default.red('error'), '[graphql-eslint]', (0, chalk_1.default)(...args)),
30
+ // eslint-disable-next-line no-console
31
+ warn: (...args) => console.warn(chalk_1.default.yellow('warning'), '[graphql-eslint]', (0, chalk_1.default)(...args)),
32
+ };
33
+ const normalizePath = (path) => (path || '').replace(/\\/g, '/');
34
+ exports.normalizePath = normalizePath;
35
+ exports.VIRTUAL_DOCUMENT_REGEX = /\/\d+_document.graphql$/;
36
+ exports.CWD = process.cwd();
37
+ const getTypeName = (node) => 'type' in node ? (0, exports.getTypeName)(node.type) : node.name.value;
38
+ exports.getTypeName = getTypeName;
39
+ exports.TYPES_KINDS = [
40
+ graphql_1.Kind.OBJECT_TYPE_DEFINITION,
41
+ graphql_1.Kind.INTERFACE_TYPE_DEFINITION,
42
+ graphql_1.Kind.ENUM_TYPE_DEFINITION,
43
+ graphql_1.Kind.SCALAR_TYPE_DEFINITION,
44
+ graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION,
45
+ graphql_1.Kind.UNION_TYPE_DEFINITION,
46
+ ];
47
+ const pascalCase = (str) => (0, lodash_lowercase_1.default)(str)
48
+ .split(' ')
49
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1))
50
+ .join('');
51
+ exports.pascalCase = pascalCase;
52
+ const camelCase = (str) => {
53
+ const result = (0, exports.pascalCase)(str);
54
+ return result.charAt(0).toLowerCase() + result.slice(1);
55
+ };
56
+ exports.camelCase = camelCase;
57
+ const convertCase = (style, str) => {
58
+ switch (style) {
59
+ case 'camelCase':
60
+ return (0, exports.camelCase)(str);
61
+ case 'PascalCase':
62
+ return (0, exports.pascalCase)(str);
63
+ case 'snake_case':
64
+ return (0, lodash_lowercase_1.default)(str).replace(/ /g, '_');
65
+ case 'UPPER_CASE':
66
+ return (0, lodash_lowercase_1.default)(str).replace(/ /g, '_').toUpperCase();
67
+ case 'kebab-case':
68
+ return (0, lodash_lowercase_1.default)(str).replace(/ /g, '-');
69
+ }
70
+ };
71
+ exports.convertCase = convertCase;
72
+ function getLocation(start, fieldName = '') {
73
+ const { line, column } = start;
74
+ return {
75
+ start: {
76
+ line,
77
+ column,
78
+ },
79
+ end: {
80
+ line,
81
+ column: column + fieldName.length,
82
+ },
83
+ };
84
+ }
85
+ exports.getLocation = getLocation;
86
+ exports.REPORT_ON_FIRST_CHARACTER = { column: 0, line: 1 };
87
+ exports.ARRAY_DEFAULT_OPTIONS = {
88
+ type: 'array',
89
+ uniqueItems: true,
90
+ minItems: 1,
91
+ items: {
92
+ type: 'string',
93
+ },
94
+ };
95
+ const englishJoinWords = words => new Intl.ListFormat('en-US', { type: 'disjunction' }).format(words);
96
+ exports.englishJoinWords = englishJoinWords;