@graphql-eslint/eslint-plugin 2.5.0 → 3.0.0-alpha-0a66b90.0
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/configs/all.d.ts +35 -2
- package/configs/index.d.ts +64 -3
- package/configs/recommended.d.ts +29 -1
- package/docs/rules/alphabetize.md +35 -10
- package/docs/rules/description-style.md +2 -2
- package/docs/rules/match-document-filename.md +7 -7
- package/docs/rules/naming-convention.md +54 -83
- package/docs/rules/require-description.md +35 -6
- package/docs/rules/strict-id-in-types.md +4 -4
- package/index.js +320 -297
- package/index.mjs +320 -297
- package/package.json +1 -1
- package/rules/alphabetize.d.ts +5 -6
- package/rules/index.d.ts +135 -21
- package/rules/naming-convention.d.ts +32 -25
- package/rules/require-description.d.ts +10 -6
- package/utils.d.ts +2 -7
package/index.mjs
CHANGED
@@ -29,7 +29,34 @@ const recommendedConfig = {
|
|
29
29
|
'@graphql-eslint/known-type-names': 'error',
|
30
30
|
'@graphql-eslint/lone-anonymous-operation': 'error',
|
31
31
|
'@graphql-eslint/lone-schema-definition': 'error',
|
32
|
-
'@graphql-eslint/naming-convention':
|
32
|
+
'@graphql-eslint/naming-convention': [
|
33
|
+
'error',
|
34
|
+
{
|
35
|
+
types: 'PascalCase',
|
36
|
+
fields: 'camelCase',
|
37
|
+
overrides: {
|
38
|
+
EnumValueDefinition: 'UPPER_CASE',
|
39
|
+
OperationDefinition: {
|
40
|
+
style: 'PascalCase',
|
41
|
+
forbiddenPrefixes: ['Query', 'Mutation', 'Subscription', 'Get'],
|
42
|
+
forbiddenSuffixes: ['Query', 'Mutation', 'Subscription'],
|
43
|
+
},
|
44
|
+
FragmentDefinition: { style: 'PascalCase', forbiddenPrefixes: ['Fragment'], forbiddenSuffixes: ['Fragment'] },
|
45
|
+
'FieldDefinition[parent.name.value=Query]': {
|
46
|
+
forbiddenPrefixes: ['query', 'get'],
|
47
|
+
forbiddenSuffixes: ['Query'],
|
48
|
+
},
|
49
|
+
'FieldDefinition[parent.name.value=Mutation]': {
|
50
|
+
forbiddenPrefixes: ['mutation'],
|
51
|
+
forbiddenSuffixes: ['Mutation'],
|
52
|
+
},
|
53
|
+
'FieldDefinition[parent.name.value=Subscription]': {
|
54
|
+
forbiddenPrefixes: ['subscription'],
|
55
|
+
forbiddenSuffixes: ['Subscription'],
|
56
|
+
},
|
57
|
+
},
|
58
|
+
},
|
59
|
+
],
|
33
60
|
'@graphql-eslint/no-anonymous-operations': 'error',
|
34
61
|
'@graphql-eslint/no-case-insensitive-enum-values-duplicates': 'error',
|
35
62
|
'@graphql-eslint/no-fragment-cycles': 'error',
|
@@ -89,7 +116,7 @@ const allConfig = {
|
|
89
116
|
'@graphql-eslint/no-unreachable-types': 'error',
|
90
117
|
'@graphql-eslint/no-unused-fields': 'error',
|
91
118
|
'@graphql-eslint/require-deprecation-date': 'error',
|
92
|
-
'@graphql-eslint/require-description': 'error',
|
119
|
+
'@graphql-eslint/require-description': ['error', { types: true, overrides: { DirectiveDefinition: true } }],
|
93
120
|
'@graphql-eslint/require-field-of-type-query-in-mutation-result': 'error',
|
94
121
|
'@graphql-eslint/require-id-when-available': 'error',
|
95
122
|
'@graphql-eslint/selection-set-depth': 'error',
|
@@ -206,9 +233,14 @@ const loaderCache = new Proxy(Object.create(null), {
|
|
206
233
|
return true;
|
207
234
|
},
|
208
235
|
});
|
209
|
-
const
|
210
|
-
|
211
|
-
|
236
|
+
const TYPES_KINDS = [
|
237
|
+
Kind.OBJECT_TYPE_DEFINITION,
|
238
|
+
Kind.INTERFACE_TYPE_DEFINITION,
|
239
|
+
Kind.ENUM_TYPE_DEFINITION,
|
240
|
+
Kind.SCALAR_TYPE_DEFINITION,
|
241
|
+
Kind.INPUT_OBJECT_TYPE_DEFINITION,
|
242
|
+
Kind.UNION_TYPE_DEFINITION,
|
243
|
+
];
|
212
244
|
var CaseStyle;
|
213
245
|
(function (CaseStyle) {
|
214
246
|
CaseStyle["camelCase"] = "camelCase";
|
@@ -259,7 +291,7 @@ function getLocation(loc, fieldName = '', offset) {
|
|
259
291
|
}
|
260
292
|
|
261
293
|
function extractRuleName(stack) {
|
262
|
-
const match = (stack || '').match(/validation[
|
294
|
+
const match = (stack || '').match(/validation[/\\]rules[/\\](.*?)\.js:/) || [];
|
263
295
|
return match[1] || null;
|
264
296
|
}
|
265
297
|
function validateDoc(sourceNode, context, schema, documentNode, rules, ruleName = null) {
|
@@ -508,11 +540,23 @@ const GRAPHQL_JS_VALIDATIONS = Object.assign({}, validationToRule('executable-de
|
|
508
540
|
}));
|
509
541
|
|
510
542
|
const ALPHABETIZE = 'ALPHABETIZE';
|
511
|
-
const fieldsEnum = [
|
543
|
+
const fieldsEnum = [
|
544
|
+
Kind.OBJECT_TYPE_DEFINITION,
|
545
|
+
Kind.INTERFACE_TYPE_DEFINITION,
|
546
|
+
Kind.INPUT_OBJECT_TYPE_DEFINITION,
|
547
|
+
];
|
512
548
|
const valuesEnum = [Kind.ENUM_TYPE_DEFINITION];
|
513
|
-
const selectionsEnum = [
|
549
|
+
const selectionsEnum = [
|
550
|
+
Kind.OPERATION_DEFINITION,
|
551
|
+
Kind.FRAGMENT_DEFINITION,
|
552
|
+
];
|
514
553
|
const variablesEnum = [Kind.OPERATION_DEFINITION];
|
515
|
-
const argumentsEnum = [
|
554
|
+
const argumentsEnum = [
|
555
|
+
Kind.FIELD_DEFINITION,
|
556
|
+
Kind.FIELD,
|
557
|
+
Kind.DIRECTIVE_DEFINITION,
|
558
|
+
Kind.DIRECTIVE,
|
559
|
+
];
|
516
560
|
const rule = {
|
517
561
|
meta: {
|
518
562
|
type: 'suggestion',
|
@@ -620,38 +664,48 @@ const rule = {
|
|
620
664
|
properties: {
|
621
665
|
fields: {
|
622
666
|
type: 'array',
|
623
|
-
|
667
|
+
uniqueItems: true,
|
668
|
+
minItems: 1,
|
669
|
+
items: {
|
624
670
|
enum: fieldsEnum,
|
625
671
|
},
|
626
|
-
description: 'Fields of `type`, `interface`, and `input
|
672
|
+
description: 'Fields of `type`, `interface`, and `input`',
|
627
673
|
},
|
628
674
|
values: {
|
629
675
|
type: 'array',
|
630
|
-
|
676
|
+
uniqueItems: true,
|
677
|
+
minItems: 1,
|
678
|
+
items: {
|
631
679
|
enum: valuesEnum,
|
632
680
|
},
|
633
|
-
description: 'Values of `enum
|
681
|
+
description: 'Values of `enum`',
|
634
682
|
},
|
635
683
|
selections: {
|
636
684
|
type: 'array',
|
637
|
-
|
685
|
+
uniqueItems: true,
|
686
|
+
minItems: 1,
|
687
|
+
items: {
|
638
688
|
enum: selectionsEnum,
|
639
689
|
},
|
640
|
-
description: 'Selections of operations (`query`, `mutation` and `subscription`) and `fragment
|
690
|
+
description: 'Selections of operations (`query`, `mutation` and `subscription`) and `fragment`',
|
641
691
|
},
|
642
692
|
variables: {
|
643
693
|
type: 'array',
|
644
|
-
|
694
|
+
uniqueItems: true,
|
695
|
+
minItems: 1,
|
696
|
+
items: {
|
645
697
|
enum: variablesEnum,
|
646
698
|
},
|
647
|
-
description: 'Variables of operations (`query`, `mutation` and `subscription`)
|
699
|
+
description: 'Variables of operations (`query`, `mutation` and `subscription`)',
|
648
700
|
},
|
649
701
|
arguments: {
|
650
702
|
type: 'array',
|
651
|
-
|
703
|
+
uniqueItems: true,
|
704
|
+
minItems: 1,
|
705
|
+
items: {
|
652
706
|
enum: argumentsEnum,
|
653
707
|
},
|
654
|
-
description: 'Arguments of fields and directives
|
708
|
+
description: 'Arguments of fields and directives',
|
655
709
|
},
|
656
710
|
},
|
657
711
|
},
|
@@ -682,7 +736,7 @@ const rule = {
|
|
682
736
|
const opts = context.options[0];
|
683
737
|
const fields = new Set((_a = opts.fields) !== null && _a !== void 0 ? _a : []);
|
684
738
|
const listeners = {};
|
685
|
-
const
|
739
|
+
const kinds = [
|
686
740
|
fields.has(Kind.OBJECT_TYPE_DEFINITION) && [Kind.OBJECT_TYPE_DEFINITION, Kind.OBJECT_TYPE_EXTENSION],
|
687
741
|
fields.has(Kind.INTERFACE_TYPE_DEFINITION) && [Kind.INTERFACE_TYPE_DEFINITION, Kind.INTERFACE_TYPE_EXTENSION],
|
688
742
|
fields.has(Kind.INPUT_OBJECT_TYPE_DEFINITION) && [
|
@@ -690,8 +744,9 @@ const rule = {
|
|
690
744
|
Kind.INPUT_OBJECT_TYPE_EXTENSION,
|
691
745
|
],
|
692
746
|
]
|
693
|
-
.
|
694
|
-
.
|
747
|
+
.filter(Boolean)
|
748
|
+
.flat();
|
749
|
+
const fieldsSelector = kinds.join(',');
|
695
750
|
const hasEnumValues = ((_b = opts.values) === null || _b === void 0 ? void 0 : _b[0]) === Kind.ENUM_TYPE_DEFINITION;
|
696
751
|
const selectionsSelector = (_c = opts.selections) === null || _c === void 0 ? void 0 : _c.join(',');
|
697
752
|
const hasVariables = ((_d = opts.variables) === null || _d === void 0 ? void 0 : _d[0]) === Kind.OPERATION_DEFINITION;
|
@@ -1091,6 +1146,9 @@ const rule$5 = {
|
|
1091
1146
|
},
|
1092
1147
|
};
|
1093
1148
|
|
1149
|
+
const isObjectType = (node) => [Kind.OBJECT_TYPE_DEFINITION, Kind.OBJECT_TYPE_EXTENSION].includes(node.type);
|
1150
|
+
const isQueryType = (node) => isObjectType(node) && node.name.value === 'Query';
|
1151
|
+
const isMutationType = (node) => isObjectType(node) && node.name.value === 'Mutation';
|
1094
1152
|
const rule$6 = {
|
1095
1153
|
meta: {
|
1096
1154
|
type: 'suggestion',
|
@@ -1131,6 +1189,7 @@ const rule$6 = {
|
|
1131
1189
|
schema: [
|
1132
1190
|
{
|
1133
1191
|
type: 'object',
|
1192
|
+
additionalProperties: false,
|
1134
1193
|
properties: {
|
1135
1194
|
checkInputType: {
|
1136
1195
|
type: 'boolean',
|
@@ -1153,24 +1212,22 @@ const rule$6 = {
|
|
1153
1212
|
description: 'Apply the rule to Mutations',
|
1154
1213
|
},
|
1155
1214
|
},
|
1156
|
-
additionalProperties: false,
|
1157
1215
|
},
|
1158
1216
|
],
|
1159
1217
|
},
|
1160
1218
|
create(context) {
|
1161
|
-
var _a;
|
1162
1219
|
const options = {
|
1163
|
-
caseSensitiveInputType: true,
|
1164
1220
|
checkInputType: false,
|
1165
|
-
|
1221
|
+
caseSensitiveInputType: true,
|
1166
1222
|
checkQueries: false,
|
1167
|
-
|
1223
|
+
checkMutations: true,
|
1224
|
+
...context.options[0],
|
1168
1225
|
};
|
1169
1226
|
const shouldCheckType = node => (options.checkMutations && isMutationType(node)) || (options.checkQueries && isQueryType(node));
|
1170
1227
|
const listeners = {
|
1171
|
-
'FieldDefinition > InputValueDefinition'
|
1172
|
-
|
1173
|
-
|
1228
|
+
'FieldDefinition > InputValueDefinition[name.value!=input]'(node) {
|
1229
|
+
if (shouldCheckType(node.parent.parent)) {
|
1230
|
+
const name = node.name.value;
|
1174
1231
|
context.report({
|
1175
1232
|
loc: getLocation(node.loc, name),
|
1176
1233
|
message: `Input "${name}" should be called "input"`,
|
@@ -1178,11 +1235,11 @@ const rule$6 = {
|
|
1178
1235
|
}
|
1179
1236
|
},
|
1180
1237
|
};
|
1181
|
-
if (options
|
1182
|
-
listeners['FieldDefinition > InputValueDefinition NamedType'] = node => {
|
1238
|
+
if (options.checkInputType) {
|
1239
|
+
listeners['FieldDefinition > InputValueDefinition NamedType'] = (node) => {
|
1183
1240
|
const findInputType = item => {
|
1184
1241
|
let currentNode = item;
|
1185
|
-
while (currentNode.type !==
|
1242
|
+
while (currentNode.type !== Kind.INPUT_VALUE_DEFINITION) {
|
1186
1243
|
currentNode = currentNode.parent;
|
1187
1244
|
}
|
1188
1245
|
return currentNode;
|
@@ -1399,69 +1456,40 @@ const rule$7 = {
|
|
1399
1456
|
},
|
1400
1457
|
};
|
1401
1458
|
|
1402
|
-
const
|
1403
|
-
|
1404
|
-
|
1405
|
-
|
1406
|
-
|
1407
|
-
|
1408
|
-
const acceptedStyles = [
|
1409
|
-
'camelCase',
|
1410
|
-
'PascalCase',
|
1411
|
-
'snake_case',
|
1412
|
-
'UPPER_CASE',
|
1459
|
+
const FIELDS_KINDS = [
|
1460
|
+
Kind.FIELD_DEFINITION,
|
1461
|
+
Kind.INPUT_VALUE_DEFINITION,
|
1462
|
+
Kind.VARIABLE_DEFINITION,
|
1463
|
+
Kind.ARGUMENT,
|
1464
|
+
Kind.DIRECTIVE_DEFINITION,
|
1413
1465
|
];
|
1414
|
-
|
1415
|
-
|
1416
|
-
|
1417
|
-
|
1418
|
-
|
1419
|
-
|
1420
|
-
|
1421
|
-
|
1422
|
-
|
1423
|
-
|
1424
|
-
|
1425
|
-
|
1426
|
-
|
1427
|
-
|
1428
|
-
|
1429
|
-
|
1430
|
-
|
1431
|
-
|
1432
|
-
|
1433
|
-
|
1434
|
-
|
1435
|
-
|
1436
|
-
|
1437
|
-
|
1438
|
-
|
1439
|
-
|
1440
|
-
|
1441
|
-
if (forbiddenPrefixes.some(forbiddenPrefix => name.startsWith(forbiddenPrefix))) {
|
1442
|
-
return {
|
1443
|
-
ok: false,
|
1444
|
-
errorMessage: '{{nodeType}} "{{nodeName}}" should not have one of the following prefix(es): {{forbiddenPrefixes}}',
|
1445
|
-
};
|
1446
|
-
}
|
1447
|
-
if (forbiddenSuffixes.some(forbiddenSuffix => name.endsWith(forbiddenSuffix))) {
|
1448
|
-
return {
|
1449
|
-
ok: false,
|
1450
|
-
errorMessage: '{{nodeType}} "{{nodeName}}" should not have one of the following suffix(es): {{forbiddenSuffixes}}',
|
1451
|
-
};
|
1452
|
-
}
|
1453
|
-
if (!formats[style]) {
|
1454
|
-
return { ok: true };
|
1455
|
-
}
|
1456
|
-
const ok = new RegExp(formats[style]).test(name);
|
1457
|
-
if (ok) {
|
1458
|
-
return { ok: true };
|
1459
|
-
}
|
1460
|
-
return {
|
1461
|
-
ok: false,
|
1462
|
-
errorMessage: '{{nodeType}} name "{{nodeName}}" should be in {{format}} format',
|
1463
|
-
};
|
1464
|
-
}
|
1466
|
+
const KindToDisplayName = {
|
1467
|
+
// types
|
1468
|
+
[Kind.OBJECT_TYPE_DEFINITION]: 'Type',
|
1469
|
+
[Kind.INTERFACE_TYPE_DEFINITION]: 'Interface',
|
1470
|
+
[Kind.ENUM_TYPE_DEFINITION]: 'Enumerator',
|
1471
|
+
[Kind.SCALAR_TYPE_DEFINITION]: 'Scalar',
|
1472
|
+
[Kind.INPUT_OBJECT_TYPE_DEFINITION]: 'Input type',
|
1473
|
+
[Kind.UNION_TYPE_DEFINITION]: 'Union',
|
1474
|
+
// fields
|
1475
|
+
[Kind.FIELD_DEFINITION]: 'Field',
|
1476
|
+
[Kind.INPUT_VALUE_DEFINITION]: 'Input property',
|
1477
|
+
[Kind.VARIABLE_DEFINITION]: 'Variable',
|
1478
|
+
[Kind.ARGUMENT]: 'Argument',
|
1479
|
+
[Kind.DIRECTIVE_DEFINITION]: 'Directive',
|
1480
|
+
// rest
|
1481
|
+
[Kind.ENUM_VALUE_DEFINITION]: 'Enumeration value',
|
1482
|
+
[Kind.OPERATION_DEFINITION]: 'Operation',
|
1483
|
+
[Kind.FRAGMENT_DEFINITION]: 'Fragment',
|
1484
|
+
};
|
1485
|
+
const StyleToRegex = {
|
1486
|
+
camelCase: /^[a-z][\dA-Za-z]*$/,
|
1487
|
+
PascalCase: /^[A-Z][\dA-Za-z]*$/,
|
1488
|
+
snake_case: /^[a-z][\d_a-z]*[\da-z]$/,
|
1489
|
+
UPPER_CASE: /^[A-Z][\dA-Z_]*[\dA-Z]$/,
|
1490
|
+
};
|
1491
|
+
const ALLOWED_KINDS = Object.keys(KindToDisplayName).sort();
|
1492
|
+
const ALLOWED_STYLES = Object.keys(StyleToRegex);
|
1465
1493
|
const schemaOption$1 = {
|
1466
1494
|
oneOf: [{ $ref: '#/definitions/asString' }, { $ref: '#/definitions/asObject' }],
|
1467
1495
|
};
|
@@ -1476,89 +1504,120 @@ const rule$8 = {
|
|
1476
1504
|
examples: [
|
1477
1505
|
{
|
1478
1506
|
title: 'Incorrect',
|
1479
|
-
usage: [{
|
1507
|
+
usage: [{ types: 'PascalCase', fields: 'camelCase' }],
|
1480
1508
|
code: /* GraphQL */ `
|
1481
|
-
type
|
1482
|
-
|
1509
|
+
type user {
|
1510
|
+
first_name: String!
|
1483
1511
|
}
|
1484
1512
|
`,
|
1485
1513
|
},
|
1486
1514
|
{
|
1487
1515
|
title: 'Correct',
|
1488
|
-
usage: [{
|
1516
|
+
usage: [{ types: 'PascalCase', fields: 'camelCase' }],
|
1489
1517
|
code: /* GraphQL */ `
|
1490
|
-
type
|
1491
|
-
|
1518
|
+
type User {
|
1519
|
+
firstName: String
|
1492
1520
|
}
|
1493
1521
|
`,
|
1494
1522
|
},
|
1495
1523
|
],
|
1524
|
+
optionsForConfig: [
|
1525
|
+
{
|
1526
|
+
types: 'PascalCase',
|
1527
|
+
fields: 'camelCase',
|
1528
|
+
overrides: {
|
1529
|
+
EnumValueDefinition: 'UPPER_CASE',
|
1530
|
+
OperationDefinition: {
|
1531
|
+
style: 'PascalCase',
|
1532
|
+
forbiddenPrefixes: ['Query', 'Mutation', 'Subscription', 'Get'],
|
1533
|
+
forbiddenSuffixes: ['Query', 'Mutation', 'Subscription'],
|
1534
|
+
},
|
1535
|
+
FragmentDefinition: {
|
1536
|
+
style: 'PascalCase',
|
1537
|
+
forbiddenPrefixes: ['Fragment'],
|
1538
|
+
forbiddenSuffixes: ['Fragment'],
|
1539
|
+
},
|
1540
|
+
'FieldDefinition[parent.name.value=Query]': {
|
1541
|
+
forbiddenPrefixes: ['query', 'get'],
|
1542
|
+
forbiddenSuffixes: ['Query'],
|
1543
|
+
},
|
1544
|
+
'FieldDefinition[parent.name.value=Mutation]': {
|
1545
|
+
forbiddenPrefixes: ['mutation'],
|
1546
|
+
forbiddenSuffixes: ['Mutation'],
|
1547
|
+
},
|
1548
|
+
'FieldDefinition[parent.name.value=Subscription]': {
|
1549
|
+
forbiddenPrefixes: ['subscription'],
|
1550
|
+
forbiddenSuffixes: ['Subscription'],
|
1551
|
+
},
|
1552
|
+
},
|
1553
|
+
},
|
1554
|
+
],
|
1496
1555
|
},
|
1497
1556
|
schema: {
|
1498
1557
|
definitions: {
|
1499
1558
|
asString: {
|
1500
|
-
|
1501
|
-
description: `One of: ${
|
1502
|
-
enum: acceptedStyles,
|
1559
|
+
enum: ALLOWED_STYLES,
|
1560
|
+
description: `One of: ${ALLOWED_STYLES.map(t => `\`${t}\``).join(', ')}`,
|
1503
1561
|
},
|
1504
1562
|
asObject: {
|
1505
1563
|
type: 'object',
|
1564
|
+
additionalProperties: false,
|
1506
1565
|
properties: {
|
1507
|
-
style: {
|
1508
|
-
|
1509
|
-
|
1510
|
-
},
|
1511
|
-
prefix: {
|
1512
|
-
type: 'string',
|
1513
|
-
},
|
1514
|
-
suffix: {
|
1515
|
-
type: 'string',
|
1516
|
-
},
|
1566
|
+
style: { enum: ALLOWED_STYLES },
|
1567
|
+
prefix: { type: 'string' },
|
1568
|
+
suffix: { type: 'string' },
|
1517
1569
|
forbiddenPrefixes: {
|
1518
|
-
additionalItems: false,
|
1519
1570
|
type: 'array',
|
1571
|
+
uniqueItems: true,
|
1520
1572
|
minItems: 1,
|
1521
|
-
items: {
|
1522
|
-
type: 'string',
|
1523
|
-
},
|
1573
|
+
items: { type: 'string' },
|
1524
1574
|
},
|
1525
1575
|
forbiddenSuffixes: {
|
1526
|
-
additionalItems: false,
|
1527
1576
|
type: 'array',
|
1577
|
+
uniqueItems: true,
|
1528
1578
|
minItems: 1,
|
1529
|
-
items: {
|
1530
|
-
type: 'string',
|
1531
|
-
},
|
1579
|
+
items: { type: 'string' },
|
1532
1580
|
},
|
1533
1581
|
},
|
1534
1582
|
},
|
1535
1583
|
},
|
1536
|
-
$schema: 'http://json-schema.org/draft-04/schema#',
|
1537
1584
|
type: 'array',
|
1585
|
+
maxItems: 1,
|
1538
1586
|
items: {
|
1539
1587
|
type: 'object',
|
1588
|
+
additionalProperties: false,
|
1540
1589
|
properties: {
|
1541
|
-
|
1542
|
-
|
1543
|
-
|
1544
|
-
[Kind.INPUT_VALUE_DEFINITION]: schemaOption$1,
|
1545
|
-
[Kind.OBJECT_TYPE_DEFINITION]: schemaOption$1,
|
1546
|
-
[Kind.INTERFACE_TYPE_DEFINITION]: schemaOption$1,
|
1547
|
-
[Kind.ENUM_TYPE_DEFINITION]: schemaOption$1,
|
1548
|
-
[Kind.UNION_TYPE_DEFINITION]: schemaOption$1,
|
1549
|
-
[Kind.SCALAR_TYPE_DEFINITION]: schemaOption$1,
|
1550
|
-
[Kind.OPERATION_DEFINITION]: schemaOption$1,
|
1551
|
-
[Kind.FRAGMENT_DEFINITION]: schemaOption$1,
|
1552
|
-
QueryDefinition: schemaOption$1,
|
1553
|
-
leadingUnderscore: {
|
1554
|
-
type: 'string',
|
1555
|
-
enum: ['allow', 'forbid'],
|
1556
|
-
default: 'forbid',
|
1590
|
+
types: {
|
1591
|
+
...schemaOption$1,
|
1592
|
+
description: `Includes:\n\n${TYPES_KINDS.map(kind => `- [${kind}](https://spec.graphql.org/October2021/#${kind})`).join('\n')}`,
|
1557
1593
|
},
|
1558
|
-
|
1559
|
-
|
1560
|
-
|
1561
|
-
|
1594
|
+
fields: {
|
1595
|
+
...schemaOption$1,
|
1596
|
+
description: `Includes:\n\n${FIELDS_KINDS.map(kind => `- [${kind}](https://spec.graphql.org/October2021/#${kind})`).join('\n')}`,
|
1597
|
+
},
|
1598
|
+
allowLeadingUnderscore: {
|
1599
|
+
type: 'boolean',
|
1600
|
+
default: false,
|
1601
|
+
},
|
1602
|
+
allowTrailingUnderscore: {
|
1603
|
+
type: 'boolean',
|
1604
|
+
default: false,
|
1605
|
+
},
|
1606
|
+
overrides: {
|
1607
|
+
type: 'object',
|
1608
|
+
additionalProperties: false,
|
1609
|
+
description: [
|
1610
|
+
'May contain the following `ASTNode` names:',
|
1611
|
+
'',
|
1612
|
+
...ALLOWED_KINDS.map(kind => `- [${kind}](https://spec.graphql.org/October2021/#${kind})`),
|
1613
|
+
'',
|
1614
|
+
"> It's also possible to use a [`selector`](https://eslint.org/docs/developer-guide/selectors) that starts with `ASTNode` name",
|
1615
|
+
'>',
|
1616
|
+
'> Example: pattern property `FieldDefinition[parent.name.value=Query]` will match only fields for type `Query`',
|
1617
|
+
].join('\n'),
|
1618
|
+
patternProperties: {
|
1619
|
+
[`^(${ALLOWED_KINDS.join('|')})(.+)?$`]: schemaOption$1,
|
1620
|
+
},
|
1562
1621
|
},
|
1563
1622
|
},
|
1564
1623
|
},
|
@@ -1566,136 +1625,83 @@ const rule$8 = {
|
|
1566
1625
|
},
|
1567
1626
|
create(context) {
|
1568
1627
|
const options = {
|
1569
|
-
|
1570
|
-
|
1571
|
-
...(context.options[0] || {}),
|
1628
|
+
overrides: {},
|
1629
|
+
...context.options[0],
|
1572
1630
|
};
|
1573
|
-
|
1574
|
-
|
1575
|
-
|
1576
|
-
|
1577
|
-
|
1578
|
-
|
1579
|
-
|
1580
|
-
|
1581
|
-
|
1582
|
-
|
1583
|
-
|
1584
|
-
}
|
1585
|
-
|
1631
|
+
function normalisePropertyOption(kind) {
|
1632
|
+
let style = options.overrides[kind];
|
1633
|
+
if (!style) {
|
1634
|
+
style = TYPES_KINDS.includes(kind) ? options.types : options.fields;
|
1635
|
+
}
|
1636
|
+
return typeof style === 'object' ? style : { style };
|
1637
|
+
}
|
1638
|
+
const checkNode = (selector) => (node) => {
|
1639
|
+
const { name } = node.kind === Kind.VARIABLE_DEFINITION ? node.variable : node;
|
1640
|
+
if (!name) {
|
1641
|
+
return;
|
1642
|
+
}
|
1643
|
+
const { prefix, suffix, forbiddenPrefixes, forbiddenSuffixes, style } = normalisePropertyOption(selector);
|
1644
|
+
const nodeType = KindToDisplayName[node.kind] || node.kind;
|
1645
|
+
const nodeName = name.value;
|
1646
|
+
const errorMessage = getErrorMessage();
|
1647
|
+
if (errorMessage) {
|
1586
1648
|
context.report({
|
1587
|
-
loc: getLocation(
|
1588
|
-
message:
|
1589
|
-
data: {
|
1590
|
-
prefix,
|
1591
|
-
suffix,
|
1592
|
-
format: style,
|
1593
|
-
forbiddenPrefixes: forbiddenPrefixes.join(', '),
|
1594
|
-
forbiddenSuffixes: forbiddenSuffixes.join(', '),
|
1595
|
-
nodeType,
|
1596
|
-
nodeName: node.value,
|
1597
|
-
},
|
1649
|
+
loc: getLocation(name.loc, name.value),
|
1650
|
+
message: `${nodeType} "${nodeName}" should ${errorMessage}`,
|
1598
1651
|
});
|
1599
1652
|
}
|
1600
|
-
|
1601
|
-
|
1602
|
-
|
1603
|
-
|
1604
|
-
}
|
1605
|
-
return {
|
1606
|
-
style: value,
|
1607
|
-
prefix: '',
|
1608
|
-
suffix: '',
|
1609
|
-
};
|
1610
|
-
};
|
1611
|
-
return {
|
1612
|
-
Name: node => {
|
1613
|
-
if (node.value.startsWith('_') && options.leadingUnderscore === 'forbid') {
|
1614
|
-
context.report({
|
1615
|
-
loc: getLocation(node.loc, node.value),
|
1616
|
-
message: 'Leading underscores are not allowed',
|
1617
|
-
});
|
1653
|
+
function getErrorMessage() {
|
1654
|
+
let name = nodeName;
|
1655
|
+
if (options.allowLeadingUnderscore) {
|
1656
|
+
name = name.replace(/^_*/, '');
|
1618
1657
|
}
|
1619
|
-
if (
|
1620
|
-
|
1621
|
-
loc: getLocation(node.loc, node.value),
|
1622
|
-
message: 'Trailing underscores are not allowed',
|
1623
|
-
});
|
1658
|
+
if (options.allowTrailingUnderscore) {
|
1659
|
+
name = name.replace(/_*$/, '');
|
1624
1660
|
}
|
1625
|
-
|
1626
|
-
|
1627
|
-
if (options.ObjectTypeDefinition) {
|
1628
|
-
const property = normalisePropertyOption(options.ObjectTypeDefinition);
|
1629
|
-
checkNode(node.name, property, 'Type');
|
1661
|
+
if (prefix && !name.startsWith(prefix)) {
|
1662
|
+
return `have "${prefix}" prefix`;
|
1630
1663
|
}
|
1631
|
-
|
1632
|
-
|
1633
|
-
if (options.InterfaceTypeDefinition) {
|
1634
|
-
const property = normalisePropertyOption(options.InterfaceTypeDefinition);
|
1635
|
-
checkNode(node.name, property, 'Interface');
|
1664
|
+
if (suffix && !name.endsWith(suffix)) {
|
1665
|
+
return `have "${suffix}" suffix`;
|
1636
1666
|
}
|
1637
|
-
|
1638
|
-
|
1639
|
-
|
1640
|
-
const property = normalisePropertyOption(options.EnumTypeDefinition);
|
1641
|
-
checkNode(node.name, property, 'Enumerator');
|
1642
|
-
}
|
1643
|
-
},
|
1644
|
-
InputObjectTypeDefinition: node => {
|
1645
|
-
if (options.InputObjectTypeDefinition) {
|
1646
|
-
const property = normalisePropertyOption(options.InputObjectTypeDefinition);
|
1647
|
-
checkNode(node.name, property, 'Input type');
|
1667
|
+
const forbiddenPrefix = forbiddenPrefixes === null || forbiddenPrefixes === void 0 ? void 0 : forbiddenPrefixes.find(prefix => name.startsWith(prefix));
|
1668
|
+
if (forbiddenPrefix) {
|
1669
|
+
return `not have "${forbiddenPrefix}" prefix`;
|
1648
1670
|
}
|
1649
|
-
|
1650
|
-
|
1651
|
-
|
1652
|
-
const property = normalisePropertyOption(options.QueryDefinition);
|
1653
|
-
checkNode(node.name, property, 'Query');
|
1671
|
+
const forbiddenSuffix = forbiddenSuffixes === null || forbiddenSuffixes === void 0 ? void 0 : forbiddenSuffixes.find(suffix => name.endsWith(suffix));
|
1672
|
+
if (forbiddenSuffix) {
|
1673
|
+
return `not have "${forbiddenSuffix}" suffix`;
|
1654
1674
|
}
|
1655
|
-
if (
|
1656
|
-
|
1657
|
-
checkNode(node.name, property, 'Field');
|
1675
|
+
if (style && !ALLOWED_STYLES.includes(style)) {
|
1676
|
+
return `be in one of the following options: ${ALLOWED_STYLES.join(', ')}`;
|
1658
1677
|
}
|
1659
|
-
|
1660
|
-
|
1661
|
-
|
1662
|
-
const property = normalisePropertyOption(options.EnumValueDefinition);
|
1663
|
-
checkNode(node.name, property, 'Enumeration value');
|
1678
|
+
const caseRegex = StyleToRegex[style];
|
1679
|
+
if (caseRegex && !caseRegex.test(name)) {
|
1680
|
+
return `be in ${style} format`;
|
1664
1681
|
}
|
1665
|
-
}
|
1666
|
-
InputValueDefinition: node => {
|
1667
|
-
if (options.InputValueDefinition) {
|
1668
|
-
const property = normalisePropertyOption(options.InputValueDefinition);
|
1669
|
-
checkNode(node.name, property, 'Input property');
|
1670
|
-
}
|
1671
|
-
},
|
1672
|
-
OperationDefinition: node => {
|
1673
|
-
if (options.OperationDefinition) {
|
1674
|
-
const property = normalisePropertyOption(options.OperationDefinition);
|
1675
|
-
if (node.name) {
|
1676
|
-
checkNode(node.name, property, 'Operation');
|
1677
|
-
}
|
1678
|
-
}
|
1679
|
-
},
|
1680
|
-
FragmentDefinition: node => {
|
1681
|
-
if (options.FragmentDefinition) {
|
1682
|
-
const property = normalisePropertyOption(options.FragmentDefinition);
|
1683
|
-
checkNode(node.name, property, 'Fragment');
|
1684
|
-
}
|
1685
|
-
},
|
1686
|
-
ScalarTypeDefinition: node => {
|
1687
|
-
if (options.ScalarTypeDefinition) {
|
1688
|
-
const property = normalisePropertyOption(options.ScalarTypeDefinition);
|
1689
|
-
checkNode(node.name, property, 'Scalar');
|
1690
|
-
}
|
1691
|
-
},
|
1692
|
-
UnionTypeDefinition: node => {
|
1693
|
-
if (options.UnionTypeDefinition) {
|
1694
|
-
const property = normalisePropertyOption(options.UnionTypeDefinition);
|
1695
|
-
checkNode(node.name, property, 'Union');
|
1696
|
-
}
|
1697
|
-
},
|
1682
|
+
}
|
1698
1683
|
};
|
1684
|
+
const checkUnderscore = (node) => {
|
1685
|
+
const name = node.value;
|
1686
|
+
context.report({
|
1687
|
+
loc: getLocation(node.loc, name),
|
1688
|
+
message: `${name.startsWith('_') ? 'Leading' : 'Trailing'} underscores are not allowed`,
|
1689
|
+
});
|
1690
|
+
};
|
1691
|
+
const listeners = {};
|
1692
|
+
if (!options.allowLeadingUnderscore) {
|
1693
|
+
listeners['Name[value=/^_/]:matches([parent.kind!=Field], [parent.kind=Field][parent.alias])'] = checkUnderscore;
|
1694
|
+
}
|
1695
|
+
if (!options.allowTrailingUnderscore) {
|
1696
|
+
listeners['Name[value=/_$/]:matches([parent.kind!=Field], [parent.kind=Field][parent.alias])'] = checkUnderscore;
|
1697
|
+
}
|
1698
|
+
const selectors = new Set([options.types && TYPES_KINDS, options.fields && FIELDS_KINDS, Object.keys(options.overrides)]
|
1699
|
+
.flat()
|
1700
|
+
.filter(Boolean));
|
1701
|
+
for (const selector of selectors) {
|
1702
|
+
listeners[selector] = checkNode(selector);
|
1703
|
+
}
|
1704
|
+
return listeners;
|
1699
1705
|
},
|
1700
1706
|
};
|
1701
1707
|
|
@@ -2412,6 +2418,7 @@ function convertDescription(node) {
|
|
2412
2418
|
return [];
|
2413
2419
|
}
|
2414
2420
|
|
2421
|
+
// eslint-disable-next-line unicorn/better-regex
|
2415
2422
|
const DATE_REGEX = /^\d{2}\/\d{2}\/\d{4}$/;
|
2416
2423
|
const MESSAGE_REQUIRE_DATE = 'MESSAGE_REQUIRE_DATE';
|
2417
2424
|
const MESSAGE_INVALID_FORMAT = 'MESSAGE_INVALID_FORMAT';
|
@@ -2575,41 +2582,23 @@ const rule$i = {
|
|
2575
2582
|
};
|
2576
2583
|
|
2577
2584
|
const REQUIRE_DESCRIPTION_ERROR = 'REQUIRE_DESCRIPTION_ERROR';
|
2578
|
-
const
|
2579
|
-
|
2580
|
-
Kind.OBJECT_TYPE_DEFINITION,
|
2585
|
+
const ALLOWED_KINDS$1 = [
|
2586
|
+
...TYPES_KINDS,
|
2581
2587
|
Kind.FIELD_DEFINITION,
|
2582
2588
|
Kind.INPUT_VALUE_DEFINITION,
|
2583
|
-
Kind.INTERFACE_TYPE_DEFINITION,
|
2584
|
-
Kind.UNION_TYPE_DEFINITION,
|
2585
|
-
Kind.ENUM_TYPE_DEFINITION,
|
2586
2589
|
Kind.ENUM_VALUE_DEFINITION,
|
2587
|
-
Kind.INPUT_OBJECT_TYPE_DEFINITION,
|
2588
2590
|
Kind.DIRECTIVE_DEFINITION,
|
2589
2591
|
];
|
2590
|
-
function verifyRule(context, node) {
|
2591
|
-
if (node) {
|
2592
|
-
if (!node.description || !node.description.value || node.description.value.trim().length === 0) {
|
2593
|
-
context.report({
|
2594
|
-
loc: getLocation(('name' in node ? node.name : node).loc, 'name' in node ? node.name.value : 'schema'),
|
2595
|
-
messageId: REQUIRE_DESCRIPTION_ERROR,
|
2596
|
-
data: {
|
2597
|
-
nodeType: node.kind,
|
2598
|
-
},
|
2599
|
-
});
|
2600
|
-
}
|
2601
|
-
}
|
2602
|
-
}
|
2603
2592
|
const rule$j = {
|
2604
2593
|
meta: {
|
2605
2594
|
docs: {
|
2606
2595
|
category: 'Best Practices',
|
2607
|
-
description:
|
2608
|
-
url:
|
2596
|
+
description: 'Enforce descriptions in your type definitions.',
|
2597
|
+
url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/require-description.md',
|
2609
2598
|
examples: [
|
2610
2599
|
{
|
2611
2600
|
title: 'Incorrect',
|
2612
|
-
usage: [{
|
2601
|
+
usage: [{ types: true, overrides: { FieldDefinition: true } }],
|
2613
2602
|
code: /* GraphQL */ `
|
2614
2603
|
type someTypeName {
|
2615
2604
|
name: String
|
@@ -2618,7 +2607,7 @@ const rule$j = {
|
|
2618
2607
|
},
|
2619
2608
|
{
|
2620
2609
|
title: 'Correct',
|
2621
|
-
usage: [{
|
2610
|
+
usage: [{ types: true, overrides: { FieldDefinition: true } }],
|
2622
2611
|
code: /* GraphQL */ `
|
2623
2612
|
"""
|
2624
2613
|
Some type description
|
@@ -2632,35 +2621,69 @@ const rule$j = {
|
|
2632
2621
|
`,
|
2633
2622
|
},
|
2634
2623
|
],
|
2624
|
+
optionsForConfig: [
|
2625
|
+
{
|
2626
|
+
types: true,
|
2627
|
+
overrides: {
|
2628
|
+
[Kind.DIRECTIVE_DEFINITION]: true,
|
2629
|
+
},
|
2630
|
+
},
|
2631
|
+
],
|
2635
2632
|
},
|
2636
2633
|
type: 'suggestion',
|
2637
2634
|
messages: {
|
2638
|
-
[REQUIRE_DESCRIPTION_ERROR]:
|
2635
|
+
[REQUIRE_DESCRIPTION_ERROR]: 'Description is required for nodes of type "{{ nodeType }}"',
|
2639
2636
|
},
|
2640
2637
|
schema: {
|
2641
2638
|
type: 'array',
|
2642
|
-
additionalItems: false,
|
2643
2639
|
minItems: 1,
|
2644
2640
|
maxItems: 1,
|
2645
2641
|
items: {
|
2646
2642
|
type: 'object',
|
2647
|
-
|
2643
|
+
additionalProperties: false,
|
2644
|
+
minProperties: 1,
|
2648
2645
|
properties: {
|
2649
|
-
|
2650
|
-
type: '
|
2651
|
-
|
2652
|
-
|
2653
|
-
|
2654
|
-
|
2655
|
-
|
2656
|
-
|
2646
|
+
types: {
|
2647
|
+
type: 'boolean',
|
2648
|
+
description: `Includes:\n\n${TYPES_KINDS.map(kind => `- [${kind}](https://spec.graphql.org/October2021/#${kind})`).join('\n')}`,
|
2649
|
+
},
|
2650
|
+
overrides: {
|
2651
|
+
type: 'object',
|
2652
|
+
description: 'Configuration for precise `ASTNode`',
|
2653
|
+
additionalProperties: false,
|
2654
|
+
properties: Object.fromEntries(ALLOWED_KINDS$1.map(kind => [kind, { type: 'boolean' }])),
|
2657
2655
|
},
|
2658
2656
|
},
|
2659
2657
|
},
|
2660
2658
|
},
|
2661
2659
|
},
|
2662
2660
|
create(context) {
|
2663
|
-
|
2661
|
+
const { types, overrides = {} } = context.options[0];
|
2662
|
+
const kinds = new Set(types ? TYPES_KINDS : []);
|
2663
|
+
for (const [kind, isEnabled] of Object.entries(overrides)) {
|
2664
|
+
if (isEnabled) {
|
2665
|
+
kinds.add(kind);
|
2666
|
+
}
|
2667
|
+
else {
|
2668
|
+
kinds.delete(kind);
|
2669
|
+
}
|
2670
|
+
}
|
2671
|
+
const selector = [...kinds].join(',');
|
2672
|
+
return {
|
2673
|
+
[selector](node) {
|
2674
|
+
var _a;
|
2675
|
+
const description = ((_a = node.description) === null || _a === void 0 ? void 0 : _a.value) || '';
|
2676
|
+
if (description.trim().length === 0) {
|
2677
|
+
context.report({
|
2678
|
+
loc: getLocation(node.name.loc, node.name.value),
|
2679
|
+
messageId: REQUIRE_DESCRIPTION_ERROR,
|
2680
|
+
data: {
|
2681
|
+
nodeType: node.kind,
|
2682
|
+
},
|
2683
|
+
});
|
2684
|
+
}
|
2685
|
+
},
|
2686
|
+
};
|
2664
2687
|
},
|
2665
2688
|
};
|
2666
2689
|
|
@@ -3205,7 +3228,7 @@ const rule$n = {
|
|
3205
3228
|
acceptedIdNames: ['id'],
|
3206
3229
|
acceptedIdTypes: ['ID'],
|
3207
3230
|
exceptions: {},
|
3208
|
-
...
|
3231
|
+
...context.options[0],
|
3209
3232
|
};
|
3210
3233
|
return {
|
3211
3234
|
ObjectTypeDefinition(node) {
|