@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.
- package/cjs/rules/naming-convention.js +77 -17
- package/esm/rules/naming-convention.js +78 -18
- package/package.json +4 -4
- package/typings/rules/index.d.cts +2 -0
- package/typings/rules/index.d.ts +2 -0
- package/typings/rules/naming-convention.d.cts +16 -0
- package/typings/rules/naming-convention.d.ts +16 -0
@@ -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,
|
263
|
+
function report(node, message, suggestedNames) {
|
222
264
|
context.report({
|
223
265
|
node,
|
224
266
|
message,
|
225
|
-
suggest:
|
226
|
-
{
|
227
|
-
|
228
|
-
|
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,
|
283
|
+
const { errorMessage, renameToNames } = error;
|
244
284
|
const [leadingUnderscores] = nodeName.match(/^_*/);
|
245
285
|
const [trailingUnderscores] = nodeName.match(/_*$/);
|
246
|
-
const
|
247
|
-
report(node, `${nodeType} "${nodeName}" should ${errorMessage}`,
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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`,
|
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,
|
260
|
+
function report(node, message, suggestedNames) {
|
219
261
|
context.report({
|
220
262
|
node,
|
221
263
|
message,
|
222
|
-
suggest:
|
223
|
-
{
|
224
|
-
|
225
|
-
|
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,
|
280
|
+
const { errorMessage, renameToNames } = error;
|
241
281
|
const [leadingUnderscores] = nodeName.match(/^_*/);
|
242
282
|
const [trailingUnderscores] = nodeName.match(/_*$/);
|
243
|
-
const
|
244
|
-
report(node, `${nodeType} "${nodeName}" should ${errorMessage}`,
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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`,
|
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.
|
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": "^
|
12
|
-
"@graphql-tools/graphql-tag-pluck": "^
|
13
|
-
"@graphql-tools/utils": "^
|
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;
|
package/typings/rules/index.d.ts
CHANGED
@@ -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";
|