@graphql-eslint/eslint-plugin 3.14.0-alpha-20221222211346-788e7eb → 3.14.0-alpha-20221223011223-bd3e820

Sign up to get free protection for your applications and to get access to all the features.
Files changed (185) hide show
  1. package/cjs/configs/index.js +10 -10
  2. package/cjs/documents.js +5 -5
  3. package/cjs/estree-converter/converter.js +2 -2
  4. package/cjs/estree-converter/index.js +3 -3
  5. package/cjs/estree-converter/utils.js +2 -2
  6. package/cjs/flat-configs.js +36 -0
  7. package/cjs/index.js +16 -14
  8. package/cjs/parser.js +13 -13
  9. package/cjs/processor.js +2 -2
  10. package/cjs/rules/alphabetize.js +7 -7
  11. package/cjs/rules/graphql-js-validation.js +9 -9
  12. package/cjs/rules/index.js +66 -66
  13. package/cjs/rules/lone-executable-definition.js +4 -4
  14. package/cjs/rules/match-document-filename.js +4 -4
  15. package/cjs/rules/naming-convention.js +6 -6
  16. package/cjs/rules/no-anonymous-operations.js +2 -2
  17. package/cjs/rules/no-deprecated.js +2 -2
  18. package/cjs/rules/no-one-place-fragments.js +3 -4
  19. package/cjs/rules/no-root-type.js +3 -3
  20. package/cjs/rules/no-scalar-result-type-on-mutation.js +2 -2
  21. package/cjs/rules/no-unreachable-types.js +3 -3
  22. package/cjs/rules/no-unused-fields.js +3 -3
  23. package/cjs/rules/relay-arguments.js +2 -2
  24. package/cjs/rules/relay-edge-types.js +6 -6
  25. package/cjs/rules/relay-page-info.js +5 -5
  26. package/cjs/rules/require-deprecation-date.js +2 -2
  27. package/cjs/rules/require-deprecation-reason.js +2 -2
  28. package/cjs/rules/require-description.js +8 -8
  29. package/cjs/rules/require-field-of-type-query-in-mutation-result.js +3 -3
  30. package/cjs/rules/require-id-when-available.js +8 -8
  31. package/cjs/rules/selection-set-depth.js +5 -5
  32. package/cjs/rules/strict-id-in-types.js +7 -7
  33. package/cjs/rules/unique-fragment-name.js +4 -4
  34. package/cjs/rules/unique-operation-name.js +2 -2
  35. package/cjs/schema.js +2 -2
  36. package/esm/cache.js +25 -0
  37. package/esm/configs/base.js +4 -0
  38. package/esm/configs/index.js +12 -0
  39. package/esm/configs/operations-all.js +29 -0
  40. package/esm/configs/operations-recommended.js +53 -0
  41. package/esm/configs/relay.js +9 -0
  42. package/esm/configs/schema-all.js +22 -0
  43. package/esm/configs/schema-recommended.js +49 -0
  44. package/esm/documents.js +144 -0
  45. package/esm/estree-converter/converter.js +58 -0
  46. package/esm/estree-converter/index.js +3 -0
  47. package/esm/estree-converter/types.js +1 -0
  48. package/esm/estree-converter/utils.js +102 -0
  49. package/esm/flat-configs.js +33 -0
  50. package/esm/graphql-config.js +49 -0
  51. package/esm/index.js +9 -0
  52. package/esm/package.json +1 -0
  53. package/esm/parser.js +56 -0
  54. package/esm/processor.js +75 -0
  55. package/esm/rules/alphabetize.js +344 -0
  56. package/esm/rules/description-style.js +75 -0
  57. package/esm/rules/graphql-js-validation.js +498 -0
  58. package/esm/rules/index.js +71 -0
  59. package/esm/rules/input-name.js +133 -0
  60. package/esm/rules/lone-executable-definition.js +85 -0
  61. package/esm/rules/match-document-filename.js +232 -0
  62. package/esm/rules/naming-convention.js +307 -0
  63. package/esm/rules/no-anonymous-operations.js +64 -0
  64. package/esm/rules/no-case-insensitive-enum-values-duplicates.js +58 -0
  65. package/esm/rules/no-deprecated.js +121 -0
  66. package/esm/rules/no-duplicate-fields.js +109 -0
  67. package/esm/rules/no-hashtag-description.js +86 -0
  68. package/esm/rules/no-one-place-fragments.js +80 -0
  69. package/esm/rules/no-root-type.js +83 -0
  70. package/esm/rules/no-scalar-result-type-on-mutation.js +63 -0
  71. package/esm/rules/no-typename-prefix.js +62 -0
  72. package/esm/rules/no-unreachable-types.js +154 -0
  73. package/esm/rules/no-unused-fields.js +127 -0
  74. package/esm/rules/relay-arguments.js +118 -0
  75. package/esm/rules/relay-connection-types.js +104 -0
  76. package/esm/rules/relay-edge-types.js +186 -0
  77. package/esm/rules/relay-page-info.js +97 -0
  78. package/esm/rules/require-deprecation-date.js +120 -0
  79. package/esm/rules/require-deprecation-reason.js +53 -0
  80. package/esm/rules/require-description.js +190 -0
  81. package/esm/rules/require-field-of-type-query-in-mutation-result.js +69 -0
  82. package/esm/rules/require-id-when-available.js +196 -0
  83. package/esm/rules/require-nullable-fields-with-oneof.js +58 -0
  84. package/esm/rules/require-type-pattern-with-oneof.js +57 -0
  85. package/esm/rules/selection-set-depth.js +131 -0
  86. package/esm/rules/strict-id-in-types.js +159 -0
  87. package/esm/rules/unique-fragment-name.js +86 -0
  88. package/esm/rules/unique-operation-name.js +62 -0
  89. package/esm/schema.js +37 -0
  90. package/esm/testkit.js +181 -0
  91. package/esm/types.js +1 -0
  92. package/esm/utils.js +83 -0
  93. package/package.json +10 -1
  94. package/typings/estree-converter/converter.d.cts +1 -1
  95. package/typings/estree-converter/converter.d.ts +1 -1
  96. package/typings/estree-converter/index.d.cts +3 -3
  97. package/typings/estree-converter/index.d.ts +3 -3
  98. package/typings/estree-converter/types.d.cts +3 -3
  99. package/typings/estree-converter/types.d.ts +3 -3
  100. package/typings/estree-converter/utils.d.cts +2 -2
  101. package/typings/estree-converter/utils.d.ts +2 -2
  102. package/typings/flat-configs.d.cts +248 -0
  103. package/typings/flat-configs.d.ts +248 -0
  104. package/typings/graphql-config.d.cts +1 -1
  105. package/typings/graphql-config.d.ts +1 -1
  106. package/typings/index.d.cts +8 -7
  107. package/typings/index.d.ts +8 -7
  108. package/typings/parser.d.cts +1 -1
  109. package/typings/parser.d.ts +1 -1
  110. package/typings/rules/alphabetize.d.cts +1 -1
  111. package/typings/rules/alphabetize.d.ts +1 -1
  112. package/typings/rules/description-style.d.cts +1 -1
  113. package/typings/rules/description-style.d.ts +1 -1
  114. package/typings/rules/graphql-js-validation.d.cts +1 -1
  115. package/typings/rules/graphql-js-validation.d.ts +1 -1
  116. package/typings/rules/index.d.cts +45 -45
  117. package/typings/rules/index.d.ts +45 -45
  118. package/typings/rules/input-name.d.cts +1 -1
  119. package/typings/rules/input-name.d.ts +1 -1
  120. package/typings/rules/lone-executable-definition.d.cts +1 -1
  121. package/typings/rules/lone-executable-definition.d.ts +1 -1
  122. package/typings/rules/match-document-filename.d.cts +2 -2
  123. package/typings/rules/match-document-filename.d.ts +2 -2
  124. package/typings/rules/naming-convention.d.cts +1 -1
  125. package/typings/rules/naming-convention.d.ts +1 -1
  126. package/typings/rules/no-anonymous-operations.d.cts +1 -1
  127. package/typings/rules/no-anonymous-operations.d.ts +1 -1
  128. package/typings/rules/no-case-insensitive-enum-values-duplicates.d.cts +1 -1
  129. package/typings/rules/no-case-insensitive-enum-values-duplicates.d.ts +1 -1
  130. package/typings/rules/no-deprecated.d.cts +1 -1
  131. package/typings/rules/no-deprecated.d.ts +1 -1
  132. package/typings/rules/no-duplicate-fields.d.cts +1 -1
  133. package/typings/rules/no-duplicate-fields.d.ts +1 -1
  134. package/typings/rules/no-hashtag-description.d.cts +1 -1
  135. package/typings/rules/no-hashtag-description.d.ts +1 -1
  136. package/typings/rules/no-one-place-fragments.d.cts +1 -1
  137. package/typings/rules/no-one-place-fragments.d.ts +1 -1
  138. package/typings/rules/no-root-type.d.cts +1 -1
  139. package/typings/rules/no-root-type.d.ts +1 -1
  140. package/typings/rules/no-scalar-result-type-on-mutation.d.cts +1 -1
  141. package/typings/rules/no-scalar-result-type-on-mutation.d.ts +1 -1
  142. package/typings/rules/no-typename-prefix.d.cts +1 -1
  143. package/typings/rules/no-typename-prefix.d.ts +1 -1
  144. package/typings/rules/no-unreachable-types.d.cts +1 -1
  145. package/typings/rules/no-unreachable-types.d.ts +1 -1
  146. package/typings/rules/no-unused-fields.d.cts +1 -1
  147. package/typings/rules/no-unused-fields.d.ts +1 -1
  148. package/typings/rules/relay-arguments.d.cts +1 -1
  149. package/typings/rules/relay-arguments.d.ts +1 -1
  150. package/typings/rules/relay-connection-types.d.cts +1 -1
  151. package/typings/rules/relay-connection-types.d.ts +1 -1
  152. package/typings/rules/relay-edge-types.d.cts +1 -1
  153. package/typings/rules/relay-edge-types.d.ts +1 -1
  154. package/typings/rules/relay-page-info.d.cts +1 -1
  155. package/typings/rules/relay-page-info.d.ts +1 -1
  156. package/typings/rules/require-deprecation-date.d.cts +1 -1
  157. package/typings/rules/require-deprecation-date.d.ts +1 -1
  158. package/typings/rules/require-deprecation-reason.d.cts +1 -1
  159. package/typings/rules/require-deprecation-reason.d.ts +1 -1
  160. package/typings/rules/require-description.d.cts +1 -1
  161. package/typings/rules/require-description.d.ts +1 -1
  162. package/typings/rules/require-field-of-type-query-in-mutation-result.d.cts +1 -1
  163. package/typings/rules/require-field-of-type-query-in-mutation-result.d.ts +1 -1
  164. package/typings/rules/require-id-when-available.d.cts +1 -1
  165. package/typings/rules/require-id-when-available.d.ts +1 -1
  166. package/typings/rules/require-nullable-fields-with-oneof.d.cts +1 -1
  167. package/typings/rules/require-nullable-fields-with-oneof.d.ts +1 -1
  168. package/typings/rules/require-type-pattern-with-oneof.d.cts +1 -1
  169. package/typings/rules/require-type-pattern-with-oneof.d.ts +1 -1
  170. package/typings/rules/selection-set-depth.d.cts +1 -1
  171. package/typings/rules/selection-set-depth.d.ts +1 -1
  172. package/typings/rules/strict-id-in-types.d.cts +1 -1
  173. package/typings/rules/strict-id-in-types.d.ts +1 -1
  174. package/typings/rules/unique-fragment-name.d.cts +2 -2
  175. package/typings/rules/unique-fragment-name.d.ts +2 -2
  176. package/typings/rules/unique-operation-name.d.cts +1 -1
  177. package/typings/rules/unique-operation-name.d.ts +1 -1
  178. package/typings/schema.d.cts +1 -1
  179. package/typings/schema.d.ts +1 -1
  180. package/typings/testkit.d.cts +3 -3
  181. package/typings/testkit.d.ts +3 -3
  182. package/typings/types.d.cts +2 -2
  183. package/typings/types.d.ts +2 -2
  184. package/typings/utils.d.cts +2 -2
  185. package/typings/utils.d.ts +2 -2
@@ -0,0 +1,75 @@
1
+ import { gqlPluckFromCodeStringSync, } from '@graphql-tools/graphql-tag-pluck';
2
+ import { asArray } from '@graphql-tools/utils';
3
+ import { loadOnDiskGraphQLConfig } from './graphql-config.js';
4
+ const blocksMap = new Map();
5
+ let onDiskConfig;
6
+ let onDiskConfigLoaded = false;
7
+ const RELEVANT_KEYWORDS = ['gql', 'graphql', 'GraphQL'];
8
+ export const processor = {
9
+ supportsAutofix: true,
10
+ preprocess(code, filePath) {
11
+ if (!onDiskConfigLoaded) {
12
+ onDiskConfig = loadOnDiskGraphQLConfig(filePath);
13
+ onDiskConfigLoaded = true;
14
+ }
15
+ let keywords = RELEVANT_KEYWORDS;
16
+ const pluckConfig = onDiskConfig === null || onDiskConfig === void 0 ? void 0 : onDiskConfig.getProjectForFile(filePath).extensions.pluckConfig;
17
+ if (pluckConfig) {
18
+ const { modules = [], globalGqlIdentifierName = ['gql', 'graphql'], gqlMagicComment = 'GraphQL', } = pluckConfig;
19
+ keywords = [
20
+ ...new Set([
21
+ ...modules.map(({ identifier }) => identifier),
22
+ ...asArray(globalGqlIdentifierName),
23
+ gqlMagicComment,
24
+ ].filter(Boolean)),
25
+ ];
26
+ }
27
+ if (keywords.every(keyword => !code.includes(keyword))) {
28
+ return [code];
29
+ }
30
+ try {
31
+ const sources = gqlPluckFromCodeStringSync(filePath, code, {
32
+ skipIndent: true,
33
+ ...pluckConfig,
34
+ });
35
+ const isSvelte = filePath.endsWith('.svelte');
36
+ const blocks = sources.map(item => ({
37
+ filename: 'document.graphql',
38
+ text: item.body,
39
+ lineOffset: item.locationOffset.line - (isSvelte ? 3 : 1),
40
+ // @ts-expect-error -- `index` field exist but show ts error
41
+ offset: item.locationOffset.index + (isSvelte ? -52 : 1),
42
+ }));
43
+ blocksMap.set(filePath, blocks);
44
+ return [...blocks, code /* source code must be provided and be last */];
45
+ }
46
+ catch (_a) {
47
+ // in case of parsing error return code as is
48
+ return [code];
49
+ }
50
+ },
51
+ postprocess(messages, filePath) {
52
+ const blocks = blocksMap.get(filePath) || [];
53
+ for (let i = 0; i < blocks.length; i += 1) {
54
+ const { lineOffset, offset } = blocks[i];
55
+ for (const message of messages[i]) {
56
+ message.line += lineOffset;
57
+ // endLine can not exist if only `loc: { start, column }` was provided to context.report
58
+ if (typeof message.endLine === 'number') {
59
+ message.endLine += lineOffset;
60
+ }
61
+ if (message.fix) {
62
+ message.fix.range[0] += offset;
63
+ message.fix.range[1] += offset;
64
+ }
65
+ for (const suggestion of message.suggestions || []) {
66
+ suggestion.fix.range[0] += offset;
67
+ suggestion.fix.range[1] += offset;
68
+ }
69
+ }
70
+ }
71
+ const result = messages.flat();
72
+ // sort eslint/graphql-eslint messages by line/column
73
+ return result.sort((a, b) => a.line - b.line || a.column - b.column);
74
+ },
75
+ };
@@ -0,0 +1,344 @@
1
+ import { Kind, } from 'graphql';
2
+ import lowerCase from 'lodash.lowercase';
3
+ import { ARRAY_DEFAULT_OPTIONS } from '../utils.js';
4
+ const RULE_ID = 'alphabetize';
5
+ const fieldsEnum = [
6
+ Kind.OBJECT_TYPE_DEFINITION,
7
+ Kind.INTERFACE_TYPE_DEFINITION,
8
+ Kind.INPUT_OBJECT_TYPE_DEFINITION,
9
+ ];
10
+ const valuesEnum = [Kind.ENUM_TYPE_DEFINITION];
11
+ const selectionsEnum = [
12
+ Kind.OPERATION_DEFINITION,
13
+ Kind.FRAGMENT_DEFINITION,
14
+ ];
15
+ const variablesEnum = [Kind.OPERATION_DEFINITION];
16
+ const argumentsEnum = [
17
+ Kind.FIELD_DEFINITION,
18
+ Kind.FIELD,
19
+ Kind.DIRECTIVE_DEFINITION,
20
+ Kind.DIRECTIVE,
21
+ ];
22
+ const schema = {
23
+ type: 'array',
24
+ minItems: 1,
25
+ maxItems: 1,
26
+ items: {
27
+ type: 'object',
28
+ additionalProperties: false,
29
+ minProperties: 1,
30
+ properties: {
31
+ fields: {
32
+ ...ARRAY_DEFAULT_OPTIONS,
33
+ items: {
34
+ enum: fieldsEnum,
35
+ },
36
+ description: 'Fields of `type`, `interface`, and `input`.',
37
+ },
38
+ values: {
39
+ ...ARRAY_DEFAULT_OPTIONS,
40
+ items: {
41
+ enum: valuesEnum,
42
+ },
43
+ description: 'Values of `enum`.',
44
+ },
45
+ selections: {
46
+ ...ARRAY_DEFAULT_OPTIONS,
47
+ items: {
48
+ enum: selectionsEnum,
49
+ },
50
+ description: 'Selections of `fragment` and operations `query`, `mutation` and `subscription`.',
51
+ },
52
+ variables: {
53
+ ...ARRAY_DEFAULT_OPTIONS,
54
+ items: {
55
+ enum: variablesEnum,
56
+ },
57
+ description: 'Variables of operations `query`, `mutation` and `subscription`.',
58
+ },
59
+ arguments: {
60
+ ...ARRAY_DEFAULT_OPTIONS,
61
+ items: {
62
+ enum: argumentsEnum,
63
+ },
64
+ description: 'Arguments of fields and directives.',
65
+ },
66
+ definitions: {
67
+ type: 'boolean',
68
+ description: 'Definitions – `type`, `interface`, `enum`, `scalar`, `input`, `union` and `directive`.',
69
+ default: false,
70
+ },
71
+ groups: {
72
+ ...ARRAY_DEFAULT_OPTIONS,
73
+ minItems: 2,
74
+ description: "Custom order group. Example: `['id', '*', 'createdAt', 'updatedAt']` where `*` says for everything else.",
75
+ },
76
+ },
77
+ },
78
+ };
79
+ export const rule = {
80
+ meta: {
81
+ type: 'suggestion',
82
+ fixable: 'code',
83
+ docs: {
84
+ category: ['Schema', 'Operations'],
85
+ description: 'Enforce arrange in alphabetical order for type fields, enum values, input object fields, operation selections and more.',
86
+ url: `https://github.com/B2o5T/graphql-eslint/blob/master/docs/rules/${RULE_ID}.md`,
87
+ examples: [
88
+ {
89
+ title: 'Incorrect',
90
+ usage: [{ fields: [Kind.OBJECT_TYPE_DEFINITION] }],
91
+ code: /* GraphQL */ `
92
+ type User {
93
+ password: String
94
+ firstName: String! # should be before "password"
95
+ age: Int # should be before "firstName"
96
+ lastName: String!
97
+ }
98
+ `,
99
+ },
100
+ {
101
+ title: 'Correct',
102
+ usage: [{ fields: [Kind.OBJECT_TYPE_DEFINITION] }],
103
+ code: /* GraphQL */ `
104
+ type User {
105
+ age: Int
106
+ firstName: String!
107
+ lastName: String!
108
+ password: String
109
+ }
110
+ `,
111
+ },
112
+ {
113
+ title: 'Incorrect',
114
+ usage: [{ values: [Kind.ENUM_TYPE_DEFINITION] }],
115
+ code: /* GraphQL */ `
116
+ enum Role {
117
+ SUPER_ADMIN
118
+ ADMIN # should be before "SUPER_ADMIN"
119
+ USER
120
+ GOD # should be before "USER"
121
+ }
122
+ `,
123
+ },
124
+ {
125
+ title: 'Correct',
126
+ usage: [{ values: [Kind.ENUM_TYPE_DEFINITION] }],
127
+ code: /* GraphQL */ `
128
+ enum Role {
129
+ ADMIN
130
+ GOD
131
+ SUPER_ADMIN
132
+ USER
133
+ }
134
+ `,
135
+ },
136
+ {
137
+ title: 'Incorrect',
138
+ usage: [{ selections: [Kind.OPERATION_DEFINITION] }],
139
+ code: /* GraphQL */ `
140
+ query {
141
+ me {
142
+ firstName
143
+ lastName
144
+ email # should be before "lastName"
145
+ }
146
+ }
147
+ `,
148
+ },
149
+ {
150
+ title: 'Correct',
151
+ usage: [{ selections: [Kind.OPERATION_DEFINITION] }],
152
+ code: /* GraphQL */ `
153
+ query {
154
+ me {
155
+ email
156
+ firstName
157
+ lastName
158
+ }
159
+ }
160
+ `,
161
+ },
162
+ ],
163
+ configOptions: {
164
+ schema: [
165
+ {
166
+ fields: fieldsEnum,
167
+ values: valuesEnum,
168
+ arguments: argumentsEnum,
169
+ // TODO: add in graphql-eslint v4
170
+ // definitions: true,
171
+ // groups: ['id', '*', 'createdAt', 'updatedAt']
172
+ },
173
+ ],
174
+ operations: [
175
+ {
176
+ selections: selectionsEnum,
177
+ variables: variablesEnum,
178
+ arguments: [Kind.FIELD, Kind.DIRECTIVE],
179
+ },
180
+ ],
181
+ },
182
+ },
183
+ messages: {
184
+ [RULE_ID]: '`{{ currName }}` should be before {{ prevName }}.',
185
+ },
186
+ schema,
187
+ },
188
+ create(context) {
189
+ var _a, _b, _c, _d, _e;
190
+ const sourceCode = context.getSourceCode();
191
+ function isNodeAndCommentOnSameLine(node, comment) {
192
+ return node.loc.end.line === comment.loc.start.line;
193
+ }
194
+ function getBeforeComments(node) {
195
+ const commentsBefore = sourceCode.getCommentsBefore(node);
196
+ if (commentsBefore.length === 0) {
197
+ return [];
198
+ }
199
+ const tokenBefore = sourceCode.getTokenBefore(node);
200
+ if (tokenBefore) {
201
+ return commentsBefore.filter(comment => !isNodeAndCommentOnSameLine(tokenBefore, comment));
202
+ }
203
+ const filteredComments = [];
204
+ const nodeLine = node.loc.start.line;
205
+ // Break on comment that not attached to node
206
+ for (let i = commentsBefore.length - 1; i >= 0; i -= 1) {
207
+ const comment = commentsBefore[i];
208
+ if (nodeLine - comment.loc.start.line - filteredComments.length > 1) {
209
+ break;
210
+ }
211
+ filteredComments.unshift(comment);
212
+ }
213
+ return filteredComments;
214
+ }
215
+ function getRangeWithComments(node) {
216
+ if (node.kind === Kind.VARIABLE) {
217
+ node = node.parent;
218
+ }
219
+ const [firstBeforeComment] = getBeforeComments(node);
220
+ const [firstAfterComment] = sourceCode.getCommentsAfter(node);
221
+ const from = firstBeforeComment || node;
222
+ const to = firstAfterComment && isNodeAndCommentOnSameLine(node, firstAfterComment)
223
+ ? firstAfterComment
224
+ : node;
225
+ return [from.range[0], to.range[1]];
226
+ }
227
+ function checkNodes(nodes) {
228
+ var _a, _b, _c, _d;
229
+ // Starts from 1, ignore nodes.length <= 1
230
+ for (let i = 1; i < nodes.length; i += 1) {
231
+ const currNode = nodes[i];
232
+ const currName = ('alias' in currNode && ((_a = currNode.alias) === null || _a === void 0 ? void 0 : _a.value)) ||
233
+ ('name' in currNode && ((_b = currNode.name) === null || _b === void 0 ? void 0 : _b.value));
234
+ if (!currName) {
235
+ // we don't move unnamed current nodes
236
+ continue;
237
+ }
238
+ const prevNode = nodes[i - 1];
239
+ const prevName = ('alias' in prevNode && ((_c = prevNode.alias) === null || _c === void 0 ? void 0 : _c.value)) ||
240
+ ('name' in prevNode && ((_d = prevNode.name) === null || _d === void 0 ? void 0 : _d.value));
241
+ if (prevName) {
242
+ // Compare with lexicographic order
243
+ const compareResult = prevName.localeCompare(currName);
244
+ const { groups } = opts;
245
+ let shouldSortByGroup = false;
246
+ if (groups === null || groups === void 0 ? void 0 : groups.length) {
247
+ if (!groups.includes('*')) {
248
+ throw new Error('`groups` option should contain `*` string.');
249
+ }
250
+ let indexForPrev = groups.indexOf(prevName);
251
+ if (indexForPrev === -1)
252
+ indexForPrev = groups.indexOf('*');
253
+ let indexForCurr = groups.indexOf(currName);
254
+ if (indexForCurr === -1)
255
+ indexForCurr = groups.indexOf('*');
256
+ shouldSortByGroup = indexForPrev - indexForCurr > 0;
257
+ if (indexForPrev < indexForCurr) {
258
+ continue;
259
+ }
260
+ }
261
+ const shouldSort = compareResult === 1;
262
+ if (!shouldSortByGroup && !shouldSort) {
263
+ const isSameName = compareResult === 0;
264
+ if (!isSameName ||
265
+ !prevNode.kind.endsWith('Extension') ||
266
+ currNode.kind.endsWith('Extension')) {
267
+ continue;
268
+ }
269
+ }
270
+ }
271
+ context.report({
272
+ node: ('alias' in currNode && currNode.alias) || currNode.name,
273
+ messageId: RULE_ID,
274
+ data: {
275
+ currName,
276
+ prevName: prevName ? `\`${prevName}\`` : lowerCase(prevNode.kind),
277
+ },
278
+ *fix(fixer) {
279
+ const prevRange = getRangeWithComments(prevNode);
280
+ const currRange = getRangeWithComments(currNode);
281
+ yield fixer.replaceTextRange(prevRange, sourceCode.getText({ range: currRange }));
282
+ yield fixer.replaceTextRange(currRange, sourceCode.getText({ range: prevRange }));
283
+ },
284
+ });
285
+ }
286
+ }
287
+ const opts = context.options[0];
288
+ const fields = new Set((_a = opts.fields) !== null && _a !== void 0 ? _a : []);
289
+ const listeners = {};
290
+ const kinds = [
291
+ fields.has(Kind.OBJECT_TYPE_DEFINITION) && [
292
+ Kind.OBJECT_TYPE_DEFINITION,
293
+ Kind.OBJECT_TYPE_EXTENSION,
294
+ ],
295
+ fields.has(Kind.INTERFACE_TYPE_DEFINITION) && [
296
+ Kind.INTERFACE_TYPE_DEFINITION,
297
+ Kind.INTERFACE_TYPE_EXTENSION,
298
+ ],
299
+ fields.has(Kind.INPUT_OBJECT_TYPE_DEFINITION) && [
300
+ Kind.INPUT_OBJECT_TYPE_DEFINITION,
301
+ Kind.INPUT_OBJECT_TYPE_EXTENSION,
302
+ ],
303
+ ]
304
+ .filter(Boolean)
305
+ .flat();
306
+ const fieldsSelector = kinds.join(',');
307
+ const hasEnumValues = ((_b = opts.values) === null || _b === void 0 ? void 0 : _b[0]) === Kind.ENUM_TYPE_DEFINITION;
308
+ const selectionsSelector = (_c = opts.selections) === null || _c === void 0 ? void 0 : _c.join(',');
309
+ const hasVariables = ((_d = opts.variables) === null || _d === void 0 ? void 0 : _d[0]) === Kind.OPERATION_DEFINITION;
310
+ const argumentsSelector = (_e = opts.arguments) === null || _e === void 0 ? void 0 : _e.join(',');
311
+ if (fieldsSelector) {
312
+ listeners[fieldsSelector] = (node) => {
313
+ checkNodes(node.fields);
314
+ };
315
+ }
316
+ if (hasEnumValues) {
317
+ const enumValuesSelector = [Kind.ENUM_TYPE_DEFINITION, Kind.ENUM_TYPE_EXTENSION].join(',');
318
+ listeners[enumValuesSelector] = (node) => {
319
+ checkNodes(node.values);
320
+ };
321
+ }
322
+ if (selectionsSelector) {
323
+ listeners[`:matches(${selectionsSelector}) SelectionSet`] = (node) => {
324
+ checkNodes(node.selections);
325
+ };
326
+ }
327
+ if (hasVariables) {
328
+ listeners.OperationDefinition = (node) => {
329
+ checkNodes(node.variableDefinitions.map(varDef => varDef.variable));
330
+ };
331
+ }
332
+ if (argumentsSelector) {
333
+ listeners[argumentsSelector] = (node) => {
334
+ checkNodes(node.arguments);
335
+ };
336
+ }
337
+ if (opts.definitions) {
338
+ listeners.Document = node => {
339
+ checkNodes(node.definitions);
340
+ };
341
+ }
342
+ return listeners;
343
+ },
344
+ };
@@ -0,0 +1,75 @@
1
+ const schema = {
2
+ type: 'array',
3
+ maxItems: 1,
4
+ items: {
5
+ type: 'object',
6
+ additionalProperties: false,
7
+ minProperties: 1,
8
+ properties: {
9
+ style: {
10
+ enum: ['block', 'inline'],
11
+ default: 'block',
12
+ },
13
+ },
14
+ },
15
+ };
16
+ export const rule = {
17
+ meta: {
18
+ type: 'suggestion',
19
+ hasSuggestions: true,
20
+ docs: {
21
+ examples: [
22
+ {
23
+ title: 'Incorrect',
24
+ usage: [{ style: 'inline' }],
25
+ code: /* GraphQL */ `
26
+ """ Description """
27
+ type someTypeName {
28
+ # ...
29
+ }
30
+ `,
31
+ },
32
+ {
33
+ title: 'Correct',
34
+ usage: [{ style: 'inline' }],
35
+ code: /* GraphQL */ `
36
+ " Description "
37
+ type someTypeName {
38
+ # ...
39
+ }
40
+ `,
41
+ },
42
+ ],
43
+ description: 'Require all comments to follow the same style (either block or inline).',
44
+ category: 'Schema',
45
+ url: 'https://github.com/B2o5T/graphql-eslint/blob/master/docs/rules/description-style.md',
46
+ recommended: true,
47
+ },
48
+ schema,
49
+ },
50
+ create(context) {
51
+ const { style = 'block' } = context.options[0] || {};
52
+ const isBlock = style === 'block';
53
+ return {
54
+ [`.description[type=StringValue][block!=${isBlock}]`](node) {
55
+ context.report({
56
+ loc: isBlock ? node.loc : node.loc.start,
57
+ message: `Unexpected ${isBlock ? 'inline' : 'block'} description.`,
58
+ suggest: [
59
+ {
60
+ desc: `Change to ${isBlock ? 'block' : 'inline'} style description`,
61
+ fix(fixer) {
62
+ const sourceCode = context.getSourceCode();
63
+ const originalText = sourceCode.getText(node);
64
+ const newText = isBlock
65
+ ? originalText.replace(/(^")|("$)/g, '"""')
66
+ : originalText.replace(/(^""")|("""$)/g, '"').replace(/\s+/g, ' ');
67
+ return fixer.replaceText(node, newText);
68
+ },
69
+ },
70
+ ],
71
+ });
72
+ },
73
+ };
74
+ },
75
+ };