@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.js
CHANGED
@@ -15,6 +15,8 @@ const depthLimit = _interopDefault(require('graphql-depth-limit'));
|
|
15
15
|
const graphqlTagPluck = require('@graphql-tools/graphql-tag-pluck');
|
16
16
|
const graphqlConfig$1 = require('graphql-config');
|
17
17
|
const codeFileLoader = require('@graphql-tools/code-file-loader');
|
18
|
+
const eslint = require('eslint');
|
19
|
+
const codeFrame = require('@babel/code-frame');
|
18
20
|
|
19
21
|
/*
|
20
22
|
* 🚨 IMPORTANT! Do not manually modify this file. Run: `yarn generate-configs`
|
@@ -71,6 +73,16 @@ const allConfig = {
|
|
71
73
|
...recommendedConfig,
|
72
74
|
rules: {
|
73
75
|
...recommendedConfig.rules,
|
76
|
+
'@graphql-eslint/alphabetize': [
|
77
|
+
'error',
|
78
|
+
{
|
79
|
+
fields: ['ObjectTypeDefinition', 'InterfaceTypeDefinition', 'InputObjectTypeDefinition'],
|
80
|
+
values: ['EnumTypeDefinition'],
|
81
|
+
selections: ['OperationDefinition', 'FragmentDefinition'],
|
82
|
+
variables: ['OperationDefinition'],
|
83
|
+
arguments: ['FieldDefinition', 'Field', 'DirectiveDefinition', 'Directive'],
|
84
|
+
},
|
85
|
+
],
|
74
86
|
'@graphql-eslint/avoid-duplicate-fields': 'error',
|
75
87
|
'@graphql-eslint/avoid-operation-name-prefix': 'error',
|
76
88
|
'@graphql-eslint/avoid-scalar-result-type-on-mutation': 'error',
|
@@ -232,6 +244,24 @@ const convertCase = (style, str) => {
|
|
232
244
|
return lowerCase(str).replace(/ /g, '-');
|
233
245
|
}
|
234
246
|
};
|
247
|
+
function getLocation(loc, fieldName = '', offset) {
|
248
|
+
const { start } = loc;
|
249
|
+
/*
|
250
|
+
* ESLint has 0-based column number
|
251
|
+
* https://eslint.org/docs/developer-guide/working-with-rules#contextreport
|
252
|
+
*/
|
253
|
+
const { offsetStart = 1, offsetEnd = 1 } = offset !== null && offset !== void 0 ? offset : {};
|
254
|
+
return {
|
255
|
+
start: {
|
256
|
+
line: start.line,
|
257
|
+
column: start.column - offsetStart,
|
258
|
+
},
|
259
|
+
end: {
|
260
|
+
line: start.line,
|
261
|
+
column: start.column - offsetEnd + fieldName.length,
|
262
|
+
},
|
263
|
+
};
|
264
|
+
}
|
235
265
|
|
236
266
|
function extractRuleName(stack) {
|
237
267
|
const match = (stack || '').match(/validation[/\\\\]rules[/\\\\](.*?)\.js:/) || [];
|
@@ -282,6 +312,7 @@ const validationToRule = (name, ruleName, docs, getDocumentNode) => {
|
|
282
312
|
meta: {
|
283
313
|
docs: {
|
284
314
|
...docs,
|
315
|
+
graphQLJSRuleName: ruleName,
|
285
316
|
category: 'Validation',
|
286
317
|
recommended: true,
|
287
318
|
requiresSchema,
|
@@ -405,7 +436,7 @@ const GRAPHQL_JS_VALIDATIONS = Object.assign({}, validationToRule('executable-de
|
|
405
436
|
description: `A GraphQL fragment is only valid when it does not have cycles in fragments usage.`,
|
406
437
|
}), validationToRule('no-undefined-variables', 'NoUndefinedVariables', {
|
407
438
|
description: `A GraphQL operation is only valid if all variables encountered, both directly and via fragment spreads, are defined by that operation.`,
|
408
|
-
}), validationToRule('no-unused-fragments', 'NoUnusedFragments', {
|
439
|
+
}, importFiles), validationToRule('no-unused-fragments', 'NoUnusedFragments', {
|
409
440
|
description: `A GraphQL document is only valid if all fragment definitions are spread within operations, or spread within other fragments spread within operations.`,
|
410
441
|
requiresSiblings: true,
|
411
442
|
}, context => {
|
@@ -481,6 +512,237 @@ const GRAPHQL_JS_VALIDATIONS = Object.assign({}, validationToRule('executable-de
|
|
481
512
|
description: `Variables passed to field arguments conform to type.`,
|
482
513
|
}));
|
483
514
|
|
515
|
+
const ALPHABETIZE = 'ALPHABETIZE';
|
516
|
+
const fieldsEnum = [graphql.Kind.OBJECT_TYPE_DEFINITION, graphql.Kind.INTERFACE_TYPE_DEFINITION, graphql.Kind.INPUT_OBJECT_TYPE_DEFINITION];
|
517
|
+
const valuesEnum = [graphql.Kind.ENUM_TYPE_DEFINITION];
|
518
|
+
const selectionsEnum = [graphql.Kind.OPERATION_DEFINITION, graphql.Kind.FRAGMENT_DEFINITION];
|
519
|
+
const variablesEnum = [graphql.Kind.OPERATION_DEFINITION];
|
520
|
+
const argumentsEnum = [graphql.Kind.FIELD_DEFINITION, graphql.Kind.FIELD, graphql.Kind.DIRECTIVE_DEFINITION, graphql.Kind.DIRECTIVE];
|
521
|
+
const rule = {
|
522
|
+
meta: {
|
523
|
+
type: 'suggestion',
|
524
|
+
docs: {
|
525
|
+
category: 'Best Practices',
|
526
|
+
description: 'Enforce arrange in alphabetical order for type fields, enum values, input object fields, operation selections and more.',
|
527
|
+
url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/alphabetize.md',
|
528
|
+
examples: [
|
529
|
+
{
|
530
|
+
title: 'Incorrect',
|
531
|
+
usage: [{ fields: [graphql.Kind.OBJECT_TYPE_DEFINITION] }],
|
532
|
+
code: /* GraphQL */ `
|
533
|
+
type User {
|
534
|
+
password: String
|
535
|
+
firstName: String! # should be before "password"
|
536
|
+
age: Int # should be before "firstName"
|
537
|
+
lastName: String!
|
538
|
+
}
|
539
|
+
`,
|
540
|
+
},
|
541
|
+
{
|
542
|
+
title: 'Correct',
|
543
|
+
usage: [{ fields: [graphql.Kind.OBJECT_TYPE_DEFINITION] }],
|
544
|
+
code: /* GraphQL */ `
|
545
|
+
type User {
|
546
|
+
age: Int
|
547
|
+
firstName: String!
|
548
|
+
lastName: String!
|
549
|
+
password: String
|
550
|
+
}
|
551
|
+
`,
|
552
|
+
},
|
553
|
+
{
|
554
|
+
title: 'Incorrect',
|
555
|
+
usage: [{ values: [graphql.Kind.ENUM_TYPE_DEFINITION] }],
|
556
|
+
code: /* GraphQL */ `
|
557
|
+
enum Role {
|
558
|
+
SUPER_ADMIN
|
559
|
+
ADMIN # should be before "SUPER_ADMIN"
|
560
|
+
USER
|
561
|
+
GOD # should be before "USER"
|
562
|
+
}
|
563
|
+
`,
|
564
|
+
},
|
565
|
+
{
|
566
|
+
title: 'Correct',
|
567
|
+
usage: [{ values: [graphql.Kind.ENUM_TYPE_DEFINITION] }],
|
568
|
+
code: /* GraphQL */ `
|
569
|
+
enum Role {
|
570
|
+
ADMIN
|
571
|
+
GOD
|
572
|
+
SUPER_ADMIN
|
573
|
+
USER
|
574
|
+
}
|
575
|
+
`,
|
576
|
+
},
|
577
|
+
{
|
578
|
+
title: 'Incorrect',
|
579
|
+
usage: [{ selections: [graphql.Kind.OPERATION_DEFINITION] }],
|
580
|
+
code: /* GraphQL */ `
|
581
|
+
query {
|
582
|
+
me {
|
583
|
+
firstName
|
584
|
+
lastName
|
585
|
+
email # should be before "lastName"
|
586
|
+
}
|
587
|
+
}
|
588
|
+
`,
|
589
|
+
},
|
590
|
+
{
|
591
|
+
title: 'Correct',
|
592
|
+
usage: [{ selections: [graphql.Kind.OPERATION_DEFINITION] }],
|
593
|
+
code: /* GraphQL */ `
|
594
|
+
query {
|
595
|
+
me {
|
596
|
+
email
|
597
|
+
firstName
|
598
|
+
lastName
|
599
|
+
}
|
600
|
+
}
|
601
|
+
`,
|
602
|
+
},
|
603
|
+
],
|
604
|
+
optionsForConfig: [
|
605
|
+
{
|
606
|
+
fields: fieldsEnum,
|
607
|
+
values: valuesEnum,
|
608
|
+
selections: selectionsEnum,
|
609
|
+
variables: variablesEnum,
|
610
|
+
arguments: argumentsEnum,
|
611
|
+
},
|
612
|
+
],
|
613
|
+
},
|
614
|
+
messages: {
|
615
|
+
[ALPHABETIZE]: '"{{ currName }}" should be before "{{ prevName }}".',
|
616
|
+
},
|
617
|
+
schema: {
|
618
|
+
type: 'array',
|
619
|
+
minItems: 1,
|
620
|
+
maxItems: 1,
|
621
|
+
items: {
|
622
|
+
type: 'object',
|
623
|
+
additionalProperties: false,
|
624
|
+
minProperties: 1,
|
625
|
+
properties: {
|
626
|
+
fields: {
|
627
|
+
type: 'array',
|
628
|
+
contains: {
|
629
|
+
enum: fieldsEnum,
|
630
|
+
},
|
631
|
+
description: 'Fields of `type`, `interface`, and `input`.',
|
632
|
+
},
|
633
|
+
values: {
|
634
|
+
type: 'array',
|
635
|
+
contains: {
|
636
|
+
enum: valuesEnum,
|
637
|
+
},
|
638
|
+
description: 'Values of `enum`.',
|
639
|
+
},
|
640
|
+
selections: {
|
641
|
+
type: 'array',
|
642
|
+
contains: {
|
643
|
+
enum: selectionsEnum,
|
644
|
+
},
|
645
|
+
description: 'Selections of operations (`query`, `mutation` and `subscription`) and `fragment`.',
|
646
|
+
},
|
647
|
+
variables: {
|
648
|
+
type: 'array',
|
649
|
+
contains: {
|
650
|
+
enum: variablesEnum,
|
651
|
+
},
|
652
|
+
description: 'Variables of operations (`query`, `mutation` and `subscription`).',
|
653
|
+
},
|
654
|
+
arguments: {
|
655
|
+
type: 'array',
|
656
|
+
contains: {
|
657
|
+
enum: argumentsEnum,
|
658
|
+
},
|
659
|
+
description: 'Arguments of fields and directives.',
|
660
|
+
},
|
661
|
+
},
|
662
|
+
},
|
663
|
+
},
|
664
|
+
},
|
665
|
+
create(context) {
|
666
|
+
var _a, _b, _c, _d, _e;
|
667
|
+
function checkNodes(nodes) {
|
668
|
+
let prevName = null;
|
669
|
+
for (const node of nodes) {
|
670
|
+
const currName = node.name.value;
|
671
|
+
if (prevName && prevName > currName) {
|
672
|
+
const { start, end } = node.name.loc;
|
673
|
+
const isVariableNode = node.kind === graphql.Kind.VARIABLE;
|
674
|
+
context.report({
|
675
|
+
loc: {
|
676
|
+
start: {
|
677
|
+
line: start.line,
|
678
|
+
column: start.column - (isVariableNode ? 2 : 1),
|
679
|
+
},
|
680
|
+
end,
|
681
|
+
},
|
682
|
+
messageId: ALPHABETIZE,
|
683
|
+
data: isVariableNode
|
684
|
+
? {
|
685
|
+
currName: `$${currName}`,
|
686
|
+
prevName: `$${prevName}`,
|
687
|
+
}
|
688
|
+
: { currName, prevName },
|
689
|
+
});
|
690
|
+
}
|
691
|
+
prevName = currName;
|
692
|
+
}
|
693
|
+
}
|
694
|
+
const opts = context.options[0];
|
695
|
+
const fields = new Set((_a = opts.fields) !== null && _a !== void 0 ? _a : []);
|
696
|
+
const listeners = {};
|
697
|
+
const fieldsSelector = [
|
698
|
+
fields.has(graphql.Kind.OBJECT_TYPE_DEFINITION) && [graphql.Kind.OBJECT_TYPE_DEFINITION, graphql.Kind.OBJECT_TYPE_EXTENSION],
|
699
|
+
fields.has(graphql.Kind.INTERFACE_TYPE_DEFINITION) && [graphql.Kind.INTERFACE_TYPE_DEFINITION, graphql.Kind.INTERFACE_TYPE_EXTENSION],
|
700
|
+
fields.has(graphql.Kind.INPUT_OBJECT_TYPE_DEFINITION) && [
|
701
|
+
graphql.Kind.INPUT_OBJECT_TYPE_DEFINITION,
|
702
|
+
graphql.Kind.INPUT_OBJECT_TYPE_EXTENSION,
|
703
|
+
],
|
704
|
+
]
|
705
|
+
.flat()
|
706
|
+
.join(',');
|
707
|
+
const hasEnumValues = ((_b = opts.values) === null || _b === void 0 ? void 0 : _b[0]) === graphql.Kind.ENUM_TYPE_DEFINITION;
|
708
|
+
const selectionsSelector = (_c = opts.selections) === null || _c === void 0 ? void 0 : _c.join(',');
|
709
|
+
const hasVariables = ((_d = opts.variables) === null || _d === void 0 ? void 0 : _d[0]) === graphql.Kind.OPERATION_DEFINITION;
|
710
|
+
const argumentsSelector = (_e = opts.arguments) === null || _e === void 0 ? void 0 : _e.join(',');
|
711
|
+
if (fieldsSelector) {
|
712
|
+
listeners[fieldsSelector] = (node) => {
|
713
|
+
checkNodes(node.fields);
|
714
|
+
};
|
715
|
+
}
|
716
|
+
if (hasEnumValues) {
|
717
|
+
const enumValuesSelector = [graphql.Kind.ENUM_TYPE_DEFINITION, graphql.Kind.ENUM_TYPE_EXTENSION].join(',');
|
718
|
+
listeners[enumValuesSelector] = (node) => {
|
719
|
+
checkNodes(node.values);
|
720
|
+
};
|
721
|
+
}
|
722
|
+
if (selectionsSelector) {
|
723
|
+
listeners[`:matches(${selectionsSelector}) SelectionSet`] = (node) => {
|
724
|
+
checkNodes(node.selections
|
725
|
+
// inline fragment don't have name, so we skip them
|
726
|
+
.filter(selection => selection.kind !== graphql.Kind.INLINE_FRAGMENT)
|
727
|
+
.map(selection =>
|
728
|
+
// sort by alias is field is renamed
|
729
|
+
'alias' in selection && selection.alias ? { name: selection.alias } : selection));
|
730
|
+
};
|
731
|
+
}
|
732
|
+
if (hasVariables) {
|
733
|
+
listeners.OperationDefinition = (node) => {
|
734
|
+
checkNodes(node.variableDefinitions.map(varDef => varDef.variable));
|
735
|
+
};
|
736
|
+
}
|
737
|
+
if (argumentsSelector) {
|
738
|
+
listeners[argumentsSelector] = (node) => {
|
739
|
+
checkNodes(node.arguments);
|
740
|
+
};
|
741
|
+
}
|
742
|
+
return listeners;
|
743
|
+
},
|
744
|
+
};
|
745
|
+
|
484
746
|
const AVOID_DUPLICATE_FIELDS = 'AVOID_DUPLICATE_FIELDS';
|
485
747
|
const ensureUnique = () => {
|
486
748
|
const set = new Set();
|
@@ -495,7 +757,7 @@ const ensureUnique = () => {
|
|
495
757
|
},
|
496
758
|
};
|
497
759
|
};
|
498
|
-
const rule = {
|
760
|
+
const rule$1 = {
|
499
761
|
meta: {
|
500
762
|
type: 'suggestion',
|
501
763
|
docs: {
|
@@ -546,6 +808,7 @@ const rule = {
|
|
546
808
|
messages: {
|
547
809
|
[AVOID_DUPLICATE_FIELDS]: `{{ type }} "{{ fieldName }}" defined multiple times.`,
|
548
810
|
},
|
811
|
+
schema: [],
|
549
812
|
},
|
550
813
|
create(context) {
|
551
814
|
return {
|
@@ -603,7 +866,7 @@ const rule = {
|
|
603
866
|
};
|
604
867
|
|
605
868
|
const AVOID_OPERATION_NAME_PREFIX = 'AVOID_OPERATION_NAME_PREFIX';
|
606
|
-
const rule$
|
869
|
+
const rule$2 = {
|
607
870
|
meta: {
|
608
871
|
type: 'suggestion',
|
609
872
|
docs: {
|
@@ -665,15 +928,16 @@ const rule$1 = {
|
|
665
928
|
const testKeyword = caseSensitive ? keyword : keyword.toLowerCase();
|
666
929
|
const testName = caseSensitive ? node.name.value : node.name.value.toLowerCase();
|
667
930
|
if (testName.startsWith(testKeyword)) {
|
931
|
+
const { start } = node.name.loc;
|
668
932
|
context.report({
|
669
933
|
loc: {
|
670
934
|
start: {
|
671
|
-
line:
|
672
|
-
column:
|
935
|
+
line: start.line,
|
936
|
+
column: start.column - 1,
|
673
937
|
},
|
674
938
|
end: {
|
675
|
-
line:
|
676
|
-
column:
|
939
|
+
line: start.line,
|
940
|
+
column: start.column - 1 + testKeyword.length,
|
677
941
|
},
|
678
942
|
},
|
679
943
|
data: {
|
@@ -689,7 +953,7 @@ const rule$1 = {
|
|
689
953
|
},
|
690
954
|
};
|
691
955
|
|
692
|
-
const rule$
|
956
|
+
const rule$3 = {
|
693
957
|
meta: {
|
694
958
|
type: 'suggestion',
|
695
959
|
docs: {
|
@@ -716,6 +980,7 @@ const rule$2 = {
|
|
716
980
|
},
|
717
981
|
],
|
718
982
|
},
|
983
|
+
schema: [],
|
719
984
|
},
|
720
985
|
create(context) {
|
721
986
|
const schema = requireGraphQLSchemaFromContext('avoid-scalar-result-type-on-mutation', context);
|
@@ -741,7 +1006,7 @@ const rule$2 = {
|
|
741
1006
|
};
|
742
1007
|
|
743
1008
|
const AVOID_TYPENAME_PREFIX = 'AVOID_TYPENAME_PREFIX';
|
744
|
-
const rule$
|
1009
|
+
const rule$4 = {
|
745
1010
|
meta: {
|
746
1011
|
type: 'suggestion',
|
747
1012
|
docs: {
|
@@ -771,22 +1036,33 @@ const rule$3 = {
|
|
771
1036
|
messages: {
|
772
1037
|
[AVOID_TYPENAME_PREFIX]: `Field "{{ fieldName }}" starts with the name of the parent type "{{ typeName }}"`,
|
773
1038
|
},
|
1039
|
+
schema: [],
|
774
1040
|
},
|
775
1041
|
create(context) {
|
776
1042
|
return {
|
777
1043
|
'ObjectTypeDefinition, ObjectTypeExtension, InterfaceTypeDefinition, InterfaceTypeExtension'(node) {
|
778
1044
|
const typeName = node.name.value;
|
779
|
-
const lowerTypeName =
|
1045
|
+
const lowerTypeName = typeName.toLowerCase();
|
780
1046
|
for (const field of node.fields) {
|
781
|
-
const fieldName = field.name.value
|
782
|
-
if (fieldName
|
1047
|
+
const fieldName = field.name.value;
|
1048
|
+
if (fieldName.toLowerCase().startsWith(lowerTypeName)) {
|
1049
|
+
const { start } = field.loc;
|
783
1050
|
context.report({
|
784
|
-
node: field.name,
|
785
1051
|
data: {
|
786
1052
|
fieldName,
|
787
1053
|
typeName,
|
788
1054
|
},
|
789
1055
|
messageId: AVOID_TYPENAME_PREFIX,
|
1056
|
+
loc: {
|
1057
|
+
start: {
|
1058
|
+
line: start.line,
|
1059
|
+
column: start.column - 1,
|
1060
|
+
},
|
1061
|
+
end: {
|
1062
|
+
line: start.line,
|
1063
|
+
column: start.column - 1 + lowerTypeName.length,
|
1064
|
+
},
|
1065
|
+
},
|
790
1066
|
});
|
791
1067
|
}
|
792
1068
|
}
|
@@ -795,7 +1071,7 @@ const rule$3 = {
|
|
795
1071
|
},
|
796
1072
|
};
|
797
1073
|
|
798
|
-
const rule$
|
1074
|
+
const rule$5 = {
|
799
1075
|
meta: {
|
800
1076
|
type: 'suggestion',
|
801
1077
|
docs: {
|
@@ -806,9 +1082,9 @@ const rule$4 = {
|
|
806
1082
|
code: /* GraphQL */ `
|
807
1083
|
""" Description """
|
808
1084
|
type someTypeName {
|
809
|
-
|
1085
|
+
# ...
|
810
1086
|
}
|
811
|
-
|
1087
|
+
`,
|
812
1088
|
},
|
813
1089
|
{
|
814
1090
|
title: 'Correct',
|
@@ -816,9 +1092,9 @@ const rule$4 = {
|
|
816
1092
|
code: /* GraphQL */ `
|
817
1093
|
" Description "
|
818
1094
|
type someTypeName {
|
819
|
-
|
1095
|
+
# ...
|
820
1096
|
}
|
821
|
-
|
1097
|
+
`,
|
822
1098
|
},
|
823
1099
|
],
|
824
1100
|
description: 'Require all comments to follow the same style (either block or inline).',
|
@@ -855,7 +1131,7 @@ const rule$4 = {
|
|
855
1131
|
},
|
856
1132
|
};
|
857
1133
|
|
858
|
-
const rule$
|
1134
|
+
const rule$6 = {
|
859
1135
|
meta: {
|
860
1136
|
type: 'suggestion',
|
861
1137
|
docs: {
|
@@ -980,7 +1256,7 @@ const CASE_STYLES = [
|
|
980
1256
|
const schemaOption = {
|
981
1257
|
oneOf: [{ $ref: '#/definitions/asString' }, { $ref: '#/definitions/asObject' }],
|
982
1258
|
};
|
983
|
-
const rule$
|
1259
|
+
const rule$7 = {
|
984
1260
|
meta: {
|
985
1261
|
type: 'suggestion',
|
986
1262
|
docs: {
|
@@ -1114,7 +1390,8 @@ const rule$6 = {
|
|
1114
1390
|
var _a;
|
1115
1391
|
if (options.fileExtension && options.fileExtension !== fileExtension) {
|
1116
1392
|
context.report({
|
1117
|
-
|
1393
|
+
// Report on first character
|
1394
|
+
loc: { column: 0, line: 1 },
|
1118
1395
|
messageId: MATCH_EXTENSION,
|
1119
1396
|
data: {
|
1120
1397
|
fileExtension,
|
@@ -1146,7 +1423,8 @@ const rule$6 = {
|
|
1146
1423
|
const filenameWithExtension = filename + expectedExtension;
|
1147
1424
|
if (expectedFilename !== filenameWithExtension) {
|
1148
1425
|
context.report({
|
1149
|
-
|
1426
|
+
// Report on first character
|
1427
|
+
loc: { column: 0, line: 1 },
|
1150
1428
|
messageId: MATCH_STYLE,
|
1151
1429
|
data: {
|
1152
1430
|
expectedFilename,
|
@@ -1225,7 +1503,7 @@ function checkNameFormat(params) {
|
|
1225
1503
|
const schemaOption$1 = {
|
1226
1504
|
oneOf: [{ $ref: '#/definitions/asString' }, { $ref: '#/definitions/asObject' }],
|
1227
1505
|
};
|
1228
|
-
const rule$
|
1506
|
+
const rule$8 = {
|
1229
1507
|
meta: {
|
1230
1508
|
type: 'suggestion',
|
1231
1509
|
docs: {
|
@@ -1337,18 +1615,18 @@ const rule$7 = {
|
|
1337
1615
|
style,
|
1338
1616
|
leadingUnderscore: options.leadingUnderscore,
|
1339
1617
|
trailingUnderscore: options.trailingUnderscore,
|
1340
|
-
prefix
|
1341
|
-
suffix
|
1342
|
-
forbiddenPrefixes
|
1343
|
-
forbiddenSuffixes
|
1618
|
+
prefix,
|
1619
|
+
suffix,
|
1620
|
+
forbiddenPrefixes,
|
1621
|
+
forbiddenSuffixes,
|
1344
1622
|
});
|
1345
1623
|
if (result.ok === false) {
|
1346
1624
|
context.report({
|
1347
1625
|
node,
|
1348
1626
|
message: result.errorMessage,
|
1349
1627
|
data: {
|
1350
|
-
prefix
|
1351
|
-
suffix
|
1628
|
+
prefix,
|
1629
|
+
suffix,
|
1352
1630
|
format: style,
|
1353
1631
|
forbiddenPrefixes: forbiddenPrefixes.join(', '),
|
1354
1632
|
forbiddenSuffixes: forbiddenSuffixes.join(', '),
|
@@ -1454,7 +1732,7 @@ const rule$7 = {
|
|
1454
1732
|
};
|
1455
1733
|
|
1456
1734
|
const NO_ANONYMOUS_OPERATIONS = 'NO_ANONYMOUS_OPERATIONS';
|
1457
|
-
const rule$
|
1735
|
+
const rule$9 = {
|
1458
1736
|
meta: {
|
1459
1737
|
type: 'suggestion',
|
1460
1738
|
docs: {
|
@@ -1484,20 +1762,24 @@ const rule$8 = {
|
|
1484
1762
|
messages: {
|
1485
1763
|
[NO_ANONYMOUS_OPERATIONS]: `Anonymous GraphQL operations are forbidden. Please make sure to name your {{ operation }}!`,
|
1486
1764
|
},
|
1765
|
+
schema: [],
|
1487
1766
|
},
|
1488
1767
|
create(context) {
|
1489
1768
|
return {
|
1490
1769
|
OperationDefinition(node) {
|
1491
|
-
|
1770
|
+
var _a;
|
1771
|
+
const isAnonymous = (((_a = node.name) === null || _a === void 0 ? void 0 : _a.value) || '').length === 0;
|
1772
|
+
if (isAnonymous) {
|
1773
|
+
const { start } = node.loc;
|
1492
1774
|
context.report({
|
1493
1775
|
loc: {
|
1494
1776
|
start: {
|
1495
|
-
column:
|
1496
|
-
line:
|
1777
|
+
column: start.column - 1,
|
1778
|
+
line: start.line,
|
1497
1779
|
},
|
1498
1780
|
end: {
|
1499
|
-
column:
|
1500
|
-
line:
|
1781
|
+
column: start.column - 1 + node.operation.length,
|
1782
|
+
line: start.line,
|
1501
1783
|
},
|
1502
1784
|
},
|
1503
1785
|
data: {
|
@@ -1512,7 +1794,7 @@ const rule$8 = {
|
|
1512
1794
|
};
|
1513
1795
|
|
1514
1796
|
const ERROR_MESSAGE_ID = 'NO_CASE_INSENSITIVE_ENUM_VALUES_DUPLICATES';
|
1515
|
-
const rule$
|
1797
|
+
const rule$a = {
|
1516
1798
|
meta: {
|
1517
1799
|
type: 'suggestion',
|
1518
1800
|
docs: {
|
@@ -1547,6 +1829,7 @@ const rule$9 = {
|
|
1547
1829
|
messages: {
|
1548
1830
|
[ERROR_MESSAGE_ID]: `Case-insensitive enum values duplicates are not allowed! Found: "{{ found }}"`,
|
1549
1831
|
},
|
1832
|
+
schema: [],
|
1550
1833
|
},
|
1551
1834
|
create(context) {
|
1552
1835
|
return {
|
@@ -1567,7 +1850,7 @@ const rule$9 = {
|
|
1567
1850
|
};
|
1568
1851
|
|
1569
1852
|
const NO_DEPRECATED = 'NO_DEPRECATED';
|
1570
|
-
const rule$
|
1853
|
+
const rule$b = {
|
1571
1854
|
meta: {
|
1572
1855
|
type: 'suggestion',
|
1573
1856
|
docs: {
|
@@ -1641,6 +1924,7 @@ const rule$a = {
|
|
1641
1924
|
messages: {
|
1642
1925
|
[NO_DEPRECATED]: `This {{ type }} is marked as deprecated in your GraphQL schema {{ reason }}`,
|
1643
1926
|
},
|
1927
|
+
schema: [],
|
1644
1928
|
},
|
1645
1929
|
create(context) {
|
1646
1930
|
return {
|
@@ -1681,7 +1965,7 @@ const rule$a = {
|
|
1681
1965
|
};
|
1682
1966
|
|
1683
1967
|
const HASHTAG_COMMENT = 'HASHTAG_COMMENT';
|
1684
|
-
const rule$
|
1968
|
+
const rule$c = {
|
1685
1969
|
meta: {
|
1686
1970
|
messages: {
|
1687
1971
|
[HASHTAG_COMMENT]: 'Using hashtag (#) for adding GraphQL descriptions is not allowed. Prefer using """ for multiline, or " for a single line description.',
|
@@ -1727,6 +2011,7 @@ const rule$b = {
|
|
1727
2011
|
],
|
1728
2012
|
},
|
1729
2013
|
type: 'suggestion',
|
2014
|
+
schema: [],
|
1730
2015
|
},
|
1731
2016
|
create(context) {
|
1732
2017
|
return {
|
@@ -1756,7 +2041,7 @@ const rule$b = {
|
|
1756
2041
|
};
|
1757
2042
|
|
1758
2043
|
const NO_OPERATION_NAME_SUFFIX = 'NO_OPERATION_NAME_SUFFIX';
|
1759
|
-
const rule$
|
2044
|
+
const rule$d = {
|
1760
2045
|
meta: {
|
1761
2046
|
fixable: 'code',
|
1762
2047
|
type: 'suggestion',
|
@@ -1787,15 +2072,28 @@ const rule$c = {
|
|
1787
2072
|
messages: {
|
1788
2073
|
[NO_OPERATION_NAME_SUFFIX]: `Unnecessary "{{ invalidSuffix }}" suffix in your operation name!`,
|
1789
2074
|
},
|
2075
|
+
schema: [],
|
1790
2076
|
},
|
1791
2077
|
create(context) {
|
1792
2078
|
return {
|
1793
2079
|
'OperationDefinition, FragmentDefinition'(node) {
|
1794
|
-
|
1795
|
-
|
1796
|
-
|
2080
|
+
var _a;
|
2081
|
+
const name = ((_a = node.name) === null || _a === void 0 ? void 0 : _a.value) || '';
|
2082
|
+
if (name.length > 0) {
|
2083
|
+
const invalidSuffix = 'operation' in node ? node.operation : 'fragment';
|
2084
|
+
if (name.toLowerCase().endsWith(invalidSuffix)) {
|
2085
|
+
const { start, end } = node.name.loc;
|
1797
2086
|
context.report({
|
1798
|
-
|
2087
|
+
loc: {
|
2088
|
+
start: {
|
2089
|
+
column: start.column - 1 + name.length - invalidSuffix.length,
|
2090
|
+
line: start.line,
|
2091
|
+
},
|
2092
|
+
end: {
|
2093
|
+
column: end.column - 1 + name.length,
|
2094
|
+
line: end.line,
|
2095
|
+
},
|
2096
|
+
},
|
1799
2097
|
data: {
|
1800
2098
|
invalidSuffix,
|
1801
2099
|
},
|
@@ -1811,7 +2109,7 @@ const rule$c = {
|
|
1811
2109
|
|
1812
2110
|
const UNREACHABLE_TYPE = 'UNREACHABLE_TYPE';
|
1813
2111
|
const RULE_NAME = 'no-unreachable-types';
|
1814
|
-
const rule$
|
2112
|
+
const rule$e = {
|
1815
2113
|
meta: {
|
1816
2114
|
messages: {
|
1817
2115
|
[UNREACHABLE_TYPE]: `Type "{{ typeName }}" is unreachable`,
|
@@ -1852,6 +2150,7 @@ const rule$d = {
|
|
1852
2150
|
},
|
1853
2151
|
fixable: 'code',
|
1854
2152
|
type: 'suggestion',
|
2153
|
+
schema: [],
|
1855
2154
|
},
|
1856
2155
|
create(context) {
|
1857
2156
|
const reachableTypes = requireReachableTypesFromContext(RULE_NAME, context);
|
@@ -1886,7 +2185,7 @@ const rule$d = {
|
|
1886
2185
|
|
1887
2186
|
const UNUSED_FIELD = 'UNUSED_FIELD';
|
1888
2187
|
const RULE_NAME$1 = 'no-unused-fields';
|
1889
|
-
const rule$
|
2188
|
+
const rule$f = {
|
1890
2189
|
meta: {
|
1891
2190
|
messages: {
|
1892
2191
|
[UNUSED_FIELD]: `Field "{{fieldName}}" is unused`,
|
@@ -1943,6 +2242,7 @@ const rule$e = {
|
|
1943
2242
|
},
|
1944
2243
|
fixable: 'code',
|
1945
2244
|
type: 'suggestion',
|
2245
|
+
schema: [],
|
1946
2246
|
},
|
1947
2247
|
create(context) {
|
1948
2248
|
const usedFields = requireUsedFieldsFromContext(RULE_NAME$1, context);
|
@@ -2073,7 +2373,7 @@ const MESSAGE_REQUIRE_DATE = 'MESSAGE_REQUIRE_DATE';
|
|
2073
2373
|
const MESSAGE_INVALID_FORMAT = 'MESSAGE_INVALID_FORMAT';
|
2074
2374
|
const MESSAGE_INVALID_DATE = 'MESSAGE_INVALID_DATE';
|
2075
2375
|
const MESSAGE_CAN_BE_REMOVED = 'MESSAGE_CAN_BE_REMOVED';
|
2076
|
-
const rule$
|
2376
|
+
const rule$g = {
|
2077
2377
|
meta: {
|
2078
2378
|
type: 'suggestion',
|
2079
2379
|
docs: {
|
@@ -2173,7 +2473,7 @@ const rule$f = {
|
|
2173
2473
|
},
|
2174
2474
|
};
|
2175
2475
|
|
2176
|
-
const rule$
|
2476
|
+
const rule$h = {
|
2177
2477
|
meta: {
|
2178
2478
|
docs: {
|
2179
2479
|
description: `Require all deprecation directives to specify a reason.`,
|
@@ -2208,6 +2508,7 @@ const rule$g = {
|
|
2208
2508
|
],
|
2209
2509
|
},
|
2210
2510
|
type: 'suggestion',
|
2511
|
+
schema: [],
|
2211
2512
|
},
|
2212
2513
|
create(context) {
|
2213
2514
|
return {
|
@@ -2244,15 +2545,18 @@ const DESCRIBABLE_NODES = [
|
|
2244
2545
|
function verifyRule(context, node) {
|
2245
2546
|
if (node) {
|
2246
2547
|
if (!node.description || !node.description.value || node.description.value.trim().length === 0) {
|
2548
|
+
const { start, end } = ('name' in node ? node.name : node).loc;
|
2247
2549
|
context.report({
|
2248
2550
|
loc: {
|
2249
2551
|
start: {
|
2250
|
-
line:
|
2251
|
-
column:
|
2552
|
+
line: start.line,
|
2553
|
+
column: start.column - 1,
|
2252
2554
|
},
|
2253
2555
|
end: {
|
2254
|
-
line:
|
2255
|
-
column:
|
2556
|
+
line: end.line,
|
2557
|
+
column:
|
2558
|
+
// node.name don't exist on SchemaDefinition
|
2559
|
+
'name' in node ? end.column - 1 + node.name.value.length : end.column,
|
2256
2560
|
},
|
2257
2561
|
},
|
2258
2562
|
messageId: REQUIRE_DESCRIPTION_ERROR,
|
@@ -2263,7 +2567,7 @@ function verifyRule(context, node) {
|
|
2263
2567
|
}
|
2264
2568
|
}
|
2265
2569
|
}
|
2266
|
-
const rule$
|
2570
|
+
const rule$i = {
|
2267
2571
|
meta: {
|
2268
2572
|
docs: {
|
2269
2573
|
category: 'Best Practices',
|
@@ -2328,7 +2632,7 @@ const rule$h = {
|
|
2328
2632
|
};
|
2329
2633
|
|
2330
2634
|
const RULE_NAME$2 = 'require-field-of-type-query-in-mutation-result';
|
2331
|
-
const rule$
|
2635
|
+
const rule$j = {
|
2332
2636
|
meta: {
|
2333
2637
|
type: 'suggestion',
|
2334
2638
|
docs: {
|
@@ -2366,6 +2670,7 @@ const rule$i = {
|
|
2366
2670
|
},
|
2367
2671
|
],
|
2368
2672
|
},
|
2673
|
+
schema: [],
|
2369
2674
|
},
|
2370
2675
|
create(context) {
|
2371
2676
|
const schema = requireGraphQLSchemaFromContext(RULE_NAME$2, context);
|
@@ -2491,7 +2796,7 @@ const convertNode = (typeInfo) => (node, key, parent) => {
|
|
2491
2796
|
|
2492
2797
|
const REQUIRE_ID_WHEN_AVAILABLE = 'REQUIRE_ID_WHEN_AVAILABLE';
|
2493
2798
|
const DEFAULT_ID_FIELD_NAME = 'id';
|
2494
|
-
const rule$
|
2799
|
+
const rule$k = {
|
2495
2800
|
meta: {
|
2496
2801
|
type: 'suggestion',
|
2497
2802
|
docs: {
|
@@ -2627,7 +2932,7 @@ const rule$j = {
|
|
2627
2932
|
},
|
2628
2933
|
};
|
2629
2934
|
|
2630
|
-
const rule$
|
2935
|
+
const rule$l = {
|
2631
2936
|
meta: {
|
2632
2937
|
docs: {
|
2633
2938
|
category: 'Best Practices',
|
@@ -2749,7 +3054,7 @@ const shouldIgnoreNode = ({ node, exceptions }) => {
|
|
2749
3054
|
}
|
2750
3055
|
return false;
|
2751
3056
|
};
|
2752
|
-
const rule$
|
3057
|
+
const rule$m = {
|
2753
3058
|
meta: {
|
2754
3059
|
type: 'suggestion',
|
2755
3060
|
docs: {
|
@@ -2902,11 +3207,7 @@ const rule$l = {
|
|
2902
3207
|
const RULE_NAME$3 = 'unique-fragment-name';
|
2903
3208
|
const UNIQUE_FRAGMENT_NAME = 'UNIQUE_FRAGMENT_NAME';
|
2904
3209
|
const checkNode = (context, node, ruleName, messageId) => {
|
2905
|
-
|
2906
|
-
const documentName = (_a = node.name) === null || _a === void 0 ? void 0 : _a.value;
|
2907
|
-
if (!documentName) {
|
2908
|
-
return;
|
2909
|
-
}
|
3210
|
+
const documentName = node.name.value;
|
2910
3211
|
const siblings = requireSiblingsOperations(ruleName, context);
|
2911
3212
|
const siblingDocuments = node.kind === graphql.Kind.FRAGMENT_DEFINITION ? siblings.getFragment(documentName) : siblings.getOperation(documentName);
|
2912
3213
|
const filepath = context.getFilename();
|
@@ -2917,7 +3218,6 @@ const checkNode = (context, node, ruleName, messageId) => {
|
|
2917
3218
|
return isSameName && !isSamePath;
|
2918
3219
|
});
|
2919
3220
|
if (conflictingDocuments.length > 0) {
|
2920
|
-
const { start, end } = node.name.loc;
|
2921
3221
|
context.report({
|
2922
3222
|
messageId,
|
2923
3223
|
data: {
|
@@ -2926,20 +3226,11 @@ const checkNode = (context, node, ruleName, messageId) => {
|
|
2926
3226
|
.map(f => `\t${path.relative(process.cwd(), getOnDiskFilepath(f.filePath))}`)
|
2927
3227
|
.join('\n'),
|
2928
3228
|
},
|
2929
|
-
loc:
|
2930
|
-
start: {
|
2931
|
-
line: start.line,
|
2932
|
-
column: start.column - 1,
|
2933
|
-
},
|
2934
|
-
end: {
|
2935
|
-
line: end.line,
|
2936
|
-
column: end.column - 1,
|
2937
|
-
},
|
2938
|
-
},
|
3229
|
+
loc: getLocation(node.name.loc, documentName),
|
2939
3230
|
});
|
2940
3231
|
}
|
2941
3232
|
};
|
2942
|
-
const rule$
|
3233
|
+
const rule$n = {
|
2943
3234
|
meta: {
|
2944
3235
|
type: 'suggestion',
|
2945
3236
|
docs: {
|
@@ -2985,6 +3276,7 @@ const rule$m = {
|
|
2985
3276
|
messages: {
|
2986
3277
|
[UNIQUE_FRAGMENT_NAME]: 'Fragment named "{{ documentName }}" already defined in:\n{{ summary }}',
|
2987
3278
|
},
|
3279
|
+
schema: [],
|
2988
3280
|
},
|
2989
3281
|
create(context) {
|
2990
3282
|
return {
|
@@ -2997,7 +3289,7 @@ const rule$m = {
|
|
2997
3289
|
|
2998
3290
|
const RULE_NAME$4 = 'unique-operation-name';
|
2999
3291
|
const UNIQUE_OPERATION_NAME = 'UNIQUE_OPERATION_NAME';
|
3000
|
-
const rule$
|
3292
|
+
const rule$o = {
|
3001
3293
|
meta: {
|
3002
3294
|
type: 'suggestion',
|
3003
3295
|
docs: {
|
@@ -3047,10 +3339,11 @@ const rule$n = {
|
|
3047
3339
|
messages: {
|
3048
3340
|
[UNIQUE_OPERATION_NAME]: 'Operation named "{{ documentName }}" already defined in:\n{{ summary }}',
|
3049
3341
|
},
|
3342
|
+
schema: [],
|
3050
3343
|
},
|
3051
3344
|
create(context) {
|
3052
3345
|
return {
|
3053
|
-
OperationDefinition(node) {
|
3346
|
+
'OperationDefinition[name!=undefined]'(node) {
|
3054
3347
|
checkNode(context, node, RULE_NAME$4, UNIQUE_OPERATION_NAME);
|
3055
3348
|
},
|
3056
3349
|
};
|
@@ -3062,30 +3355,31 @@ const rule$n = {
|
|
3062
3355
|
*/
|
3063
3356
|
const rules = {
|
3064
3357
|
...GRAPHQL_JS_VALIDATIONS,
|
3065
|
-
|
3066
|
-
'avoid-
|
3067
|
-
'avoid-
|
3068
|
-
'avoid-
|
3069
|
-
'
|
3070
|
-
'
|
3071
|
-
'
|
3072
|
-
'
|
3073
|
-
'
|
3074
|
-
'no-
|
3075
|
-
'no-
|
3076
|
-
'no-
|
3077
|
-
'no-
|
3078
|
-
'no-
|
3079
|
-
'no-
|
3080
|
-
'
|
3081
|
-
'require-deprecation-
|
3082
|
-
'require-
|
3083
|
-
'require-
|
3084
|
-
'require-
|
3085
|
-
'
|
3086
|
-
'
|
3087
|
-
'
|
3088
|
-
'unique-
|
3358
|
+
alphabetize: rule,
|
3359
|
+
'avoid-duplicate-fields': rule$1,
|
3360
|
+
'avoid-operation-name-prefix': rule$2,
|
3361
|
+
'avoid-scalar-result-type-on-mutation': rule$3,
|
3362
|
+
'avoid-typename-prefix': rule$4,
|
3363
|
+
'description-style': rule$5,
|
3364
|
+
'input-name': rule$6,
|
3365
|
+
'match-document-filename': rule$7,
|
3366
|
+
'naming-convention': rule$8,
|
3367
|
+
'no-anonymous-operations': rule$9,
|
3368
|
+
'no-case-insensitive-enum-values-duplicates': rule$a,
|
3369
|
+
'no-deprecated': rule$b,
|
3370
|
+
'no-hashtag-description': rule$c,
|
3371
|
+
'no-operation-name-suffix': rule$d,
|
3372
|
+
'no-unreachable-types': rule$e,
|
3373
|
+
'no-unused-fields': rule$f,
|
3374
|
+
'require-deprecation-date': rule$g,
|
3375
|
+
'require-deprecation-reason': rule$h,
|
3376
|
+
'require-description': rule$i,
|
3377
|
+
'require-field-of-type-query-in-mutation-result': rule$j,
|
3378
|
+
'require-id-when-available': rule$k,
|
3379
|
+
'selection-set-depth': rule$l,
|
3380
|
+
'strict-id-in-types': rule$m,
|
3381
|
+
'unique-fragment-name': rule$n,
|
3382
|
+
'unique-operation-name': rule$o,
|
3089
3383
|
};
|
3090
3384
|
|
3091
3385
|
const RELEVANT_KEYWORDS = ['gql', 'graphql', '/* GraphQL */'];
|
@@ -3479,23 +3773,93 @@ function parseForESLint(code, options = {}) {
|
|
3479
3773
|
}
|
3480
3774
|
}
|
3481
3775
|
|
3482
|
-
class GraphQLRuleTester extends
|
3776
|
+
class GraphQLRuleTester extends eslint.RuleTester {
|
3483
3777
|
constructor(parserOptions = {}) {
|
3484
|
-
|
3778
|
+
const config = {
|
3485
3779
|
parser: require.resolve('@graphql-eslint/eslint-plugin'),
|
3486
3780
|
parserOptions: {
|
3487
3781
|
...parserOptions,
|
3488
3782
|
skipGraphQLConfig: true,
|
3489
3783
|
},
|
3490
|
-
}
|
3784
|
+
};
|
3785
|
+
super(config);
|
3786
|
+
this.config = config;
|
3491
3787
|
}
|
3492
3788
|
fromMockFile(path$1) {
|
3493
3789
|
return fs.readFileSync(path.resolve(__dirname, `../tests/mocks/${path$1}`), 'utf-8');
|
3494
3790
|
}
|
3495
3791
|
runGraphQLTests(name, rule, tests) {
|
3496
3792
|
super.run(name, rule, tests);
|
3793
|
+
// Skip snapshot testing if `expect` variable is not defined
|
3794
|
+
if (typeof expect === 'undefined') {
|
3795
|
+
return;
|
3796
|
+
}
|
3797
|
+
const linter = new eslint.Linter();
|
3798
|
+
linter.defineRule(name, rule);
|
3799
|
+
for (const testCase of tests.invalid) {
|
3800
|
+
const verifyConfig = getVerifyConfig(name, this.config, testCase);
|
3801
|
+
defineParser(linter, verifyConfig.parser);
|
3802
|
+
const { code, filename } = testCase;
|
3803
|
+
const messages = linter.verify(code, verifyConfig, { filename });
|
3804
|
+
for (const message of messages) {
|
3805
|
+
if (message.fatal) {
|
3806
|
+
throw new Error(message.message);
|
3807
|
+
}
|
3808
|
+
const messageForSnapshot = visualizeEslintMessage(code, message);
|
3809
|
+
// eslint-disable-next-line no-undef
|
3810
|
+
expect(messageForSnapshot).toMatchSnapshot();
|
3811
|
+
}
|
3812
|
+
}
|
3497
3813
|
}
|
3498
3814
|
}
|
3815
|
+
function getVerifyConfig(ruleId, testerConfig, testCase) {
|
3816
|
+
const { options, parserOptions, parser = testerConfig.parser } = testCase;
|
3817
|
+
return {
|
3818
|
+
...testerConfig,
|
3819
|
+
parser,
|
3820
|
+
parserOptions: {
|
3821
|
+
...testerConfig.parserOptions,
|
3822
|
+
...parserOptions,
|
3823
|
+
},
|
3824
|
+
rules: {
|
3825
|
+
[ruleId]: ['error', ...(Array.isArray(options) ? options : [])],
|
3826
|
+
},
|
3827
|
+
};
|
3828
|
+
}
|
3829
|
+
const parsers = new WeakMap();
|
3830
|
+
function defineParser(linter, parser) {
|
3831
|
+
if (!parser) {
|
3832
|
+
return;
|
3833
|
+
}
|
3834
|
+
if (!parsers.has(linter)) {
|
3835
|
+
parsers.set(linter, new Set());
|
3836
|
+
}
|
3837
|
+
const defined = parsers.get(linter);
|
3838
|
+
if (!defined.has(parser)) {
|
3839
|
+
defined.add(parser);
|
3840
|
+
linter.defineParser(parser, require(parser));
|
3841
|
+
}
|
3842
|
+
}
|
3843
|
+
function visualizeEslintMessage(text, result) {
|
3844
|
+
const { line, column, endLine, endColumn, message } = result;
|
3845
|
+
const location = {
|
3846
|
+
start: {
|
3847
|
+
line,
|
3848
|
+
column,
|
3849
|
+
},
|
3850
|
+
};
|
3851
|
+
if (typeof endLine === 'number' && typeof endColumn === 'number') {
|
3852
|
+
location.end = {
|
3853
|
+
line: endLine,
|
3854
|
+
column: endColumn,
|
3855
|
+
};
|
3856
|
+
}
|
3857
|
+
return codeFrame.codeFrameColumns(text, location, {
|
3858
|
+
linesAbove: Number.POSITIVE_INFINITY,
|
3859
|
+
linesBelow: Number.POSITIVE_INFINITY,
|
3860
|
+
message,
|
3861
|
+
});
|
3862
|
+
}
|
3499
3863
|
|
3500
3864
|
exports.GraphQLRuleTester = GraphQLRuleTester;
|
3501
3865
|
exports.configs = configs;
|