@graphql-eslint/eslint-plugin 2.3.2-alpha-6c4312e.0 → 2.3.2-alpha-5497183.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/docs/rules/avoid-duplicate-fields.md +9 -7
- package/index.js +48 -84
- package/index.mjs +48 -84
- package/package.json +1 -1
- package/rules/avoid-duplicate-fields.d.ts +1 -1
- package/rules/index.d.ts +1 -1
- package/testkit.d.ts +0 -1
@@ -14,11 +14,11 @@ Checks for duplicate fields in selection set, variables in operation definition,
|
|
14
14
|
```graphql
|
15
15
|
# eslint @graphql-eslint/avoid-duplicate-fields: 'error'
|
16
16
|
|
17
|
-
query
|
17
|
+
query {
|
18
18
|
user {
|
19
|
-
name
|
19
|
+
name
|
20
20
|
email
|
21
|
-
name #
|
21
|
+
name # duplicate field
|
22
22
|
}
|
23
23
|
}
|
24
24
|
```
|
@@ -28,7 +28,7 @@ query getUserDetails {
|
|
28
28
|
```graphql
|
29
29
|
# eslint @graphql-eslint/avoid-duplicate-fields: 'error'
|
30
30
|
|
31
|
-
query
|
31
|
+
query {
|
32
32
|
users(
|
33
33
|
first: 100
|
34
34
|
skip: 50
|
@@ -45,9 +45,11 @@ query getUsers {
|
|
45
45
|
```graphql
|
46
46
|
# eslint @graphql-eslint/avoid-duplicate-fields: 'error'
|
47
47
|
|
48
|
-
query
|
49
|
-
|
50
|
-
|
48
|
+
query (
|
49
|
+
$first: Int!
|
50
|
+
$first: Int! # duplicate variable
|
51
|
+
) {
|
52
|
+
users(first: $first, skip: 50) {
|
51
53
|
id
|
52
54
|
}
|
53
55
|
}
|
package/index.js
CHANGED
@@ -612,7 +612,7 @@ const rule = {
|
|
612
612
|
],
|
613
613
|
},
|
614
614
|
messages: {
|
615
|
-
[ALPHABETIZE]: '"{{ currName }}" should be before "{{ prevName }}"',
|
615
|
+
[ALPHABETIZE]: '"{{ currName }}" should be before "{{ prevName }}".',
|
616
616
|
},
|
617
617
|
schema: {
|
618
618
|
type: 'array',
|
@@ -669,9 +669,16 @@ const rule = {
|
|
669
669
|
for (const node of nodes) {
|
670
670
|
const currName = node.name.value;
|
671
671
|
if (prevName && prevName > currName) {
|
672
|
+
const { start, end } = node.name.loc;
|
672
673
|
const isVariableNode = node.kind === graphql.Kind.VARIABLE;
|
673
674
|
context.report({
|
674
|
-
loc:
|
675
|
+
loc: {
|
676
|
+
start: {
|
677
|
+
line: start.line,
|
678
|
+
column: start.column - (isVariableNode ? 2 : 1),
|
679
|
+
},
|
680
|
+
end,
|
681
|
+
},
|
675
682
|
messageId: ALPHABETIZE,
|
676
683
|
data: isVariableNode
|
677
684
|
? {
|
@@ -737,35 +744,22 @@ const rule = {
|
|
737
744
|
};
|
738
745
|
|
739
746
|
const AVOID_DUPLICATE_FIELDS = 'AVOID_DUPLICATE_FIELDS';
|
740
|
-
const ensureUnique = () => {
|
741
|
-
const set = new Set();
|
742
|
-
return {
|
743
|
-
add: (item, onError) => {
|
744
|
-
if (set.has(item)) {
|
745
|
-
onError();
|
746
|
-
}
|
747
|
-
else {
|
748
|
-
set.add(item);
|
749
|
-
}
|
750
|
-
},
|
751
|
-
};
|
752
|
-
};
|
753
747
|
const rule$1 = {
|
754
748
|
meta: {
|
755
749
|
type: 'suggestion',
|
756
750
|
docs: {
|
757
|
-
description:
|
751
|
+
description: `Checks for duplicate fields in selection set, variables in operation definition, or in arguments set of a field.`,
|
758
752
|
category: 'Stylistic Issues',
|
759
753
|
url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/avoid-duplicate-fields.md',
|
760
754
|
examples: [
|
761
755
|
{
|
762
756
|
title: 'Incorrect',
|
763
757
|
code: /* GraphQL */ `
|
764
|
-
query
|
758
|
+
query {
|
765
759
|
user {
|
766
|
-
name
|
760
|
+
name
|
767
761
|
email
|
768
|
-
name #
|
762
|
+
name # duplicate field
|
769
763
|
}
|
770
764
|
}
|
771
765
|
`,
|
@@ -773,7 +767,7 @@ const rule$1 = {
|
|
773
767
|
{
|
774
768
|
title: 'Incorrect',
|
775
769
|
code: /* GraphQL */ `
|
776
|
-
query
|
770
|
+
query {
|
777
771
|
users(
|
778
772
|
first: 100
|
779
773
|
skip: 50
|
@@ -788,9 +782,11 @@ const rule$1 = {
|
|
788
782
|
{
|
789
783
|
title: 'Incorrect',
|
790
784
|
code: /* GraphQL */ `
|
791
|
-
query
|
792
|
-
|
793
|
-
|
785
|
+
query (
|
786
|
+
$first: Int!
|
787
|
+
$first: Int! # duplicate variable
|
788
|
+
) {
|
789
|
+
users(first: $first, skip: 50) {
|
794
790
|
id
|
795
791
|
}
|
796
792
|
}
|
@@ -799,58 +795,47 @@ const rule$1 = {
|
|
799
795
|
],
|
800
796
|
},
|
801
797
|
messages: {
|
802
|
-
[AVOID_DUPLICATE_FIELDS]: `{{ type }} "{{ fieldName }}" defined multiple times
|
798
|
+
[AVOID_DUPLICATE_FIELDS]: `{{ type }} "{{ fieldName }}" defined multiple times`,
|
803
799
|
},
|
804
800
|
schema: [],
|
805
801
|
},
|
806
802
|
create(context) {
|
803
|
+
function checkNode(usedFields, fieldName, type, node) {
|
804
|
+
if (usedFields.has(fieldName)) {
|
805
|
+
context.report({
|
806
|
+
loc: getLocation((node.kind === graphql.Kind.FIELD && node.alias ? node.alias : node).loc, fieldName, {
|
807
|
+
offsetEnd: node.kind === graphql.Kind.VARIABLE_DEFINITION ? 0 : 1,
|
808
|
+
}),
|
809
|
+
messageId: AVOID_DUPLICATE_FIELDS,
|
810
|
+
data: {
|
811
|
+
type,
|
812
|
+
fieldName,
|
813
|
+
},
|
814
|
+
});
|
815
|
+
}
|
816
|
+
else {
|
817
|
+
usedFields.add(fieldName);
|
818
|
+
}
|
819
|
+
}
|
807
820
|
return {
|
808
821
|
OperationDefinition(node) {
|
809
|
-
const
|
810
|
-
for (const
|
811
|
-
|
812
|
-
context.report({
|
813
|
-
messageId: AVOID_DUPLICATE_FIELDS,
|
814
|
-
data: {
|
815
|
-
type: 'Operation variable',
|
816
|
-
fieldName: arg.variable.name.value,
|
817
|
-
},
|
818
|
-
node: arg,
|
819
|
-
});
|
820
|
-
});
|
822
|
+
const set = new Set();
|
823
|
+
for (const varDef of node.variableDefinitions) {
|
824
|
+
checkNode(set, varDef.variable.name.value, 'Operation variable', varDef);
|
821
825
|
}
|
822
826
|
},
|
823
827
|
Field(node) {
|
824
|
-
const
|
825
|
-
for (const arg of node.arguments
|
826
|
-
|
827
|
-
context.report({
|
828
|
-
messageId: AVOID_DUPLICATE_FIELDS,
|
829
|
-
data: {
|
830
|
-
type: 'Field argument',
|
831
|
-
fieldName: arg.name.value,
|
832
|
-
},
|
833
|
-
node: arg,
|
834
|
-
});
|
835
|
-
});
|
828
|
+
const set = new Set();
|
829
|
+
for (const arg of node.arguments) {
|
830
|
+
checkNode(set, arg.name.value, 'Field argument', arg);
|
836
831
|
}
|
837
832
|
},
|
838
833
|
SelectionSet(node) {
|
839
834
|
var _a;
|
840
|
-
const
|
841
|
-
for (const selection of node.selections
|
835
|
+
const set = new Set();
|
836
|
+
for (const selection of node.selections) {
|
842
837
|
if (selection.kind === graphql.Kind.FIELD) {
|
843
|
-
|
844
|
-
uniqueCheck.add(nameToCheck, () => {
|
845
|
-
context.report({
|
846
|
-
messageId: AVOID_DUPLICATE_FIELDS,
|
847
|
-
data: {
|
848
|
-
type: 'Field',
|
849
|
-
fieldName: nameToCheck,
|
850
|
-
},
|
851
|
-
node: selection,
|
852
|
-
});
|
853
|
-
});
|
838
|
+
checkNode(set, ((_a = selection.alias) === null || _a === void 0 ? void 0 : _a.value) || selection.name.value, 'Field', selection);
|
854
839
|
}
|
855
840
|
}
|
856
841
|
},
|
@@ -2023,10 +2008,7 @@ const rule$c = {
|
|
2023
2008
|
if (!isEslintComment && line !== prev.line && next.kind === graphql.TokenKind.NAME && linesAfter < 2) {
|
2024
2009
|
context.report({
|
2025
2010
|
messageId: HASHTAG_COMMENT,
|
2026
|
-
loc: {
|
2027
|
-
start: { line, column },
|
2028
|
-
end: { line, column },
|
2029
|
-
},
|
2011
|
+
loc: getLocation({ start: { line, column } }),
|
2030
2012
|
});
|
2031
2013
|
}
|
2032
2014
|
}
|
@@ -3786,25 +3768,7 @@ class GraphQLRuleTester extends eslint.RuleTester {
|
|
3786
3768
|
return fs.readFileSync(path.resolve(__dirname, `../tests/mocks/${path$1}`), 'utf-8');
|
3787
3769
|
}
|
3788
3770
|
runGraphQLTests(name, rule, tests) {
|
3789
|
-
|
3790
|
-
? tests
|
3791
|
-
: {
|
3792
|
-
valid: tests.valid.map(test => {
|
3793
|
-
if (typeof test === 'string') {
|
3794
|
-
return test;
|
3795
|
-
}
|
3796
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
3797
|
-
const { name, ...testCaseOptions } = test;
|
3798
|
-
return testCaseOptions;
|
3799
|
-
}),
|
3800
|
-
invalid: tests.invalid.map(test => {
|
3801
|
-
// ESLint 7 throws an error on CI - Unexpected top-level property "name"
|
3802
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
3803
|
-
const { name, ...testCaseOptions } = test;
|
3804
|
-
return testCaseOptions;
|
3805
|
-
}),
|
3806
|
-
};
|
3807
|
-
super.run(name, rule, ruleTests);
|
3771
|
+
super.run(name, rule, tests);
|
3808
3772
|
// Skip snapshot testing if `expect` variable is not defined
|
3809
3773
|
if (typeof expect === 'undefined') {
|
3810
3774
|
return;
|
package/index.mjs
CHANGED
@@ -606,7 +606,7 @@ const rule = {
|
|
606
606
|
],
|
607
607
|
},
|
608
608
|
messages: {
|
609
|
-
[ALPHABETIZE]: '"{{ currName }}" should be before "{{ prevName }}"',
|
609
|
+
[ALPHABETIZE]: '"{{ currName }}" should be before "{{ prevName }}".',
|
610
610
|
},
|
611
611
|
schema: {
|
612
612
|
type: 'array',
|
@@ -663,9 +663,16 @@ const rule = {
|
|
663
663
|
for (const node of nodes) {
|
664
664
|
const currName = node.name.value;
|
665
665
|
if (prevName && prevName > currName) {
|
666
|
+
const { start, end } = node.name.loc;
|
666
667
|
const isVariableNode = node.kind === Kind.VARIABLE;
|
667
668
|
context.report({
|
668
|
-
loc:
|
669
|
+
loc: {
|
670
|
+
start: {
|
671
|
+
line: start.line,
|
672
|
+
column: start.column - (isVariableNode ? 2 : 1),
|
673
|
+
},
|
674
|
+
end,
|
675
|
+
},
|
669
676
|
messageId: ALPHABETIZE,
|
670
677
|
data: isVariableNode
|
671
678
|
? {
|
@@ -731,35 +738,22 @@ const rule = {
|
|
731
738
|
};
|
732
739
|
|
733
740
|
const AVOID_DUPLICATE_FIELDS = 'AVOID_DUPLICATE_FIELDS';
|
734
|
-
const ensureUnique = () => {
|
735
|
-
const set = new Set();
|
736
|
-
return {
|
737
|
-
add: (item, onError) => {
|
738
|
-
if (set.has(item)) {
|
739
|
-
onError();
|
740
|
-
}
|
741
|
-
else {
|
742
|
-
set.add(item);
|
743
|
-
}
|
744
|
-
},
|
745
|
-
};
|
746
|
-
};
|
747
741
|
const rule$1 = {
|
748
742
|
meta: {
|
749
743
|
type: 'suggestion',
|
750
744
|
docs: {
|
751
|
-
description:
|
745
|
+
description: `Checks for duplicate fields in selection set, variables in operation definition, or in arguments set of a field.`,
|
752
746
|
category: 'Stylistic Issues',
|
753
747
|
url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/avoid-duplicate-fields.md',
|
754
748
|
examples: [
|
755
749
|
{
|
756
750
|
title: 'Incorrect',
|
757
751
|
code: /* GraphQL */ `
|
758
|
-
query
|
752
|
+
query {
|
759
753
|
user {
|
760
|
-
name
|
754
|
+
name
|
761
755
|
email
|
762
|
-
name #
|
756
|
+
name # duplicate field
|
763
757
|
}
|
764
758
|
}
|
765
759
|
`,
|
@@ -767,7 +761,7 @@ const rule$1 = {
|
|
767
761
|
{
|
768
762
|
title: 'Incorrect',
|
769
763
|
code: /* GraphQL */ `
|
770
|
-
query
|
764
|
+
query {
|
771
765
|
users(
|
772
766
|
first: 100
|
773
767
|
skip: 50
|
@@ -782,9 +776,11 @@ const rule$1 = {
|
|
782
776
|
{
|
783
777
|
title: 'Incorrect',
|
784
778
|
code: /* GraphQL */ `
|
785
|
-
query
|
786
|
-
|
787
|
-
|
779
|
+
query (
|
780
|
+
$first: Int!
|
781
|
+
$first: Int! # duplicate variable
|
782
|
+
) {
|
783
|
+
users(first: $first, skip: 50) {
|
788
784
|
id
|
789
785
|
}
|
790
786
|
}
|
@@ -793,58 +789,47 @@ const rule$1 = {
|
|
793
789
|
],
|
794
790
|
},
|
795
791
|
messages: {
|
796
|
-
[AVOID_DUPLICATE_FIELDS]: `{{ type }} "{{ fieldName }}" defined multiple times
|
792
|
+
[AVOID_DUPLICATE_FIELDS]: `{{ type }} "{{ fieldName }}" defined multiple times`,
|
797
793
|
},
|
798
794
|
schema: [],
|
799
795
|
},
|
800
796
|
create(context) {
|
797
|
+
function checkNode(usedFields, fieldName, type, node) {
|
798
|
+
if (usedFields.has(fieldName)) {
|
799
|
+
context.report({
|
800
|
+
loc: getLocation((node.kind === Kind.FIELD && node.alias ? node.alias : node).loc, fieldName, {
|
801
|
+
offsetEnd: node.kind === Kind.VARIABLE_DEFINITION ? 0 : 1,
|
802
|
+
}),
|
803
|
+
messageId: AVOID_DUPLICATE_FIELDS,
|
804
|
+
data: {
|
805
|
+
type,
|
806
|
+
fieldName,
|
807
|
+
},
|
808
|
+
});
|
809
|
+
}
|
810
|
+
else {
|
811
|
+
usedFields.add(fieldName);
|
812
|
+
}
|
813
|
+
}
|
801
814
|
return {
|
802
815
|
OperationDefinition(node) {
|
803
|
-
const
|
804
|
-
for (const
|
805
|
-
|
806
|
-
context.report({
|
807
|
-
messageId: AVOID_DUPLICATE_FIELDS,
|
808
|
-
data: {
|
809
|
-
type: 'Operation variable',
|
810
|
-
fieldName: arg.variable.name.value,
|
811
|
-
},
|
812
|
-
node: arg,
|
813
|
-
});
|
814
|
-
});
|
816
|
+
const set = new Set();
|
817
|
+
for (const varDef of node.variableDefinitions) {
|
818
|
+
checkNode(set, varDef.variable.name.value, 'Operation variable', varDef);
|
815
819
|
}
|
816
820
|
},
|
817
821
|
Field(node) {
|
818
|
-
const
|
819
|
-
for (const arg of node.arguments
|
820
|
-
|
821
|
-
context.report({
|
822
|
-
messageId: AVOID_DUPLICATE_FIELDS,
|
823
|
-
data: {
|
824
|
-
type: 'Field argument',
|
825
|
-
fieldName: arg.name.value,
|
826
|
-
},
|
827
|
-
node: arg,
|
828
|
-
});
|
829
|
-
});
|
822
|
+
const set = new Set();
|
823
|
+
for (const arg of node.arguments) {
|
824
|
+
checkNode(set, arg.name.value, 'Field argument', arg);
|
830
825
|
}
|
831
826
|
},
|
832
827
|
SelectionSet(node) {
|
833
828
|
var _a;
|
834
|
-
const
|
835
|
-
for (const selection of node.selections
|
829
|
+
const set = new Set();
|
830
|
+
for (const selection of node.selections) {
|
836
831
|
if (selection.kind === Kind.FIELD) {
|
837
|
-
|
838
|
-
uniqueCheck.add(nameToCheck, () => {
|
839
|
-
context.report({
|
840
|
-
messageId: AVOID_DUPLICATE_FIELDS,
|
841
|
-
data: {
|
842
|
-
type: 'Field',
|
843
|
-
fieldName: nameToCheck,
|
844
|
-
},
|
845
|
-
node: selection,
|
846
|
-
});
|
847
|
-
});
|
832
|
+
checkNode(set, ((_a = selection.alias) === null || _a === void 0 ? void 0 : _a.value) || selection.name.value, 'Field', selection);
|
848
833
|
}
|
849
834
|
}
|
850
835
|
},
|
@@ -2017,10 +2002,7 @@ const rule$c = {
|
|
2017
2002
|
if (!isEslintComment && line !== prev.line && next.kind === TokenKind.NAME && linesAfter < 2) {
|
2018
2003
|
context.report({
|
2019
2004
|
messageId: HASHTAG_COMMENT,
|
2020
|
-
loc: {
|
2021
|
-
start: { line, column },
|
2022
|
-
end: { line, column },
|
2023
|
-
},
|
2005
|
+
loc: getLocation({ start: { line, column } }),
|
2024
2006
|
});
|
2025
2007
|
}
|
2026
2008
|
}
|
@@ -3780,25 +3762,7 @@ class GraphQLRuleTester extends RuleTester {
|
|
3780
3762
|
return readFileSync(resolve(__dirname, `../tests/mocks/${path}`), 'utf-8');
|
3781
3763
|
}
|
3782
3764
|
runGraphQLTests(name, rule, tests) {
|
3783
|
-
|
3784
|
-
? tests
|
3785
|
-
: {
|
3786
|
-
valid: tests.valid.map(test => {
|
3787
|
-
if (typeof test === 'string') {
|
3788
|
-
return test;
|
3789
|
-
}
|
3790
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
3791
|
-
const { name, ...testCaseOptions } = test;
|
3792
|
-
return testCaseOptions;
|
3793
|
-
}),
|
3794
|
-
invalid: tests.invalid.map(test => {
|
3795
|
-
// ESLint 7 throws an error on CI - Unexpected top-level property "name"
|
3796
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
3797
|
-
const { name, ...testCaseOptions } = test;
|
3798
|
-
return testCaseOptions;
|
3799
|
-
}),
|
3800
|
-
};
|
3801
|
-
super.run(name, rule, ruleTests);
|
3765
|
+
super.run(name, rule, tests);
|
3802
3766
|
// Skip snapshot testing if `expect` variable is not defined
|
3803
3767
|
if (typeof expect === 'undefined') {
|
3804
3768
|
return;
|
package/package.json
CHANGED
package/rules/index.d.ts
CHANGED
@@ -6,7 +6,7 @@ export declare const rules: {
|
|
6
6
|
variables?: "OperationDefinition"[];
|
7
7
|
arguments?: ("Field" | "Directive" | "FieldDefinition" | "DirectiveDefinition")[];
|
8
8
|
}], false>;
|
9
|
-
'avoid-duplicate-fields': import("..").GraphQLESLintRule<[], false>;
|
9
|
+
'avoid-duplicate-fields': import("..").GraphQLESLintRule<any[], false>;
|
10
10
|
'avoid-operation-name-prefix': import("..").GraphQLESLintRule<import("./avoid-operation-name-prefix").AvoidOperationNamePrefixConfig, false>;
|
11
11
|
'avoid-scalar-result-type-on-mutation': import("..").GraphQLESLintRule<any[], false>;
|
12
12
|
'avoid-typename-prefix': import("..").GraphQLESLintRule<any[], false>;
|
package/testkit.d.ts
CHANGED
@@ -6,7 +6,6 @@ export declare type GraphQLESLintRuleListener<WithTypeInfo extends boolean = fal
|
|
6
6
|
[K in keyof ASTKindToNode]?: (node: GraphQLESTreeNode<ASTKindToNode[K], WithTypeInfo>) => void;
|
7
7
|
} & Record<string, any>;
|
8
8
|
export declare type GraphQLValidTestCase<Options> = Omit<RuleTester.ValidTestCase, 'options' | 'parserOptions'> & {
|
9
|
-
name: string;
|
10
9
|
options?: Options;
|
11
10
|
parserOptions?: ParserOptions;
|
12
11
|
};
|