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

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