@graphql-eslint/eslint-plugin 2.5.0-alpha-14532ce.0 → 3.0.0-alpha-069461d.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 +32 -1
- package/configs/index.d.ts +61 -2
- package/configs/recommended.d.ts +29 -1
- package/docs/README.md +1 -0
- 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/no-deprecated.md +2 -2
- package/docs/rules/no-root-type.md +56 -0
- package/docs/rules/strict-id-in-types.md +4 -4
- package/index.js +382 -282
- package/index.mjs +382 -282
- package/package.json +1 -1
- package/rules/alphabetize.d.ts +5 -6
- package/rules/index.d.ts +124 -19
- package/rules/naming-convention.d.ts +32 -25
- package/rules/no-root-type.d.ts +7 -0
- package/testkit.d.ts +1 -1
- 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',
|
@@ -85,6 +112,7 @@ const allConfig = {
|
|
85
112
|
'@graphql-eslint/match-document-filename': 'error',
|
86
113
|
'@graphql-eslint/no-deprecated': 'error',
|
87
114
|
'@graphql-eslint/no-hashtag-description': 'error',
|
115
|
+
'@graphql-eslint/no-root-type': ['error', { disallow: ['subscription'] }],
|
88
116
|
'@graphql-eslint/no-unreachable-types': 'error',
|
89
117
|
'@graphql-eslint/no-unused-fields': 'error',
|
90
118
|
'@graphql-eslint/require-deprecation-date': 'error',
|
@@ -205,9 +233,14 @@ const loaderCache = new Proxy(Object.create(null), {
|
|
205
233
|
return true;
|
206
234
|
},
|
207
235
|
});
|
208
|
-
const
|
209
|
-
|
210
|
-
|
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
|
+
];
|
211
244
|
var CaseStyle;
|
212
245
|
(function (CaseStyle) {
|
213
246
|
CaseStyle["camelCase"] = "camelCase";
|
@@ -258,7 +291,7 @@ function getLocation(loc, fieldName = '', offset) {
|
|
258
291
|
}
|
259
292
|
|
260
293
|
function extractRuleName(stack) {
|
261
|
-
const match = (stack || '').match(/validation[
|
294
|
+
const match = (stack || '').match(/validation[/\\]rules[/\\](.*?)\.js:/) || [];
|
262
295
|
return match[1] || null;
|
263
296
|
}
|
264
297
|
function validateDoc(sourceNode, context, schema, documentNode, rules, ruleName = null) {
|
@@ -507,11 +540,23 @@ const GRAPHQL_JS_VALIDATIONS = Object.assign({}, validationToRule('executable-de
|
|
507
540
|
}));
|
508
541
|
|
509
542
|
const ALPHABETIZE = 'ALPHABETIZE';
|
510
|
-
const fieldsEnum = [
|
543
|
+
const fieldsEnum = [
|
544
|
+
Kind.OBJECT_TYPE_DEFINITION,
|
545
|
+
Kind.INTERFACE_TYPE_DEFINITION,
|
546
|
+
Kind.INPUT_OBJECT_TYPE_DEFINITION,
|
547
|
+
];
|
511
548
|
const valuesEnum = [Kind.ENUM_TYPE_DEFINITION];
|
512
|
-
const selectionsEnum = [
|
549
|
+
const selectionsEnum = [
|
550
|
+
Kind.OPERATION_DEFINITION,
|
551
|
+
Kind.FRAGMENT_DEFINITION,
|
552
|
+
];
|
513
553
|
const variablesEnum = [Kind.OPERATION_DEFINITION];
|
514
|
-
const argumentsEnum = [
|
554
|
+
const argumentsEnum = [
|
555
|
+
Kind.FIELD_DEFINITION,
|
556
|
+
Kind.FIELD,
|
557
|
+
Kind.DIRECTIVE_DEFINITION,
|
558
|
+
Kind.DIRECTIVE,
|
559
|
+
];
|
515
560
|
const rule = {
|
516
561
|
meta: {
|
517
562
|
type: 'suggestion',
|
@@ -619,38 +664,48 @@ const rule = {
|
|
619
664
|
properties: {
|
620
665
|
fields: {
|
621
666
|
type: 'array',
|
622
|
-
|
667
|
+
uniqueItems: true,
|
668
|
+
minItems: 1,
|
669
|
+
items: {
|
623
670
|
enum: fieldsEnum,
|
624
671
|
},
|
625
|
-
description: 'Fields of `type`, `interface`, and `input
|
672
|
+
description: 'Fields of `type`, `interface`, and `input`',
|
626
673
|
},
|
627
674
|
values: {
|
628
675
|
type: 'array',
|
629
|
-
|
676
|
+
uniqueItems: true,
|
677
|
+
minItems: 1,
|
678
|
+
items: {
|
630
679
|
enum: valuesEnum,
|
631
680
|
},
|
632
|
-
description: 'Values of `enum
|
681
|
+
description: 'Values of `enum`',
|
633
682
|
},
|
634
683
|
selections: {
|
635
684
|
type: 'array',
|
636
|
-
|
685
|
+
uniqueItems: true,
|
686
|
+
minItems: 1,
|
687
|
+
items: {
|
637
688
|
enum: selectionsEnum,
|
638
689
|
},
|
639
|
-
description: 'Selections of operations (`query`, `mutation` and `subscription`) and `fragment
|
690
|
+
description: 'Selections of operations (`query`, `mutation` and `subscription`) and `fragment`',
|
640
691
|
},
|
641
692
|
variables: {
|
642
693
|
type: 'array',
|
643
|
-
|
694
|
+
uniqueItems: true,
|
695
|
+
minItems: 1,
|
696
|
+
items: {
|
644
697
|
enum: variablesEnum,
|
645
698
|
},
|
646
|
-
description: 'Variables of operations (`query`, `mutation` and `subscription`)
|
699
|
+
description: 'Variables of operations (`query`, `mutation` and `subscription`)',
|
647
700
|
},
|
648
701
|
arguments: {
|
649
702
|
type: 'array',
|
650
|
-
|
703
|
+
uniqueItems: true,
|
704
|
+
minItems: 1,
|
705
|
+
items: {
|
651
706
|
enum: argumentsEnum,
|
652
707
|
},
|
653
|
-
description: 'Arguments of fields and directives
|
708
|
+
description: 'Arguments of fields and directives',
|
654
709
|
},
|
655
710
|
},
|
656
711
|
},
|
@@ -681,7 +736,7 @@ const rule = {
|
|
681
736
|
const opts = context.options[0];
|
682
737
|
const fields = new Set((_a = opts.fields) !== null && _a !== void 0 ? _a : []);
|
683
738
|
const listeners = {};
|
684
|
-
const
|
739
|
+
const kinds = [
|
685
740
|
fields.has(Kind.OBJECT_TYPE_DEFINITION) && [Kind.OBJECT_TYPE_DEFINITION, Kind.OBJECT_TYPE_EXTENSION],
|
686
741
|
fields.has(Kind.INTERFACE_TYPE_DEFINITION) && [Kind.INTERFACE_TYPE_DEFINITION, Kind.INTERFACE_TYPE_EXTENSION],
|
687
742
|
fields.has(Kind.INPUT_OBJECT_TYPE_DEFINITION) && [
|
@@ -689,8 +744,9 @@ const rule = {
|
|
689
744
|
Kind.INPUT_OBJECT_TYPE_EXTENSION,
|
690
745
|
],
|
691
746
|
]
|
692
|
-
.
|
693
|
-
.
|
747
|
+
.filter(Boolean)
|
748
|
+
.flat();
|
749
|
+
const fieldsSelector = kinds.join(',');
|
694
750
|
const hasEnumValues = ((_b = opts.values) === null || _b === void 0 ? void 0 : _b[0]) === Kind.ENUM_TYPE_DEFINITION;
|
695
751
|
const selectionsSelector = (_c = opts.selections) === null || _c === void 0 ? void 0 : _c.join(',');
|
696
752
|
const hasVariables = ((_d = opts.variables) === null || _d === void 0 ? void 0 : _d[0]) === Kind.OPERATION_DEFINITION;
|
@@ -1090,6 +1146,9 @@ const rule$5 = {
|
|
1090
1146
|
},
|
1091
1147
|
};
|
1092
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';
|
1093
1152
|
const rule$6 = {
|
1094
1153
|
meta: {
|
1095
1154
|
type: 'suggestion',
|
@@ -1130,6 +1189,7 @@ const rule$6 = {
|
|
1130
1189
|
schema: [
|
1131
1190
|
{
|
1132
1191
|
type: 'object',
|
1192
|
+
additionalProperties: false,
|
1133
1193
|
properties: {
|
1134
1194
|
checkInputType: {
|
1135
1195
|
type: 'boolean',
|
@@ -1152,24 +1212,22 @@ const rule$6 = {
|
|
1152
1212
|
description: 'Apply the rule to Mutations',
|
1153
1213
|
},
|
1154
1214
|
},
|
1155
|
-
additionalProperties: false,
|
1156
1215
|
},
|
1157
1216
|
],
|
1158
1217
|
},
|
1159
1218
|
create(context) {
|
1160
|
-
var _a;
|
1161
1219
|
const options = {
|
1162
|
-
caseSensitiveInputType: true,
|
1163
1220
|
checkInputType: false,
|
1164
|
-
|
1221
|
+
caseSensitiveInputType: true,
|
1165
1222
|
checkQueries: false,
|
1166
|
-
|
1223
|
+
checkMutations: true,
|
1224
|
+
...context.options[0],
|
1167
1225
|
};
|
1168
1226
|
const shouldCheckType = node => (options.checkMutations && isMutationType(node)) || (options.checkQueries && isQueryType(node));
|
1169
1227
|
const listeners = {
|
1170
|
-
'FieldDefinition > InputValueDefinition'
|
1171
|
-
|
1172
|
-
|
1228
|
+
'FieldDefinition > InputValueDefinition[name.value!=input]'(node) {
|
1229
|
+
if (shouldCheckType(node.parent.parent)) {
|
1230
|
+
const name = node.name.value;
|
1173
1231
|
context.report({
|
1174
1232
|
loc: getLocation(node.loc, name),
|
1175
1233
|
message: `Input "${name}" should be called "input"`,
|
@@ -1177,11 +1235,11 @@ const rule$6 = {
|
|
1177
1235
|
}
|
1178
1236
|
},
|
1179
1237
|
};
|
1180
|
-
if (options
|
1181
|
-
listeners['FieldDefinition > InputValueDefinition NamedType'] = node => {
|
1238
|
+
if (options.checkInputType) {
|
1239
|
+
listeners['FieldDefinition > InputValueDefinition NamedType'] = (node) => {
|
1182
1240
|
const findInputType = item => {
|
1183
1241
|
let currentNode = item;
|
1184
|
-
while (currentNode.type !==
|
1242
|
+
while (currentNode.type !== Kind.INPUT_VALUE_DEFINITION) {
|
1185
1243
|
currentNode = currentNode.parent;
|
1186
1244
|
}
|
1187
1245
|
return currentNode;
|
@@ -1398,69 +1456,40 @@ const rule$7 = {
|
|
1398
1456
|
},
|
1399
1457
|
};
|
1400
1458
|
|
1401
|
-
const
|
1402
|
-
|
1403
|
-
|
1404
|
-
|
1405
|
-
|
1406
|
-
|
1407
|
-
const acceptedStyles = [
|
1408
|
-
'camelCase',
|
1409
|
-
'PascalCase',
|
1410
|
-
'snake_case',
|
1411
|
-
'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,
|
1412
1465
|
];
|
1413
|
-
|
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
|
-
if (forbiddenPrefixes.some(forbiddenPrefix => name.startsWith(forbiddenPrefix))) {
|
1441
|
-
return {
|
1442
|
-
ok: false,
|
1443
|
-
errorMessage: '{{nodeType}} "{{nodeName}}" should not have one of the following prefix(es): {{forbiddenPrefixes}}',
|
1444
|
-
};
|
1445
|
-
}
|
1446
|
-
if (forbiddenSuffixes.some(forbiddenSuffix => name.endsWith(forbiddenSuffix))) {
|
1447
|
-
return {
|
1448
|
-
ok: false,
|
1449
|
-
errorMessage: '{{nodeType}} "{{nodeName}}" should not have one of the following suffix(es): {{forbiddenSuffixes}}',
|
1450
|
-
};
|
1451
|
-
}
|
1452
|
-
if (!formats[style]) {
|
1453
|
-
return { ok: true };
|
1454
|
-
}
|
1455
|
-
const ok = new RegExp(formats[style]).test(name);
|
1456
|
-
if (ok) {
|
1457
|
-
return { ok: true };
|
1458
|
-
}
|
1459
|
-
return {
|
1460
|
-
ok: false,
|
1461
|
-
errorMessage: '{{nodeType}} name "{{nodeName}}" should be in {{format}} format',
|
1462
|
-
};
|
1463
|
-
}
|
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);
|
1464
1493
|
const schemaOption$1 = {
|
1465
1494
|
oneOf: [{ $ref: '#/definitions/asString' }, { $ref: '#/definitions/asObject' }],
|
1466
1495
|
};
|
@@ -1475,89 +1504,120 @@ const rule$8 = {
|
|
1475
1504
|
examples: [
|
1476
1505
|
{
|
1477
1506
|
title: 'Incorrect',
|
1478
|
-
usage: [{
|
1507
|
+
usage: [{ types: 'PascalCase', fields: 'camelCase' }],
|
1479
1508
|
code: /* GraphQL */ `
|
1480
|
-
type
|
1481
|
-
|
1509
|
+
type user {
|
1510
|
+
first_name: String!
|
1482
1511
|
}
|
1483
1512
|
`,
|
1484
1513
|
},
|
1485
1514
|
{
|
1486
1515
|
title: 'Correct',
|
1487
|
-
usage: [{
|
1516
|
+
usage: [{ types: 'PascalCase', fields: 'camelCase' }],
|
1488
1517
|
code: /* GraphQL */ `
|
1489
|
-
type
|
1490
|
-
|
1518
|
+
type User {
|
1519
|
+
firstName: String
|
1491
1520
|
}
|
1492
1521
|
`,
|
1493
1522
|
},
|
1494
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
|
+
],
|
1495
1555
|
},
|
1496
1556
|
schema: {
|
1497
1557
|
definitions: {
|
1498
1558
|
asString: {
|
1499
|
-
|
1500
|
-
description: `One of: ${
|
1501
|
-
enum: acceptedStyles,
|
1559
|
+
enum: ALLOWED_STYLES,
|
1560
|
+
description: `One of: ${ALLOWED_STYLES.map(t => `\`${t}\``).join(', ')}`,
|
1502
1561
|
},
|
1503
1562
|
asObject: {
|
1504
1563
|
type: 'object',
|
1564
|
+
additionalProperties: false,
|
1505
1565
|
properties: {
|
1506
|
-
style: {
|
1507
|
-
|
1508
|
-
|
1509
|
-
},
|
1510
|
-
prefix: {
|
1511
|
-
type: 'string',
|
1512
|
-
},
|
1513
|
-
suffix: {
|
1514
|
-
type: 'string',
|
1515
|
-
},
|
1566
|
+
style: { enum: ALLOWED_STYLES },
|
1567
|
+
prefix: { type: 'string' },
|
1568
|
+
suffix: { type: 'string' },
|
1516
1569
|
forbiddenPrefixes: {
|
1517
|
-
additionalItems: false,
|
1518
1570
|
type: 'array',
|
1571
|
+
uniqueItems: true,
|
1519
1572
|
minItems: 1,
|
1520
|
-
items: {
|
1521
|
-
type: 'string',
|
1522
|
-
},
|
1573
|
+
items: { type: 'string' },
|
1523
1574
|
},
|
1524
1575
|
forbiddenSuffixes: {
|
1525
|
-
additionalItems: false,
|
1526
1576
|
type: 'array',
|
1577
|
+
uniqueItems: true,
|
1527
1578
|
minItems: 1,
|
1528
|
-
items: {
|
1529
|
-
type: 'string',
|
1530
|
-
},
|
1579
|
+
items: { type: 'string' },
|
1531
1580
|
},
|
1532
1581
|
},
|
1533
1582
|
},
|
1534
1583
|
},
|
1535
|
-
$schema: 'http://json-schema.org/draft-04/schema#',
|
1536
1584
|
type: 'array',
|
1585
|
+
maxItems: 1,
|
1537
1586
|
items: {
|
1538
1587
|
type: 'object',
|
1588
|
+
additionalProperties: false,
|
1539
1589
|
properties: {
|
1540
|
-
|
1541
|
-
|
1542
|
-
|
1543
|
-
[Kind.INPUT_VALUE_DEFINITION]: schemaOption$1,
|
1544
|
-
[Kind.OBJECT_TYPE_DEFINITION]: schemaOption$1,
|
1545
|
-
[Kind.INTERFACE_TYPE_DEFINITION]: schemaOption$1,
|
1546
|
-
[Kind.ENUM_TYPE_DEFINITION]: schemaOption$1,
|
1547
|
-
[Kind.UNION_TYPE_DEFINITION]: schemaOption$1,
|
1548
|
-
[Kind.SCALAR_TYPE_DEFINITION]: schemaOption$1,
|
1549
|
-
[Kind.OPERATION_DEFINITION]: schemaOption$1,
|
1550
|
-
[Kind.FRAGMENT_DEFINITION]: schemaOption$1,
|
1551
|
-
QueryDefinition: schemaOption$1,
|
1552
|
-
leadingUnderscore: {
|
1553
|
-
type: 'string',
|
1554
|
-
enum: ['allow', 'forbid'],
|
1555
|
-
default: 'forbid',
|
1590
|
+
types: {
|
1591
|
+
...schemaOption$1,
|
1592
|
+
description: `Includes:\n\n${TYPES_KINDS.map(kind => `- \`${kind}\``).join('\n')}`,
|
1556
1593
|
},
|
1557
|
-
|
1558
|
-
|
1559
|
-
|
1560
|
-
|
1594
|
+
fields: {
|
1595
|
+
...schemaOption$1,
|
1596
|
+
description: `Includes:\n\n${FIELDS_KINDS.map(kind => `- \`${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}\``),
|
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
|
+
},
|
1561
1621
|
},
|
1562
1622
|
},
|
1563
1623
|
},
|
@@ -1565,136 +1625,83 @@ const rule$8 = {
|
|
1565
1625
|
},
|
1566
1626
|
create(context) {
|
1567
1627
|
const options = {
|
1568
|
-
|
1569
|
-
|
1570
|
-
...(context.options[0] || {}),
|
1628
|
+
overrides: {},
|
1629
|
+
...context.options[0],
|
1571
1630
|
};
|
1572
|
-
|
1573
|
-
|
1574
|
-
|
1575
|
-
|
1576
|
-
|
1577
|
-
|
1578
|
-
|
1579
|
-
|
1580
|
-
|
1581
|
-
|
1582
|
-
|
1583
|
-
}
|
1584
|
-
|
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) {
|
1585
1648
|
context.report({
|
1586
|
-
loc: getLocation(
|
1587
|
-
message:
|
1588
|
-
data: {
|
1589
|
-
prefix,
|
1590
|
-
suffix,
|
1591
|
-
format: style,
|
1592
|
-
forbiddenPrefixes: forbiddenPrefixes.join(', '),
|
1593
|
-
forbiddenSuffixes: forbiddenSuffixes.join(', '),
|
1594
|
-
nodeType,
|
1595
|
-
nodeName: node.value,
|
1596
|
-
},
|
1649
|
+
loc: getLocation(name.loc, name.value),
|
1650
|
+
message: `${nodeType} "${nodeName}" should ${errorMessage}`,
|
1597
1651
|
});
|
1598
1652
|
}
|
1599
|
-
|
1600
|
-
|
1601
|
-
|
1602
|
-
|
1603
|
-
}
|
1604
|
-
return {
|
1605
|
-
style: value,
|
1606
|
-
prefix: '',
|
1607
|
-
suffix: '',
|
1608
|
-
};
|
1609
|
-
};
|
1610
|
-
return {
|
1611
|
-
Name: node => {
|
1612
|
-
if (node.value.startsWith('_') && options.leadingUnderscore === 'forbid') {
|
1613
|
-
context.report({
|
1614
|
-
loc: getLocation(node.loc, node.value),
|
1615
|
-
message: 'Leading underscores are not allowed',
|
1616
|
-
});
|
1617
|
-
}
|
1618
|
-
if (node.value.endsWith('_') && options.trailingUnderscore === 'forbid') {
|
1619
|
-
context.report({
|
1620
|
-
loc: getLocation(node.loc, node.value),
|
1621
|
-
message: 'Trailing underscores are not allowed',
|
1622
|
-
});
|
1623
|
-
}
|
1624
|
-
},
|
1625
|
-
ObjectTypeDefinition: node => {
|
1626
|
-
if (options.ObjectTypeDefinition) {
|
1627
|
-
const property = normalisePropertyOption(options.ObjectTypeDefinition);
|
1628
|
-
checkNode(node.name, property, 'Type');
|
1629
|
-
}
|
1630
|
-
},
|
1631
|
-
InterfaceTypeDefinition: node => {
|
1632
|
-
if (options.InterfaceTypeDefinition) {
|
1633
|
-
const property = normalisePropertyOption(options.InterfaceTypeDefinition);
|
1634
|
-
checkNode(node.name, property, 'Interface');
|
1635
|
-
}
|
1636
|
-
},
|
1637
|
-
EnumTypeDefinition: node => {
|
1638
|
-
if (options.EnumTypeDefinition) {
|
1639
|
-
const property = normalisePropertyOption(options.EnumTypeDefinition);
|
1640
|
-
checkNode(node.name, property, 'Enumerator');
|
1653
|
+
function getErrorMessage() {
|
1654
|
+
let name = nodeName;
|
1655
|
+
if (options.allowLeadingUnderscore) {
|
1656
|
+
name = name.replace(/^_*/, '');
|
1641
1657
|
}
|
1642
|
-
|
1643
|
-
|
1644
|
-
if (options.InputObjectTypeDefinition) {
|
1645
|
-
const property = normalisePropertyOption(options.InputObjectTypeDefinition);
|
1646
|
-
checkNode(node.name, property, 'Input type');
|
1658
|
+
if (options.allowTrailingUnderscore) {
|
1659
|
+
name = name.replace(/_*$/, '');
|
1647
1660
|
}
|
1648
|
-
|
1649
|
-
|
1650
|
-
if (options.QueryDefinition && isQueryType(node.parent)) {
|
1651
|
-
const property = normalisePropertyOption(options.QueryDefinition);
|
1652
|
-
checkNode(node.name, property, 'Query');
|
1661
|
+
if (prefix && !name.startsWith(prefix)) {
|
1662
|
+
return `have "${prefix}" prefix`;
|
1653
1663
|
}
|
1654
|
-
if (
|
1655
|
-
|
1656
|
-
checkNode(node.name, property, 'Field');
|
1664
|
+
if (suffix && !name.endsWith(suffix)) {
|
1665
|
+
return `have "${suffix}" suffix`;
|
1657
1666
|
}
|
1658
|
-
|
1659
|
-
|
1660
|
-
|
1661
|
-
const property = normalisePropertyOption(options.EnumValueDefinition);
|
1662
|
-
checkNode(node.name, property, 'Enumeration value');
|
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`;
|
1663
1670
|
}
|
1664
|
-
|
1665
|
-
|
1666
|
-
|
1667
|
-
const property = normalisePropertyOption(options.InputValueDefinition);
|
1668
|
-
checkNode(node.name, property, 'Input property');
|
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`;
|
1669
1674
|
}
|
1670
|
-
|
1671
|
-
|
1672
|
-
if (options.OperationDefinition) {
|
1673
|
-
const property = normalisePropertyOption(options.OperationDefinition);
|
1674
|
-
if (node.name) {
|
1675
|
-
checkNode(node.name, property, 'Operation');
|
1676
|
-
}
|
1675
|
+
if (style && !ALLOWED_STYLES.includes(style)) {
|
1676
|
+
return `be in one of the following options: ${ALLOWED_STYLES.join(', ')}`;
|
1677
1677
|
}
|
1678
|
-
|
1679
|
-
|
1680
|
-
|
1681
|
-
const property = normalisePropertyOption(options.FragmentDefinition);
|
1682
|
-
checkNode(node.name, property, 'Fragment');
|
1678
|
+
const caseRegex = StyleToRegex[style];
|
1679
|
+
if (caseRegex && !caseRegex.test(name)) {
|
1680
|
+
return `be in ${style} format`;
|
1683
1681
|
}
|
1684
|
-
}
|
1685
|
-
ScalarTypeDefinition: node => {
|
1686
|
-
if (options.ScalarTypeDefinition) {
|
1687
|
-
const property = normalisePropertyOption(options.ScalarTypeDefinition);
|
1688
|
-
checkNode(node.name, property, 'Scalar');
|
1689
|
-
}
|
1690
|
-
},
|
1691
|
-
UnionTypeDefinition: node => {
|
1692
|
-
if (options.UnionTypeDefinition) {
|
1693
|
-
const property = normalisePropertyOption(options.UnionTypeDefinition);
|
1694
|
-
checkNode(node.name, property, 'Union');
|
1695
|
-
}
|
1696
|
-
},
|
1682
|
+
}
|
1697
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;
|
1698
1705
|
},
|
1699
1706
|
};
|
1700
1707
|
|
@@ -2059,9 +2066,100 @@ const rule$d = {
|
|
2059
2066
|
},
|
2060
2067
|
};
|
2061
2068
|
|
2069
|
+
const ROOT_TYPES = ['query', 'mutation', 'subscription'];
|
2070
|
+
const rule$e = {
|
2071
|
+
meta: {
|
2072
|
+
type: 'suggestion',
|
2073
|
+
docs: {
|
2074
|
+
category: 'Validation',
|
2075
|
+
description: 'Disallow using root types for `read-only` or `write-only` schemas.',
|
2076
|
+
url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/no-root-type.md',
|
2077
|
+
requiresSchema: true,
|
2078
|
+
examples: [
|
2079
|
+
{
|
2080
|
+
title: 'Incorrect (`read-only` schema)',
|
2081
|
+
usage: [{ disallow: ['mutation', 'subscription'] }],
|
2082
|
+
code: /* GraphQL */ `
|
2083
|
+
type Mutation {
|
2084
|
+
createUser(input: CreateUserInput!): User!
|
2085
|
+
}
|
2086
|
+
`,
|
2087
|
+
},
|
2088
|
+
{
|
2089
|
+
title: 'Incorrect (`write-only` schema)',
|
2090
|
+
usage: [{ disallow: ['query'] }],
|
2091
|
+
code: /* GraphQL */ `
|
2092
|
+
type Query {
|
2093
|
+
users: [User!]!
|
2094
|
+
}
|
2095
|
+
`,
|
2096
|
+
},
|
2097
|
+
{
|
2098
|
+
title: 'Correct (`read-only` schema)',
|
2099
|
+
usage: [{ disallow: ['mutation', 'subscription'] }],
|
2100
|
+
code: /* GraphQL */ `
|
2101
|
+
type Query {
|
2102
|
+
users: [User!]!
|
2103
|
+
}
|
2104
|
+
`,
|
2105
|
+
},
|
2106
|
+
],
|
2107
|
+
optionsForConfig: [{ disallow: ['subscription'] }],
|
2108
|
+
},
|
2109
|
+
schema: {
|
2110
|
+
type: 'array',
|
2111
|
+
minItems: 1,
|
2112
|
+
maxItems: 1,
|
2113
|
+
items: {
|
2114
|
+
type: 'object',
|
2115
|
+
additionalProperties: false,
|
2116
|
+
required: ['disallow'],
|
2117
|
+
properties: {
|
2118
|
+
disallow: {
|
2119
|
+
type: 'array',
|
2120
|
+
uniqueItems: true,
|
2121
|
+
minItems: 1,
|
2122
|
+
items: {
|
2123
|
+
enum: ROOT_TYPES,
|
2124
|
+
},
|
2125
|
+
},
|
2126
|
+
},
|
2127
|
+
},
|
2128
|
+
},
|
2129
|
+
},
|
2130
|
+
create(context) {
|
2131
|
+
const schema = requireGraphQLSchemaFromContext('no-root-type', context);
|
2132
|
+
const disallow = new Set(context.options[0].disallow);
|
2133
|
+
const rootTypeNames = [
|
2134
|
+
disallow.has('query') && schema.getQueryType(),
|
2135
|
+
disallow.has('mutation') && schema.getMutationType(),
|
2136
|
+
disallow.has('subscription') && schema.getSubscriptionType(),
|
2137
|
+
]
|
2138
|
+
.filter(Boolean)
|
2139
|
+
.map(type => type.name);
|
2140
|
+
if (rootTypeNames.length === 0) {
|
2141
|
+
return {};
|
2142
|
+
}
|
2143
|
+
const selector = [
|
2144
|
+
`:matches(${Kind.OBJECT_TYPE_DEFINITION}, ${Kind.OBJECT_TYPE_EXTENSION})`,
|
2145
|
+
'>',
|
2146
|
+
`${Kind.NAME}[value=/^(${rootTypeNames.join('|')})$/]`,
|
2147
|
+
].join(' ');
|
2148
|
+
return {
|
2149
|
+
[selector](node) {
|
2150
|
+
const typeName = node.value;
|
2151
|
+
context.report({
|
2152
|
+
loc: getLocation(node.loc, typeName),
|
2153
|
+
message: `Root type "${typeName}" is forbidden`,
|
2154
|
+
});
|
2155
|
+
},
|
2156
|
+
};
|
2157
|
+
},
|
2158
|
+
};
|
2159
|
+
|
2062
2160
|
const UNREACHABLE_TYPE = 'UNREACHABLE_TYPE';
|
2063
2161
|
const RULE_NAME = 'no-unreachable-types';
|
2064
|
-
const rule$
|
2162
|
+
const rule$f = {
|
2065
2163
|
meta: {
|
2066
2164
|
messages: {
|
2067
2165
|
[UNREACHABLE_TYPE]: `Type "{{ typeName }}" is unreachable`,
|
@@ -2137,7 +2235,7 @@ const rule$e = {
|
|
2137
2235
|
|
2138
2236
|
const UNUSED_FIELD = 'UNUSED_FIELD';
|
2139
2237
|
const RULE_NAME$1 = 'no-unused-fields';
|
2140
|
-
const rule$
|
2238
|
+
const rule$g = {
|
2141
2239
|
meta: {
|
2142
2240
|
messages: {
|
2143
2241
|
[UNUSED_FIELD]: `Field "{{fieldName}}" is unused`,
|
@@ -2320,12 +2418,13 @@ function convertDescription(node) {
|
|
2320
2418
|
return [];
|
2321
2419
|
}
|
2322
2420
|
|
2421
|
+
// eslint-disable-next-line unicorn/better-regex
|
2323
2422
|
const DATE_REGEX = /^\d{2}\/\d{2}\/\d{4}$/;
|
2324
2423
|
const MESSAGE_REQUIRE_DATE = 'MESSAGE_REQUIRE_DATE';
|
2325
2424
|
const MESSAGE_INVALID_FORMAT = 'MESSAGE_INVALID_FORMAT';
|
2326
2425
|
const MESSAGE_INVALID_DATE = 'MESSAGE_INVALID_DATE';
|
2327
2426
|
const MESSAGE_CAN_BE_REMOVED = 'MESSAGE_CAN_BE_REMOVED';
|
2328
|
-
const rule$
|
2427
|
+
const rule$h = {
|
2329
2428
|
meta: {
|
2330
2429
|
type: 'suggestion',
|
2331
2430
|
docs: {
|
@@ -2428,7 +2527,7 @@ const rule$g = {
|
|
2428
2527
|
},
|
2429
2528
|
};
|
2430
2529
|
|
2431
|
-
const rule$
|
2530
|
+
const rule$i = {
|
2432
2531
|
meta: {
|
2433
2532
|
docs: {
|
2434
2533
|
description: `Require all deprecation directives to specify a reason.`,
|
@@ -2508,7 +2607,7 @@ function verifyRule(context, node) {
|
|
2508
2607
|
}
|
2509
2608
|
}
|
2510
2609
|
}
|
2511
|
-
const rule$
|
2610
|
+
const rule$j = {
|
2512
2611
|
meta: {
|
2513
2612
|
docs: {
|
2514
2613
|
category: 'Best Practices',
|
@@ -2573,7 +2672,7 @@ const rule$i = {
|
|
2573
2672
|
};
|
2574
2673
|
|
2575
2674
|
const RULE_NAME$2 = 'require-field-of-type-query-in-mutation-result';
|
2576
|
-
const rule$
|
2675
|
+
const rule$k = {
|
2577
2676
|
meta: {
|
2578
2677
|
type: 'suggestion',
|
2579
2678
|
docs: {
|
@@ -2741,7 +2840,7 @@ const convertNode = (typeInfo) => (node, key, parent) => {
|
|
2741
2840
|
|
2742
2841
|
const REQUIRE_ID_WHEN_AVAILABLE = 'REQUIRE_ID_WHEN_AVAILABLE';
|
2743
2842
|
const DEFAULT_ID_FIELD_NAME = 'id';
|
2744
|
-
const rule$
|
2843
|
+
const rule$l = {
|
2745
2844
|
meta: {
|
2746
2845
|
type: 'suggestion',
|
2747
2846
|
docs: {
|
@@ -2877,7 +2976,7 @@ const rule$k = {
|
|
2877
2976
|
},
|
2878
2977
|
};
|
2879
2978
|
|
2880
|
-
const rule$
|
2979
|
+
const rule$m = {
|
2881
2980
|
meta: {
|
2882
2981
|
docs: {
|
2883
2982
|
category: 'Best Practices',
|
@@ -2999,7 +3098,7 @@ const shouldIgnoreNode = ({ node, exceptions }) => {
|
|
2999
3098
|
}
|
3000
3099
|
return false;
|
3001
3100
|
};
|
3002
|
-
const rule$
|
3101
|
+
const rule$n = {
|
3003
3102
|
meta: {
|
3004
3103
|
type: 'suggestion',
|
3005
3104
|
docs: {
|
@@ -3113,7 +3212,7 @@ const rule$m = {
|
|
3113
3212
|
acceptedIdNames: ['id'],
|
3114
3213
|
acceptedIdTypes: ['ID'],
|
3115
3214
|
exceptions: {},
|
3116
|
-
...
|
3215
|
+
...context.options[0],
|
3117
3216
|
};
|
3118
3217
|
return {
|
3119
3218
|
ObjectTypeDefinition(node) {
|
@@ -3176,7 +3275,7 @@ const checkNode = (context, node, ruleName, messageId) => {
|
|
3176
3275
|
});
|
3177
3276
|
}
|
3178
3277
|
};
|
3179
|
-
const rule$
|
3278
|
+
const rule$o = {
|
3180
3279
|
meta: {
|
3181
3280
|
type: 'suggestion',
|
3182
3281
|
docs: {
|
@@ -3235,7 +3334,7 @@ const rule$n = {
|
|
3235
3334
|
|
3236
3335
|
const RULE_NAME$4 = 'unique-operation-name';
|
3237
3336
|
const UNIQUE_OPERATION_NAME = 'UNIQUE_OPERATION_NAME';
|
3238
|
-
const rule$
|
3337
|
+
const rule$p = {
|
3239
3338
|
meta: {
|
3240
3339
|
type: 'suggestion',
|
3241
3340
|
docs: {
|
@@ -3315,17 +3414,18 @@ const rules = {
|
|
3315
3414
|
'no-deprecated': rule$b,
|
3316
3415
|
'no-hashtag-description': rule$c,
|
3317
3416
|
'no-operation-name-suffix': rule$d,
|
3318
|
-
'no-
|
3319
|
-
'no-
|
3320
|
-
'
|
3321
|
-
'require-deprecation-
|
3322
|
-
'require-
|
3323
|
-
'require-
|
3324
|
-
'require-
|
3325
|
-
'
|
3326
|
-
'
|
3327
|
-
'
|
3328
|
-
'unique-
|
3417
|
+
'no-root-type': rule$e,
|
3418
|
+
'no-unreachable-types': rule$f,
|
3419
|
+
'no-unused-fields': rule$g,
|
3420
|
+
'require-deprecation-date': rule$h,
|
3421
|
+
'require-deprecation-reason': rule$i,
|
3422
|
+
'require-description': rule$j,
|
3423
|
+
'require-field-of-type-query-in-mutation-result': rule$k,
|
3424
|
+
'require-id-when-available': rule$l,
|
3425
|
+
'selection-set-depth': rule$m,
|
3426
|
+
'strict-id-in-types': rule$n,
|
3427
|
+
'unique-fragment-name': rule$o,
|
3428
|
+
'unique-operation-name': rule$p,
|
3329
3429
|
};
|
3330
3430
|
|
3331
3431
|
const RELEVANT_KEYWORDS = ['gql', 'graphql', '/* GraphQL */'];
|