@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.js
CHANGED
@@ -35,7 +35,34 @@ const recommendedConfig = {
|
|
35
35
|
'@graphql-eslint/known-type-names': 'error',
|
36
36
|
'@graphql-eslint/lone-anonymous-operation': 'error',
|
37
37
|
'@graphql-eslint/lone-schema-definition': 'error',
|
38
|
-
'@graphql-eslint/naming-convention':
|
38
|
+
'@graphql-eslint/naming-convention': [
|
39
|
+
'error',
|
40
|
+
{
|
41
|
+
types: 'PascalCase',
|
42
|
+
fields: 'camelCase',
|
43
|
+
overrides: {
|
44
|
+
EnumValueDefinition: 'UPPER_CASE',
|
45
|
+
OperationDefinition: {
|
46
|
+
style: 'PascalCase',
|
47
|
+
forbiddenPrefixes: ['Query', 'Mutation', 'Subscription', 'Get'],
|
48
|
+
forbiddenSuffixes: ['Query', 'Mutation', 'Subscription'],
|
49
|
+
},
|
50
|
+
FragmentDefinition: { style: 'PascalCase', forbiddenPrefixes: ['Fragment'], forbiddenSuffixes: ['Fragment'] },
|
51
|
+
'FieldDefinition[parent.name.value=Query]': {
|
52
|
+
forbiddenPrefixes: ['query', 'get'],
|
53
|
+
forbiddenSuffixes: ['Query'],
|
54
|
+
},
|
55
|
+
'FieldDefinition[parent.name.value=Mutation]': {
|
56
|
+
forbiddenPrefixes: ['mutation'],
|
57
|
+
forbiddenSuffixes: ['Mutation'],
|
58
|
+
},
|
59
|
+
'FieldDefinition[parent.name.value=Subscription]': {
|
60
|
+
forbiddenPrefixes: ['subscription'],
|
61
|
+
forbiddenSuffixes: ['Subscription'],
|
62
|
+
},
|
63
|
+
},
|
64
|
+
},
|
65
|
+
],
|
39
66
|
'@graphql-eslint/no-anonymous-operations': 'error',
|
40
67
|
'@graphql-eslint/no-case-insensitive-enum-values-duplicates': 'error',
|
41
68
|
'@graphql-eslint/no-fragment-cycles': 'error',
|
@@ -95,7 +122,7 @@ const allConfig = {
|
|
95
122
|
'@graphql-eslint/no-unreachable-types': 'error',
|
96
123
|
'@graphql-eslint/no-unused-fields': 'error',
|
97
124
|
'@graphql-eslint/require-deprecation-date': 'error',
|
98
|
-
'@graphql-eslint/require-description': 'error',
|
125
|
+
'@graphql-eslint/require-description': ['error', { types: true, overrides: { DirectiveDefinition: true } }],
|
99
126
|
'@graphql-eslint/require-field-of-type-query-in-mutation-result': 'error',
|
100
127
|
'@graphql-eslint/require-id-when-available': 'error',
|
101
128
|
'@graphql-eslint/selection-set-depth': 'error',
|
@@ -212,9 +239,14 @@ const loaderCache = new Proxy(Object.create(null), {
|
|
212
239
|
return true;
|
213
240
|
},
|
214
241
|
});
|
215
|
-
const
|
216
|
-
|
217
|
-
|
242
|
+
const TYPES_KINDS = [
|
243
|
+
graphql.Kind.OBJECT_TYPE_DEFINITION,
|
244
|
+
graphql.Kind.INTERFACE_TYPE_DEFINITION,
|
245
|
+
graphql.Kind.ENUM_TYPE_DEFINITION,
|
246
|
+
graphql.Kind.SCALAR_TYPE_DEFINITION,
|
247
|
+
graphql.Kind.INPUT_OBJECT_TYPE_DEFINITION,
|
248
|
+
graphql.Kind.UNION_TYPE_DEFINITION,
|
249
|
+
];
|
218
250
|
var CaseStyle;
|
219
251
|
(function (CaseStyle) {
|
220
252
|
CaseStyle["camelCase"] = "camelCase";
|
@@ -265,7 +297,7 @@ function getLocation(loc, fieldName = '', offset) {
|
|
265
297
|
}
|
266
298
|
|
267
299
|
function extractRuleName(stack) {
|
268
|
-
const match = (stack || '').match(/validation[
|
300
|
+
const match = (stack || '').match(/validation[/\\]rules[/\\](.*?)\.js:/) || [];
|
269
301
|
return match[1] || null;
|
270
302
|
}
|
271
303
|
function validateDoc(sourceNode, context, schema, documentNode, rules, ruleName = null) {
|
@@ -514,11 +546,23 @@ const GRAPHQL_JS_VALIDATIONS = Object.assign({}, validationToRule('executable-de
|
|
514
546
|
}));
|
515
547
|
|
516
548
|
const ALPHABETIZE = 'ALPHABETIZE';
|
517
|
-
const fieldsEnum = [
|
549
|
+
const fieldsEnum = [
|
550
|
+
graphql.Kind.OBJECT_TYPE_DEFINITION,
|
551
|
+
graphql.Kind.INTERFACE_TYPE_DEFINITION,
|
552
|
+
graphql.Kind.INPUT_OBJECT_TYPE_DEFINITION,
|
553
|
+
];
|
518
554
|
const valuesEnum = [graphql.Kind.ENUM_TYPE_DEFINITION];
|
519
|
-
const selectionsEnum = [
|
555
|
+
const selectionsEnum = [
|
556
|
+
graphql.Kind.OPERATION_DEFINITION,
|
557
|
+
graphql.Kind.FRAGMENT_DEFINITION,
|
558
|
+
];
|
520
559
|
const variablesEnum = [graphql.Kind.OPERATION_DEFINITION];
|
521
|
-
const argumentsEnum = [
|
560
|
+
const argumentsEnum = [
|
561
|
+
graphql.Kind.FIELD_DEFINITION,
|
562
|
+
graphql.Kind.FIELD,
|
563
|
+
graphql.Kind.DIRECTIVE_DEFINITION,
|
564
|
+
graphql.Kind.DIRECTIVE,
|
565
|
+
];
|
522
566
|
const rule = {
|
523
567
|
meta: {
|
524
568
|
type: 'suggestion',
|
@@ -626,38 +670,48 @@ const rule = {
|
|
626
670
|
properties: {
|
627
671
|
fields: {
|
628
672
|
type: 'array',
|
629
|
-
|
673
|
+
uniqueItems: true,
|
674
|
+
minItems: 1,
|
675
|
+
items: {
|
630
676
|
enum: fieldsEnum,
|
631
677
|
},
|
632
|
-
description: 'Fields of `type`, `interface`, and `input
|
678
|
+
description: 'Fields of `type`, `interface`, and `input`',
|
633
679
|
},
|
634
680
|
values: {
|
635
681
|
type: 'array',
|
636
|
-
|
682
|
+
uniqueItems: true,
|
683
|
+
minItems: 1,
|
684
|
+
items: {
|
637
685
|
enum: valuesEnum,
|
638
686
|
},
|
639
|
-
description: 'Values of `enum
|
687
|
+
description: 'Values of `enum`',
|
640
688
|
},
|
641
689
|
selections: {
|
642
690
|
type: 'array',
|
643
|
-
|
691
|
+
uniqueItems: true,
|
692
|
+
minItems: 1,
|
693
|
+
items: {
|
644
694
|
enum: selectionsEnum,
|
645
695
|
},
|
646
|
-
description: 'Selections of operations (`query`, `mutation` and `subscription`) and `fragment
|
696
|
+
description: 'Selections of operations (`query`, `mutation` and `subscription`) and `fragment`',
|
647
697
|
},
|
648
698
|
variables: {
|
649
699
|
type: 'array',
|
650
|
-
|
700
|
+
uniqueItems: true,
|
701
|
+
minItems: 1,
|
702
|
+
items: {
|
651
703
|
enum: variablesEnum,
|
652
704
|
},
|
653
|
-
description: 'Variables of operations (`query`, `mutation` and `subscription`)
|
705
|
+
description: 'Variables of operations (`query`, `mutation` and `subscription`)',
|
654
706
|
},
|
655
707
|
arguments: {
|
656
708
|
type: 'array',
|
657
|
-
|
709
|
+
uniqueItems: true,
|
710
|
+
minItems: 1,
|
711
|
+
items: {
|
658
712
|
enum: argumentsEnum,
|
659
713
|
},
|
660
|
-
description: 'Arguments of fields and directives
|
714
|
+
description: 'Arguments of fields and directives',
|
661
715
|
},
|
662
716
|
},
|
663
717
|
},
|
@@ -688,7 +742,7 @@ const rule = {
|
|
688
742
|
const opts = context.options[0];
|
689
743
|
const fields = new Set((_a = opts.fields) !== null && _a !== void 0 ? _a : []);
|
690
744
|
const listeners = {};
|
691
|
-
const
|
745
|
+
const kinds = [
|
692
746
|
fields.has(graphql.Kind.OBJECT_TYPE_DEFINITION) && [graphql.Kind.OBJECT_TYPE_DEFINITION, graphql.Kind.OBJECT_TYPE_EXTENSION],
|
693
747
|
fields.has(graphql.Kind.INTERFACE_TYPE_DEFINITION) && [graphql.Kind.INTERFACE_TYPE_DEFINITION, graphql.Kind.INTERFACE_TYPE_EXTENSION],
|
694
748
|
fields.has(graphql.Kind.INPUT_OBJECT_TYPE_DEFINITION) && [
|
@@ -696,8 +750,9 @@ const rule = {
|
|
696
750
|
graphql.Kind.INPUT_OBJECT_TYPE_EXTENSION,
|
697
751
|
],
|
698
752
|
]
|
699
|
-
.
|
700
|
-
.
|
753
|
+
.filter(Boolean)
|
754
|
+
.flat();
|
755
|
+
const fieldsSelector = kinds.join(',');
|
701
756
|
const hasEnumValues = ((_b = opts.values) === null || _b === void 0 ? void 0 : _b[0]) === graphql.Kind.ENUM_TYPE_DEFINITION;
|
702
757
|
const selectionsSelector = (_c = opts.selections) === null || _c === void 0 ? void 0 : _c.join(',');
|
703
758
|
const hasVariables = ((_d = opts.variables) === null || _d === void 0 ? void 0 : _d[0]) === graphql.Kind.OPERATION_DEFINITION;
|
@@ -1097,6 +1152,9 @@ const rule$5 = {
|
|
1097
1152
|
},
|
1098
1153
|
};
|
1099
1154
|
|
1155
|
+
const isObjectType = (node) => [graphql.Kind.OBJECT_TYPE_DEFINITION, graphql.Kind.OBJECT_TYPE_EXTENSION].includes(node.type);
|
1156
|
+
const isQueryType = (node) => isObjectType(node) && node.name.value === 'Query';
|
1157
|
+
const isMutationType = (node) => isObjectType(node) && node.name.value === 'Mutation';
|
1100
1158
|
const rule$6 = {
|
1101
1159
|
meta: {
|
1102
1160
|
type: 'suggestion',
|
@@ -1137,6 +1195,7 @@ const rule$6 = {
|
|
1137
1195
|
schema: [
|
1138
1196
|
{
|
1139
1197
|
type: 'object',
|
1198
|
+
additionalProperties: false,
|
1140
1199
|
properties: {
|
1141
1200
|
checkInputType: {
|
1142
1201
|
type: 'boolean',
|
@@ -1159,24 +1218,22 @@ const rule$6 = {
|
|
1159
1218
|
description: 'Apply the rule to Mutations',
|
1160
1219
|
},
|
1161
1220
|
},
|
1162
|
-
additionalProperties: false,
|
1163
1221
|
},
|
1164
1222
|
],
|
1165
1223
|
},
|
1166
1224
|
create(context) {
|
1167
|
-
var _a;
|
1168
1225
|
const options = {
|
1169
|
-
caseSensitiveInputType: true,
|
1170
1226
|
checkInputType: false,
|
1171
|
-
|
1227
|
+
caseSensitiveInputType: true,
|
1172
1228
|
checkQueries: false,
|
1173
|
-
|
1229
|
+
checkMutations: true,
|
1230
|
+
...context.options[0],
|
1174
1231
|
};
|
1175
1232
|
const shouldCheckType = node => (options.checkMutations && isMutationType(node)) || (options.checkQueries && isQueryType(node));
|
1176
1233
|
const listeners = {
|
1177
|
-
'FieldDefinition > InputValueDefinition'
|
1178
|
-
|
1179
|
-
|
1234
|
+
'FieldDefinition > InputValueDefinition[name.value!=input]'(node) {
|
1235
|
+
if (shouldCheckType(node.parent.parent)) {
|
1236
|
+
const name = node.name.value;
|
1180
1237
|
context.report({
|
1181
1238
|
loc: getLocation(node.loc, name),
|
1182
1239
|
message: `Input "${name}" should be called "input"`,
|
@@ -1184,11 +1241,11 @@ const rule$6 = {
|
|
1184
1241
|
}
|
1185
1242
|
},
|
1186
1243
|
};
|
1187
|
-
if (options
|
1188
|
-
listeners['FieldDefinition > InputValueDefinition NamedType'] = node => {
|
1244
|
+
if (options.checkInputType) {
|
1245
|
+
listeners['FieldDefinition > InputValueDefinition NamedType'] = (node) => {
|
1189
1246
|
const findInputType = item => {
|
1190
1247
|
let currentNode = item;
|
1191
|
-
while (currentNode.type !==
|
1248
|
+
while (currentNode.type !== graphql.Kind.INPUT_VALUE_DEFINITION) {
|
1192
1249
|
currentNode = currentNode.parent;
|
1193
1250
|
}
|
1194
1251
|
return currentNode;
|
@@ -1405,69 +1462,40 @@ const rule$7 = {
|
|
1405
1462
|
},
|
1406
1463
|
};
|
1407
1464
|
|
1408
|
-
const
|
1409
|
-
|
1410
|
-
|
1411
|
-
|
1412
|
-
|
1413
|
-
|
1414
|
-
const acceptedStyles = [
|
1415
|
-
'camelCase',
|
1416
|
-
'PascalCase',
|
1417
|
-
'snake_case',
|
1418
|
-
'UPPER_CASE',
|
1465
|
+
const FIELDS_KINDS = [
|
1466
|
+
graphql.Kind.FIELD_DEFINITION,
|
1467
|
+
graphql.Kind.INPUT_VALUE_DEFINITION,
|
1468
|
+
graphql.Kind.VARIABLE_DEFINITION,
|
1469
|
+
graphql.Kind.ARGUMENT,
|
1470
|
+
graphql.Kind.DIRECTIVE_DEFINITION,
|
1419
1471
|
];
|
1420
|
-
|
1421
|
-
|
1422
|
-
|
1423
|
-
|
1424
|
-
|
1425
|
-
|
1426
|
-
|
1427
|
-
|
1428
|
-
|
1429
|
-
|
1430
|
-
|
1431
|
-
|
1432
|
-
|
1433
|
-
|
1434
|
-
|
1435
|
-
|
1436
|
-
|
1437
|
-
|
1438
|
-
|
1439
|
-
|
1440
|
-
|
1441
|
-
|
1442
|
-
|
1443
|
-
|
1444
|
-
|
1445
|
-
|
1446
|
-
|
1447
|
-
if (forbiddenPrefixes.some(forbiddenPrefix => name.startsWith(forbiddenPrefix))) {
|
1448
|
-
return {
|
1449
|
-
ok: false,
|
1450
|
-
errorMessage: '{{nodeType}} "{{nodeName}}" should not have one of the following prefix(es): {{forbiddenPrefixes}}',
|
1451
|
-
};
|
1452
|
-
}
|
1453
|
-
if (forbiddenSuffixes.some(forbiddenSuffix => name.endsWith(forbiddenSuffix))) {
|
1454
|
-
return {
|
1455
|
-
ok: false,
|
1456
|
-
errorMessage: '{{nodeType}} "{{nodeName}}" should not have one of the following suffix(es): {{forbiddenSuffixes}}',
|
1457
|
-
};
|
1458
|
-
}
|
1459
|
-
if (!formats[style]) {
|
1460
|
-
return { ok: true };
|
1461
|
-
}
|
1462
|
-
const ok = new RegExp(formats[style]).test(name);
|
1463
|
-
if (ok) {
|
1464
|
-
return { ok: true };
|
1465
|
-
}
|
1466
|
-
return {
|
1467
|
-
ok: false,
|
1468
|
-
errorMessage: '{{nodeType}} name "{{nodeName}}" should be in {{format}} format',
|
1469
|
-
};
|
1470
|
-
}
|
1472
|
+
const KindToDisplayName = {
|
1473
|
+
// types
|
1474
|
+
[graphql.Kind.OBJECT_TYPE_DEFINITION]: 'Type',
|
1475
|
+
[graphql.Kind.INTERFACE_TYPE_DEFINITION]: 'Interface',
|
1476
|
+
[graphql.Kind.ENUM_TYPE_DEFINITION]: 'Enumerator',
|
1477
|
+
[graphql.Kind.SCALAR_TYPE_DEFINITION]: 'Scalar',
|
1478
|
+
[graphql.Kind.INPUT_OBJECT_TYPE_DEFINITION]: 'Input type',
|
1479
|
+
[graphql.Kind.UNION_TYPE_DEFINITION]: 'Union',
|
1480
|
+
// fields
|
1481
|
+
[graphql.Kind.FIELD_DEFINITION]: 'Field',
|
1482
|
+
[graphql.Kind.INPUT_VALUE_DEFINITION]: 'Input property',
|
1483
|
+
[graphql.Kind.VARIABLE_DEFINITION]: 'Variable',
|
1484
|
+
[graphql.Kind.ARGUMENT]: 'Argument',
|
1485
|
+
[graphql.Kind.DIRECTIVE_DEFINITION]: 'Directive',
|
1486
|
+
// rest
|
1487
|
+
[graphql.Kind.ENUM_VALUE_DEFINITION]: 'Enumeration value',
|
1488
|
+
[graphql.Kind.OPERATION_DEFINITION]: 'Operation',
|
1489
|
+
[graphql.Kind.FRAGMENT_DEFINITION]: 'Fragment',
|
1490
|
+
};
|
1491
|
+
const StyleToRegex = {
|
1492
|
+
camelCase: /^[a-z][\dA-Za-z]*$/,
|
1493
|
+
PascalCase: /^[A-Z][\dA-Za-z]*$/,
|
1494
|
+
snake_case: /^[a-z][\d_a-z]*[\da-z]$/,
|
1495
|
+
UPPER_CASE: /^[A-Z][\dA-Z_]*[\dA-Z]$/,
|
1496
|
+
};
|
1497
|
+
const ALLOWED_KINDS = Object.keys(KindToDisplayName).sort();
|
1498
|
+
const ALLOWED_STYLES = Object.keys(StyleToRegex);
|
1471
1499
|
const schemaOption$1 = {
|
1472
1500
|
oneOf: [{ $ref: '#/definitions/asString' }, { $ref: '#/definitions/asObject' }],
|
1473
1501
|
};
|
@@ -1482,89 +1510,120 @@ const rule$8 = {
|
|
1482
1510
|
examples: [
|
1483
1511
|
{
|
1484
1512
|
title: 'Incorrect',
|
1485
|
-
usage: [{
|
1513
|
+
usage: [{ types: 'PascalCase', fields: 'camelCase' }],
|
1486
1514
|
code: /* GraphQL */ `
|
1487
|
-
type
|
1488
|
-
|
1515
|
+
type user {
|
1516
|
+
first_name: String!
|
1489
1517
|
}
|
1490
1518
|
`,
|
1491
1519
|
},
|
1492
1520
|
{
|
1493
1521
|
title: 'Correct',
|
1494
|
-
usage: [{
|
1522
|
+
usage: [{ types: 'PascalCase', fields: 'camelCase' }],
|
1495
1523
|
code: /* GraphQL */ `
|
1496
|
-
type
|
1497
|
-
|
1524
|
+
type User {
|
1525
|
+
firstName: String
|
1498
1526
|
}
|
1499
1527
|
`,
|
1500
1528
|
},
|
1501
1529
|
],
|
1530
|
+
optionsForConfig: [
|
1531
|
+
{
|
1532
|
+
types: 'PascalCase',
|
1533
|
+
fields: 'camelCase',
|
1534
|
+
overrides: {
|
1535
|
+
EnumValueDefinition: 'UPPER_CASE',
|
1536
|
+
OperationDefinition: {
|
1537
|
+
style: 'PascalCase',
|
1538
|
+
forbiddenPrefixes: ['Query', 'Mutation', 'Subscription', 'Get'],
|
1539
|
+
forbiddenSuffixes: ['Query', 'Mutation', 'Subscription'],
|
1540
|
+
},
|
1541
|
+
FragmentDefinition: {
|
1542
|
+
style: 'PascalCase',
|
1543
|
+
forbiddenPrefixes: ['Fragment'],
|
1544
|
+
forbiddenSuffixes: ['Fragment'],
|
1545
|
+
},
|
1546
|
+
'FieldDefinition[parent.name.value=Query]': {
|
1547
|
+
forbiddenPrefixes: ['query', 'get'],
|
1548
|
+
forbiddenSuffixes: ['Query'],
|
1549
|
+
},
|
1550
|
+
'FieldDefinition[parent.name.value=Mutation]': {
|
1551
|
+
forbiddenPrefixes: ['mutation'],
|
1552
|
+
forbiddenSuffixes: ['Mutation'],
|
1553
|
+
},
|
1554
|
+
'FieldDefinition[parent.name.value=Subscription]': {
|
1555
|
+
forbiddenPrefixes: ['subscription'],
|
1556
|
+
forbiddenSuffixes: ['Subscription'],
|
1557
|
+
},
|
1558
|
+
},
|
1559
|
+
},
|
1560
|
+
],
|
1502
1561
|
},
|
1503
1562
|
schema: {
|
1504
1563
|
definitions: {
|
1505
1564
|
asString: {
|
1506
|
-
|
1507
|
-
description: `One of: ${
|
1508
|
-
enum: acceptedStyles,
|
1565
|
+
enum: ALLOWED_STYLES,
|
1566
|
+
description: `One of: ${ALLOWED_STYLES.map(t => `\`${t}\``).join(', ')}`,
|
1509
1567
|
},
|
1510
1568
|
asObject: {
|
1511
1569
|
type: 'object',
|
1570
|
+
additionalProperties: false,
|
1512
1571
|
properties: {
|
1513
|
-
style: {
|
1514
|
-
|
1515
|
-
|
1516
|
-
},
|
1517
|
-
prefix: {
|
1518
|
-
type: 'string',
|
1519
|
-
},
|
1520
|
-
suffix: {
|
1521
|
-
type: 'string',
|
1522
|
-
},
|
1572
|
+
style: { enum: ALLOWED_STYLES },
|
1573
|
+
prefix: { type: 'string' },
|
1574
|
+
suffix: { type: 'string' },
|
1523
1575
|
forbiddenPrefixes: {
|
1524
|
-
additionalItems: false,
|
1525
1576
|
type: 'array',
|
1577
|
+
uniqueItems: true,
|
1526
1578
|
minItems: 1,
|
1527
|
-
items: {
|
1528
|
-
type: 'string',
|
1529
|
-
},
|
1579
|
+
items: { type: 'string' },
|
1530
1580
|
},
|
1531
1581
|
forbiddenSuffixes: {
|
1532
|
-
additionalItems: false,
|
1533
1582
|
type: 'array',
|
1583
|
+
uniqueItems: true,
|
1534
1584
|
minItems: 1,
|
1535
|
-
items: {
|
1536
|
-
type: 'string',
|
1537
|
-
},
|
1585
|
+
items: { type: 'string' },
|
1538
1586
|
},
|
1539
1587
|
},
|
1540
1588
|
},
|
1541
1589
|
},
|
1542
|
-
$schema: 'http://json-schema.org/draft-04/schema#',
|
1543
1590
|
type: 'array',
|
1591
|
+
maxItems: 1,
|
1544
1592
|
items: {
|
1545
1593
|
type: 'object',
|
1594
|
+
additionalProperties: false,
|
1546
1595
|
properties: {
|
1547
|
-
|
1548
|
-
|
1549
|
-
|
1550
|
-
[graphql.Kind.INPUT_VALUE_DEFINITION]: schemaOption$1,
|
1551
|
-
[graphql.Kind.OBJECT_TYPE_DEFINITION]: schemaOption$1,
|
1552
|
-
[graphql.Kind.INTERFACE_TYPE_DEFINITION]: schemaOption$1,
|
1553
|
-
[graphql.Kind.ENUM_TYPE_DEFINITION]: schemaOption$1,
|
1554
|
-
[graphql.Kind.UNION_TYPE_DEFINITION]: schemaOption$1,
|
1555
|
-
[graphql.Kind.SCALAR_TYPE_DEFINITION]: schemaOption$1,
|
1556
|
-
[graphql.Kind.OPERATION_DEFINITION]: schemaOption$1,
|
1557
|
-
[graphql.Kind.FRAGMENT_DEFINITION]: schemaOption$1,
|
1558
|
-
QueryDefinition: schemaOption$1,
|
1559
|
-
leadingUnderscore: {
|
1560
|
-
type: 'string',
|
1561
|
-
enum: ['allow', 'forbid'],
|
1562
|
-
default: 'forbid',
|
1596
|
+
types: {
|
1597
|
+
...schemaOption$1,
|
1598
|
+
description: `Includes:\n\n${TYPES_KINDS.map(kind => `- [${kind}](https://spec.graphql.org/October2021/#${kind})`).join('\n')}`,
|
1563
1599
|
},
|
1564
|
-
|
1565
|
-
|
1566
|
-
|
1567
|
-
|
1600
|
+
fields: {
|
1601
|
+
...schemaOption$1,
|
1602
|
+
description: `Includes:\n\n${FIELDS_KINDS.map(kind => `- [${kind}](https://spec.graphql.org/October2021/#${kind})`).join('\n')}`,
|
1603
|
+
},
|
1604
|
+
allowLeadingUnderscore: {
|
1605
|
+
type: 'boolean',
|
1606
|
+
default: false,
|
1607
|
+
},
|
1608
|
+
allowTrailingUnderscore: {
|
1609
|
+
type: 'boolean',
|
1610
|
+
default: false,
|
1611
|
+
},
|
1612
|
+
overrides: {
|
1613
|
+
type: 'object',
|
1614
|
+
additionalProperties: false,
|
1615
|
+
description: [
|
1616
|
+
'May contain the following `ASTNode` names:',
|
1617
|
+
'',
|
1618
|
+
...ALLOWED_KINDS.map(kind => `- [${kind}](https://spec.graphql.org/October2021/#${kind})`),
|
1619
|
+
'',
|
1620
|
+
"> It's also possible to use a [`selector`](https://eslint.org/docs/developer-guide/selectors) that starts with `ASTNode` name",
|
1621
|
+
'>',
|
1622
|
+
'> Example: pattern property `FieldDefinition[parent.name.value=Query]` will match only fields for type `Query`',
|
1623
|
+
].join('\n'),
|
1624
|
+
patternProperties: {
|
1625
|
+
[`^(${ALLOWED_KINDS.join('|')})(.+)?$`]: schemaOption$1,
|
1626
|
+
},
|
1568
1627
|
},
|
1569
1628
|
},
|
1570
1629
|
},
|
@@ -1572,136 +1631,83 @@ const rule$8 = {
|
|
1572
1631
|
},
|
1573
1632
|
create(context) {
|
1574
1633
|
const options = {
|
1575
|
-
|
1576
|
-
|
1577
|
-
...(context.options[0] || {}),
|
1634
|
+
overrides: {},
|
1635
|
+
...context.options[0],
|
1578
1636
|
};
|
1579
|
-
|
1580
|
-
|
1581
|
-
|
1582
|
-
|
1583
|
-
|
1584
|
-
|
1585
|
-
|
1586
|
-
|
1587
|
-
|
1588
|
-
|
1589
|
-
|
1590
|
-
}
|
1591
|
-
|
1637
|
+
function normalisePropertyOption(kind) {
|
1638
|
+
let style = options.overrides[kind];
|
1639
|
+
if (!style) {
|
1640
|
+
style = TYPES_KINDS.includes(kind) ? options.types : options.fields;
|
1641
|
+
}
|
1642
|
+
return typeof style === 'object' ? style : { style };
|
1643
|
+
}
|
1644
|
+
const checkNode = (selector) => (node) => {
|
1645
|
+
const { name } = node.kind === graphql.Kind.VARIABLE_DEFINITION ? node.variable : node;
|
1646
|
+
if (!name) {
|
1647
|
+
return;
|
1648
|
+
}
|
1649
|
+
const { prefix, suffix, forbiddenPrefixes, forbiddenSuffixes, style } = normalisePropertyOption(selector);
|
1650
|
+
const nodeType = KindToDisplayName[node.kind] || node.kind;
|
1651
|
+
const nodeName = name.value;
|
1652
|
+
const errorMessage = getErrorMessage();
|
1653
|
+
if (errorMessage) {
|
1592
1654
|
context.report({
|
1593
|
-
loc: getLocation(
|
1594
|
-
message:
|
1595
|
-
data: {
|
1596
|
-
prefix,
|
1597
|
-
suffix,
|
1598
|
-
format: style,
|
1599
|
-
forbiddenPrefixes: forbiddenPrefixes.join(', '),
|
1600
|
-
forbiddenSuffixes: forbiddenSuffixes.join(', '),
|
1601
|
-
nodeType,
|
1602
|
-
nodeName: node.value,
|
1603
|
-
},
|
1655
|
+
loc: getLocation(name.loc, name.value),
|
1656
|
+
message: `${nodeType} "${nodeName}" should ${errorMessage}`,
|
1604
1657
|
});
|
1605
1658
|
}
|
1606
|
-
|
1607
|
-
|
1608
|
-
|
1609
|
-
|
1610
|
-
}
|
1611
|
-
return {
|
1612
|
-
style: value,
|
1613
|
-
prefix: '',
|
1614
|
-
suffix: '',
|
1615
|
-
};
|
1616
|
-
};
|
1617
|
-
return {
|
1618
|
-
Name: node => {
|
1619
|
-
if (node.value.startsWith('_') && options.leadingUnderscore === 'forbid') {
|
1620
|
-
context.report({
|
1621
|
-
loc: getLocation(node.loc, node.value),
|
1622
|
-
message: 'Leading underscores are not allowed',
|
1623
|
-
});
|
1659
|
+
function getErrorMessage() {
|
1660
|
+
let name = nodeName;
|
1661
|
+
if (options.allowLeadingUnderscore) {
|
1662
|
+
name = name.replace(/^_*/, '');
|
1624
1663
|
}
|
1625
|
-
if (
|
1626
|
-
|
1627
|
-
loc: getLocation(node.loc, node.value),
|
1628
|
-
message: 'Trailing underscores are not allowed',
|
1629
|
-
});
|
1664
|
+
if (options.allowTrailingUnderscore) {
|
1665
|
+
name = name.replace(/_*$/, '');
|
1630
1666
|
}
|
1631
|
-
|
1632
|
-
|
1633
|
-
if (options.ObjectTypeDefinition) {
|
1634
|
-
const property = normalisePropertyOption(options.ObjectTypeDefinition);
|
1635
|
-
checkNode(node.name, property, 'Type');
|
1667
|
+
if (prefix && !name.startsWith(prefix)) {
|
1668
|
+
return `have "${prefix}" prefix`;
|
1636
1669
|
}
|
1637
|
-
|
1638
|
-
|
1639
|
-
if (options.InterfaceTypeDefinition) {
|
1640
|
-
const property = normalisePropertyOption(options.InterfaceTypeDefinition);
|
1641
|
-
checkNode(node.name, property, 'Interface');
|
1670
|
+
if (suffix && !name.endsWith(suffix)) {
|
1671
|
+
return `have "${suffix}" suffix`;
|
1642
1672
|
}
|
1643
|
-
|
1644
|
-
|
1645
|
-
|
1646
|
-
const property = normalisePropertyOption(options.EnumTypeDefinition);
|
1647
|
-
checkNode(node.name, property, 'Enumerator');
|
1648
|
-
}
|
1649
|
-
},
|
1650
|
-
InputObjectTypeDefinition: node => {
|
1651
|
-
if (options.InputObjectTypeDefinition) {
|
1652
|
-
const property = normalisePropertyOption(options.InputObjectTypeDefinition);
|
1653
|
-
checkNode(node.name, property, 'Input type');
|
1673
|
+
const forbiddenPrefix = forbiddenPrefixes === null || forbiddenPrefixes === void 0 ? void 0 : forbiddenPrefixes.find(prefix => name.startsWith(prefix));
|
1674
|
+
if (forbiddenPrefix) {
|
1675
|
+
return `not have "${forbiddenPrefix}" prefix`;
|
1654
1676
|
}
|
1655
|
-
|
1656
|
-
|
1657
|
-
|
1658
|
-
const property = normalisePropertyOption(options.QueryDefinition);
|
1659
|
-
checkNode(node.name, property, 'Query');
|
1677
|
+
const forbiddenSuffix = forbiddenSuffixes === null || forbiddenSuffixes === void 0 ? void 0 : forbiddenSuffixes.find(suffix => name.endsWith(suffix));
|
1678
|
+
if (forbiddenSuffix) {
|
1679
|
+
return `not have "${forbiddenSuffix}" suffix`;
|
1660
1680
|
}
|
1661
|
-
if (
|
1662
|
-
|
1663
|
-
checkNode(node.name, property, 'Field');
|
1681
|
+
if (style && !ALLOWED_STYLES.includes(style)) {
|
1682
|
+
return `be in one of the following options: ${ALLOWED_STYLES.join(', ')}`;
|
1664
1683
|
}
|
1665
|
-
|
1666
|
-
|
1667
|
-
|
1668
|
-
const property = normalisePropertyOption(options.EnumValueDefinition);
|
1669
|
-
checkNode(node.name, property, 'Enumeration value');
|
1684
|
+
const caseRegex = StyleToRegex[style];
|
1685
|
+
if (caseRegex && !caseRegex.test(name)) {
|
1686
|
+
return `be in ${style} format`;
|
1670
1687
|
}
|
1671
|
-
}
|
1672
|
-
InputValueDefinition: node => {
|
1673
|
-
if (options.InputValueDefinition) {
|
1674
|
-
const property = normalisePropertyOption(options.InputValueDefinition);
|
1675
|
-
checkNode(node.name, property, 'Input property');
|
1676
|
-
}
|
1677
|
-
},
|
1678
|
-
OperationDefinition: node => {
|
1679
|
-
if (options.OperationDefinition) {
|
1680
|
-
const property = normalisePropertyOption(options.OperationDefinition);
|
1681
|
-
if (node.name) {
|
1682
|
-
checkNode(node.name, property, 'Operation');
|
1683
|
-
}
|
1684
|
-
}
|
1685
|
-
},
|
1686
|
-
FragmentDefinition: node => {
|
1687
|
-
if (options.FragmentDefinition) {
|
1688
|
-
const property = normalisePropertyOption(options.FragmentDefinition);
|
1689
|
-
checkNode(node.name, property, 'Fragment');
|
1690
|
-
}
|
1691
|
-
},
|
1692
|
-
ScalarTypeDefinition: node => {
|
1693
|
-
if (options.ScalarTypeDefinition) {
|
1694
|
-
const property = normalisePropertyOption(options.ScalarTypeDefinition);
|
1695
|
-
checkNode(node.name, property, 'Scalar');
|
1696
|
-
}
|
1697
|
-
},
|
1698
|
-
UnionTypeDefinition: node => {
|
1699
|
-
if (options.UnionTypeDefinition) {
|
1700
|
-
const property = normalisePropertyOption(options.UnionTypeDefinition);
|
1701
|
-
checkNode(node.name, property, 'Union');
|
1702
|
-
}
|
1703
|
-
},
|
1688
|
+
}
|
1704
1689
|
};
|
1690
|
+
const checkUnderscore = (node) => {
|
1691
|
+
const name = node.value;
|
1692
|
+
context.report({
|
1693
|
+
loc: getLocation(node.loc, name),
|
1694
|
+
message: `${name.startsWith('_') ? 'Leading' : 'Trailing'} underscores are not allowed`,
|
1695
|
+
});
|
1696
|
+
};
|
1697
|
+
const listeners = {};
|
1698
|
+
if (!options.allowLeadingUnderscore) {
|
1699
|
+
listeners['Name[value=/^_/]:matches([parent.kind!=Field], [parent.kind=Field][parent.alias])'] = checkUnderscore;
|
1700
|
+
}
|
1701
|
+
if (!options.allowTrailingUnderscore) {
|
1702
|
+
listeners['Name[value=/_$/]:matches([parent.kind!=Field], [parent.kind=Field][parent.alias])'] = checkUnderscore;
|
1703
|
+
}
|
1704
|
+
const selectors = new Set([options.types && TYPES_KINDS, options.fields && FIELDS_KINDS, Object.keys(options.overrides)]
|
1705
|
+
.flat()
|
1706
|
+
.filter(Boolean));
|
1707
|
+
for (const selector of selectors) {
|
1708
|
+
listeners[selector] = checkNode(selector);
|
1709
|
+
}
|
1710
|
+
return listeners;
|
1705
1711
|
},
|
1706
1712
|
};
|
1707
1713
|
|
@@ -2418,6 +2424,7 @@ function convertDescription(node) {
|
|
2418
2424
|
return [];
|
2419
2425
|
}
|
2420
2426
|
|
2427
|
+
// eslint-disable-next-line unicorn/better-regex
|
2421
2428
|
const DATE_REGEX = /^\d{2}\/\d{2}\/\d{4}$/;
|
2422
2429
|
const MESSAGE_REQUIRE_DATE = 'MESSAGE_REQUIRE_DATE';
|
2423
2430
|
const MESSAGE_INVALID_FORMAT = 'MESSAGE_INVALID_FORMAT';
|
@@ -2581,41 +2588,23 @@ const rule$i = {
|
|
2581
2588
|
};
|
2582
2589
|
|
2583
2590
|
const REQUIRE_DESCRIPTION_ERROR = 'REQUIRE_DESCRIPTION_ERROR';
|
2584
|
-
const
|
2585
|
-
|
2586
|
-
graphql.Kind.OBJECT_TYPE_DEFINITION,
|
2591
|
+
const ALLOWED_KINDS$1 = [
|
2592
|
+
...TYPES_KINDS,
|
2587
2593
|
graphql.Kind.FIELD_DEFINITION,
|
2588
2594
|
graphql.Kind.INPUT_VALUE_DEFINITION,
|
2589
|
-
graphql.Kind.INTERFACE_TYPE_DEFINITION,
|
2590
|
-
graphql.Kind.UNION_TYPE_DEFINITION,
|
2591
|
-
graphql.Kind.ENUM_TYPE_DEFINITION,
|
2592
2595
|
graphql.Kind.ENUM_VALUE_DEFINITION,
|
2593
|
-
graphql.Kind.INPUT_OBJECT_TYPE_DEFINITION,
|
2594
2596
|
graphql.Kind.DIRECTIVE_DEFINITION,
|
2595
2597
|
];
|
2596
|
-
function verifyRule(context, node) {
|
2597
|
-
if (node) {
|
2598
|
-
if (!node.description || !node.description.value || node.description.value.trim().length === 0) {
|
2599
|
-
context.report({
|
2600
|
-
loc: getLocation(('name' in node ? node.name : node).loc, 'name' in node ? node.name.value : 'schema'),
|
2601
|
-
messageId: REQUIRE_DESCRIPTION_ERROR,
|
2602
|
-
data: {
|
2603
|
-
nodeType: node.kind,
|
2604
|
-
},
|
2605
|
-
});
|
2606
|
-
}
|
2607
|
-
}
|
2608
|
-
}
|
2609
2598
|
const rule$j = {
|
2610
2599
|
meta: {
|
2611
2600
|
docs: {
|
2612
2601
|
category: 'Best Practices',
|
2613
|
-
description:
|
2614
|
-
url:
|
2602
|
+
description: 'Enforce descriptions in your type definitions.',
|
2603
|
+
url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/require-description.md',
|
2615
2604
|
examples: [
|
2616
2605
|
{
|
2617
2606
|
title: 'Incorrect',
|
2618
|
-
usage: [{
|
2607
|
+
usage: [{ types: true, overrides: { FieldDefinition: true } }],
|
2619
2608
|
code: /* GraphQL */ `
|
2620
2609
|
type someTypeName {
|
2621
2610
|
name: String
|
@@ -2624,7 +2613,7 @@ const rule$j = {
|
|
2624
2613
|
},
|
2625
2614
|
{
|
2626
2615
|
title: 'Correct',
|
2627
|
-
usage: [{
|
2616
|
+
usage: [{ types: true, overrides: { FieldDefinition: true } }],
|
2628
2617
|
code: /* GraphQL */ `
|
2629
2618
|
"""
|
2630
2619
|
Some type description
|
@@ -2638,35 +2627,69 @@ const rule$j = {
|
|
2638
2627
|
`,
|
2639
2628
|
},
|
2640
2629
|
],
|
2630
|
+
optionsForConfig: [
|
2631
|
+
{
|
2632
|
+
types: true,
|
2633
|
+
overrides: {
|
2634
|
+
[graphql.Kind.DIRECTIVE_DEFINITION]: true,
|
2635
|
+
},
|
2636
|
+
},
|
2637
|
+
],
|
2641
2638
|
},
|
2642
2639
|
type: 'suggestion',
|
2643
2640
|
messages: {
|
2644
|
-
[REQUIRE_DESCRIPTION_ERROR]:
|
2641
|
+
[REQUIRE_DESCRIPTION_ERROR]: 'Description is required for nodes of type "{{ nodeType }}"',
|
2645
2642
|
},
|
2646
2643
|
schema: {
|
2647
2644
|
type: 'array',
|
2648
|
-
additionalItems: false,
|
2649
2645
|
minItems: 1,
|
2650
2646
|
maxItems: 1,
|
2651
2647
|
items: {
|
2652
2648
|
type: 'object',
|
2653
|
-
|
2649
|
+
additionalProperties: false,
|
2650
|
+
minProperties: 1,
|
2654
2651
|
properties: {
|
2655
|
-
|
2656
|
-
type: '
|
2657
|
-
|
2658
|
-
|
2659
|
-
|
2660
|
-
|
2661
|
-
|
2662
|
-
|
2652
|
+
types: {
|
2653
|
+
type: 'boolean',
|
2654
|
+
description: `Includes:\n\n${TYPES_KINDS.map(kind => `- [${kind}](https://spec.graphql.org/October2021/#${kind})`).join('\n')}`,
|
2655
|
+
},
|
2656
|
+
overrides: {
|
2657
|
+
type: 'object',
|
2658
|
+
description: 'Configuration for precise `ASTNode`',
|
2659
|
+
additionalProperties: false,
|
2660
|
+
properties: Object.fromEntries(ALLOWED_KINDS$1.map(kind => [kind, { type: 'boolean' }])),
|
2663
2661
|
},
|
2664
2662
|
},
|
2665
2663
|
},
|
2666
2664
|
},
|
2667
2665
|
},
|
2668
2666
|
create(context) {
|
2669
|
-
|
2667
|
+
const { types, overrides = {} } = context.options[0];
|
2668
|
+
const kinds = new Set(types ? TYPES_KINDS : []);
|
2669
|
+
for (const [kind, isEnabled] of Object.entries(overrides)) {
|
2670
|
+
if (isEnabled) {
|
2671
|
+
kinds.add(kind);
|
2672
|
+
}
|
2673
|
+
else {
|
2674
|
+
kinds.delete(kind);
|
2675
|
+
}
|
2676
|
+
}
|
2677
|
+
const selector = [...kinds].join(',');
|
2678
|
+
return {
|
2679
|
+
[selector](node) {
|
2680
|
+
var _a;
|
2681
|
+
const description = ((_a = node.description) === null || _a === void 0 ? void 0 : _a.value) || '';
|
2682
|
+
if (description.trim().length === 0) {
|
2683
|
+
context.report({
|
2684
|
+
loc: getLocation(node.name.loc, node.name.value),
|
2685
|
+
messageId: REQUIRE_DESCRIPTION_ERROR,
|
2686
|
+
data: {
|
2687
|
+
nodeType: node.kind,
|
2688
|
+
},
|
2689
|
+
});
|
2690
|
+
}
|
2691
|
+
},
|
2692
|
+
};
|
2670
2693
|
},
|
2671
2694
|
};
|
2672
2695
|
|
@@ -3211,7 +3234,7 @@ const rule$n = {
|
|
3211
3234
|
acceptedIdNames: ['id'],
|
3212
3235
|
acceptedIdTypes: ['ID'],
|
3213
3236
|
exceptions: {},
|
3214
|
-
...
|
3237
|
+
...context.options[0],
|
3215
3238
|
};
|
3216
3239
|
return {
|
3217
3240
|
ObjectTypeDefinition(node) {
|