@graphql-eslint/eslint-plugin 3.17.0 → 3.18.1-alpha-20230519144328-0e3b663

Sign up to get free protection for your applications and to get access to all the features.
@@ -48,6 +48,8 @@ const schema = {
48
48
  suffix: { type: 'string' },
49
49
  forbiddenPrefixes: utils_js_1.ARRAY_DEFAULT_OPTIONS,
50
50
  forbiddenSuffixes: utils_js_1.ARRAY_DEFAULT_OPTIONS,
51
+ requiredPrefixes: utils_js_1.ARRAY_DEFAULT_OPTIONS,
52
+ requiredSuffixes: utils_js_1.ARRAY_DEFAULT_OPTIONS,
51
53
  ignorePattern: {
52
54
  type: 'string',
53
55
  description: 'Option to skip validation of some words, e.g. acronyms',
@@ -165,6 +167,46 @@ exports.rule = {
165
167
  UPC: String
166
168
  UKFlag: String
167
169
  }
170
+ `,
171
+ },
172
+ {
173
+ title: 'Correct',
174
+ usage: [
175
+ {
176
+ 'FieldDefinition[gqlType.name.value=Boolean]': {
177
+ style: 'camelCase',
178
+ requiredPrefixes: ['is', 'has'],
179
+ },
180
+ 'FieldDefinition[gqlType.gqlType.name.value=Boolean]': {
181
+ style: 'camelCase',
182
+ requiredPrefixes: ['is', 'has'],
183
+ },
184
+ },
185
+ ],
186
+ code: /* GraphQL */ `
187
+ type Product {
188
+ isBackordered: Boolean
189
+ isNew: Boolean!
190
+ hasDiscount: Boolean!
191
+ }
192
+ `,
193
+ },
194
+ {
195
+ title: 'Correct',
196
+ usage: [
197
+ {
198
+ 'FieldDefinition[gqlType.gqlType.name.value=SensitiveSecret]': {
199
+ style: 'camelCase',
200
+ requiredSuffixes: ['SensitiveSecret'],
201
+ },
202
+ },
203
+ ],
204
+ code: /* GraphQL */ `
205
+ scalar SensitiveSecret
206
+
207
+ type Account {
208
+ accountSensitiveSecret: SensitiveSecret!
209
+ }
168
210
  `,
169
211
  },
170
212
  ],
@@ -218,16 +260,14 @@ exports.rule = {
218
260
  const style = (restOptions[kind] || types);
219
261
  return typeof style === 'object' ? style : { style };
220
262
  }
221
- function report(node, message, suggestedName) {
263
+ function report(node, message, suggestedNames) {
222
264
  context.report({
223
265
  node,
224
266
  message,
225
- suggest: [
226
- {
227
- desc: `Rename to \`${suggestedName}\``,
228
- fix: fixer => fixer.replaceText(node, suggestedName),
229
- },
230
- ],
267
+ suggest: suggestedNames.map(suggestedName => ({
268
+ desc: `Rename to \`${suggestedName}\``,
269
+ fix: fixer => fixer.replaceText(node, suggestedName),
270
+ })),
231
271
  });
232
272
  }
233
273
  const checkNode = (selector) => (n) => {
@@ -235,16 +275,16 @@ exports.rule = {
235
275
  if (!node) {
236
276
  return;
237
277
  }
238
- const { prefix, suffix, forbiddenPrefixes, forbiddenSuffixes, style, ignorePattern } = normalisePropertyOption(selector);
278
+ const { prefix, suffix, forbiddenPrefixes, forbiddenSuffixes, style, ignorePattern, requiredPrefixes, requiredSuffixes, } = normalisePropertyOption(selector);
239
279
  const nodeType = KindToDisplayName[n.kind] || n.kind;
240
280
  const nodeName = node.value;
241
281
  const error = getError();
242
282
  if (error) {
243
- const { errorMessage, renameToName } = error;
283
+ const { errorMessage, renameToNames } = error;
244
284
  const [leadingUnderscores] = nodeName.match(/^_*/);
245
285
  const [trailingUnderscores] = nodeName.match(/_*$/);
246
- const suggestedName = leadingUnderscores + renameToName + trailingUnderscores;
247
- report(node, `${nodeType} "${nodeName}" should ${errorMessage}`, suggestedName);
286
+ const suggestedNames = renameToNames.map(renameToName => leadingUnderscores + renameToName + trailingUnderscores);
287
+ report(node, `${nodeType} "${nodeName}" should ${errorMessage}`, suggestedNames);
248
288
  }
249
289
  function getError() {
250
290
  const name = nodeName.replace(/(^_+)|(_+$)/g, '');
@@ -254,27 +294,45 @@ exports.rule = {
254
294
  if (prefix && !name.startsWith(prefix)) {
255
295
  return {
256
296
  errorMessage: `have "${prefix}" prefix`,
257
- renameToName: prefix + name,
297
+ renameToNames: [prefix + name],
258
298
  };
259
299
  }
260
300
  if (suffix && !name.endsWith(suffix)) {
261
301
  return {
262
302
  errorMessage: `have "${suffix}" suffix`,
263
- renameToName: name + suffix,
303
+ renameToNames: [name + suffix],
264
304
  };
265
305
  }
266
306
  const forbiddenPrefix = forbiddenPrefixes === null || forbiddenPrefixes === void 0 ? void 0 : forbiddenPrefixes.find(prefix => name.startsWith(prefix));
267
307
  if (forbiddenPrefix) {
268
308
  return {
269
309
  errorMessage: `not have "${forbiddenPrefix}" prefix`,
270
- renameToName: name.replace(new RegExp(`^${forbiddenPrefix}`), ''),
310
+ renameToNames: [name.replace(new RegExp(`^${forbiddenPrefix}`), '')],
271
311
  };
272
312
  }
273
313
  const forbiddenSuffix = forbiddenSuffixes === null || forbiddenSuffixes === void 0 ? void 0 : forbiddenSuffixes.find(suffix => name.endsWith(suffix));
274
314
  if (forbiddenSuffix) {
275
315
  return {
276
316
  errorMessage: `not have "${forbiddenSuffix}" suffix`,
277
- renameToName: name.replace(new RegExp(`${forbiddenSuffix}$`), ''),
317
+ renameToNames: [name.replace(new RegExp(`${forbiddenSuffix}$`), '')],
318
+ };
319
+ }
320
+ if (requiredPrefixes &&
321
+ !requiredPrefixes.some(requiredPrefix => name.startsWith(requiredPrefix))) {
322
+ return {
323
+ errorMessage: `have one of the following prefixes: ${(0, utils_js_1.englishJoinWords)(requiredPrefixes)}`,
324
+ renameToNames: style
325
+ ? requiredPrefixes.map(prefix => (0, utils_js_1.convertCase)(style, `${prefix} ${name}`))
326
+ : requiredPrefixes.map(prefix => `${prefix}${name}`),
327
+ };
328
+ }
329
+ if (requiredSuffixes &&
330
+ !requiredSuffixes.some(requiredSuffix => name.endsWith(requiredSuffix))) {
331
+ return {
332
+ errorMessage: `have one of the following suffixes: ${(0, utils_js_1.englishJoinWords)(requiredSuffixes)}`,
333
+ renameToNames: style
334
+ ? requiredSuffixes.map(suffix => (0, utils_js_1.convertCase)(style, `${name} ${suffix}`))
335
+ : requiredSuffixes.map(suffix => `${name}${suffix}`),
278
336
  };
279
337
  }
280
338
  // Style is optional
@@ -285,14 +343,16 @@ exports.rule = {
285
343
  if (!caseRegex.test(name)) {
286
344
  return {
287
345
  errorMessage: `be in ${style} format`,
288
- renameToName: (0, utils_js_1.convertCase)(style, name),
346
+ renameToNames: [(0, utils_js_1.convertCase)(style, name)],
289
347
  };
290
348
  }
291
349
  }
292
350
  };
293
351
  const checkUnderscore = (isLeading) => (node) => {
294
352
  const suggestedName = node.value.replace(isLeading ? /^_+/ : /_+$/, '');
295
- report(node, `${isLeading ? 'Leading' : 'Trailing'} underscores are not allowed`, suggestedName);
353
+ report(node, `${isLeading ? 'Leading' : 'Trailing'} underscores are not allowed`, [
354
+ suggestedName,
355
+ ]);
296
356
  };
297
357
  const listeners = {};
298
358
  if (!allowLeadingUnderscore) {
@@ -1,5 +1,5 @@
1
1
  import { Kind } from 'graphql';
2
- import { ARRAY_DEFAULT_OPTIONS, convertCase, truthy, TYPES_KINDS } from '../utils.js';
2
+ import { ARRAY_DEFAULT_OPTIONS, convertCase, englishJoinWords, truthy, TYPES_KINDS, } from '../utils.js';
3
3
  const KindToDisplayName = {
4
4
  // types
5
5
  [Kind.OBJECT_TYPE_DEFINITION]: 'Type',
@@ -45,6 +45,8 @@ const schema = {
45
45
  suffix: { type: 'string' },
46
46
  forbiddenPrefixes: ARRAY_DEFAULT_OPTIONS,
47
47
  forbiddenSuffixes: ARRAY_DEFAULT_OPTIONS,
48
+ requiredPrefixes: ARRAY_DEFAULT_OPTIONS,
49
+ requiredSuffixes: ARRAY_DEFAULT_OPTIONS,
48
50
  ignorePattern: {
49
51
  type: 'string',
50
52
  description: 'Option to skip validation of some words, e.g. acronyms',
@@ -162,6 +164,46 @@ export const rule = {
162
164
  UPC: String
163
165
  UKFlag: String
164
166
  }
167
+ `,
168
+ },
169
+ {
170
+ title: 'Correct',
171
+ usage: [
172
+ {
173
+ 'FieldDefinition[gqlType.name.value=Boolean]': {
174
+ style: 'camelCase',
175
+ requiredPrefixes: ['is', 'has'],
176
+ },
177
+ 'FieldDefinition[gqlType.gqlType.name.value=Boolean]': {
178
+ style: 'camelCase',
179
+ requiredPrefixes: ['is', 'has'],
180
+ },
181
+ },
182
+ ],
183
+ code: /* GraphQL */ `
184
+ type Product {
185
+ isBackordered: Boolean
186
+ isNew: Boolean!
187
+ hasDiscount: Boolean!
188
+ }
189
+ `,
190
+ },
191
+ {
192
+ title: 'Correct',
193
+ usage: [
194
+ {
195
+ 'FieldDefinition[gqlType.gqlType.name.value=SensitiveSecret]': {
196
+ style: 'camelCase',
197
+ requiredSuffixes: ['SensitiveSecret'],
198
+ },
199
+ },
200
+ ],
201
+ code: /* GraphQL */ `
202
+ scalar SensitiveSecret
203
+
204
+ type Account {
205
+ accountSensitiveSecret: SensitiveSecret!
206
+ }
165
207
  `,
166
208
  },
167
209
  ],
@@ -215,16 +257,14 @@ export const rule = {
215
257
  const style = (restOptions[kind] || types);
216
258
  return typeof style === 'object' ? style : { style };
217
259
  }
218
- function report(node, message, suggestedName) {
260
+ function report(node, message, suggestedNames) {
219
261
  context.report({
220
262
  node,
221
263
  message,
222
- suggest: [
223
- {
224
- desc: `Rename to \`${suggestedName}\``,
225
- fix: fixer => fixer.replaceText(node, suggestedName),
226
- },
227
- ],
264
+ suggest: suggestedNames.map(suggestedName => ({
265
+ desc: `Rename to \`${suggestedName}\``,
266
+ fix: fixer => fixer.replaceText(node, suggestedName),
267
+ })),
228
268
  });
229
269
  }
230
270
  const checkNode = (selector) => (n) => {
@@ -232,16 +272,16 @@ export const rule = {
232
272
  if (!node) {
233
273
  return;
234
274
  }
235
- const { prefix, suffix, forbiddenPrefixes, forbiddenSuffixes, style, ignorePattern } = normalisePropertyOption(selector);
275
+ const { prefix, suffix, forbiddenPrefixes, forbiddenSuffixes, style, ignorePattern, requiredPrefixes, requiredSuffixes, } = normalisePropertyOption(selector);
236
276
  const nodeType = KindToDisplayName[n.kind] || n.kind;
237
277
  const nodeName = node.value;
238
278
  const error = getError();
239
279
  if (error) {
240
- const { errorMessage, renameToName } = error;
280
+ const { errorMessage, renameToNames } = error;
241
281
  const [leadingUnderscores] = nodeName.match(/^_*/);
242
282
  const [trailingUnderscores] = nodeName.match(/_*$/);
243
- const suggestedName = leadingUnderscores + renameToName + trailingUnderscores;
244
- report(node, `${nodeType} "${nodeName}" should ${errorMessage}`, suggestedName);
283
+ const suggestedNames = renameToNames.map(renameToName => leadingUnderscores + renameToName + trailingUnderscores);
284
+ report(node, `${nodeType} "${nodeName}" should ${errorMessage}`, suggestedNames);
245
285
  }
246
286
  function getError() {
247
287
  const name = nodeName.replace(/(^_+)|(_+$)/g, '');
@@ -251,27 +291,45 @@ export const rule = {
251
291
  if (prefix && !name.startsWith(prefix)) {
252
292
  return {
253
293
  errorMessage: `have "${prefix}" prefix`,
254
- renameToName: prefix + name,
294
+ renameToNames: [prefix + name],
255
295
  };
256
296
  }
257
297
  if (suffix && !name.endsWith(suffix)) {
258
298
  return {
259
299
  errorMessage: `have "${suffix}" suffix`,
260
- renameToName: name + suffix,
300
+ renameToNames: [name + suffix],
261
301
  };
262
302
  }
263
303
  const forbiddenPrefix = forbiddenPrefixes === null || forbiddenPrefixes === void 0 ? void 0 : forbiddenPrefixes.find(prefix => name.startsWith(prefix));
264
304
  if (forbiddenPrefix) {
265
305
  return {
266
306
  errorMessage: `not have "${forbiddenPrefix}" prefix`,
267
- renameToName: name.replace(new RegExp(`^${forbiddenPrefix}`), ''),
307
+ renameToNames: [name.replace(new RegExp(`^${forbiddenPrefix}`), '')],
268
308
  };
269
309
  }
270
310
  const forbiddenSuffix = forbiddenSuffixes === null || forbiddenSuffixes === void 0 ? void 0 : forbiddenSuffixes.find(suffix => name.endsWith(suffix));
271
311
  if (forbiddenSuffix) {
272
312
  return {
273
313
  errorMessage: `not have "${forbiddenSuffix}" suffix`,
274
- renameToName: name.replace(new RegExp(`${forbiddenSuffix}$`), ''),
314
+ renameToNames: [name.replace(new RegExp(`${forbiddenSuffix}$`), '')],
315
+ };
316
+ }
317
+ if (requiredPrefixes &&
318
+ !requiredPrefixes.some(requiredPrefix => name.startsWith(requiredPrefix))) {
319
+ return {
320
+ errorMessage: `have one of the following prefixes: ${englishJoinWords(requiredPrefixes)}`,
321
+ renameToNames: style
322
+ ? requiredPrefixes.map(prefix => convertCase(style, `${prefix} ${name}`))
323
+ : requiredPrefixes.map(prefix => `${prefix}${name}`),
324
+ };
325
+ }
326
+ if (requiredSuffixes &&
327
+ !requiredSuffixes.some(requiredSuffix => name.endsWith(requiredSuffix))) {
328
+ return {
329
+ errorMessage: `have one of the following suffixes: ${englishJoinWords(requiredSuffixes)}`,
330
+ renameToNames: style
331
+ ? requiredSuffixes.map(suffix => convertCase(style, `${name} ${suffix}`))
332
+ : requiredSuffixes.map(suffix => `${name}${suffix}`),
275
333
  };
276
334
  }
277
335
  // Style is optional
@@ -282,14 +340,16 @@ export const rule = {
282
340
  if (!caseRegex.test(name)) {
283
341
  return {
284
342
  errorMessage: `be in ${style} format`,
285
- renameToName: convertCase(style, name),
343
+ renameToNames: [convertCase(style, name)],
286
344
  };
287
345
  }
288
346
  }
289
347
  };
290
348
  const checkUnderscore = (isLeading) => (node) => {
291
349
  const suggestedName = node.value.replace(isLeading ? /^_+/ : /_+$/, '');
292
- report(node, `${isLeading ? 'Leading' : 'Trailing'} underscores are not allowed`, suggestedName);
350
+ report(node, `${isLeading ? 'Leading' : 'Trailing'} underscores are not allowed`, [
351
+ suggestedName,
352
+ ]);
293
353
  };
294
354
  const listeners = {};
295
355
  if (!allowLeadingUnderscore) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graphql-eslint/eslint-plugin",
3
- "version": "3.17.0",
3
+ "version": "3.18.1-alpha-20230519144328-0e3b663",
4
4
  "description": "GraphQL plugin for ESLint",
5
5
  "sideEffects": false,
6
6
  "peerDependencies": {
@@ -8,9 +8,9 @@
8
8
  },
9
9
  "dependencies": {
10
10
  "@babel/code-frame": "^7.18.6",
11
- "@graphql-tools/code-file-loader": "^7.3.6",
12
- "@graphql-tools/graphql-tag-pluck": "^7.3.6",
13
- "@graphql-tools/utils": "^9.0.0",
11
+ "@graphql-tools/code-file-loader": "^8.0.0",
12
+ "@graphql-tools/graphql-tag-pluck": "^8.0.0",
13
+ "@graphql-tools/utils": "^10.0.0",
14
14
  "chalk": "^4.1.2",
15
15
  "debug": "^4.3.4",
16
16
  "fast-glob": "^3.2.12",
@@ -51,6 +51,8 @@ export declare const rules: {
51
51
  prefix?: string | undefined;
52
52
  forbiddenPrefixes?: string[] | undefined;
53
53
  forbiddenSuffixes?: string[] | undefined;
54
+ requiredPrefixes?: string[] | undefined;
55
+ requiredSuffixes?: string[] | undefined;
54
56
  ignorePattern?: string | undefined;
55
57
  } | undefined;
56
58
  allowLeadingUnderscore?: boolean | undefined;
@@ -51,6 +51,8 @@ export declare const rules: {
51
51
  prefix?: string | undefined;
52
52
  forbiddenPrefixes?: string[] | undefined;
53
53
  forbiddenSuffixes?: string[] | undefined;
54
+ requiredPrefixes?: string[] | undefined;
55
+ requiredSuffixes?: string[] | undefined;
54
56
  ignorePattern?: string | undefined;
55
57
  } | undefined;
56
58
  allowLeadingUnderscore?: boolean | undefined;
@@ -36,6 +36,22 @@ declare const schema: {
36
36
  readonly type: "string";
37
37
  };
38
38
  };
39
+ readonly requiredPrefixes: {
40
+ readonly type: "array";
41
+ readonly uniqueItems: true;
42
+ readonly minItems: 1;
43
+ readonly items: {
44
+ readonly type: "string";
45
+ };
46
+ };
47
+ readonly requiredSuffixes: {
48
+ readonly type: "array";
49
+ readonly uniqueItems: true;
50
+ readonly minItems: 1;
51
+ readonly items: {
52
+ readonly type: "string";
53
+ };
54
+ };
39
55
  readonly ignorePattern: {
40
56
  readonly type: "string";
41
57
  readonly description: "Option to skip validation of some words, e.g. acronyms";
@@ -36,6 +36,22 @@ declare const schema: {
36
36
  readonly type: "string";
37
37
  };
38
38
  };
39
+ readonly requiredPrefixes: {
40
+ readonly type: "array";
41
+ readonly uniqueItems: true;
42
+ readonly minItems: 1;
43
+ readonly items: {
44
+ readonly type: "string";
45
+ };
46
+ };
47
+ readonly requiredSuffixes: {
48
+ readonly type: "array";
49
+ readonly uniqueItems: true;
50
+ readonly minItems: 1;
51
+ readonly items: {
52
+ readonly type: "string";
53
+ };
54
+ };
39
55
  readonly ignorePattern: {
40
56
  readonly type: "string";
41
57
  readonly description: "Option to skip validation of some words, e.g. acronyms";