@graphql-eslint/eslint-plugin 2.4.0-alpha-60dfe26.0 → 3.0.0-alpha-5388f29.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 +29 -1
- package/configs/index.d.ts +58 -2
- package/configs/recommended.d.ts +29 -1
- package/docs/custom-rules.md +1 -1
- package/docs/rules/alphabetize.md +35 -10
- package/docs/rules/avoid-duplicate-fields.md +9 -7
- package/docs/rules/description-style.md +2 -2
- package/docs/rules/fragments-on-composite-type.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/one-field-subscriptions.md +2 -2
- package/docs/rules/possible-fragment-spread.md +2 -2
- package/docs/rules/possible-type-extension.md +2 -2
- package/docs/rules/strict-id-in-types.md +4 -4
- package/docs/rules/unique-directive-names-per-location.md +2 -2
- package/docs/rules/value-literals-of-correct-type.md +2 -2
- package/estree-parser/converter.d.ts +3 -2
- package/index.js +484 -440
- package/index.mjs +484 -440
- package/package.json +1 -1
- package/rules/avoid-duplicate-fields.d.ts +1 -1
- package/rules/index.d.ts +117 -15
- package/rules/naming-convention.d.ts +32 -25
- package/testkit.d.ts +6 -3
- package/types.d.ts +1 -0
- package/utils.d.ts +6 -8
package/index.mjs
CHANGED
@@ -9,6 +9,8 @@ import depthLimit from 'graphql-depth-limit';
|
|
9
9
|
import { parseCode } from '@graphql-tools/graphql-tag-pluck';
|
10
10
|
import { loadConfigSync, GraphQLConfig } from 'graphql-config';
|
11
11
|
import { CodeFileLoader } from '@graphql-tools/code-file-loader';
|
12
|
+
import { RuleTester, Linter } from 'eslint';
|
13
|
+
import { codeFrameColumns } from '@babel/code-frame';
|
12
14
|
|
13
15
|
/*
|
14
16
|
* 🚨 IMPORTANT! Do not manually modify this file. Run: `yarn generate-configs`
|
@@ -27,7 +29,34 @@ const recommendedConfig = {
|
|
27
29
|
'@graphql-eslint/known-type-names': 'error',
|
28
30
|
'@graphql-eslint/lone-anonymous-operation': 'error',
|
29
31
|
'@graphql-eslint/lone-schema-definition': 'error',
|
30
|
-
'@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
|
+
],
|
31
60
|
'@graphql-eslint/no-anonymous-operations': 'error',
|
32
61
|
'@graphql-eslint/no-case-insensitive-enum-values-duplicates': 'error',
|
33
62
|
'@graphql-eslint/no-fragment-cycles': 'error',
|
@@ -203,9 +232,14 @@ const loaderCache = new Proxy(Object.create(null), {
|
|
203
232
|
return true;
|
204
233
|
},
|
205
234
|
});
|
206
|
-
const
|
207
|
-
|
208
|
-
|
235
|
+
const TYPES_KINDS = [
|
236
|
+
Kind.OBJECT_TYPE_DEFINITION,
|
237
|
+
Kind.INTERFACE_TYPE_DEFINITION,
|
238
|
+
Kind.ENUM_TYPE_DEFINITION,
|
239
|
+
Kind.SCALAR_TYPE_DEFINITION,
|
240
|
+
Kind.INPUT_OBJECT_TYPE_DEFINITION,
|
241
|
+
Kind.UNION_TYPE_DEFINITION,
|
242
|
+
];
|
209
243
|
var CaseStyle;
|
210
244
|
(function (CaseStyle) {
|
211
245
|
CaseStyle["camelCase"] = "camelCase";
|
@@ -236,9 +270,27 @@ const convertCase = (style, str) => {
|
|
236
270
|
return lowerCase(str).replace(/ /g, '-');
|
237
271
|
}
|
238
272
|
};
|
273
|
+
function getLocation(loc, fieldName = '', offset) {
|
274
|
+
const { start } = loc;
|
275
|
+
/*
|
276
|
+
* ESLint has 0-based column number
|
277
|
+
* https://eslint.org/docs/developer-guide/working-with-rules#contextreport
|
278
|
+
*/
|
279
|
+
const { offsetStart = 1, offsetEnd = 1 } = offset !== null && offset !== void 0 ? offset : {};
|
280
|
+
return {
|
281
|
+
start: {
|
282
|
+
line: start.line,
|
283
|
+
column: start.column - offsetStart,
|
284
|
+
},
|
285
|
+
end: {
|
286
|
+
line: start.line,
|
287
|
+
column: start.column - offsetEnd + fieldName.length,
|
288
|
+
},
|
289
|
+
};
|
290
|
+
}
|
239
291
|
|
240
292
|
function extractRuleName(stack) {
|
241
|
-
const match = (stack || '').match(/validation[
|
293
|
+
const match = (stack || '').match(/validation[/\\]rules[/\\](.*?)\.js:/) || [];
|
242
294
|
return match[1] || null;
|
243
295
|
}
|
244
296
|
function validateDoc(sourceNode, context, schema, documentNode, rules, ruleName = null) {
|
@@ -249,7 +301,7 @@ function validateDoc(sourceNode, context, schema, documentNode, rules, ruleName
|
|
249
301
|
for (const error of validationErrors) {
|
250
302
|
const validateRuleName = ruleName || `[${extractRuleName(error.stack)}]`;
|
251
303
|
context.report({
|
252
|
-
loc: error.locations[0],
|
304
|
+
loc: getLocation({ start: error.locations[0] }),
|
253
305
|
message: ruleName ? error.message : `${validateRuleName} ${error.message}`,
|
254
306
|
});
|
255
307
|
}
|
@@ -286,6 +338,7 @@ const validationToRule = (name, ruleName, docs, getDocumentNode) => {
|
|
286
338
|
meta: {
|
287
339
|
docs: {
|
288
340
|
...docs,
|
341
|
+
graphQLJSRuleName: ruleName,
|
289
342
|
category: 'Validation',
|
290
343
|
recommended: true,
|
291
344
|
requiresSchema,
|
@@ -585,7 +638,7 @@ const rule = {
|
|
585
638
|
],
|
586
639
|
},
|
587
640
|
messages: {
|
588
|
-
[ALPHABETIZE]: '"{{ currName }}" should be before "{{ prevName }}"
|
641
|
+
[ALPHABETIZE]: '"{{ currName }}" should be before "{{ prevName }}"',
|
589
642
|
},
|
590
643
|
schema: {
|
591
644
|
type: 'array',
|
@@ -598,38 +651,48 @@ const rule = {
|
|
598
651
|
properties: {
|
599
652
|
fields: {
|
600
653
|
type: 'array',
|
601
|
-
|
654
|
+
uniqueItems: true,
|
655
|
+
minItems: 1,
|
656
|
+
items: {
|
602
657
|
enum: fieldsEnum,
|
603
658
|
},
|
604
|
-
description: 'Fields of `type`, `interface`, and `input
|
659
|
+
description: 'Fields of `type`, `interface`, and `input`',
|
605
660
|
},
|
606
661
|
values: {
|
607
662
|
type: 'array',
|
608
|
-
|
663
|
+
uniqueItems: true,
|
664
|
+
minItems: 1,
|
665
|
+
items: {
|
609
666
|
enum: valuesEnum,
|
610
667
|
},
|
611
|
-
description: 'Values of `enum
|
668
|
+
description: 'Values of `enum`',
|
612
669
|
},
|
613
670
|
selections: {
|
614
671
|
type: 'array',
|
615
|
-
|
672
|
+
uniqueItems: true,
|
673
|
+
minItems: 1,
|
674
|
+
items: {
|
616
675
|
enum: selectionsEnum,
|
617
676
|
},
|
618
|
-
description: 'Selections of operations (`query`, `mutation` and `subscription`) and `fragment
|
677
|
+
description: 'Selections of operations (`query`, `mutation` and `subscription`) and `fragment`',
|
619
678
|
},
|
620
679
|
variables: {
|
621
680
|
type: 'array',
|
622
|
-
|
681
|
+
uniqueItems: true,
|
682
|
+
minItems: 1,
|
683
|
+
items: {
|
623
684
|
enum: variablesEnum,
|
624
685
|
},
|
625
|
-
description: 'Variables of operations (`query`, `mutation` and `subscription`)
|
686
|
+
description: 'Variables of operations (`query`, `mutation` and `subscription`)',
|
626
687
|
},
|
627
688
|
arguments: {
|
628
689
|
type: 'array',
|
629
|
-
|
690
|
+
uniqueItems: true,
|
691
|
+
minItems: 1,
|
692
|
+
items: {
|
630
693
|
enum: argumentsEnum,
|
631
694
|
},
|
632
|
-
description: 'Arguments of fields and directives
|
695
|
+
description: 'Arguments of fields and directives',
|
633
696
|
},
|
634
697
|
},
|
635
698
|
},
|
@@ -642,16 +705,9 @@ const rule = {
|
|
642
705
|
for (const node of nodes) {
|
643
706
|
const currName = node.name.value;
|
644
707
|
if (prevName && prevName > currName) {
|
645
|
-
const { start, end } = node.name.loc;
|
646
708
|
const isVariableNode = node.kind === Kind.VARIABLE;
|
647
709
|
context.report({
|
648
|
-
loc: {
|
649
|
-
start: {
|
650
|
-
line: start.line,
|
651
|
-
column: start.column - (isVariableNode ? 2 : 1),
|
652
|
-
},
|
653
|
-
end,
|
654
|
-
},
|
710
|
+
loc: getLocation(node.loc, node.name.value, { offsetEnd: isVariableNode ? 0 : 1 }),
|
655
711
|
messageId: ALPHABETIZE,
|
656
712
|
data: isVariableNode
|
657
713
|
? {
|
@@ -667,7 +723,7 @@ const rule = {
|
|
667
723
|
const opts = context.options[0];
|
668
724
|
const fields = new Set((_a = opts.fields) !== null && _a !== void 0 ? _a : []);
|
669
725
|
const listeners = {};
|
670
|
-
const
|
726
|
+
const kinds = [
|
671
727
|
fields.has(Kind.OBJECT_TYPE_DEFINITION) && [Kind.OBJECT_TYPE_DEFINITION, Kind.OBJECT_TYPE_EXTENSION],
|
672
728
|
fields.has(Kind.INTERFACE_TYPE_DEFINITION) && [Kind.INTERFACE_TYPE_DEFINITION, Kind.INTERFACE_TYPE_EXTENSION],
|
673
729
|
fields.has(Kind.INPUT_OBJECT_TYPE_DEFINITION) && [
|
@@ -675,8 +731,9 @@ const rule = {
|
|
675
731
|
Kind.INPUT_OBJECT_TYPE_EXTENSION,
|
676
732
|
],
|
677
733
|
]
|
678
|
-
.
|
679
|
-
.
|
734
|
+
.filter(Boolean)
|
735
|
+
.flat();
|
736
|
+
const fieldsSelector = kinds.join(',');
|
680
737
|
const hasEnumValues = ((_b = opts.values) === null || _b === void 0 ? void 0 : _b[0]) === Kind.ENUM_TYPE_DEFINITION;
|
681
738
|
const selectionsSelector = (_c = opts.selections) === null || _c === void 0 ? void 0 : _c.join(',');
|
682
739
|
const hasVariables = ((_d = opts.variables) === null || _d === void 0 ? void 0 : _d[0]) === Kind.OPERATION_DEFINITION;
|
@@ -717,35 +774,22 @@ const rule = {
|
|
717
774
|
};
|
718
775
|
|
719
776
|
const AVOID_DUPLICATE_FIELDS = 'AVOID_DUPLICATE_FIELDS';
|
720
|
-
const ensureUnique = () => {
|
721
|
-
const set = new Set();
|
722
|
-
return {
|
723
|
-
add: (item, onError) => {
|
724
|
-
if (set.has(item)) {
|
725
|
-
onError();
|
726
|
-
}
|
727
|
-
else {
|
728
|
-
set.add(item);
|
729
|
-
}
|
730
|
-
},
|
731
|
-
};
|
732
|
-
};
|
733
777
|
const rule$1 = {
|
734
778
|
meta: {
|
735
779
|
type: 'suggestion',
|
736
780
|
docs: {
|
737
|
-
description:
|
781
|
+
description: `Checks for duplicate fields in selection set, variables in operation definition, or in arguments set of a field.`,
|
738
782
|
category: 'Stylistic Issues',
|
739
783
|
url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/avoid-duplicate-fields.md',
|
740
784
|
examples: [
|
741
785
|
{
|
742
786
|
title: 'Incorrect',
|
743
787
|
code: /* GraphQL */ `
|
744
|
-
query
|
788
|
+
query {
|
745
789
|
user {
|
746
|
-
name
|
790
|
+
name
|
747
791
|
email
|
748
|
-
name #
|
792
|
+
name # duplicate field
|
749
793
|
}
|
750
794
|
}
|
751
795
|
`,
|
@@ -753,7 +797,7 @@ const rule$1 = {
|
|
753
797
|
{
|
754
798
|
title: 'Incorrect',
|
755
799
|
code: /* GraphQL */ `
|
756
|
-
query
|
800
|
+
query {
|
757
801
|
users(
|
758
802
|
first: 100
|
759
803
|
skip: 50
|
@@ -768,9 +812,11 @@ const rule$1 = {
|
|
768
812
|
{
|
769
813
|
title: 'Incorrect',
|
770
814
|
code: /* GraphQL */ `
|
771
|
-
query
|
772
|
-
|
773
|
-
|
815
|
+
query (
|
816
|
+
$first: Int!
|
817
|
+
$first: Int! # duplicate variable
|
818
|
+
) {
|
819
|
+
users(first: $first, skip: 50) {
|
774
820
|
id
|
775
821
|
}
|
776
822
|
}
|
@@ -779,58 +825,47 @@ const rule$1 = {
|
|
779
825
|
],
|
780
826
|
},
|
781
827
|
messages: {
|
782
|
-
[AVOID_DUPLICATE_FIELDS]: `{{ type }} "{{ fieldName }}" defined multiple times
|
828
|
+
[AVOID_DUPLICATE_FIELDS]: `{{ type }} "{{ fieldName }}" defined multiple times`,
|
783
829
|
},
|
784
830
|
schema: [],
|
785
831
|
},
|
786
832
|
create(context) {
|
833
|
+
function checkNode(usedFields, fieldName, type, node) {
|
834
|
+
if (usedFields.has(fieldName)) {
|
835
|
+
context.report({
|
836
|
+
loc: getLocation((node.kind === Kind.FIELD && node.alias ? node.alias : node).loc, fieldName, {
|
837
|
+
offsetEnd: node.kind === Kind.VARIABLE_DEFINITION ? 0 : 1,
|
838
|
+
}),
|
839
|
+
messageId: AVOID_DUPLICATE_FIELDS,
|
840
|
+
data: {
|
841
|
+
type,
|
842
|
+
fieldName,
|
843
|
+
},
|
844
|
+
});
|
845
|
+
}
|
846
|
+
else {
|
847
|
+
usedFields.add(fieldName);
|
848
|
+
}
|
849
|
+
}
|
787
850
|
return {
|
788
851
|
OperationDefinition(node) {
|
789
|
-
const
|
790
|
-
for (const
|
791
|
-
|
792
|
-
context.report({
|
793
|
-
messageId: AVOID_DUPLICATE_FIELDS,
|
794
|
-
data: {
|
795
|
-
type: 'Operation variable',
|
796
|
-
fieldName: arg.variable.name.value,
|
797
|
-
},
|
798
|
-
node: arg,
|
799
|
-
});
|
800
|
-
});
|
852
|
+
const set = new Set();
|
853
|
+
for (const varDef of node.variableDefinitions) {
|
854
|
+
checkNode(set, varDef.variable.name.value, 'Operation variable', varDef);
|
801
855
|
}
|
802
856
|
},
|
803
857
|
Field(node) {
|
804
|
-
const
|
805
|
-
for (const arg of node.arguments
|
806
|
-
|
807
|
-
context.report({
|
808
|
-
messageId: AVOID_DUPLICATE_FIELDS,
|
809
|
-
data: {
|
810
|
-
type: 'Field argument',
|
811
|
-
fieldName: arg.name.value,
|
812
|
-
},
|
813
|
-
node: arg,
|
814
|
-
});
|
815
|
-
});
|
858
|
+
const set = new Set();
|
859
|
+
for (const arg of node.arguments) {
|
860
|
+
checkNode(set, arg.name.value, 'Field argument', arg);
|
816
861
|
}
|
817
862
|
},
|
818
863
|
SelectionSet(node) {
|
819
864
|
var _a;
|
820
|
-
const
|
821
|
-
for (const selection of node.selections
|
865
|
+
const set = new Set();
|
866
|
+
for (const selection of node.selections) {
|
822
867
|
if (selection.kind === Kind.FIELD) {
|
823
|
-
|
824
|
-
uniqueCheck.add(nameToCheck, () => {
|
825
|
-
context.report({
|
826
|
-
messageId: AVOID_DUPLICATE_FIELDS,
|
827
|
-
data: {
|
828
|
-
type: 'Field',
|
829
|
-
fieldName: nameToCheck,
|
830
|
-
},
|
831
|
-
node: selection,
|
832
|
-
});
|
833
|
-
});
|
868
|
+
checkNode(set, ((_a = selection.alias) === null || _a === void 0 ? void 0 : _a.value) || selection.name.value, 'Field', selection);
|
834
869
|
}
|
835
870
|
}
|
836
871
|
},
|
@@ -961,16 +996,20 @@ const rule$3 = {
|
|
961
996
|
if (!mutationType) {
|
962
997
|
return {};
|
963
998
|
}
|
964
|
-
const selector =
|
999
|
+
const selector = [
|
1000
|
+
`:matches(${Kind.OBJECT_TYPE_DEFINITION}, ${Kind.OBJECT_TYPE_EXTENSION})[name.value=${mutationType.name}]`,
|
1001
|
+
'>',
|
1002
|
+
Kind.FIELD_DEFINITION,
|
1003
|
+
Kind.NAMED_TYPE,
|
1004
|
+
].join(' ');
|
965
1005
|
return {
|
966
1006
|
[selector](node) {
|
967
|
-
const
|
968
|
-
const typeName = getTypeName(rawNode);
|
1007
|
+
const typeName = node.name.value;
|
969
1008
|
const graphQLType = schema.getType(typeName);
|
970
1009
|
if (isScalarType(graphQLType)) {
|
971
1010
|
context.report({
|
972
|
-
node,
|
973
|
-
message: `Unexpected scalar result type "${typeName}"
|
1011
|
+
loc: getLocation(node.loc, typeName),
|
1012
|
+
message: `Unexpected scalar result type "${typeName}"`,
|
974
1013
|
});
|
975
1014
|
}
|
976
1015
|
},
|
@@ -1019,23 +1058,13 @@ const rule$4 = {
|
|
1019
1058
|
for (const field of node.fields) {
|
1020
1059
|
const fieldName = field.name.value;
|
1021
1060
|
if (fieldName.toLowerCase().startsWith(lowerTypeName)) {
|
1022
|
-
const { start } = field.loc;
|
1023
1061
|
context.report({
|
1024
1062
|
data: {
|
1025
1063
|
fieldName,
|
1026
1064
|
typeName,
|
1027
1065
|
},
|
1028
1066
|
messageId: AVOID_TYPENAME_PREFIX,
|
1029
|
-
loc:
|
1030
|
-
start: {
|
1031
|
-
line: start.line,
|
1032
|
-
column: start.column - 1,
|
1033
|
-
},
|
1034
|
-
end: {
|
1035
|
-
line: start.line,
|
1036
|
-
column: start.column - 1 + lowerTypeName.length,
|
1037
|
-
},
|
1038
|
-
},
|
1067
|
+
loc: getLocation(field.loc, lowerTypeName),
|
1039
1068
|
});
|
1040
1069
|
}
|
1041
1070
|
}
|
@@ -1095,7 +1124,7 @@ const rule$5 = {
|
|
1095
1124
|
'[description.type="StringValue"]': node => {
|
1096
1125
|
if (node.description.block !== (style === 'block')) {
|
1097
1126
|
context.report({
|
1098
|
-
|
1127
|
+
loc: getLocation(node.description.loc),
|
1099
1128
|
message: `Unexpected ${wrongDescriptionType} description`,
|
1100
1129
|
});
|
1101
1130
|
}
|
@@ -1104,6 +1133,9 @@ const rule$5 = {
|
|
1104
1133
|
},
|
1105
1134
|
};
|
1106
1135
|
|
1136
|
+
const isObjectType = (node) => [Kind.OBJECT_TYPE_DEFINITION, Kind.OBJECT_TYPE_EXTENSION].includes(node.type);
|
1137
|
+
const isQueryType = (node) => isObjectType(node) && node.name.value === 'Query';
|
1138
|
+
const isMutationType = (node) => isObjectType(node) && node.name.value === 'Mutation';
|
1107
1139
|
const rule$6 = {
|
1108
1140
|
meta: {
|
1109
1141
|
type: 'suggestion',
|
@@ -1144,6 +1176,7 @@ const rule$6 = {
|
|
1144
1176
|
schema: [
|
1145
1177
|
{
|
1146
1178
|
type: 'object',
|
1179
|
+
additionalProperties: false,
|
1147
1180
|
properties: {
|
1148
1181
|
checkInputType: {
|
1149
1182
|
type: 'boolean',
|
@@ -1166,35 +1199,34 @@ const rule$6 = {
|
|
1166
1199
|
description: 'Apply the rule to Mutations',
|
1167
1200
|
},
|
1168
1201
|
},
|
1169
|
-
additionalProperties: false,
|
1170
1202
|
},
|
1171
1203
|
],
|
1172
1204
|
},
|
1173
1205
|
create(context) {
|
1174
|
-
var _a;
|
1175
1206
|
const options = {
|
1176
|
-
caseSensitiveInputType: true,
|
1177
1207
|
checkInputType: false,
|
1178
|
-
|
1208
|
+
caseSensitiveInputType: true,
|
1179
1209
|
checkQueries: false,
|
1180
|
-
|
1210
|
+
checkMutations: true,
|
1211
|
+
...context.options[0],
|
1181
1212
|
};
|
1182
1213
|
const shouldCheckType = node => (options.checkMutations && isMutationType(node)) || (options.checkQueries && isQueryType(node));
|
1183
1214
|
const listeners = {
|
1184
|
-
'FieldDefinition > InputValueDefinition'
|
1185
|
-
if (
|
1215
|
+
'FieldDefinition > InputValueDefinition[name.value!=input]'(node) {
|
1216
|
+
if (shouldCheckType(node.parent.parent)) {
|
1217
|
+
const name = node.name.value;
|
1186
1218
|
context.report({
|
1187
|
-
|
1188
|
-
message: `Input "${
|
1219
|
+
loc: getLocation(node.loc, name),
|
1220
|
+
message: `Input "${name}" should be called "input"`,
|
1189
1221
|
});
|
1190
1222
|
}
|
1191
1223
|
},
|
1192
1224
|
};
|
1193
|
-
if (options
|
1194
|
-
listeners['FieldDefinition > InputValueDefinition NamedType'] = node => {
|
1225
|
+
if (options.checkInputType) {
|
1226
|
+
listeners['FieldDefinition > InputValueDefinition NamedType'] = (node) => {
|
1195
1227
|
const findInputType = item => {
|
1196
1228
|
let currentNode = item;
|
1197
|
-
while (currentNode.type !==
|
1229
|
+
while (currentNode.type !== Kind.INPUT_VALUE_DEFINITION) {
|
1198
1230
|
currentNode = currentNode.parent;
|
1199
1231
|
}
|
1200
1232
|
return currentNode;
|
@@ -1202,11 +1234,12 @@ const rule$6 = {
|
|
1202
1234
|
const inputValueNode = findInputType(node);
|
1203
1235
|
if (shouldCheckType(inputValueNode.parent.parent)) {
|
1204
1236
|
const mutationName = `${inputValueNode.parent.name.value}Input`;
|
1237
|
+
const name = node.name.value;
|
1205
1238
|
if ((options.caseSensitiveInputType && node.name.value !== mutationName) ||
|
1206
|
-
|
1239
|
+
name.toLowerCase() !== mutationName.toLowerCase()) {
|
1207
1240
|
context.report({
|
1208
|
-
node,
|
1209
|
-
message: `InputType "${
|
1241
|
+
loc: getLocation(node.loc, name),
|
1242
|
+
message: `InputType "${name}" name should be "${mutationName}"`,
|
1210
1243
|
});
|
1211
1244
|
}
|
1212
1245
|
}
|
@@ -1363,7 +1396,8 @@ const rule$7 = {
|
|
1363
1396
|
var _a;
|
1364
1397
|
if (options.fileExtension && options.fileExtension !== fileExtension) {
|
1365
1398
|
context.report({
|
1366
|
-
|
1399
|
+
// Report on first character
|
1400
|
+
loc: { column: 0, line: 1 },
|
1367
1401
|
messageId: MATCH_EXTENSION,
|
1368
1402
|
data: {
|
1369
1403
|
fileExtension,
|
@@ -1395,7 +1429,8 @@ const rule$7 = {
|
|
1395
1429
|
const filenameWithExtension = filename + expectedExtension;
|
1396
1430
|
if (expectedFilename !== filenameWithExtension) {
|
1397
1431
|
context.report({
|
1398
|
-
|
1432
|
+
// Report on first character
|
1433
|
+
loc: { column: 0, line: 1 },
|
1399
1434
|
messageId: MATCH_STYLE,
|
1400
1435
|
data: {
|
1401
1436
|
expectedFilename,
|
@@ -1408,69 +1443,40 @@ const rule$7 = {
|
|
1408
1443
|
},
|
1409
1444
|
};
|
1410
1445
|
|
1411
|
-
const
|
1412
|
-
|
1413
|
-
|
1414
|
-
|
1415
|
-
|
1416
|
-
|
1417
|
-
const acceptedStyles = [
|
1418
|
-
'camelCase',
|
1419
|
-
'PascalCase',
|
1420
|
-
'snake_case',
|
1421
|
-
'UPPER_CASE',
|
1446
|
+
const FIELDS_KINDS = [
|
1447
|
+
Kind.FIELD_DEFINITION,
|
1448
|
+
Kind.INPUT_VALUE_DEFINITION,
|
1449
|
+
Kind.VARIABLE_DEFINITION,
|
1450
|
+
Kind.ARGUMENT,
|
1451
|
+
Kind.DIRECTIVE_DEFINITION,
|
1422
1452
|
];
|
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
|
-
|
1448
|
-
|
1449
|
-
|
1450
|
-
if (forbiddenPrefixes.some(forbiddenPrefix => name.startsWith(forbiddenPrefix))) {
|
1451
|
-
return {
|
1452
|
-
ok: false,
|
1453
|
-
errorMessage: '{{nodeType}} "{{nodeName}}" should not have one of the following prefix(es): {{forbiddenPrefixes}}',
|
1454
|
-
};
|
1455
|
-
}
|
1456
|
-
if (forbiddenSuffixes.some(forbiddenSuffix => name.endsWith(forbiddenSuffix))) {
|
1457
|
-
return {
|
1458
|
-
ok: false,
|
1459
|
-
errorMessage: '{{nodeType}} "{{nodeName}}" should not have one of the following suffix(es): {{forbiddenSuffixes}}',
|
1460
|
-
};
|
1461
|
-
}
|
1462
|
-
if (!formats[style]) {
|
1463
|
-
return { ok: true };
|
1464
|
-
}
|
1465
|
-
const ok = new RegExp(formats[style]).test(name);
|
1466
|
-
if (ok) {
|
1467
|
-
return { ok: true };
|
1468
|
-
}
|
1469
|
-
return {
|
1470
|
-
ok: false,
|
1471
|
-
errorMessage: '{{nodeType}} name "{{nodeName}}" should be in {{format}} format',
|
1472
|
-
};
|
1473
|
-
}
|
1453
|
+
const KindToDisplayName = {
|
1454
|
+
// types
|
1455
|
+
[Kind.OBJECT_TYPE_DEFINITION]: 'Type',
|
1456
|
+
[Kind.INTERFACE_TYPE_DEFINITION]: 'Interface',
|
1457
|
+
[Kind.ENUM_TYPE_DEFINITION]: 'Enumerator',
|
1458
|
+
[Kind.SCALAR_TYPE_DEFINITION]: 'Scalar',
|
1459
|
+
[Kind.INPUT_OBJECT_TYPE_DEFINITION]: 'Input type',
|
1460
|
+
[Kind.UNION_TYPE_DEFINITION]: 'Union',
|
1461
|
+
// fields
|
1462
|
+
[Kind.FIELD_DEFINITION]: 'Field',
|
1463
|
+
[Kind.INPUT_VALUE_DEFINITION]: 'Input property',
|
1464
|
+
[Kind.VARIABLE_DEFINITION]: 'Variable',
|
1465
|
+
[Kind.ARGUMENT]: 'Argument',
|
1466
|
+
[Kind.DIRECTIVE_DEFINITION]: 'Directive',
|
1467
|
+
// rest
|
1468
|
+
[Kind.ENUM_VALUE_DEFINITION]: 'Enumeration value',
|
1469
|
+
[Kind.OPERATION_DEFINITION]: 'Operation',
|
1470
|
+
[Kind.FRAGMENT_DEFINITION]: 'Fragment',
|
1471
|
+
};
|
1472
|
+
const StyleToRegex = {
|
1473
|
+
camelCase: /^[a-z][\dA-Za-z]*$/,
|
1474
|
+
PascalCase: /^[A-Z][\dA-Za-z]*$/,
|
1475
|
+
snake_case: /^[a-z][\d_a-z]*[\da-z]$/,
|
1476
|
+
UPPER_CASE: /^[A-Z][\dA-Z_]*[\dA-Z]$/,
|
1477
|
+
};
|
1478
|
+
const ALLOWED_KINDS = Object.keys(KindToDisplayName).sort();
|
1479
|
+
const ALLOWED_STYLES = Object.keys(StyleToRegex);
|
1474
1480
|
const schemaOption$1 = {
|
1475
1481
|
oneOf: [{ $ref: '#/definitions/asString' }, { $ref: '#/definitions/asObject' }],
|
1476
1482
|
};
|
@@ -1485,89 +1491,120 @@ const rule$8 = {
|
|
1485
1491
|
examples: [
|
1486
1492
|
{
|
1487
1493
|
title: 'Incorrect',
|
1488
|
-
usage: [{
|
1494
|
+
usage: [{ types: 'PascalCase', fields: 'camelCase' }],
|
1489
1495
|
code: /* GraphQL */ `
|
1490
|
-
type
|
1491
|
-
|
1496
|
+
type user {
|
1497
|
+
first_name: String!
|
1492
1498
|
}
|
1493
1499
|
`,
|
1494
1500
|
},
|
1495
1501
|
{
|
1496
1502
|
title: 'Correct',
|
1497
|
-
usage: [{
|
1503
|
+
usage: [{ types: 'PascalCase', fields: 'camelCase' }],
|
1498
1504
|
code: /* GraphQL */ `
|
1499
|
-
type
|
1500
|
-
|
1505
|
+
type User {
|
1506
|
+
firstName: String
|
1501
1507
|
}
|
1502
1508
|
`,
|
1503
1509
|
},
|
1504
1510
|
],
|
1511
|
+
optionsForConfig: [
|
1512
|
+
{
|
1513
|
+
types: 'PascalCase',
|
1514
|
+
fields: 'camelCase',
|
1515
|
+
overrides: {
|
1516
|
+
EnumValueDefinition: 'UPPER_CASE',
|
1517
|
+
OperationDefinition: {
|
1518
|
+
style: 'PascalCase',
|
1519
|
+
forbiddenPrefixes: ['Query', 'Mutation', 'Subscription', 'Get'],
|
1520
|
+
forbiddenSuffixes: ['Query', 'Mutation', 'Subscription'],
|
1521
|
+
},
|
1522
|
+
FragmentDefinition: {
|
1523
|
+
style: 'PascalCase',
|
1524
|
+
forbiddenPrefixes: ['Fragment'],
|
1525
|
+
forbiddenSuffixes: ['Fragment'],
|
1526
|
+
},
|
1527
|
+
'FieldDefinition[parent.name.value=Query]': {
|
1528
|
+
forbiddenPrefixes: ['query', 'get'],
|
1529
|
+
forbiddenSuffixes: ['Query'],
|
1530
|
+
},
|
1531
|
+
'FieldDefinition[parent.name.value=Mutation]': {
|
1532
|
+
forbiddenPrefixes: ['mutation'],
|
1533
|
+
forbiddenSuffixes: ['Mutation'],
|
1534
|
+
},
|
1535
|
+
'FieldDefinition[parent.name.value=Subscription]': {
|
1536
|
+
forbiddenPrefixes: ['subscription'],
|
1537
|
+
forbiddenSuffixes: ['Subscription'],
|
1538
|
+
},
|
1539
|
+
},
|
1540
|
+
},
|
1541
|
+
],
|
1505
1542
|
},
|
1506
1543
|
schema: {
|
1507
1544
|
definitions: {
|
1508
1545
|
asString: {
|
1509
|
-
|
1510
|
-
description: `One of: ${
|
1511
|
-
enum: acceptedStyles,
|
1546
|
+
enum: ALLOWED_STYLES,
|
1547
|
+
description: `One of: ${ALLOWED_STYLES.map(t => `\`${t}\``).join(', ')}`,
|
1512
1548
|
},
|
1513
1549
|
asObject: {
|
1514
1550
|
type: 'object',
|
1551
|
+
additionalProperties: false,
|
1515
1552
|
properties: {
|
1516
|
-
style: {
|
1517
|
-
|
1518
|
-
|
1519
|
-
},
|
1520
|
-
prefix: {
|
1521
|
-
type: 'string',
|
1522
|
-
},
|
1523
|
-
suffix: {
|
1524
|
-
type: 'string',
|
1525
|
-
},
|
1553
|
+
style: { enum: ALLOWED_STYLES },
|
1554
|
+
prefix: { type: 'string' },
|
1555
|
+
suffix: { type: 'string' },
|
1526
1556
|
forbiddenPrefixes: {
|
1527
|
-
additionalItems: false,
|
1528
1557
|
type: 'array',
|
1558
|
+
uniqueItems: true,
|
1529
1559
|
minItems: 1,
|
1530
|
-
items: {
|
1531
|
-
type: 'string',
|
1532
|
-
},
|
1560
|
+
items: { type: 'string' },
|
1533
1561
|
},
|
1534
1562
|
forbiddenSuffixes: {
|
1535
|
-
additionalItems: false,
|
1536
1563
|
type: 'array',
|
1564
|
+
uniqueItems: true,
|
1537
1565
|
minItems: 1,
|
1538
|
-
items: {
|
1539
|
-
type: 'string',
|
1540
|
-
},
|
1566
|
+
items: { type: 'string' },
|
1541
1567
|
},
|
1542
1568
|
},
|
1543
1569
|
},
|
1544
1570
|
},
|
1545
|
-
$schema: 'http://json-schema.org/draft-04/schema#',
|
1546
1571
|
type: 'array',
|
1572
|
+
maxItems: 1,
|
1547
1573
|
items: {
|
1548
1574
|
type: 'object',
|
1575
|
+
additionalProperties: false,
|
1549
1576
|
properties: {
|
1550
|
-
|
1551
|
-
|
1552
|
-
|
1553
|
-
[Kind.INPUT_VALUE_DEFINITION]: schemaOption$1,
|
1554
|
-
[Kind.OBJECT_TYPE_DEFINITION]: schemaOption$1,
|
1555
|
-
[Kind.INTERFACE_TYPE_DEFINITION]: schemaOption$1,
|
1556
|
-
[Kind.ENUM_TYPE_DEFINITION]: schemaOption$1,
|
1557
|
-
[Kind.UNION_TYPE_DEFINITION]: schemaOption$1,
|
1558
|
-
[Kind.SCALAR_TYPE_DEFINITION]: schemaOption$1,
|
1559
|
-
[Kind.OPERATION_DEFINITION]: schemaOption$1,
|
1560
|
-
[Kind.FRAGMENT_DEFINITION]: schemaOption$1,
|
1561
|
-
QueryDefinition: schemaOption$1,
|
1562
|
-
leadingUnderscore: {
|
1563
|
-
type: 'string',
|
1564
|
-
enum: ['allow', 'forbid'],
|
1565
|
-
default: 'forbid',
|
1577
|
+
types: {
|
1578
|
+
...schemaOption$1,
|
1579
|
+
description: `Includes:\n\n${TYPES_KINDS.map(kind => `- \`${kind}\``).join('\n')}`,
|
1566
1580
|
},
|
1567
|
-
|
1568
|
-
|
1569
|
-
|
1570
|
-
|
1581
|
+
fields: {
|
1582
|
+
...schemaOption$1,
|
1583
|
+
description: `Includes:\n\n${FIELDS_KINDS.map(kind => `- \`${kind}\``).join('\n')}`,
|
1584
|
+
},
|
1585
|
+
allowLeadingUnderscore: {
|
1586
|
+
type: 'boolean',
|
1587
|
+
default: false,
|
1588
|
+
},
|
1589
|
+
allowTrailingUnderscore: {
|
1590
|
+
type: 'boolean',
|
1591
|
+
default: false,
|
1592
|
+
},
|
1593
|
+
overrides: {
|
1594
|
+
type: 'object',
|
1595
|
+
additionalProperties: false,
|
1596
|
+
description: [
|
1597
|
+
'May contain the following `ASTNode` names:',
|
1598
|
+
'',
|
1599
|
+
...ALLOWED_KINDS.map(kind => `- \`${kind}\``),
|
1600
|
+
'',
|
1601
|
+
"> It's also possible to use a [`selector`](https://eslint.org/docs/developer-guide/selectors) that starts with `ASTNode` name",
|
1602
|
+
'>',
|
1603
|
+
'> Example: pattern property `FieldDefinition[parent.name.value=Query]` will match only fields for type `Query`',
|
1604
|
+
].join('\n'),
|
1605
|
+
patternProperties: {
|
1606
|
+
[`^(${ALLOWED_KINDS.join('|')})(.+)?$`]: schemaOption$1,
|
1607
|
+
},
|
1571
1608
|
},
|
1572
1609
|
},
|
1573
1610
|
},
|
@@ -1575,130 +1612,83 @@ const rule$8 = {
|
|
1575
1612
|
},
|
1576
1613
|
create(context) {
|
1577
1614
|
const options = {
|
1578
|
-
|
1579
|
-
|
1580
|
-
...(context.options[0] || {}),
|
1615
|
+
overrides: {},
|
1616
|
+
...context.options[0],
|
1581
1617
|
};
|
1582
|
-
|
1583
|
-
|
1584
|
-
|
1585
|
-
|
1586
|
-
|
1587
|
-
|
1588
|
-
|
1589
|
-
|
1590
|
-
|
1591
|
-
|
1592
|
-
|
1593
|
-
}
|
1594
|
-
|
1618
|
+
function normalisePropertyOption(kind) {
|
1619
|
+
let style = options.overrides[kind];
|
1620
|
+
if (!style) {
|
1621
|
+
style = TYPES_KINDS.includes(kind) ? options.types : options.fields;
|
1622
|
+
}
|
1623
|
+
return typeof style === 'object' ? style : { style };
|
1624
|
+
}
|
1625
|
+
const checkNode = (selector) => (node) => {
|
1626
|
+
const { name } = node.kind === Kind.VARIABLE_DEFINITION ? node.variable : node;
|
1627
|
+
if (!name) {
|
1628
|
+
return;
|
1629
|
+
}
|
1630
|
+
const { prefix, suffix, forbiddenPrefixes, forbiddenSuffixes, style } = normalisePropertyOption(selector);
|
1631
|
+
const nodeType = KindToDisplayName[node.kind] || node.kind;
|
1632
|
+
const nodeName = name.value;
|
1633
|
+
const errorMessage = getErrorMessage();
|
1634
|
+
if (errorMessage) {
|
1595
1635
|
context.report({
|
1596
|
-
|
1597
|
-
message:
|
1598
|
-
data: {
|
1599
|
-
prefix,
|
1600
|
-
suffix,
|
1601
|
-
format: style,
|
1602
|
-
forbiddenPrefixes: forbiddenPrefixes.join(', '),
|
1603
|
-
forbiddenSuffixes: forbiddenSuffixes.join(', '),
|
1604
|
-
nodeType,
|
1605
|
-
nodeName: node.value,
|
1606
|
-
},
|
1636
|
+
loc: getLocation(name.loc, name.value),
|
1637
|
+
message: `${nodeType} "${nodeName}" should ${errorMessage}`,
|
1607
1638
|
});
|
1608
1639
|
}
|
1609
|
-
|
1610
|
-
|
1611
|
-
|
1612
|
-
|
1613
|
-
}
|
1614
|
-
return {
|
1615
|
-
style: value,
|
1616
|
-
prefix: '',
|
1617
|
-
suffix: '',
|
1618
|
-
};
|
1619
|
-
};
|
1620
|
-
return {
|
1621
|
-
Name: node => {
|
1622
|
-
if (node.value.startsWith('_') && options.leadingUnderscore === 'forbid') {
|
1623
|
-
context.report({ node, message: 'Leading underscores are not allowed' });
|
1640
|
+
function getErrorMessage() {
|
1641
|
+
let name = nodeName;
|
1642
|
+
if (options.allowLeadingUnderscore) {
|
1643
|
+
name = name.replace(/^_*/, '');
|
1624
1644
|
}
|
1625
|
-
if (
|
1626
|
-
|
1645
|
+
if (options.allowTrailingUnderscore) {
|
1646
|
+
name = name.replace(/_*$/, '');
|
1627
1647
|
}
|
1628
|
-
|
1629
|
-
|
1630
|
-
if (options.ObjectTypeDefinition) {
|
1631
|
-
const property = normalisePropertyOption(options.ObjectTypeDefinition);
|
1632
|
-
checkNode(node.name, property, 'Type');
|
1648
|
+
if (prefix && !name.startsWith(prefix)) {
|
1649
|
+
return `have "${prefix}" prefix`;
|
1633
1650
|
}
|
1634
|
-
|
1635
|
-
|
1636
|
-
if (options.InterfaceTypeDefinition) {
|
1637
|
-
const property = normalisePropertyOption(options.InterfaceTypeDefinition);
|
1638
|
-
checkNode(node.name, property, 'Interface');
|
1651
|
+
if (suffix && !name.endsWith(suffix)) {
|
1652
|
+
return `have "${suffix}" suffix`;
|
1639
1653
|
}
|
1640
|
-
|
1641
|
-
|
1642
|
-
|
1643
|
-
const property = normalisePropertyOption(options.EnumTypeDefinition);
|
1644
|
-
checkNode(node.name, property, 'Enumerator');
|
1654
|
+
const forbiddenPrefix = forbiddenPrefixes === null || forbiddenPrefixes === void 0 ? void 0 : forbiddenPrefixes.find(prefix => name.startsWith(prefix));
|
1655
|
+
if (forbiddenPrefix) {
|
1656
|
+
return `not have "${forbiddenPrefix}" prefix`;
|
1645
1657
|
}
|
1646
|
-
|
1647
|
-
|
1648
|
-
|
1649
|
-
const property = normalisePropertyOption(options.InputObjectTypeDefinition);
|
1650
|
-
checkNode(node.name, property, 'Input type');
|
1658
|
+
const forbiddenSuffix = forbiddenSuffixes === null || forbiddenSuffixes === void 0 ? void 0 : forbiddenSuffixes.find(suffix => name.endsWith(suffix));
|
1659
|
+
if (forbiddenSuffix) {
|
1660
|
+
return `not have "${forbiddenSuffix}" suffix`;
|
1651
1661
|
}
|
1652
|
-
|
1653
|
-
|
1654
|
-
if (options.QueryDefinition && isQueryType(node.parent)) {
|
1655
|
-
const property = normalisePropertyOption(options.QueryDefinition);
|
1656
|
-
checkNode(node.name, property, 'Query');
|
1662
|
+
if (style && !ALLOWED_STYLES.includes(style)) {
|
1663
|
+
return `be in one of the following options: ${ALLOWED_STYLES.join(', ')}`;
|
1657
1664
|
}
|
1658
|
-
|
1659
|
-
|
1660
|
-
|
1661
|
-
}
|
1662
|
-
},
|
1663
|
-
EnumValueDefinition: node => {
|
1664
|
-
if (options.EnumValueDefinition) {
|
1665
|
-
const property = normalisePropertyOption(options.EnumValueDefinition);
|
1666
|
-
checkNode(node.name, property, 'Enumeration value');
|
1667
|
-
}
|
1668
|
-
},
|
1669
|
-
InputValueDefinition: node => {
|
1670
|
-
if (options.InputValueDefinition) {
|
1671
|
-
const property = normalisePropertyOption(options.InputValueDefinition);
|
1672
|
-
checkNode(node.name, property, 'Input property');
|
1665
|
+
const caseRegex = StyleToRegex[style];
|
1666
|
+
if (caseRegex && !caseRegex.test(name)) {
|
1667
|
+
return `be in ${style} format`;
|
1673
1668
|
}
|
1674
|
-
}
|
1675
|
-
|
1676
|
-
|
1677
|
-
|
1678
|
-
|
1679
|
-
|
1680
|
-
|
1681
|
-
|
1682
|
-
},
|
1683
|
-
FragmentDefinition: node => {
|
1684
|
-
if (options.FragmentDefinition) {
|
1685
|
-
const property = normalisePropertyOption(options.FragmentDefinition);
|
1686
|
-
checkNode(node.name, property, 'Fragment');
|
1687
|
-
}
|
1688
|
-
},
|
1689
|
-
ScalarTypeDefinition: node => {
|
1690
|
-
if (options.ScalarTypeDefinition) {
|
1691
|
-
const property = normalisePropertyOption(options.ScalarTypeDefinition);
|
1692
|
-
checkNode(node.name, property, 'Scalar');
|
1693
|
-
}
|
1694
|
-
},
|
1695
|
-
UnionTypeDefinition: node => {
|
1696
|
-
if (options.UnionTypeDefinition) {
|
1697
|
-
const property = normalisePropertyOption(options.UnionTypeDefinition);
|
1698
|
-
checkNode(node.name, property, 'Union');
|
1699
|
-
}
|
1700
|
-
},
|
1669
|
+
}
|
1670
|
+
};
|
1671
|
+
const checkUnderscore = (node) => {
|
1672
|
+
const name = node.value;
|
1673
|
+
context.report({
|
1674
|
+
loc: getLocation(node.loc, name),
|
1675
|
+
message: `${name.startsWith('_') ? 'Leading' : 'Trailing'} underscores are not allowed`,
|
1676
|
+
});
|
1701
1677
|
};
|
1678
|
+
const listeners = {};
|
1679
|
+
if (!options.allowLeadingUnderscore) {
|
1680
|
+
listeners['Name[value=/^_/]:matches([parent.kind!=Field], [parent.kind=Field][parent.alias])'] = checkUnderscore;
|
1681
|
+
}
|
1682
|
+
if (!options.allowTrailingUnderscore) {
|
1683
|
+
listeners['Name[value=/_$/]:matches([parent.kind!=Field], [parent.kind=Field][parent.alias])'] = checkUnderscore;
|
1684
|
+
}
|
1685
|
+
const selectors = new Set([options.types && TYPES_KINDS, options.fields && FIELDS_KINDS, Object.keys(options.overrides)]
|
1686
|
+
.flat()
|
1687
|
+
.filter(Boolean));
|
1688
|
+
for (const selector of selectors) {
|
1689
|
+
listeners[selector] = checkNode(selector);
|
1690
|
+
}
|
1691
|
+
return listeners;
|
1702
1692
|
},
|
1703
1693
|
};
|
1704
1694
|
|
@@ -1737,28 +1727,14 @@ const rule$9 = {
|
|
1737
1727
|
},
|
1738
1728
|
create(context) {
|
1739
1729
|
return {
|
1740
|
-
OperationDefinition(node) {
|
1741
|
-
|
1742
|
-
|
1743
|
-
|
1744
|
-
|
1745
|
-
|
1746
|
-
|
1747
|
-
|
1748
|
-
column: start.column - 1,
|
1749
|
-
line: start.line,
|
1750
|
-
},
|
1751
|
-
end: {
|
1752
|
-
column: start.column - 1 + node.operation.length,
|
1753
|
-
line: start.line,
|
1754
|
-
},
|
1755
|
-
},
|
1756
|
-
data: {
|
1757
|
-
operation: node.operation,
|
1758
|
-
},
|
1759
|
-
messageId: NO_ANONYMOUS_OPERATIONS,
|
1760
|
-
});
|
1761
|
-
}
|
1730
|
+
'OperationDefinition[name=undefined]'(node) {
|
1731
|
+
context.report({
|
1732
|
+
loc: getLocation(node.loc, node.operation),
|
1733
|
+
data: {
|
1734
|
+
operation: node.operation,
|
1735
|
+
},
|
1736
|
+
messageId: NO_ANONYMOUS_OPERATIONS,
|
1737
|
+
});
|
1762
1738
|
},
|
1763
1739
|
};
|
1764
1740
|
},
|
@@ -1865,8 +1841,8 @@ const rule$b = {
|
|
1865
1841
|
mutation {
|
1866
1842
|
changeSomething(
|
1867
1843
|
type: OLD # This is deprecated, so you'll get an error
|
1868
|
-
) {
|
1869
|
-
...
|
1844
|
+
) {
|
1845
|
+
...
|
1870
1846
|
}
|
1871
1847
|
}
|
1872
1848
|
`,
|
@@ -1903,9 +1879,10 @@ const rule$b = {
|
|
1903
1879
|
requireGraphQLSchemaFromContext('no-deprecated', context);
|
1904
1880
|
const typeInfo = node.typeInfo();
|
1905
1881
|
if (typeInfo && typeInfo.enumValue) {
|
1906
|
-
if (typeInfo.enumValue.
|
1882
|
+
if (typeInfo.enumValue.isDeprecated) {
|
1883
|
+
const enumValueName = node.value;
|
1907
1884
|
context.report({
|
1908
|
-
loc: node.loc,
|
1885
|
+
loc: getLocation(node.loc, enumValueName),
|
1909
1886
|
messageId: NO_DEPRECATED,
|
1910
1887
|
data: {
|
1911
1888
|
type: 'enum value',
|
@@ -1919,9 +1896,10 @@ const rule$b = {
|
|
1919
1896
|
requireGraphQLSchemaFromContext('no-deprecated', context);
|
1920
1897
|
const typeInfo = node.typeInfo();
|
1921
1898
|
if (typeInfo && typeInfo.fieldDef) {
|
1922
|
-
if (typeInfo.fieldDef.
|
1899
|
+
if (typeInfo.fieldDef.isDeprecated) {
|
1900
|
+
const fieldName = node.name.value;
|
1923
1901
|
context.report({
|
1924
|
-
loc: node.loc,
|
1902
|
+
loc: getLocation(node.loc, fieldName),
|
1925
1903
|
messageId: NO_DEPRECATED,
|
1926
1904
|
data: {
|
1927
1905
|
type: 'field',
|
@@ -1997,10 +1975,7 @@ const rule$c = {
|
|
1997
1975
|
if (!isEslintComment && line !== prev.line && next.kind === TokenKind.NAME && linesAfter < 2) {
|
1998
1976
|
context.report({
|
1999
1977
|
messageId: HASHTAG_COMMENT,
|
2000
|
-
loc: {
|
2001
|
-
start: { line, column },
|
2002
|
-
end: { line, column },
|
2003
|
-
},
|
1978
|
+
loc: getLocation({ start: { line, column } }),
|
2004
1979
|
});
|
2005
1980
|
}
|
2006
1981
|
}
|
@@ -2129,7 +2104,7 @@ const rule$e = {
|
|
2129
2104
|
const typeName = node.name.value;
|
2130
2105
|
if (!reachableTypes.has(typeName)) {
|
2131
2106
|
context.report({
|
2132
|
-
node,
|
2107
|
+
loc: getLocation(node.name.loc, typeName, { offsetStart: node.kind === Kind.DIRECTIVE_DEFINITION ? 2 : 1 }),
|
2133
2108
|
messageId: UNREACHABLE_TYPE,
|
2134
2109
|
data: { typeName },
|
2135
2110
|
fix: fixer => fixer.remove(node),
|
@@ -2227,7 +2202,7 @@ const rule$f = {
|
|
2227
2202
|
return;
|
2228
2203
|
}
|
2229
2204
|
context.report({
|
2230
|
-
node,
|
2205
|
+
loc: getLocation(node.loc, fieldName),
|
2231
2206
|
messageId: UNUSED_FIELD,
|
2232
2207
|
data: { fieldName },
|
2233
2208
|
fix(fixer) {
|
@@ -2339,6 +2314,7 @@ function convertDescription(node) {
|
|
2339
2314
|
return [];
|
2340
2315
|
}
|
2341
2316
|
|
2317
|
+
// eslint-disable-next-line unicorn/better-regex
|
2342
2318
|
const DATE_REGEX = /^\d{2}\/\d{2}\/\d{4}$/;
|
2343
2319
|
const MESSAGE_REQUIRE_DATE = 'MESSAGE_REQUIRE_DATE';
|
2344
2320
|
const MESSAGE_INVALID_FORMAT = 'MESSAGE_INVALID_FORMAT';
|
@@ -2382,10 +2358,10 @@ const rule$g = {
|
|
2382
2358
|
],
|
2383
2359
|
},
|
2384
2360
|
messages: {
|
2385
|
-
[MESSAGE_REQUIRE_DATE]: 'Directive "@deprecated" must have a deletion date
|
2386
|
-
[MESSAGE_INVALID_FORMAT]: 'Deletion date must be in format "DD/MM/YYYY"
|
2387
|
-
[MESSAGE_INVALID_DATE]: 'Invalid "{{ deletionDate }}" deletion date
|
2388
|
-
[MESSAGE_CAN_BE_REMOVED]: '"{{ nodeName }}" сan be removed
|
2361
|
+
[MESSAGE_REQUIRE_DATE]: 'Directive "@deprecated" must have a deletion date',
|
2362
|
+
[MESSAGE_INVALID_FORMAT]: 'Deletion date must be in format "DD/MM/YYYY"',
|
2363
|
+
[MESSAGE_INVALID_DATE]: 'Invalid "{{ deletionDate }}" deletion date',
|
2364
|
+
[MESSAGE_CAN_BE_REMOVED]: '"{{ nodeName }}" сan be removed',
|
2389
2365
|
},
|
2390
2366
|
schema: [
|
2391
2367
|
{
|
@@ -2406,13 +2382,16 @@ const rule$g = {
|
|
2406
2382
|
const argName = ((_a = context.options[0]) === null || _a === void 0 ? void 0 : _a.argumentName) || 'deletionDate';
|
2407
2383
|
const deletionDateNode = node.arguments.find(arg => arg.name.value === argName);
|
2408
2384
|
if (!deletionDateNode) {
|
2409
|
-
context.report({
|
2385
|
+
context.report({
|
2386
|
+
loc: getLocation(node.loc, node.name.value, { offsetEnd: 0 }),
|
2387
|
+
messageId: MESSAGE_REQUIRE_DATE,
|
2388
|
+
});
|
2410
2389
|
return;
|
2411
2390
|
}
|
2412
2391
|
const deletionDate = valueFromNode(deletionDateNode.value);
|
2413
2392
|
const isValidDate = DATE_REGEX.test(deletionDate);
|
2414
2393
|
if (!isValidDate) {
|
2415
|
-
context.report({ node:
|
2394
|
+
context.report({ node: deletionDateNode.value, messageId: MESSAGE_INVALID_FORMAT });
|
2416
2395
|
return;
|
2417
2396
|
}
|
2418
2397
|
let [day, month, year] = deletionDate.split('/');
|
@@ -2421,7 +2400,7 @@ const rule$g = {
|
|
2421
2400
|
const deletionDateInMS = Date.parse(`${year}-${month}-${day}`);
|
2422
2401
|
if (Number.isNaN(deletionDateInMS)) {
|
2423
2402
|
context.report({
|
2424
|
-
node:
|
2403
|
+
node: deletionDateNode.value,
|
2425
2404
|
messageId: MESSAGE_INVALID_DATE,
|
2426
2405
|
data: {
|
2427
2406
|
deletionDate,
|
@@ -2432,7 +2411,7 @@ const rule$g = {
|
|
2432
2411
|
const canRemove = Date.now() > deletionDateInMS;
|
2433
2412
|
if (canRemove) {
|
2434
2413
|
context.report({
|
2435
|
-
node
|
2414
|
+
node,
|
2436
2415
|
messageId: MESSAGE_CAN_BE_REMOVED,
|
2437
2416
|
data: {
|
2438
2417
|
nodeName: node.parent.name.value,
|
@@ -2483,17 +2462,15 @@ const rule$h = {
|
|
2483
2462
|
},
|
2484
2463
|
create(context) {
|
2485
2464
|
return {
|
2486
|
-
Directive(node) {
|
2487
|
-
|
2488
|
-
|
2489
|
-
|
2490
|
-
|
2491
|
-
|
2492
|
-
|
2493
|
-
|
2494
|
-
|
2495
|
-
});
|
2496
|
-
}
|
2465
|
+
'Directive[name.value=deprecated]'(node) {
|
2466
|
+
const args = node.arguments || [];
|
2467
|
+
const reasonArg = args.find(arg => arg.name && arg.name.value === 'reason');
|
2468
|
+
const value = reasonArg ? String(valueFromNode(reasonArg.value) || '').trim() : null;
|
2469
|
+
if (!value) {
|
2470
|
+
context.report({
|
2471
|
+
loc: getLocation(node.loc, node.name.value, { offsetEnd: 0 }),
|
2472
|
+
message: 'Directive "@deprecated" must have a reason!',
|
2473
|
+
});
|
2497
2474
|
}
|
2498
2475
|
},
|
2499
2476
|
};
|
@@ -2516,20 +2493,8 @@ const DESCRIBABLE_NODES = [
|
|
2516
2493
|
function verifyRule(context, node) {
|
2517
2494
|
if (node) {
|
2518
2495
|
if (!node.description || !node.description.value || node.description.value.trim().length === 0) {
|
2519
|
-
const { start, end } = ('name' in node ? node.name : node).loc;
|
2520
2496
|
context.report({
|
2521
|
-
loc:
|
2522
|
-
start: {
|
2523
|
-
line: start.line,
|
2524
|
-
column: start.column - 1,
|
2525
|
-
},
|
2526
|
-
end: {
|
2527
|
-
line: end.line,
|
2528
|
-
column:
|
2529
|
-
// node.name don't exist on SchemaDefinition
|
2530
|
-
'name' in node ? end.column - 1 + node.name.value.length : end.column,
|
2531
|
-
},
|
2532
|
-
},
|
2497
|
+
loc: getLocation(('name' in node ? node.name : node).loc, 'name' in node ? node.name.value : 'schema'),
|
2533
2498
|
messageId: REQUIRE_DESCRIPTION_ERROR,
|
2534
2499
|
data: {
|
2535
2500
|
nodeType: node.kind,
|
@@ -2547,7 +2512,7 @@ const rule$i = {
|
|
2547
2512
|
examples: [
|
2548
2513
|
{
|
2549
2514
|
title: 'Incorrect',
|
2550
|
-
usage: [{ on: [
|
2515
|
+
usage: [{ on: ['ObjectTypeDefinition', 'FieldDefinition'] }],
|
2551
2516
|
code: /* GraphQL */ `
|
2552
2517
|
type someTypeName {
|
2553
2518
|
name: String
|
@@ -2556,7 +2521,7 @@ const rule$i = {
|
|
2556
2521
|
},
|
2557
2522
|
{
|
2558
2523
|
title: 'Correct',
|
2559
|
-
usage: [{ on: [
|
2524
|
+
usage: [{ on: ['ObjectTypeDefinition', 'FieldDefinition'] }],
|
2560
2525
|
code: /* GraphQL */ `
|
2561
2526
|
"""
|
2562
2527
|
Some type description
|
@@ -2650,18 +2615,22 @@ const rule$j = {
|
|
2650
2615
|
if (!mutationType || !queryType) {
|
2651
2616
|
return {};
|
2652
2617
|
}
|
2653
|
-
const selector =
|
2618
|
+
const selector = [
|
2619
|
+
`:matches(${Kind.OBJECT_TYPE_DEFINITION}, ${Kind.OBJECT_TYPE_EXTENSION})[name.value=${mutationType.name}]`,
|
2620
|
+
'>',
|
2621
|
+
Kind.FIELD_DEFINITION,
|
2622
|
+
Kind.NAMED_TYPE,
|
2623
|
+
].join(' ');
|
2654
2624
|
return {
|
2655
2625
|
[selector](node) {
|
2656
|
-
const
|
2657
|
-
const typeName = getTypeName(rawNode);
|
2626
|
+
const typeName = node.name.value;
|
2658
2627
|
const graphQLType = schema.getType(typeName);
|
2659
2628
|
if (isObjectType$1(graphQLType)) {
|
2660
2629
|
const { fields } = graphQLType.astNode;
|
2661
2630
|
const hasQueryType = fields.some(field => getTypeName(field) === queryType.name);
|
2662
2631
|
if (!hasQueryType) {
|
2663
2632
|
context.report({
|
2664
|
-
node,
|
2633
|
+
loc: getLocation(node.loc, typeName),
|
2665
2634
|
message: `Mutation result type "${graphQLType.name}" must contain field of type "${queryType.name}".`,
|
2666
2635
|
});
|
2667
2636
|
}
|
@@ -3000,7 +2969,7 @@ const rule$l = {
|
|
3000
2969
|
getDocument: () => document,
|
3001
2970
|
reportError: (error) => {
|
3002
2971
|
context.report({
|
3003
|
-
loc: error.locations[0],
|
2972
|
+
loc: getLocation({ start: error.locations[0] }),
|
3004
2973
|
message: error.message,
|
3005
2974
|
});
|
3006
2975
|
},
|
@@ -3139,7 +3108,7 @@ const rule$m = {
|
|
3139
3108
|
acceptedIdNames: ['id'],
|
3140
3109
|
acceptedIdTypes: ['ID'],
|
3141
3110
|
exceptions: {},
|
3142
|
-
...
|
3111
|
+
...context.options[0],
|
3143
3112
|
};
|
3144
3113
|
return {
|
3145
3114
|
ObjectTypeDefinition(node) {
|
@@ -3156,15 +3125,16 @@ const rule$m = {
|
|
3156
3125
|
}
|
3157
3126
|
return isValidIdName && isValidIdType;
|
3158
3127
|
});
|
3128
|
+
const typeName = node.name.value;
|
3159
3129
|
// Usually, there should be only one unique identifier field per type.
|
3160
3130
|
// Some clients allow multiple fields to be used. If more people need this,
|
3161
3131
|
// we can extend this rule later.
|
3162
3132
|
if (validIds.length !== 1) {
|
3163
3133
|
context.report({
|
3164
|
-
node,
|
3165
|
-
message:
|
3134
|
+
loc: getLocation(node.name.loc, typeName),
|
3135
|
+
message: `{{ typeName }} must have exactly one non-nullable unique identifier. Accepted name(s): {{ acceptedNamesString }} ; Accepted type(s): {{ acceptedTypesString }}`,
|
3166
3136
|
data: {
|
3167
|
-
|
3137
|
+
typeName,
|
3168
3138
|
acceptedNamesString: options.acceptedIdNames.join(','),
|
3169
3139
|
acceptedTypesString: options.acceptedIdTypes.join(','),
|
3170
3140
|
},
|
@@ -3178,11 +3148,7 @@ const rule$m = {
|
|
3178
3148
|
const RULE_NAME$3 = 'unique-fragment-name';
|
3179
3149
|
const UNIQUE_FRAGMENT_NAME = 'UNIQUE_FRAGMENT_NAME';
|
3180
3150
|
const checkNode = (context, node, ruleName, messageId) => {
|
3181
|
-
|
3182
|
-
const documentName = (_a = node.name) === null || _a === void 0 ? void 0 : _a.value;
|
3183
|
-
if (!documentName) {
|
3184
|
-
return;
|
3185
|
-
}
|
3151
|
+
const documentName = node.name.value;
|
3186
3152
|
const siblings = requireSiblingsOperations(ruleName, context);
|
3187
3153
|
const siblingDocuments = node.kind === Kind.FRAGMENT_DEFINITION ? siblings.getFragment(documentName) : siblings.getOperation(documentName);
|
3188
3154
|
const filepath = context.getFilename();
|
@@ -3193,7 +3159,6 @@ const checkNode = (context, node, ruleName, messageId) => {
|
|
3193
3159
|
return isSameName && !isSamePath;
|
3194
3160
|
});
|
3195
3161
|
if (conflictingDocuments.length > 0) {
|
3196
|
-
const { start, end } = node.name.loc;
|
3197
3162
|
context.report({
|
3198
3163
|
messageId,
|
3199
3164
|
data: {
|
@@ -3202,16 +3167,7 @@ const checkNode = (context, node, ruleName, messageId) => {
|
|
3202
3167
|
.map(f => `\t${relative(process.cwd(), getOnDiskFilepath(f.filePath))}`)
|
3203
3168
|
.join('\n'),
|
3204
3169
|
},
|
3205
|
-
loc:
|
3206
|
-
start: {
|
3207
|
-
line: start.line,
|
3208
|
-
column: start.column - 1,
|
3209
|
-
},
|
3210
|
-
end: {
|
3211
|
-
line: end.line,
|
3212
|
-
column: end.column - 1,
|
3213
|
-
},
|
3214
|
-
},
|
3170
|
+
loc: getLocation(node.name.loc, documentName),
|
3215
3171
|
});
|
3216
3172
|
}
|
3217
3173
|
};
|
@@ -3328,7 +3284,7 @@ const rule$o = {
|
|
3328
3284
|
},
|
3329
3285
|
create(context) {
|
3330
3286
|
return {
|
3331
|
-
OperationDefinition(node) {
|
3287
|
+
'OperationDefinition[name!=undefined]'(node) {
|
3332
3288
|
checkNode(context, node, RULE_NAME$4, UNIQUE_OPERATION_NAME);
|
3333
3289
|
},
|
3334
3290
|
};
|
@@ -3758,22 +3714,110 @@ function parseForESLint(code, options = {}) {
|
|
3758
3714
|
}
|
3759
3715
|
}
|
3760
3716
|
|
3761
|
-
class GraphQLRuleTester extends
|
3717
|
+
class GraphQLRuleTester extends RuleTester {
|
3762
3718
|
constructor(parserOptions = {}) {
|
3763
|
-
|
3719
|
+
const config = {
|
3764
3720
|
parser: require.resolve('@graphql-eslint/eslint-plugin'),
|
3765
3721
|
parserOptions: {
|
3766
3722
|
...parserOptions,
|
3767
3723
|
skipGraphQLConfig: true,
|
3768
3724
|
},
|
3769
|
-
}
|
3725
|
+
};
|
3726
|
+
super(config);
|
3727
|
+
this.config = config;
|
3770
3728
|
}
|
3771
3729
|
fromMockFile(path) {
|
3772
3730
|
return readFileSync(resolve(__dirname, `../tests/mocks/${path}`), 'utf-8');
|
3773
3731
|
}
|
3774
3732
|
runGraphQLTests(name, rule, tests) {
|
3775
|
-
|
3733
|
+
const ruleTests = Linter.version.startsWith('8')
|
3734
|
+
? tests
|
3735
|
+
: {
|
3736
|
+
valid: tests.valid.map(test => {
|
3737
|
+
if (typeof test === 'string') {
|
3738
|
+
return test;
|
3739
|
+
}
|
3740
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
3741
|
+
const { name, ...testCaseOptions } = test;
|
3742
|
+
return testCaseOptions;
|
3743
|
+
}),
|
3744
|
+
invalid: tests.invalid.map(test => {
|
3745
|
+
// ESLint 7 throws an error on CI - Unexpected top-level property "name"
|
3746
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
3747
|
+
const { name, ...testCaseOptions } = test;
|
3748
|
+
return testCaseOptions;
|
3749
|
+
}),
|
3750
|
+
};
|
3751
|
+
super.run(name, rule, ruleTests);
|
3752
|
+
// Skip snapshot testing if `expect` variable is not defined
|
3753
|
+
if (typeof expect === 'undefined') {
|
3754
|
+
return;
|
3755
|
+
}
|
3756
|
+
const linter = new Linter();
|
3757
|
+
linter.defineRule(name, rule);
|
3758
|
+
for (const testCase of tests.invalid) {
|
3759
|
+
const verifyConfig = getVerifyConfig(name, this.config, testCase);
|
3760
|
+
defineParser(linter, verifyConfig.parser);
|
3761
|
+
const { code, filename } = testCase;
|
3762
|
+
const messages = linter.verify(code, verifyConfig, { filename });
|
3763
|
+
for (const message of messages) {
|
3764
|
+
if (message.fatal) {
|
3765
|
+
throw new Error(message.message);
|
3766
|
+
}
|
3767
|
+
const messageForSnapshot = visualizeEslintMessage(code, message);
|
3768
|
+
// eslint-disable-next-line no-undef
|
3769
|
+
expect(messageForSnapshot).toMatchSnapshot();
|
3770
|
+
}
|
3771
|
+
}
|
3776
3772
|
}
|
3777
3773
|
}
|
3774
|
+
function getVerifyConfig(ruleId, testerConfig, testCase) {
|
3775
|
+
const { options, parserOptions, parser = testerConfig.parser } = testCase;
|
3776
|
+
return {
|
3777
|
+
...testerConfig,
|
3778
|
+
parser,
|
3779
|
+
parserOptions: {
|
3780
|
+
...testerConfig.parserOptions,
|
3781
|
+
...parserOptions,
|
3782
|
+
},
|
3783
|
+
rules: {
|
3784
|
+
[ruleId]: ['error', ...(Array.isArray(options) ? options : [])],
|
3785
|
+
},
|
3786
|
+
};
|
3787
|
+
}
|
3788
|
+
const parsers = new WeakMap();
|
3789
|
+
function defineParser(linter, parser) {
|
3790
|
+
if (!parser) {
|
3791
|
+
return;
|
3792
|
+
}
|
3793
|
+
if (!parsers.has(linter)) {
|
3794
|
+
parsers.set(linter, new Set());
|
3795
|
+
}
|
3796
|
+
const defined = parsers.get(linter);
|
3797
|
+
if (!defined.has(parser)) {
|
3798
|
+
defined.add(parser);
|
3799
|
+
linter.defineParser(parser, require(parser));
|
3800
|
+
}
|
3801
|
+
}
|
3802
|
+
function visualizeEslintMessage(text, result) {
|
3803
|
+
const { line, column, endLine, endColumn, message } = result;
|
3804
|
+
const location = {
|
3805
|
+
start: {
|
3806
|
+
line,
|
3807
|
+
column,
|
3808
|
+
},
|
3809
|
+
};
|
3810
|
+
if (typeof endLine === 'number' && typeof endColumn === 'number') {
|
3811
|
+
location.end = {
|
3812
|
+
line: endLine,
|
3813
|
+
column: endColumn,
|
3814
|
+
};
|
3815
|
+
}
|
3816
|
+
return codeFrameColumns(text, location, {
|
3817
|
+
linesAbove: Number.POSITIVE_INFINITY,
|
3818
|
+
linesBelow: Number.POSITIVE_INFINITY,
|
3819
|
+
message,
|
3820
|
+
});
|
3821
|
+
}
|
3778
3822
|
|
3779
3823
|
export { GraphQLRuleTester, configs, convertDescription, convertLocation, convertRange, convertToESTree, extractCommentsFromAst, getBaseType, isNodeWithDescription, parse, parseForESLint, processors, rules, valueFromNode };
|