@graphql-eslint/eslint-plugin 2.3.0-alpha-6ba4002.0 → 2.3.2-alpha-99be3d2.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/README.md +1 -1
- package/configs/all.d.ts +7 -0
- package/configs/index.d.ts +7 -0
- package/docs/README.md +2 -1
- package/docs/custom-rules.md +3 -3
- package/docs/rules/alphabetize.md +145 -0
- package/docs/rules/avoid-duplicate-fields.md +6 -1
- package/docs/rules/avoid-operation-name-prefix.md +8 -8
- package/docs/rules/avoid-scalar-result-type-on-mutation.md +6 -1
- package/docs/rules/avoid-typename-prefix.md +6 -1
- package/docs/rules/description-style.md +10 -9
- package/docs/rules/executable-definitions.md +6 -1
- package/docs/rules/fields-on-correct-type.md +6 -1
- package/docs/rules/fragments-on-composite-type.md +6 -1
- package/docs/rules/input-name.md +11 -10
- package/docs/rules/known-argument-names.md +6 -1
- package/docs/rules/known-directives.md +6 -1
- package/docs/rules/known-fragment-names.md +6 -1
- package/docs/rules/known-type-names.md +6 -1
- package/docs/rules/lone-anonymous-operation.md +6 -1
- package/docs/rules/lone-schema-definition.md +6 -1
- package/docs/rules/match-document-filename.md +12 -11
- package/docs/rules/naming-convention.md +21 -20
- package/docs/rules/no-anonymous-operations.md +6 -1
- package/docs/rules/no-case-insensitive-enum-values-duplicates.md +5 -1
- package/docs/rules/no-deprecated.md +6 -1
- package/docs/rules/no-fragment-cycles.md +6 -1
- package/docs/rules/no-hashtag-description.md +6 -1
- package/docs/rules/no-operation-name-suffix.md +5 -1
- package/docs/rules/no-undefined-variables.md +6 -1
- package/docs/rules/no-unreachable-types.md +6 -1
- package/docs/rules/no-unused-fields.md +6 -1
- package/docs/rules/no-unused-fragments.md +6 -1
- package/docs/rules/no-unused-variables.md +6 -1
- package/docs/rules/one-field-subscriptions.md +6 -1
- package/docs/rules/overlapping-fields-can-be-merged.md +6 -1
- package/docs/rules/possible-fragment-spread.md +6 -1
- package/docs/rules/possible-type-extension.md +6 -1
- package/docs/rules/provided-required-arguments.md +6 -1
- package/docs/rules/require-deprecation-date.md +5 -4
- package/docs/rules/require-deprecation-reason.md +6 -1
- package/docs/rules/require-description.md +5 -8
- package/docs/rules/require-field-of-type-query-in-mutation-result.md +6 -1
- package/docs/rules/require-id-when-available.md +6 -5
- package/docs/rules/scalar-leafs.md +6 -1
- package/docs/rules/selection-set-depth.md +6 -9
- package/docs/rules/strict-id-in-types.md +12 -11
- package/docs/rules/unique-argument-names.md +6 -1
- package/docs/rules/unique-directive-names-per-location.md +6 -1
- package/docs/rules/unique-directive-names.md +6 -1
- package/docs/rules/unique-enum-value-names.md +6 -1
- package/docs/rules/unique-field-definition-names.md +6 -1
- package/docs/rules/unique-fragment-name.md +6 -1
- package/docs/rules/unique-input-field-names.md +6 -1
- package/docs/rules/unique-operation-name.md +6 -1
- package/docs/rules/unique-operation-types.md +6 -1
- package/docs/rules/unique-type-names.md +6 -1
- package/docs/rules/unique-variable-names.md +6 -1
- package/docs/rules/value-literals-of-correct-type.md +6 -1
- package/docs/rules/variables-are-input-types.md +6 -1
- package/docs/rules/variables-in-allowed-position.md +6 -1
- package/estree-parser/estree-ast.d.ts +2 -2
- package/index.js +466 -102
- package/index.mjs +466 -102
- package/package.json +1 -1
- package/rules/alphabetize.d.ts +17 -0
- package/rules/index.d.ts +7 -0
- package/testkit.d.ts +6 -4
- package/types.d.ts +3 -0
- package/utils.d.ts +4 -0
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`
|
@@ -65,6 +67,16 @@ const allConfig = {
|
|
65
67
|
...recommendedConfig,
|
66
68
|
rules: {
|
67
69
|
...recommendedConfig.rules,
|
70
|
+
'@graphql-eslint/alphabetize': [
|
71
|
+
'error',
|
72
|
+
{
|
73
|
+
fields: ['ObjectTypeDefinition', 'InterfaceTypeDefinition', 'InputObjectTypeDefinition'],
|
74
|
+
values: ['EnumTypeDefinition'],
|
75
|
+
selections: ['OperationDefinition', 'FragmentDefinition'],
|
76
|
+
variables: ['OperationDefinition'],
|
77
|
+
arguments: ['FieldDefinition', 'Field', 'DirectiveDefinition', 'Directive'],
|
78
|
+
},
|
79
|
+
],
|
68
80
|
'@graphql-eslint/avoid-duplicate-fields': 'error',
|
69
81
|
'@graphql-eslint/avoid-operation-name-prefix': 'error',
|
70
82
|
'@graphql-eslint/avoid-scalar-result-type-on-mutation': 'error',
|
@@ -226,6 +238,24 @@ const convertCase = (style, str) => {
|
|
226
238
|
return lowerCase(str).replace(/ /g, '-');
|
227
239
|
}
|
228
240
|
};
|
241
|
+
function getLocation(loc, fieldName = '', offset) {
|
242
|
+
const { start } = loc;
|
243
|
+
/*
|
244
|
+
* ESLint has 0-based column number
|
245
|
+
* https://eslint.org/docs/developer-guide/working-with-rules#contextreport
|
246
|
+
*/
|
247
|
+
const { offsetStart = 1, offsetEnd = 1 } = offset !== null && offset !== void 0 ? offset : {};
|
248
|
+
return {
|
249
|
+
start: {
|
250
|
+
line: start.line,
|
251
|
+
column: start.column - offsetStart,
|
252
|
+
},
|
253
|
+
end: {
|
254
|
+
line: start.line,
|
255
|
+
column: start.column - offsetEnd + fieldName.length,
|
256
|
+
},
|
257
|
+
};
|
258
|
+
}
|
229
259
|
|
230
260
|
function extractRuleName(stack) {
|
231
261
|
const match = (stack || '').match(/validation[/\\\\]rules[/\\\\](.*?)\.js:/) || [];
|
@@ -276,6 +306,7 @@ const validationToRule = (name, ruleName, docs, getDocumentNode) => {
|
|
276
306
|
meta: {
|
277
307
|
docs: {
|
278
308
|
...docs,
|
309
|
+
graphQLJSRuleName: ruleName,
|
279
310
|
category: 'Validation',
|
280
311
|
recommended: true,
|
281
312
|
requiresSchema,
|
@@ -399,7 +430,7 @@ const GRAPHQL_JS_VALIDATIONS = Object.assign({}, validationToRule('executable-de
|
|
399
430
|
description: `A GraphQL fragment is only valid when it does not have cycles in fragments usage.`,
|
400
431
|
}), validationToRule('no-undefined-variables', 'NoUndefinedVariables', {
|
401
432
|
description: `A GraphQL operation is only valid if all variables encountered, both directly and via fragment spreads, are defined by that operation.`,
|
402
|
-
}), validationToRule('no-unused-fragments', 'NoUnusedFragments', {
|
433
|
+
}, importFiles), validationToRule('no-unused-fragments', 'NoUnusedFragments', {
|
403
434
|
description: `A GraphQL document is only valid if all fragment definitions are spread within operations, or spread within other fragments spread within operations.`,
|
404
435
|
requiresSiblings: true,
|
405
436
|
}, context => {
|
@@ -475,6 +506,237 @@ const GRAPHQL_JS_VALIDATIONS = Object.assign({}, validationToRule('executable-de
|
|
475
506
|
description: `Variables passed to field arguments conform to type.`,
|
476
507
|
}));
|
477
508
|
|
509
|
+
const ALPHABETIZE = 'ALPHABETIZE';
|
510
|
+
const fieldsEnum = [Kind.OBJECT_TYPE_DEFINITION, Kind.INTERFACE_TYPE_DEFINITION, Kind.INPUT_OBJECT_TYPE_DEFINITION];
|
511
|
+
const valuesEnum = [Kind.ENUM_TYPE_DEFINITION];
|
512
|
+
const selectionsEnum = [Kind.OPERATION_DEFINITION, Kind.FRAGMENT_DEFINITION];
|
513
|
+
const variablesEnum = [Kind.OPERATION_DEFINITION];
|
514
|
+
const argumentsEnum = [Kind.FIELD_DEFINITION, Kind.FIELD, Kind.DIRECTIVE_DEFINITION, Kind.DIRECTIVE];
|
515
|
+
const rule = {
|
516
|
+
meta: {
|
517
|
+
type: 'suggestion',
|
518
|
+
docs: {
|
519
|
+
category: 'Best Practices',
|
520
|
+
description: 'Enforce arrange in alphabetical order for type fields, enum values, input object fields, operation selections and more.',
|
521
|
+
url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/alphabetize.md',
|
522
|
+
examples: [
|
523
|
+
{
|
524
|
+
title: 'Incorrect',
|
525
|
+
usage: [{ fields: [Kind.OBJECT_TYPE_DEFINITION] }],
|
526
|
+
code: /* GraphQL */ `
|
527
|
+
type User {
|
528
|
+
password: String
|
529
|
+
firstName: String! # should be before "password"
|
530
|
+
age: Int # should be before "firstName"
|
531
|
+
lastName: String!
|
532
|
+
}
|
533
|
+
`,
|
534
|
+
},
|
535
|
+
{
|
536
|
+
title: 'Correct',
|
537
|
+
usage: [{ fields: [Kind.OBJECT_TYPE_DEFINITION] }],
|
538
|
+
code: /* GraphQL */ `
|
539
|
+
type User {
|
540
|
+
age: Int
|
541
|
+
firstName: String!
|
542
|
+
lastName: String!
|
543
|
+
password: String
|
544
|
+
}
|
545
|
+
`,
|
546
|
+
},
|
547
|
+
{
|
548
|
+
title: 'Incorrect',
|
549
|
+
usage: [{ values: [Kind.ENUM_TYPE_DEFINITION] }],
|
550
|
+
code: /* GraphQL */ `
|
551
|
+
enum Role {
|
552
|
+
SUPER_ADMIN
|
553
|
+
ADMIN # should be before "SUPER_ADMIN"
|
554
|
+
USER
|
555
|
+
GOD # should be before "USER"
|
556
|
+
}
|
557
|
+
`,
|
558
|
+
},
|
559
|
+
{
|
560
|
+
title: 'Correct',
|
561
|
+
usage: [{ values: [Kind.ENUM_TYPE_DEFINITION] }],
|
562
|
+
code: /* GraphQL */ `
|
563
|
+
enum Role {
|
564
|
+
ADMIN
|
565
|
+
GOD
|
566
|
+
SUPER_ADMIN
|
567
|
+
USER
|
568
|
+
}
|
569
|
+
`,
|
570
|
+
},
|
571
|
+
{
|
572
|
+
title: 'Incorrect',
|
573
|
+
usage: [{ selections: [Kind.OPERATION_DEFINITION] }],
|
574
|
+
code: /* GraphQL */ `
|
575
|
+
query {
|
576
|
+
me {
|
577
|
+
firstName
|
578
|
+
lastName
|
579
|
+
email # should be before "lastName"
|
580
|
+
}
|
581
|
+
}
|
582
|
+
`,
|
583
|
+
},
|
584
|
+
{
|
585
|
+
title: 'Correct',
|
586
|
+
usage: [{ selections: [Kind.OPERATION_DEFINITION] }],
|
587
|
+
code: /* GraphQL */ `
|
588
|
+
query {
|
589
|
+
me {
|
590
|
+
email
|
591
|
+
firstName
|
592
|
+
lastName
|
593
|
+
}
|
594
|
+
}
|
595
|
+
`,
|
596
|
+
},
|
597
|
+
],
|
598
|
+
optionsForConfig: [
|
599
|
+
{
|
600
|
+
fields: fieldsEnum,
|
601
|
+
values: valuesEnum,
|
602
|
+
selections: selectionsEnum,
|
603
|
+
variables: variablesEnum,
|
604
|
+
arguments: argumentsEnum,
|
605
|
+
},
|
606
|
+
],
|
607
|
+
},
|
608
|
+
messages: {
|
609
|
+
[ALPHABETIZE]: '"{{ currName }}" should be before "{{ prevName }}".',
|
610
|
+
},
|
611
|
+
schema: {
|
612
|
+
type: 'array',
|
613
|
+
minItems: 1,
|
614
|
+
maxItems: 1,
|
615
|
+
items: {
|
616
|
+
type: 'object',
|
617
|
+
additionalProperties: false,
|
618
|
+
minProperties: 1,
|
619
|
+
properties: {
|
620
|
+
fields: {
|
621
|
+
type: 'array',
|
622
|
+
contains: {
|
623
|
+
enum: fieldsEnum,
|
624
|
+
},
|
625
|
+
description: 'Fields of `type`, `interface`, and `input`.',
|
626
|
+
},
|
627
|
+
values: {
|
628
|
+
type: 'array',
|
629
|
+
contains: {
|
630
|
+
enum: valuesEnum,
|
631
|
+
},
|
632
|
+
description: 'Values of `enum`.',
|
633
|
+
},
|
634
|
+
selections: {
|
635
|
+
type: 'array',
|
636
|
+
contains: {
|
637
|
+
enum: selectionsEnum,
|
638
|
+
},
|
639
|
+
description: 'Selections of operations (`query`, `mutation` and `subscription`) and `fragment`.',
|
640
|
+
},
|
641
|
+
variables: {
|
642
|
+
type: 'array',
|
643
|
+
contains: {
|
644
|
+
enum: variablesEnum,
|
645
|
+
},
|
646
|
+
description: 'Variables of operations (`query`, `mutation` and `subscription`).',
|
647
|
+
},
|
648
|
+
arguments: {
|
649
|
+
type: 'array',
|
650
|
+
contains: {
|
651
|
+
enum: argumentsEnum,
|
652
|
+
},
|
653
|
+
description: 'Arguments of fields and directives.',
|
654
|
+
},
|
655
|
+
},
|
656
|
+
},
|
657
|
+
},
|
658
|
+
},
|
659
|
+
create(context) {
|
660
|
+
var _a, _b, _c, _d, _e;
|
661
|
+
function checkNodes(nodes) {
|
662
|
+
let prevName = null;
|
663
|
+
for (const node of nodes) {
|
664
|
+
const currName = node.name.value;
|
665
|
+
if (prevName && prevName > currName) {
|
666
|
+
const { start, end } = node.name.loc;
|
667
|
+
const isVariableNode = node.kind === Kind.VARIABLE;
|
668
|
+
context.report({
|
669
|
+
loc: {
|
670
|
+
start: {
|
671
|
+
line: start.line,
|
672
|
+
column: start.column - (isVariableNode ? 2 : 1),
|
673
|
+
},
|
674
|
+
end,
|
675
|
+
},
|
676
|
+
messageId: ALPHABETIZE,
|
677
|
+
data: isVariableNode
|
678
|
+
? {
|
679
|
+
currName: `$${currName}`,
|
680
|
+
prevName: `$${prevName}`,
|
681
|
+
}
|
682
|
+
: { currName, prevName },
|
683
|
+
});
|
684
|
+
}
|
685
|
+
prevName = currName;
|
686
|
+
}
|
687
|
+
}
|
688
|
+
const opts = context.options[0];
|
689
|
+
const fields = new Set((_a = opts.fields) !== null && _a !== void 0 ? _a : []);
|
690
|
+
const listeners = {};
|
691
|
+
const fieldsSelector = [
|
692
|
+
fields.has(Kind.OBJECT_TYPE_DEFINITION) && [Kind.OBJECT_TYPE_DEFINITION, Kind.OBJECT_TYPE_EXTENSION],
|
693
|
+
fields.has(Kind.INTERFACE_TYPE_DEFINITION) && [Kind.INTERFACE_TYPE_DEFINITION, Kind.INTERFACE_TYPE_EXTENSION],
|
694
|
+
fields.has(Kind.INPUT_OBJECT_TYPE_DEFINITION) && [
|
695
|
+
Kind.INPUT_OBJECT_TYPE_DEFINITION,
|
696
|
+
Kind.INPUT_OBJECT_TYPE_EXTENSION,
|
697
|
+
],
|
698
|
+
]
|
699
|
+
.flat()
|
700
|
+
.join(',');
|
701
|
+
const hasEnumValues = ((_b = opts.values) === null || _b === void 0 ? void 0 : _b[0]) === Kind.ENUM_TYPE_DEFINITION;
|
702
|
+
const selectionsSelector = (_c = opts.selections) === null || _c === void 0 ? void 0 : _c.join(',');
|
703
|
+
const hasVariables = ((_d = opts.variables) === null || _d === void 0 ? void 0 : _d[0]) === Kind.OPERATION_DEFINITION;
|
704
|
+
const argumentsSelector = (_e = opts.arguments) === null || _e === void 0 ? void 0 : _e.join(',');
|
705
|
+
if (fieldsSelector) {
|
706
|
+
listeners[fieldsSelector] = (node) => {
|
707
|
+
checkNodes(node.fields);
|
708
|
+
};
|
709
|
+
}
|
710
|
+
if (hasEnumValues) {
|
711
|
+
const enumValuesSelector = [Kind.ENUM_TYPE_DEFINITION, Kind.ENUM_TYPE_EXTENSION].join(',');
|
712
|
+
listeners[enumValuesSelector] = (node) => {
|
713
|
+
checkNodes(node.values);
|
714
|
+
};
|
715
|
+
}
|
716
|
+
if (selectionsSelector) {
|
717
|
+
listeners[`:matches(${selectionsSelector}) SelectionSet`] = (node) => {
|
718
|
+
checkNodes(node.selections
|
719
|
+
// inline fragment don't have name, so we skip them
|
720
|
+
.filter(selection => selection.kind !== Kind.INLINE_FRAGMENT)
|
721
|
+
.map(selection =>
|
722
|
+
// sort by alias is field is renamed
|
723
|
+
'alias' in selection && selection.alias ? { name: selection.alias } : selection));
|
724
|
+
};
|
725
|
+
}
|
726
|
+
if (hasVariables) {
|
727
|
+
listeners.OperationDefinition = (node) => {
|
728
|
+
checkNodes(node.variableDefinitions.map(varDef => varDef.variable));
|
729
|
+
};
|
730
|
+
}
|
731
|
+
if (argumentsSelector) {
|
732
|
+
listeners[argumentsSelector] = (node) => {
|
733
|
+
checkNodes(node.arguments);
|
734
|
+
};
|
735
|
+
}
|
736
|
+
return listeners;
|
737
|
+
},
|
738
|
+
};
|
739
|
+
|
478
740
|
const AVOID_DUPLICATE_FIELDS = 'AVOID_DUPLICATE_FIELDS';
|
479
741
|
const ensureUnique = () => {
|
480
742
|
const set = new Set();
|
@@ -489,7 +751,7 @@ const ensureUnique = () => {
|
|
489
751
|
},
|
490
752
|
};
|
491
753
|
};
|
492
|
-
const rule = {
|
754
|
+
const rule$1 = {
|
493
755
|
meta: {
|
494
756
|
type: 'suggestion',
|
495
757
|
docs: {
|
@@ -540,6 +802,7 @@ const rule = {
|
|
540
802
|
messages: {
|
541
803
|
[AVOID_DUPLICATE_FIELDS]: `{{ type }} "{{ fieldName }}" defined multiple times.`,
|
542
804
|
},
|
805
|
+
schema: [],
|
543
806
|
},
|
544
807
|
create(context) {
|
545
808
|
return {
|
@@ -597,7 +860,7 @@ const rule = {
|
|
597
860
|
};
|
598
861
|
|
599
862
|
const AVOID_OPERATION_NAME_PREFIX = 'AVOID_OPERATION_NAME_PREFIX';
|
600
|
-
const rule$
|
863
|
+
const rule$2 = {
|
601
864
|
meta: {
|
602
865
|
type: 'suggestion',
|
603
866
|
docs: {
|
@@ -659,15 +922,16 @@ const rule$1 = {
|
|
659
922
|
const testKeyword = caseSensitive ? keyword : keyword.toLowerCase();
|
660
923
|
const testName = caseSensitive ? node.name.value : node.name.value.toLowerCase();
|
661
924
|
if (testName.startsWith(testKeyword)) {
|
925
|
+
const { start } = node.name.loc;
|
662
926
|
context.report({
|
663
927
|
loc: {
|
664
928
|
start: {
|
665
|
-
line:
|
666
|
-
column:
|
929
|
+
line: start.line,
|
930
|
+
column: start.column - 1,
|
667
931
|
},
|
668
932
|
end: {
|
669
|
-
line:
|
670
|
-
column:
|
933
|
+
line: start.line,
|
934
|
+
column: start.column - 1 + testKeyword.length,
|
671
935
|
},
|
672
936
|
},
|
673
937
|
data: {
|
@@ -683,7 +947,7 @@ const rule$1 = {
|
|
683
947
|
},
|
684
948
|
};
|
685
949
|
|
686
|
-
const rule$
|
950
|
+
const rule$3 = {
|
687
951
|
meta: {
|
688
952
|
type: 'suggestion',
|
689
953
|
docs: {
|
@@ -710,6 +974,7 @@ const rule$2 = {
|
|
710
974
|
},
|
711
975
|
],
|
712
976
|
},
|
977
|
+
schema: [],
|
713
978
|
},
|
714
979
|
create(context) {
|
715
980
|
const schema = requireGraphQLSchemaFromContext('avoid-scalar-result-type-on-mutation', context);
|
@@ -735,7 +1000,7 @@ const rule$2 = {
|
|
735
1000
|
};
|
736
1001
|
|
737
1002
|
const AVOID_TYPENAME_PREFIX = 'AVOID_TYPENAME_PREFIX';
|
738
|
-
const rule$
|
1003
|
+
const rule$4 = {
|
739
1004
|
meta: {
|
740
1005
|
type: 'suggestion',
|
741
1006
|
docs: {
|
@@ -765,22 +1030,33 @@ const rule$3 = {
|
|
765
1030
|
messages: {
|
766
1031
|
[AVOID_TYPENAME_PREFIX]: `Field "{{ fieldName }}" starts with the name of the parent type "{{ typeName }}"`,
|
767
1032
|
},
|
1033
|
+
schema: [],
|
768
1034
|
},
|
769
1035
|
create(context) {
|
770
1036
|
return {
|
771
1037
|
'ObjectTypeDefinition, ObjectTypeExtension, InterfaceTypeDefinition, InterfaceTypeExtension'(node) {
|
772
1038
|
const typeName = node.name.value;
|
773
|
-
const lowerTypeName =
|
1039
|
+
const lowerTypeName = typeName.toLowerCase();
|
774
1040
|
for (const field of node.fields) {
|
775
|
-
const fieldName = field.name.value
|
776
|
-
if (fieldName
|
1041
|
+
const fieldName = field.name.value;
|
1042
|
+
if (fieldName.toLowerCase().startsWith(lowerTypeName)) {
|
1043
|
+
const { start } = field.loc;
|
777
1044
|
context.report({
|
778
|
-
node: field.name,
|
779
1045
|
data: {
|
780
1046
|
fieldName,
|
781
1047
|
typeName,
|
782
1048
|
},
|
783
1049
|
messageId: AVOID_TYPENAME_PREFIX,
|
1050
|
+
loc: {
|
1051
|
+
start: {
|
1052
|
+
line: start.line,
|
1053
|
+
column: start.column - 1,
|
1054
|
+
},
|
1055
|
+
end: {
|
1056
|
+
line: start.line,
|
1057
|
+
column: start.column - 1 + lowerTypeName.length,
|
1058
|
+
},
|
1059
|
+
},
|
784
1060
|
});
|
785
1061
|
}
|
786
1062
|
}
|
@@ -789,7 +1065,7 @@ const rule$3 = {
|
|
789
1065
|
},
|
790
1066
|
};
|
791
1067
|
|
792
|
-
const rule$
|
1068
|
+
const rule$5 = {
|
793
1069
|
meta: {
|
794
1070
|
type: 'suggestion',
|
795
1071
|
docs: {
|
@@ -800,9 +1076,9 @@ const rule$4 = {
|
|
800
1076
|
code: /* GraphQL */ `
|
801
1077
|
""" Description """
|
802
1078
|
type someTypeName {
|
803
|
-
|
1079
|
+
# ...
|
804
1080
|
}
|
805
|
-
|
1081
|
+
`,
|
806
1082
|
},
|
807
1083
|
{
|
808
1084
|
title: 'Correct',
|
@@ -810,9 +1086,9 @@ const rule$4 = {
|
|
810
1086
|
code: /* GraphQL */ `
|
811
1087
|
" Description "
|
812
1088
|
type someTypeName {
|
813
|
-
|
1089
|
+
# ...
|
814
1090
|
}
|
815
|
-
|
1091
|
+
`,
|
816
1092
|
},
|
817
1093
|
],
|
818
1094
|
description: 'Require all comments to follow the same style (either block or inline).',
|
@@ -849,7 +1125,7 @@ const rule$4 = {
|
|
849
1125
|
},
|
850
1126
|
};
|
851
1127
|
|
852
|
-
const rule$
|
1128
|
+
const rule$6 = {
|
853
1129
|
meta: {
|
854
1130
|
type: 'suggestion',
|
855
1131
|
docs: {
|
@@ -974,7 +1250,7 @@ const CASE_STYLES = [
|
|
974
1250
|
const schemaOption = {
|
975
1251
|
oneOf: [{ $ref: '#/definitions/asString' }, { $ref: '#/definitions/asObject' }],
|
976
1252
|
};
|
977
|
-
const rule$
|
1253
|
+
const rule$7 = {
|
978
1254
|
meta: {
|
979
1255
|
type: 'suggestion',
|
980
1256
|
docs: {
|
@@ -1108,7 +1384,8 @@ const rule$6 = {
|
|
1108
1384
|
var _a;
|
1109
1385
|
if (options.fileExtension && options.fileExtension !== fileExtension) {
|
1110
1386
|
context.report({
|
1111
|
-
|
1387
|
+
// Report on first character
|
1388
|
+
loc: { column: 0, line: 1 },
|
1112
1389
|
messageId: MATCH_EXTENSION,
|
1113
1390
|
data: {
|
1114
1391
|
fileExtension,
|
@@ -1140,7 +1417,8 @@ const rule$6 = {
|
|
1140
1417
|
const filenameWithExtension = filename + expectedExtension;
|
1141
1418
|
if (expectedFilename !== filenameWithExtension) {
|
1142
1419
|
context.report({
|
1143
|
-
|
1420
|
+
// Report on first character
|
1421
|
+
loc: { column: 0, line: 1 },
|
1144
1422
|
messageId: MATCH_STYLE,
|
1145
1423
|
data: {
|
1146
1424
|
expectedFilename,
|
@@ -1219,7 +1497,7 @@ function checkNameFormat(params) {
|
|
1219
1497
|
const schemaOption$1 = {
|
1220
1498
|
oneOf: [{ $ref: '#/definitions/asString' }, { $ref: '#/definitions/asObject' }],
|
1221
1499
|
};
|
1222
|
-
const rule$
|
1500
|
+
const rule$8 = {
|
1223
1501
|
meta: {
|
1224
1502
|
type: 'suggestion',
|
1225
1503
|
docs: {
|
@@ -1331,18 +1609,18 @@ const rule$7 = {
|
|
1331
1609
|
style,
|
1332
1610
|
leadingUnderscore: options.leadingUnderscore,
|
1333
1611
|
trailingUnderscore: options.trailingUnderscore,
|
1334
|
-
prefix
|
1335
|
-
suffix
|
1336
|
-
forbiddenPrefixes
|
1337
|
-
forbiddenSuffixes
|
1612
|
+
prefix,
|
1613
|
+
suffix,
|
1614
|
+
forbiddenPrefixes,
|
1615
|
+
forbiddenSuffixes,
|
1338
1616
|
});
|
1339
1617
|
if (result.ok === false) {
|
1340
1618
|
context.report({
|
1341
1619
|
node,
|
1342
1620
|
message: result.errorMessage,
|
1343
1621
|
data: {
|
1344
|
-
prefix
|
1345
|
-
suffix
|
1622
|
+
prefix,
|
1623
|
+
suffix,
|
1346
1624
|
format: style,
|
1347
1625
|
forbiddenPrefixes: forbiddenPrefixes.join(', '),
|
1348
1626
|
forbiddenSuffixes: forbiddenSuffixes.join(', '),
|
@@ -1448,7 +1726,7 @@ const rule$7 = {
|
|
1448
1726
|
};
|
1449
1727
|
|
1450
1728
|
const NO_ANONYMOUS_OPERATIONS = 'NO_ANONYMOUS_OPERATIONS';
|
1451
|
-
const rule$
|
1729
|
+
const rule$9 = {
|
1452
1730
|
meta: {
|
1453
1731
|
type: 'suggestion',
|
1454
1732
|
docs: {
|
@@ -1478,20 +1756,24 @@ const rule$8 = {
|
|
1478
1756
|
messages: {
|
1479
1757
|
[NO_ANONYMOUS_OPERATIONS]: `Anonymous GraphQL operations are forbidden. Please make sure to name your {{ operation }}!`,
|
1480
1758
|
},
|
1759
|
+
schema: [],
|
1481
1760
|
},
|
1482
1761
|
create(context) {
|
1483
1762
|
return {
|
1484
1763
|
OperationDefinition(node) {
|
1485
|
-
|
1764
|
+
var _a;
|
1765
|
+
const isAnonymous = (((_a = node.name) === null || _a === void 0 ? void 0 : _a.value) || '').length === 0;
|
1766
|
+
if (isAnonymous) {
|
1767
|
+
const { start } = node.loc;
|
1486
1768
|
context.report({
|
1487
1769
|
loc: {
|
1488
1770
|
start: {
|
1489
|
-
column:
|
1490
|
-
line:
|
1771
|
+
column: start.column - 1,
|
1772
|
+
line: start.line,
|
1491
1773
|
},
|
1492
1774
|
end: {
|
1493
|
-
column:
|
1494
|
-
line:
|
1775
|
+
column: start.column - 1 + node.operation.length,
|
1776
|
+
line: start.line,
|
1495
1777
|
},
|
1496
1778
|
},
|
1497
1779
|
data: {
|
@@ -1506,7 +1788,7 @@ const rule$8 = {
|
|
1506
1788
|
};
|
1507
1789
|
|
1508
1790
|
const ERROR_MESSAGE_ID = 'NO_CASE_INSENSITIVE_ENUM_VALUES_DUPLICATES';
|
1509
|
-
const rule$
|
1791
|
+
const rule$a = {
|
1510
1792
|
meta: {
|
1511
1793
|
type: 'suggestion',
|
1512
1794
|
docs: {
|
@@ -1541,6 +1823,7 @@ const rule$9 = {
|
|
1541
1823
|
messages: {
|
1542
1824
|
[ERROR_MESSAGE_ID]: `Case-insensitive enum values duplicates are not allowed! Found: "{{ found }}"`,
|
1543
1825
|
},
|
1826
|
+
schema: [],
|
1544
1827
|
},
|
1545
1828
|
create(context) {
|
1546
1829
|
return {
|
@@ -1561,7 +1844,7 @@ const rule$9 = {
|
|
1561
1844
|
};
|
1562
1845
|
|
1563
1846
|
const NO_DEPRECATED = 'NO_DEPRECATED';
|
1564
|
-
const rule$
|
1847
|
+
const rule$b = {
|
1565
1848
|
meta: {
|
1566
1849
|
type: 'suggestion',
|
1567
1850
|
docs: {
|
@@ -1635,6 +1918,7 @@ const rule$a = {
|
|
1635
1918
|
messages: {
|
1636
1919
|
[NO_DEPRECATED]: `This {{ type }} is marked as deprecated in your GraphQL schema {{ reason }}`,
|
1637
1920
|
},
|
1921
|
+
schema: [],
|
1638
1922
|
},
|
1639
1923
|
create(context) {
|
1640
1924
|
return {
|
@@ -1675,7 +1959,7 @@ const rule$a = {
|
|
1675
1959
|
};
|
1676
1960
|
|
1677
1961
|
const HASHTAG_COMMENT = 'HASHTAG_COMMENT';
|
1678
|
-
const rule$
|
1962
|
+
const rule$c = {
|
1679
1963
|
meta: {
|
1680
1964
|
messages: {
|
1681
1965
|
[HASHTAG_COMMENT]: 'Using hashtag (#) for adding GraphQL descriptions is not allowed. Prefer using """ for multiline, or " for a single line description.',
|
@@ -1721,6 +2005,7 @@ const rule$b = {
|
|
1721
2005
|
],
|
1722
2006
|
},
|
1723
2007
|
type: 'suggestion',
|
2008
|
+
schema: [],
|
1724
2009
|
},
|
1725
2010
|
create(context) {
|
1726
2011
|
return {
|
@@ -1750,7 +2035,7 @@ const rule$b = {
|
|
1750
2035
|
};
|
1751
2036
|
|
1752
2037
|
const NO_OPERATION_NAME_SUFFIX = 'NO_OPERATION_NAME_SUFFIX';
|
1753
|
-
const rule$
|
2038
|
+
const rule$d = {
|
1754
2039
|
meta: {
|
1755
2040
|
fixable: 'code',
|
1756
2041
|
type: 'suggestion',
|
@@ -1781,15 +2066,28 @@ const rule$c = {
|
|
1781
2066
|
messages: {
|
1782
2067
|
[NO_OPERATION_NAME_SUFFIX]: `Unnecessary "{{ invalidSuffix }}" suffix in your operation name!`,
|
1783
2068
|
},
|
2069
|
+
schema: [],
|
1784
2070
|
},
|
1785
2071
|
create(context) {
|
1786
2072
|
return {
|
1787
2073
|
'OperationDefinition, FragmentDefinition'(node) {
|
1788
|
-
|
1789
|
-
|
1790
|
-
|
2074
|
+
var _a;
|
2075
|
+
const name = ((_a = node.name) === null || _a === void 0 ? void 0 : _a.value) || '';
|
2076
|
+
if (name.length > 0) {
|
2077
|
+
const invalidSuffix = 'operation' in node ? node.operation : 'fragment';
|
2078
|
+
if (name.toLowerCase().endsWith(invalidSuffix)) {
|
2079
|
+
const { start, end } = node.name.loc;
|
1791
2080
|
context.report({
|
1792
|
-
|
2081
|
+
loc: {
|
2082
|
+
start: {
|
2083
|
+
column: start.column - 1 + name.length - invalidSuffix.length,
|
2084
|
+
line: start.line,
|
2085
|
+
},
|
2086
|
+
end: {
|
2087
|
+
column: end.column - 1 + name.length,
|
2088
|
+
line: end.line,
|
2089
|
+
},
|
2090
|
+
},
|
1793
2091
|
data: {
|
1794
2092
|
invalidSuffix,
|
1795
2093
|
},
|
@@ -1805,7 +2103,7 @@ const rule$c = {
|
|
1805
2103
|
|
1806
2104
|
const UNREACHABLE_TYPE = 'UNREACHABLE_TYPE';
|
1807
2105
|
const RULE_NAME = 'no-unreachable-types';
|
1808
|
-
const rule$
|
2106
|
+
const rule$e = {
|
1809
2107
|
meta: {
|
1810
2108
|
messages: {
|
1811
2109
|
[UNREACHABLE_TYPE]: `Type "{{ typeName }}" is unreachable`,
|
@@ -1846,6 +2144,7 @@ const rule$d = {
|
|
1846
2144
|
},
|
1847
2145
|
fixable: 'code',
|
1848
2146
|
type: 'suggestion',
|
2147
|
+
schema: [],
|
1849
2148
|
},
|
1850
2149
|
create(context) {
|
1851
2150
|
const reachableTypes = requireReachableTypesFromContext(RULE_NAME, context);
|
@@ -1880,7 +2179,7 @@ const rule$d = {
|
|
1880
2179
|
|
1881
2180
|
const UNUSED_FIELD = 'UNUSED_FIELD';
|
1882
2181
|
const RULE_NAME$1 = 'no-unused-fields';
|
1883
|
-
const rule$
|
2182
|
+
const rule$f = {
|
1884
2183
|
meta: {
|
1885
2184
|
messages: {
|
1886
2185
|
[UNUSED_FIELD]: `Field "{{fieldName}}" is unused`,
|
@@ -1937,6 +2236,7 @@ const rule$e = {
|
|
1937
2236
|
},
|
1938
2237
|
fixable: 'code',
|
1939
2238
|
type: 'suggestion',
|
2239
|
+
schema: [],
|
1940
2240
|
},
|
1941
2241
|
create(context) {
|
1942
2242
|
const usedFields = requireUsedFieldsFromContext(RULE_NAME$1, context);
|
@@ -2067,7 +2367,7 @@ const MESSAGE_REQUIRE_DATE = 'MESSAGE_REQUIRE_DATE';
|
|
2067
2367
|
const MESSAGE_INVALID_FORMAT = 'MESSAGE_INVALID_FORMAT';
|
2068
2368
|
const MESSAGE_INVALID_DATE = 'MESSAGE_INVALID_DATE';
|
2069
2369
|
const MESSAGE_CAN_BE_REMOVED = 'MESSAGE_CAN_BE_REMOVED';
|
2070
|
-
const rule$
|
2370
|
+
const rule$g = {
|
2071
2371
|
meta: {
|
2072
2372
|
type: 'suggestion',
|
2073
2373
|
docs: {
|
@@ -2167,7 +2467,7 @@ const rule$f = {
|
|
2167
2467
|
},
|
2168
2468
|
};
|
2169
2469
|
|
2170
|
-
const rule$
|
2470
|
+
const rule$h = {
|
2171
2471
|
meta: {
|
2172
2472
|
docs: {
|
2173
2473
|
description: `Require all deprecation directives to specify a reason.`,
|
@@ -2202,6 +2502,7 @@ const rule$g = {
|
|
2202
2502
|
],
|
2203
2503
|
},
|
2204
2504
|
type: 'suggestion',
|
2505
|
+
schema: [],
|
2205
2506
|
},
|
2206
2507
|
create(context) {
|
2207
2508
|
return {
|
@@ -2238,15 +2539,18 @@ const DESCRIBABLE_NODES = [
|
|
2238
2539
|
function verifyRule(context, node) {
|
2239
2540
|
if (node) {
|
2240
2541
|
if (!node.description || !node.description.value || node.description.value.trim().length === 0) {
|
2542
|
+
const { start, end } = ('name' in node ? node.name : node).loc;
|
2241
2543
|
context.report({
|
2242
2544
|
loc: {
|
2243
2545
|
start: {
|
2244
|
-
line:
|
2245
|
-
column:
|
2546
|
+
line: start.line,
|
2547
|
+
column: start.column - 1,
|
2246
2548
|
},
|
2247
2549
|
end: {
|
2248
|
-
line:
|
2249
|
-
column:
|
2550
|
+
line: end.line,
|
2551
|
+
column:
|
2552
|
+
// node.name don't exist on SchemaDefinition
|
2553
|
+
'name' in node ? end.column - 1 + node.name.value.length : end.column,
|
2250
2554
|
},
|
2251
2555
|
},
|
2252
2556
|
messageId: REQUIRE_DESCRIPTION_ERROR,
|
@@ -2257,7 +2561,7 @@ function verifyRule(context, node) {
|
|
2257
2561
|
}
|
2258
2562
|
}
|
2259
2563
|
}
|
2260
|
-
const rule$
|
2564
|
+
const rule$i = {
|
2261
2565
|
meta: {
|
2262
2566
|
docs: {
|
2263
2567
|
category: 'Best Practices',
|
@@ -2322,7 +2626,7 @@ const rule$h = {
|
|
2322
2626
|
};
|
2323
2627
|
|
2324
2628
|
const RULE_NAME$2 = 'require-field-of-type-query-in-mutation-result';
|
2325
|
-
const rule$
|
2629
|
+
const rule$j = {
|
2326
2630
|
meta: {
|
2327
2631
|
type: 'suggestion',
|
2328
2632
|
docs: {
|
@@ -2360,6 +2664,7 @@ const rule$i = {
|
|
2360
2664
|
},
|
2361
2665
|
],
|
2362
2666
|
},
|
2667
|
+
schema: [],
|
2363
2668
|
},
|
2364
2669
|
create(context) {
|
2365
2670
|
const schema = requireGraphQLSchemaFromContext(RULE_NAME$2, context);
|
@@ -2485,7 +2790,7 @@ const convertNode = (typeInfo) => (node, key, parent) => {
|
|
2485
2790
|
|
2486
2791
|
const REQUIRE_ID_WHEN_AVAILABLE = 'REQUIRE_ID_WHEN_AVAILABLE';
|
2487
2792
|
const DEFAULT_ID_FIELD_NAME = 'id';
|
2488
|
-
const rule$
|
2793
|
+
const rule$k = {
|
2489
2794
|
meta: {
|
2490
2795
|
type: 'suggestion',
|
2491
2796
|
docs: {
|
@@ -2621,7 +2926,7 @@ const rule$j = {
|
|
2621
2926
|
},
|
2622
2927
|
};
|
2623
2928
|
|
2624
|
-
const rule$
|
2929
|
+
const rule$l = {
|
2625
2930
|
meta: {
|
2626
2931
|
docs: {
|
2627
2932
|
category: 'Best Practices',
|
@@ -2743,7 +3048,7 @@ const shouldIgnoreNode = ({ node, exceptions }) => {
|
|
2743
3048
|
}
|
2744
3049
|
return false;
|
2745
3050
|
};
|
2746
|
-
const rule$
|
3051
|
+
const rule$m = {
|
2747
3052
|
meta: {
|
2748
3053
|
type: 'suggestion',
|
2749
3054
|
docs: {
|
@@ -2896,11 +3201,7 @@ const rule$l = {
|
|
2896
3201
|
const RULE_NAME$3 = 'unique-fragment-name';
|
2897
3202
|
const UNIQUE_FRAGMENT_NAME = 'UNIQUE_FRAGMENT_NAME';
|
2898
3203
|
const checkNode = (context, node, ruleName, messageId) => {
|
2899
|
-
|
2900
|
-
const documentName = (_a = node.name) === null || _a === void 0 ? void 0 : _a.value;
|
2901
|
-
if (!documentName) {
|
2902
|
-
return;
|
2903
|
-
}
|
3204
|
+
const documentName = node.name.value;
|
2904
3205
|
const siblings = requireSiblingsOperations(ruleName, context);
|
2905
3206
|
const siblingDocuments = node.kind === Kind.FRAGMENT_DEFINITION ? siblings.getFragment(documentName) : siblings.getOperation(documentName);
|
2906
3207
|
const filepath = context.getFilename();
|
@@ -2911,7 +3212,6 @@ const checkNode = (context, node, ruleName, messageId) => {
|
|
2911
3212
|
return isSameName && !isSamePath;
|
2912
3213
|
});
|
2913
3214
|
if (conflictingDocuments.length > 0) {
|
2914
|
-
const { start, end } = node.name.loc;
|
2915
3215
|
context.report({
|
2916
3216
|
messageId,
|
2917
3217
|
data: {
|
@@ -2920,20 +3220,11 @@ const checkNode = (context, node, ruleName, messageId) => {
|
|
2920
3220
|
.map(f => `\t${relative(process.cwd(), getOnDiskFilepath(f.filePath))}`)
|
2921
3221
|
.join('\n'),
|
2922
3222
|
},
|
2923
|
-
loc:
|
2924
|
-
start: {
|
2925
|
-
line: start.line,
|
2926
|
-
column: start.column - 1,
|
2927
|
-
},
|
2928
|
-
end: {
|
2929
|
-
line: end.line,
|
2930
|
-
column: end.column - 1,
|
2931
|
-
},
|
2932
|
-
},
|
3223
|
+
loc: getLocation(node.name.loc, documentName),
|
2933
3224
|
});
|
2934
3225
|
}
|
2935
3226
|
};
|
2936
|
-
const rule$
|
3227
|
+
const rule$n = {
|
2937
3228
|
meta: {
|
2938
3229
|
type: 'suggestion',
|
2939
3230
|
docs: {
|
@@ -2979,6 +3270,7 @@ const rule$m = {
|
|
2979
3270
|
messages: {
|
2980
3271
|
[UNIQUE_FRAGMENT_NAME]: 'Fragment named "{{ documentName }}" already defined in:\n{{ summary }}',
|
2981
3272
|
},
|
3273
|
+
schema: [],
|
2982
3274
|
},
|
2983
3275
|
create(context) {
|
2984
3276
|
return {
|
@@ -2991,7 +3283,7 @@ const rule$m = {
|
|
2991
3283
|
|
2992
3284
|
const RULE_NAME$4 = 'unique-operation-name';
|
2993
3285
|
const UNIQUE_OPERATION_NAME = 'UNIQUE_OPERATION_NAME';
|
2994
|
-
const rule$
|
3286
|
+
const rule$o = {
|
2995
3287
|
meta: {
|
2996
3288
|
type: 'suggestion',
|
2997
3289
|
docs: {
|
@@ -3041,10 +3333,11 @@ const rule$n = {
|
|
3041
3333
|
messages: {
|
3042
3334
|
[UNIQUE_OPERATION_NAME]: 'Operation named "{{ documentName }}" already defined in:\n{{ summary }}',
|
3043
3335
|
},
|
3336
|
+
schema: [],
|
3044
3337
|
},
|
3045
3338
|
create(context) {
|
3046
3339
|
return {
|
3047
|
-
OperationDefinition(node) {
|
3340
|
+
'OperationDefinition[name!=undefined]'(node) {
|
3048
3341
|
checkNode(context, node, RULE_NAME$4, UNIQUE_OPERATION_NAME);
|
3049
3342
|
},
|
3050
3343
|
};
|
@@ -3056,30 +3349,31 @@ const rule$n = {
|
|
3056
3349
|
*/
|
3057
3350
|
const rules = {
|
3058
3351
|
...GRAPHQL_JS_VALIDATIONS,
|
3059
|
-
|
3060
|
-
'avoid-
|
3061
|
-
'avoid-
|
3062
|
-
'avoid-
|
3063
|
-
'
|
3064
|
-
'
|
3065
|
-
'
|
3066
|
-
'
|
3067
|
-
'
|
3068
|
-
'no-
|
3069
|
-
'no-
|
3070
|
-
'no-
|
3071
|
-
'no-
|
3072
|
-
'no-
|
3073
|
-
'no-
|
3074
|
-
'
|
3075
|
-
'require-deprecation-
|
3076
|
-
'require-
|
3077
|
-
'require-
|
3078
|
-
'require-
|
3079
|
-
'
|
3080
|
-
'
|
3081
|
-
'
|
3082
|
-
'unique-
|
3352
|
+
alphabetize: rule,
|
3353
|
+
'avoid-duplicate-fields': rule$1,
|
3354
|
+
'avoid-operation-name-prefix': rule$2,
|
3355
|
+
'avoid-scalar-result-type-on-mutation': rule$3,
|
3356
|
+
'avoid-typename-prefix': rule$4,
|
3357
|
+
'description-style': rule$5,
|
3358
|
+
'input-name': rule$6,
|
3359
|
+
'match-document-filename': rule$7,
|
3360
|
+
'naming-convention': rule$8,
|
3361
|
+
'no-anonymous-operations': rule$9,
|
3362
|
+
'no-case-insensitive-enum-values-duplicates': rule$a,
|
3363
|
+
'no-deprecated': rule$b,
|
3364
|
+
'no-hashtag-description': rule$c,
|
3365
|
+
'no-operation-name-suffix': rule$d,
|
3366
|
+
'no-unreachable-types': rule$e,
|
3367
|
+
'no-unused-fields': rule$f,
|
3368
|
+
'require-deprecation-date': rule$g,
|
3369
|
+
'require-deprecation-reason': rule$h,
|
3370
|
+
'require-description': rule$i,
|
3371
|
+
'require-field-of-type-query-in-mutation-result': rule$j,
|
3372
|
+
'require-id-when-available': rule$k,
|
3373
|
+
'selection-set-depth': rule$l,
|
3374
|
+
'strict-id-in-types': rule$m,
|
3375
|
+
'unique-fragment-name': rule$n,
|
3376
|
+
'unique-operation-name': rule$o,
|
3083
3377
|
};
|
3084
3378
|
|
3085
3379
|
const RELEVANT_KEYWORDS = ['gql', 'graphql', '/* GraphQL */'];
|
@@ -3473,22 +3767,92 @@ function parseForESLint(code, options = {}) {
|
|
3473
3767
|
}
|
3474
3768
|
}
|
3475
3769
|
|
3476
|
-
class GraphQLRuleTester extends
|
3770
|
+
class GraphQLRuleTester extends RuleTester {
|
3477
3771
|
constructor(parserOptions = {}) {
|
3478
|
-
|
3772
|
+
const config = {
|
3479
3773
|
parser: require.resolve('@graphql-eslint/eslint-plugin'),
|
3480
3774
|
parserOptions: {
|
3481
3775
|
...parserOptions,
|
3482
3776
|
skipGraphQLConfig: true,
|
3483
3777
|
},
|
3484
|
-
}
|
3778
|
+
};
|
3779
|
+
super(config);
|
3780
|
+
this.config = config;
|
3485
3781
|
}
|
3486
3782
|
fromMockFile(path) {
|
3487
3783
|
return readFileSync(resolve(__dirname, `../tests/mocks/${path}`), 'utf-8');
|
3488
3784
|
}
|
3489
3785
|
runGraphQLTests(name, rule, tests) {
|
3490
3786
|
super.run(name, rule, tests);
|
3787
|
+
// Skip snapshot testing if `expect` variable is not defined
|
3788
|
+
if (typeof expect === 'undefined') {
|
3789
|
+
return;
|
3790
|
+
}
|
3791
|
+
const linter = new Linter();
|
3792
|
+
linter.defineRule(name, rule);
|
3793
|
+
for (const testCase of tests.invalid) {
|
3794
|
+
const verifyConfig = getVerifyConfig(name, this.config, testCase);
|
3795
|
+
defineParser(linter, verifyConfig.parser);
|
3796
|
+
const { code, filename } = testCase;
|
3797
|
+
const messages = linter.verify(code, verifyConfig, { filename });
|
3798
|
+
for (const message of messages) {
|
3799
|
+
if (message.fatal) {
|
3800
|
+
throw new Error(message.message);
|
3801
|
+
}
|
3802
|
+
const messageForSnapshot = visualizeEslintMessage(code, message);
|
3803
|
+
// eslint-disable-next-line no-undef
|
3804
|
+
expect(messageForSnapshot).toMatchSnapshot();
|
3805
|
+
}
|
3806
|
+
}
|
3491
3807
|
}
|
3492
3808
|
}
|
3809
|
+
function getVerifyConfig(ruleId, testerConfig, testCase) {
|
3810
|
+
const { options, parserOptions, parser = testerConfig.parser } = testCase;
|
3811
|
+
return {
|
3812
|
+
...testerConfig,
|
3813
|
+
parser,
|
3814
|
+
parserOptions: {
|
3815
|
+
...testerConfig.parserOptions,
|
3816
|
+
...parserOptions,
|
3817
|
+
},
|
3818
|
+
rules: {
|
3819
|
+
[ruleId]: ['error', ...(Array.isArray(options) ? options : [])],
|
3820
|
+
},
|
3821
|
+
};
|
3822
|
+
}
|
3823
|
+
const parsers = new WeakMap();
|
3824
|
+
function defineParser(linter, parser) {
|
3825
|
+
if (!parser) {
|
3826
|
+
return;
|
3827
|
+
}
|
3828
|
+
if (!parsers.has(linter)) {
|
3829
|
+
parsers.set(linter, new Set());
|
3830
|
+
}
|
3831
|
+
const defined = parsers.get(linter);
|
3832
|
+
if (!defined.has(parser)) {
|
3833
|
+
defined.add(parser);
|
3834
|
+
linter.defineParser(parser, require(parser));
|
3835
|
+
}
|
3836
|
+
}
|
3837
|
+
function visualizeEslintMessage(text, result) {
|
3838
|
+
const { line, column, endLine, endColumn, message } = result;
|
3839
|
+
const location = {
|
3840
|
+
start: {
|
3841
|
+
line,
|
3842
|
+
column,
|
3843
|
+
},
|
3844
|
+
};
|
3845
|
+
if (typeof endLine === 'number' && typeof endColumn === 'number') {
|
3846
|
+
location.end = {
|
3847
|
+
line: endLine,
|
3848
|
+
column: endColumn,
|
3849
|
+
};
|
3850
|
+
}
|
3851
|
+
return codeFrameColumns(text, location, {
|
3852
|
+
linesAbove: Number.POSITIVE_INFINITY,
|
3853
|
+
linesBelow: Number.POSITIVE_INFINITY,
|
3854
|
+
message,
|
3855
|
+
});
|
3856
|
+
}
|
3493
3857
|
|
3494
3858
|
export { GraphQLRuleTester, configs, convertDescription, convertLocation, convertRange, convertToESTree, extractCommentsFromAst, getBaseType, isNodeWithDescription, parse, parseForESLint, processors, rules, valueFromNode };
|