@graphql-eslint/eslint-plugin 3.14.0-alpha-20221221142641-4e1a924 → 3.14.0-alpha-20221222124206-b82954b

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.
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;