@graphql-eslint/eslint-plugin 2.3.0 → 2.3.2-alpha-13a11c2.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/docs/README.md +1 -1
- package/docs/custom-rules.md +3 -3
- package/docs/rules/alphabetize.md +6 -1
- package/docs/rules/avoid-duplicate-fields.md +6 -1
- package/docs/rules/avoid-operation-name-prefix.md +5 -1
- 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 +6 -1
- 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 +6 -1
- 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 +6 -1
- package/docs/rules/naming-convention.md +6 -1
- 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 +6 -1
- package/docs/rules/require-deprecation-reason.md +6 -1
- package/docs/rules/require-description.md +6 -1
- package/docs/rules/require-field-of-type-query-in-mutation-result.md +6 -1
- package/docs/rules/require-id-when-available.md +6 -1
- package/docs/rules/scalar-leafs.md +6 -1
- package/docs/rules/selection-set-depth.md +6 -1
- package/docs/rules/strict-id-in-types.md +6 -1
- 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/index.js +183 -58
- package/index.mjs +183 -58
- package/package.json +1 -1
- package/testkit.d.ts +5 -3
- package/types.d.ts +2 -0
- package/utils.d.ts +4 -0
@@ -43,4 +43,9 @@ fragment AllUserFields on User {
|
|
43
43
|
fragment UserFields on User {
|
44
44
|
id
|
45
45
|
}
|
46
|
-
```
|
46
|
+
```
|
47
|
+
|
48
|
+
## Resources
|
49
|
+
|
50
|
+
- [Rule source](../../packages/plugin/src/rules/unique-fragment-name.ts)
|
51
|
+
- [Test source](../../packages/plugin/tests/unique-fragment-name.spec.ts)
|
@@ -9,4 +9,9 @@
|
|
9
9
|
|
10
10
|
A GraphQL input object value is only valid if all supplied fields are uniquely named.
|
11
11
|
|
12
|
-
> This rule is a wrapper around a `graphql-js` validation function. [You can find its source code here](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/UniqueInputFieldNamesRule.ts).
|
12
|
+
> This rule is a wrapper around a `graphql-js` validation function. [You can find its source code here](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/UniqueInputFieldNamesRule.ts).
|
13
|
+
|
14
|
+
## Resources
|
15
|
+
|
16
|
+
- [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/UniqueInputFieldNamesRule.ts)
|
17
|
+
- [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/UniqueInputFieldNamesRule-test.ts)
|
@@ -9,4 +9,9 @@
|
|
9
9
|
|
10
10
|
A GraphQL document is only valid if it has only one type per operation.
|
11
11
|
|
12
|
-
> This rule is a wrapper around a `graphql-js` validation function. [You can find its source code here](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/UniqueOperationTypesRule.ts).
|
12
|
+
> This rule is a wrapper around a `graphql-js` validation function. [You can find its source code here](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/UniqueOperationTypesRule.ts).
|
13
|
+
|
14
|
+
## Resources
|
15
|
+
|
16
|
+
- [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/UniqueOperationTypesRule.ts)
|
17
|
+
- [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/UniqueOperationTypesRule-test.ts)
|
@@ -9,4 +9,9 @@
|
|
9
9
|
|
10
10
|
A GraphQL document is only valid if all defined types have unique names.
|
11
11
|
|
12
|
-
> This rule is a wrapper around a `graphql-js` validation function. [You can find its source code here](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/UniqueTypeNamesRule.ts).
|
12
|
+
> This rule is a wrapper around a `graphql-js` validation function. [You can find its source code here](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/UniqueTypeNamesRule.ts).
|
13
|
+
|
14
|
+
## Resources
|
15
|
+
|
16
|
+
- [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/UniqueTypeNamesRule.ts)
|
17
|
+
- [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/UniqueTypeNamesRule-test.ts)
|
@@ -9,4 +9,9 @@
|
|
9
9
|
|
10
10
|
A GraphQL operation is only valid if all its variables are uniquely named.
|
11
11
|
|
12
|
-
> This rule is a wrapper around a `graphql-js` validation function. [You can find its source code here](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/UniqueVariableNamesRule.ts).
|
12
|
+
> This rule is a wrapper around a `graphql-js` validation function. [You can find its source code here](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/UniqueVariableNamesRule.ts).
|
13
|
+
|
14
|
+
## Resources
|
15
|
+
|
16
|
+
- [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/UniqueVariableNamesRule.ts)
|
17
|
+
- [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/UniqueVariableNamesRule-test.ts)
|
@@ -9,4 +9,9 @@
|
|
9
9
|
|
10
10
|
A GraphQL document is only valid if all value literals are of the type expected at their position.
|
11
11
|
|
12
|
-
> This rule is a wrapper around a `graphql-js` validation function. [You can find its source code here](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/ValuesOfCorrectTypeRule.ts).
|
12
|
+
> This rule is a wrapper around a `graphql-js` validation function. [You can find its source code here](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/ValuesOfCorrectTypeRule.ts).
|
13
|
+
|
14
|
+
## Resources
|
15
|
+
|
16
|
+
- [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/ValuesOfCorrectTypeRule.ts)
|
17
|
+
- [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/ValuesOfCorrectTypeRule-test.ts)
|
@@ -9,4 +9,9 @@
|
|
9
9
|
|
10
10
|
A GraphQL operation is only valid if all the variables it defines are of input types (scalar, enum, or input object).
|
11
11
|
|
12
|
-
> This rule is a wrapper around a `graphql-js` validation function. [You can find its source code here](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/VariablesAreInputTypesRule.ts).
|
12
|
+
> This rule is a wrapper around a `graphql-js` validation function. [You can find its source code here](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/VariablesAreInputTypesRule.ts).
|
13
|
+
|
14
|
+
## Resources
|
15
|
+
|
16
|
+
- [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/VariablesAreInputTypesRule.ts)
|
17
|
+
- [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/VariablesAreInputTypesRule-test.ts)
|
@@ -9,4 +9,9 @@
|
|
9
9
|
|
10
10
|
Variables passed to field arguments conform to type.
|
11
11
|
|
12
|
-
> This rule is a wrapper around a `graphql-js` validation function. [You can find its source code here](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/VariablesInAllowedPositionRule.ts).
|
12
|
+
> This rule is a wrapper around a `graphql-js` validation function. [You can find its source code here](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/VariablesInAllowedPositionRule.ts).
|
13
|
+
|
14
|
+
## Resources
|
15
|
+
|
16
|
+
- [Rule source](https://github.com/graphql/graphql-js/blob/main/src/validation/rules/VariablesInAllowedPositionRule.ts)
|
17
|
+
- [Test source](https://github.com/graphql/graphql-js/tree/main/src/validation/__tests__/VariablesInAllowedPositionRule-test.ts)
|
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`
|
@@ -242,6 +244,24 @@ const convertCase = (style, str) => {
|
|
242
244
|
return lowerCase(str).replace(/ /g, '-');
|
243
245
|
}
|
244
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
|
+
}
|
245
265
|
|
246
266
|
function extractRuleName(stack) {
|
247
267
|
const match = (stack || '').match(/validation[/\\\\]rules[/\\\\](.*?)\.js:/) || [];
|
@@ -292,6 +312,7 @@ const validationToRule = (name, ruleName, docs, getDocumentNode) => {
|
|
292
312
|
meta: {
|
293
313
|
docs: {
|
294
314
|
...docs,
|
315
|
+
graphQLJSRuleName: ruleName,
|
295
316
|
category: 'Validation',
|
296
317
|
recommended: true,
|
297
318
|
requiresSchema,
|
@@ -656,10 +677,7 @@ const rule = {
|
|
656
677
|
line: start.line,
|
657
678
|
column: start.column - (isVariableNode ? 2 : 1),
|
658
679
|
},
|
659
|
-
end
|
660
|
-
line: end.line,
|
661
|
-
column: end.column,
|
662
|
-
},
|
680
|
+
end,
|
663
681
|
},
|
664
682
|
messageId: ALPHABETIZE,
|
665
683
|
data: isVariableNode
|
@@ -790,6 +808,7 @@ const rule$1 = {
|
|
790
808
|
messages: {
|
791
809
|
[AVOID_DUPLICATE_FIELDS]: `{{ type }} "{{ fieldName }}" defined multiple times.`,
|
792
810
|
},
|
811
|
+
schema: [],
|
793
812
|
},
|
794
813
|
create(context) {
|
795
814
|
return {
|
@@ -909,15 +928,16 @@ const rule$2 = {
|
|
909
928
|
const testKeyword = caseSensitive ? keyword : keyword.toLowerCase();
|
910
929
|
const testName = caseSensitive ? node.name.value : node.name.value.toLowerCase();
|
911
930
|
if (testName.startsWith(testKeyword)) {
|
931
|
+
const { start } = node.name.loc;
|
912
932
|
context.report({
|
913
933
|
loc: {
|
914
934
|
start: {
|
915
|
-
line:
|
916
|
-
column:
|
935
|
+
line: start.line,
|
936
|
+
column: start.column - 1,
|
917
937
|
},
|
918
938
|
end: {
|
919
|
-
line:
|
920
|
-
column:
|
939
|
+
line: start.line,
|
940
|
+
column: start.column - 1 + testKeyword.length,
|
921
941
|
},
|
922
942
|
},
|
923
943
|
data: {
|
@@ -960,6 +980,7 @@ const rule$3 = {
|
|
960
980
|
},
|
961
981
|
],
|
962
982
|
},
|
983
|
+
schema: [],
|
963
984
|
},
|
964
985
|
create(context) {
|
965
986
|
const schema = requireGraphQLSchemaFromContext('avoid-scalar-result-type-on-mutation', context);
|
@@ -967,16 +988,20 @@ const rule$3 = {
|
|
967
988
|
if (!mutationType) {
|
968
989
|
return {};
|
969
990
|
}
|
970
|
-
const selector =
|
991
|
+
const selector = [
|
992
|
+
`:matches(${graphql.Kind.OBJECT_TYPE_DEFINITION}, ${graphql.Kind.OBJECT_TYPE_EXTENSION})[name.value=${mutationType.name}]`,
|
993
|
+
'>',
|
994
|
+
graphql.Kind.FIELD_DEFINITION,
|
995
|
+
graphql.Kind.NAMED_TYPE,
|
996
|
+
].join(' ');
|
971
997
|
return {
|
972
998
|
[selector](node) {
|
973
|
-
const
|
974
|
-
const typeName = getTypeName(rawNode);
|
999
|
+
const typeName = node.name.value;
|
975
1000
|
const graphQLType = schema.getType(typeName);
|
976
1001
|
if (graphql.isScalarType(graphQLType)) {
|
977
1002
|
context.report({
|
978
|
-
node,
|
979
|
-
message: `Unexpected scalar result type "${typeName}"
|
1003
|
+
loc: getLocation(node.loc, typeName),
|
1004
|
+
message: `Unexpected scalar result type "${typeName}"`,
|
980
1005
|
});
|
981
1006
|
}
|
982
1007
|
},
|
@@ -1015,22 +1040,33 @@ const rule$4 = {
|
|
1015
1040
|
messages: {
|
1016
1041
|
[AVOID_TYPENAME_PREFIX]: `Field "{{ fieldName }}" starts with the name of the parent type "{{ typeName }}"`,
|
1017
1042
|
},
|
1043
|
+
schema: [],
|
1018
1044
|
},
|
1019
1045
|
create(context) {
|
1020
1046
|
return {
|
1021
1047
|
'ObjectTypeDefinition, ObjectTypeExtension, InterfaceTypeDefinition, InterfaceTypeExtension'(node) {
|
1022
1048
|
const typeName = node.name.value;
|
1023
|
-
const lowerTypeName =
|
1049
|
+
const lowerTypeName = typeName.toLowerCase();
|
1024
1050
|
for (const field of node.fields) {
|
1025
|
-
const fieldName = field.name.value
|
1026
|
-
if (fieldName
|
1051
|
+
const fieldName = field.name.value;
|
1052
|
+
if (fieldName.toLowerCase().startsWith(lowerTypeName)) {
|
1053
|
+
const { start } = field.loc;
|
1027
1054
|
context.report({
|
1028
|
-
node: field.name,
|
1029
1055
|
data: {
|
1030
1056
|
fieldName,
|
1031
1057
|
typeName,
|
1032
1058
|
},
|
1033
1059
|
messageId: AVOID_TYPENAME_PREFIX,
|
1060
|
+
loc: {
|
1061
|
+
start: {
|
1062
|
+
line: start.line,
|
1063
|
+
column: start.column - 1,
|
1064
|
+
},
|
1065
|
+
end: {
|
1066
|
+
line: start.line,
|
1067
|
+
column: start.column - 1 + lowerTypeName.length,
|
1068
|
+
},
|
1069
|
+
},
|
1034
1070
|
});
|
1035
1071
|
}
|
1036
1072
|
}
|
@@ -1177,10 +1213,11 @@ const rule$6 = {
|
|
1177
1213
|
const shouldCheckType = node => (options.checkMutations && isMutationType(node)) || (options.checkQueries && isQueryType(node));
|
1178
1214
|
const listeners = {
|
1179
1215
|
'FieldDefinition > InputValueDefinition': node => {
|
1180
|
-
|
1216
|
+
const name = node.name.value;
|
1217
|
+
if (name !== 'input' && shouldCheckType(node.parent.parent)) {
|
1181
1218
|
context.report({
|
1182
|
-
|
1183
|
-
message: `Input "${
|
1219
|
+
loc: getLocation(node.loc, name),
|
1220
|
+
message: `Input "${name}" should be called "input"`,
|
1184
1221
|
});
|
1185
1222
|
}
|
1186
1223
|
},
|
@@ -1197,11 +1234,12 @@ const rule$6 = {
|
|
1197
1234
|
const inputValueNode = findInputType(node);
|
1198
1235
|
if (shouldCheckType(inputValueNode.parent.parent)) {
|
1199
1236
|
const mutationName = `${inputValueNode.parent.name.value}Input`;
|
1237
|
+
const name = node.name.value;
|
1200
1238
|
if ((options.caseSensitiveInputType && node.name.value !== mutationName) ||
|
1201
|
-
|
1239
|
+
name.toLowerCase() !== mutationName.toLowerCase()) {
|
1202
1240
|
context.report({
|
1203
|
-
node,
|
1204
|
-
message: `InputType "${
|
1241
|
+
loc: getLocation(node.loc, name),
|
1242
|
+
message: `InputType "${name}" name should be "${mutationName}"`,
|
1205
1243
|
});
|
1206
1244
|
}
|
1207
1245
|
}
|
@@ -1358,7 +1396,8 @@ const rule$7 = {
|
|
1358
1396
|
var _a;
|
1359
1397
|
if (options.fileExtension && options.fileExtension !== fileExtension) {
|
1360
1398
|
context.report({
|
1361
|
-
|
1399
|
+
// Report on first character
|
1400
|
+
loc: { column: 0, line: 1 },
|
1362
1401
|
messageId: MATCH_EXTENSION,
|
1363
1402
|
data: {
|
1364
1403
|
fileExtension,
|
@@ -1390,7 +1429,8 @@ const rule$7 = {
|
|
1390
1429
|
const filenameWithExtension = filename + expectedExtension;
|
1391
1430
|
if (expectedFilename !== filenameWithExtension) {
|
1392
1431
|
context.report({
|
1393
|
-
|
1432
|
+
// Report on first character
|
1433
|
+
loc: { column: 0, line: 1 },
|
1394
1434
|
messageId: MATCH_STYLE,
|
1395
1435
|
data: {
|
1396
1436
|
expectedFilename,
|
@@ -1728,20 +1768,24 @@ const rule$9 = {
|
|
1728
1768
|
messages: {
|
1729
1769
|
[NO_ANONYMOUS_OPERATIONS]: `Anonymous GraphQL operations are forbidden. Please make sure to name your {{ operation }}!`,
|
1730
1770
|
},
|
1771
|
+
schema: [],
|
1731
1772
|
},
|
1732
1773
|
create(context) {
|
1733
1774
|
return {
|
1734
1775
|
OperationDefinition(node) {
|
1735
|
-
|
1776
|
+
var _a;
|
1777
|
+
const isAnonymous = (((_a = node.name) === null || _a === void 0 ? void 0 : _a.value) || '').length === 0;
|
1778
|
+
if (isAnonymous) {
|
1779
|
+
const { start } = node.loc;
|
1736
1780
|
context.report({
|
1737
1781
|
loc: {
|
1738
1782
|
start: {
|
1739
|
-
column:
|
1740
|
-
line:
|
1783
|
+
column: start.column - 1,
|
1784
|
+
line: start.line,
|
1741
1785
|
},
|
1742
1786
|
end: {
|
1743
|
-
column:
|
1744
|
-
line:
|
1787
|
+
column: start.column - 1 + node.operation.length,
|
1788
|
+
line: start.line,
|
1745
1789
|
},
|
1746
1790
|
},
|
1747
1791
|
data: {
|
@@ -1791,6 +1835,7 @@ const rule$a = {
|
|
1791
1835
|
messages: {
|
1792
1836
|
[ERROR_MESSAGE_ID]: `Case-insensitive enum values duplicates are not allowed! Found: "{{ found }}"`,
|
1793
1837
|
},
|
1838
|
+
schema: [],
|
1794
1839
|
},
|
1795
1840
|
create(context) {
|
1796
1841
|
return {
|
@@ -1885,6 +1930,7 @@ const rule$b = {
|
|
1885
1930
|
messages: {
|
1886
1931
|
[NO_DEPRECATED]: `This {{ type }} is marked as deprecated in your GraphQL schema {{ reason }}`,
|
1887
1932
|
},
|
1933
|
+
schema: [],
|
1888
1934
|
},
|
1889
1935
|
create(context) {
|
1890
1936
|
return {
|
@@ -1971,6 +2017,7 @@ const rule$c = {
|
|
1971
2017
|
],
|
1972
2018
|
},
|
1973
2019
|
type: 'suggestion',
|
2020
|
+
schema: [],
|
1974
2021
|
},
|
1975
2022
|
create(context) {
|
1976
2023
|
return {
|
@@ -2031,15 +2078,28 @@ const rule$d = {
|
|
2031
2078
|
messages: {
|
2032
2079
|
[NO_OPERATION_NAME_SUFFIX]: `Unnecessary "{{ invalidSuffix }}" suffix in your operation name!`,
|
2033
2080
|
},
|
2081
|
+
schema: [],
|
2034
2082
|
},
|
2035
2083
|
create(context) {
|
2036
2084
|
return {
|
2037
2085
|
'OperationDefinition, FragmentDefinition'(node) {
|
2038
|
-
|
2039
|
-
|
2040
|
-
|
2086
|
+
var _a;
|
2087
|
+
const name = ((_a = node.name) === null || _a === void 0 ? void 0 : _a.value) || '';
|
2088
|
+
if (name.length > 0) {
|
2089
|
+
const invalidSuffix = 'operation' in node ? node.operation : 'fragment';
|
2090
|
+
if (name.toLowerCase().endsWith(invalidSuffix)) {
|
2091
|
+
const { start, end } = node.name.loc;
|
2041
2092
|
context.report({
|
2042
|
-
|
2093
|
+
loc: {
|
2094
|
+
start: {
|
2095
|
+
column: start.column - 1 + name.length - invalidSuffix.length,
|
2096
|
+
line: start.line,
|
2097
|
+
},
|
2098
|
+
end: {
|
2099
|
+
column: end.column - 1 + name.length,
|
2100
|
+
line: end.line,
|
2101
|
+
},
|
2102
|
+
},
|
2043
2103
|
data: {
|
2044
2104
|
invalidSuffix,
|
2045
2105
|
},
|
@@ -2096,6 +2156,7 @@ const rule$e = {
|
|
2096
2156
|
},
|
2097
2157
|
fixable: 'code',
|
2098
2158
|
type: 'suggestion',
|
2159
|
+
schema: [],
|
2099
2160
|
},
|
2100
2161
|
create(context) {
|
2101
2162
|
const reachableTypes = requireReachableTypesFromContext(RULE_NAME, context);
|
@@ -2187,6 +2248,7 @@ const rule$f = {
|
|
2187
2248
|
},
|
2188
2249
|
fixable: 'code',
|
2189
2250
|
type: 'suggestion',
|
2251
|
+
schema: [],
|
2190
2252
|
},
|
2191
2253
|
create(context) {
|
2192
2254
|
const usedFields = requireUsedFieldsFromContext(RULE_NAME$1, context);
|
@@ -2452,6 +2514,7 @@ const rule$h = {
|
|
2452
2514
|
],
|
2453
2515
|
},
|
2454
2516
|
type: 'suggestion',
|
2517
|
+
schema: [],
|
2455
2518
|
},
|
2456
2519
|
create(context) {
|
2457
2520
|
return {
|
@@ -2488,15 +2551,18 @@ const DESCRIBABLE_NODES = [
|
|
2488
2551
|
function verifyRule(context, node) {
|
2489
2552
|
if (node) {
|
2490
2553
|
if (!node.description || !node.description.value || node.description.value.trim().length === 0) {
|
2554
|
+
const { start, end } = ('name' in node ? node.name : node).loc;
|
2491
2555
|
context.report({
|
2492
2556
|
loc: {
|
2493
2557
|
start: {
|
2494
|
-
line:
|
2495
|
-
column:
|
2558
|
+
line: start.line,
|
2559
|
+
column: start.column - 1,
|
2496
2560
|
},
|
2497
2561
|
end: {
|
2498
|
-
line:
|
2499
|
-
column:
|
2562
|
+
line: end.line,
|
2563
|
+
column:
|
2564
|
+
// node.name don't exist on SchemaDefinition
|
2565
|
+
'name' in node ? end.column - 1 + node.name.value.length : end.column,
|
2500
2566
|
},
|
2501
2567
|
},
|
2502
2568
|
messageId: REQUIRE_DESCRIPTION_ERROR,
|
@@ -2610,6 +2676,7 @@ const rule$j = {
|
|
2610
2676
|
},
|
2611
2677
|
],
|
2612
2678
|
},
|
2679
|
+
schema: [],
|
2613
2680
|
},
|
2614
2681
|
create(context) {
|
2615
2682
|
const schema = requireGraphQLSchemaFromContext(RULE_NAME$2, context);
|
@@ -3146,11 +3213,7 @@ const rule$m = {
|
|
3146
3213
|
const RULE_NAME$3 = 'unique-fragment-name';
|
3147
3214
|
const UNIQUE_FRAGMENT_NAME = 'UNIQUE_FRAGMENT_NAME';
|
3148
3215
|
const checkNode = (context, node, ruleName, messageId) => {
|
3149
|
-
|
3150
|
-
const documentName = (_a = node.name) === null || _a === void 0 ? void 0 : _a.value;
|
3151
|
-
if (!documentName) {
|
3152
|
-
return;
|
3153
|
-
}
|
3216
|
+
const documentName = node.name.value;
|
3154
3217
|
const siblings = requireSiblingsOperations(ruleName, context);
|
3155
3218
|
const siblingDocuments = node.kind === graphql.Kind.FRAGMENT_DEFINITION ? siblings.getFragment(documentName) : siblings.getOperation(documentName);
|
3156
3219
|
const filepath = context.getFilename();
|
@@ -3161,7 +3224,6 @@ const checkNode = (context, node, ruleName, messageId) => {
|
|
3161
3224
|
return isSameName && !isSamePath;
|
3162
3225
|
});
|
3163
3226
|
if (conflictingDocuments.length > 0) {
|
3164
|
-
const { start, end } = node.name.loc;
|
3165
3227
|
context.report({
|
3166
3228
|
messageId,
|
3167
3229
|
data: {
|
@@ -3170,16 +3232,7 @@ const checkNode = (context, node, ruleName, messageId) => {
|
|
3170
3232
|
.map(f => `\t${path.relative(process.cwd(), getOnDiskFilepath(f.filePath))}`)
|
3171
3233
|
.join('\n'),
|
3172
3234
|
},
|
3173
|
-
loc:
|
3174
|
-
start: {
|
3175
|
-
line: start.line,
|
3176
|
-
column: start.column - 1,
|
3177
|
-
},
|
3178
|
-
end: {
|
3179
|
-
line: end.line,
|
3180
|
-
column: end.column - 1,
|
3181
|
-
},
|
3182
|
-
},
|
3235
|
+
loc: getLocation(node.name.loc, documentName),
|
3183
3236
|
});
|
3184
3237
|
}
|
3185
3238
|
};
|
@@ -3229,6 +3282,7 @@ const rule$n = {
|
|
3229
3282
|
messages: {
|
3230
3283
|
[UNIQUE_FRAGMENT_NAME]: 'Fragment named "{{ documentName }}" already defined in:\n{{ summary }}',
|
3231
3284
|
},
|
3285
|
+
schema: [],
|
3232
3286
|
},
|
3233
3287
|
create(context) {
|
3234
3288
|
return {
|
@@ -3291,10 +3345,11 @@ const rule$o = {
|
|
3291
3345
|
messages: {
|
3292
3346
|
[UNIQUE_OPERATION_NAME]: 'Operation named "{{ documentName }}" already defined in:\n{{ summary }}',
|
3293
3347
|
},
|
3348
|
+
schema: [],
|
3294
3349
|
},
|
3295
3350
|
create(context) {
|
3296
3351
|
return {
|
3297
|
-
OperationDefinition(node) {
|
3352
|
+
'OperationDefinition[name!=undefined]'(node) {
|
3298
3353
|
checkNode(context, node, RULE_NAME$4, UNIQUE_OPERATION_NAME);
|
3299
3354
|
},
|
3300
3355
|
};
|
@@ -3724,23 +3779,93 @@ function parseForESLint(code, options = {}) {
|
|
3724
3779
|
}
|
3725
3780
|
}
|
3726
3781
|
|
3727
|
-
class GraphQLRuleTester extends
|
3782
|
+
class GraphQLRuleTester extends eslint.RuleTester {
|
3728
3783
|
constructor(parserOptions = {}) {
|
3729
|
-
|
3784
|
+
const config = {
|
3730
3785
|
parser: require.resolve('@graphql-eslint/eslint-plugin'),
|
3731
3786
|
parserOptions: {
|
3732
3787
|
...parserOptions,
|
3733
3788
|
skipGraphQLConfig: true,
|
3734
3789
|
},
|
3735
|
-
}
|
3790
|
+
};
|
3791
|
+
super(config);
|
3792
|
+
this.config = config;
|
3736
3793
|
}
|
3737
3794
|
fromMockFile(path$1) {
|
3738
3795
|
return fs.readFileSync(path.resolve(__dirname, `../tests/mocks/${path$1}`), 'utf-8');
|
3739
3796
|
}
|
3740
3797
|
runGraphQLTests(name, rule, tests) {
|
3741
3798
|
super.run(name, rule, tests);
|
3799
|
+
// Skip snapshot testing if `expect` variable is not defined
|
3800
|
+
if (typeof expect === 'undefined') {
|
3801
|
+
return;
|
3802
|
+
}
|
3803
|
+
const linter = new eslint.Linter();
|
3804
|
+
linter.defineRule(name, rule);
|
3805
|
+
for (const testCase of tests.invalid) {
|
3806
|
+
const verifyConfig = getVerifyConfig(name, this.config, testCase);
|
3807
|
+
defineParser(linter, verifyConfig.parser);
|
3808
|
+
const { code, filename } = testCase;
|
3809
|
+
const messages = linter.verify(code, verifyConfig, { filename });
|
3810
|
+
for (const message of messages) {
|
3811
|
+
if (message.fatal) {
|
3812
|
+
throw new Error(message.message);
|
3813
|
+
}
|
3814
|
+
const messageForSnapshot = visualizeEslintMessage(code, message);
|
3815
|
+
// eslint-disable-next-line no-undef
|
3816
|
+
expect(messageForSnapshot).toMatchSnapshot();
|
3817
|
+
}
|
3818
|
+
}
|
3742
3819
|
}
|
3743
3820
|
}
|
3821
|
+
function getVerifyConfig(ruleId, testerConfig, testCase) {
|
3822
|
+
const { options, parserOptions, parser = testerConfig.parser } = testCase;
|
3823
|
+
return {
|
3824
|
+
...testerConfig,
|
3825
|
+
parser,
|
3826
|
+
parserOptions: {
|
3827
|
+
...testerConfig.parserOptions,
|
3828
|
+
...parserOptions,
|
3829
|
+
},
|
3830
|
+
rules: {
|
3831
|
+
[ruleId]: ['error', ...(Array.isArray(options) ? options : [])],
|
3832
|
+
},
|
3833
|
+
};
|
3834
|
+
}
|
3835
|
+
const parsers = new WeakMap();
|
3836
|
+
function defineParser(linter, parser) {
|
3837
|
+
if (!parser) {
|
3838
|
+
return;
|
3839
|
+
}
|
3840
|
+
if (!parsers.has(linter)) {
|
3841
|
+
parsers.set(linter, new Set());
|
3842
|
+
}
|
3843
|
+
const defined = parsers.get(linter);
|
3844
|
+
if (!defined.has(parser)) {
|
3845
|
+
defined.add(parser);
|
3846
|
+
linter.defineParser(parser, require(parser));
|
3847
|
+
}
|
3848
|
+
}
|
3849
|
+
function visualizeEslintMessage(text, result) {
|
3850
|
+
const { line, column, endLine, endColumn, message } = result;
|
3851
|
+
const location = {
|
3852
|
+
start: {
|
3853
|
+
line,
|
3854
|
+
column,
|
3855
|
+
},
|
3856
|
+
};
|
3857
|
+
if (typeof endLine === 'number' && typeof endColumn === 'number') {
|
3858
|
+
location.end = {
|
3859
|
+
line: endLine,
|
3860
|
+
column: endColumn,
|
3861
|
+
};
|
3862
|
+
}
|
3863
|
+
return codeFrame.codeFrameColumns(text, location, {
|
3864
|
+
linesAbove: Number.POSITIVE_INFINITY,
|
3865
|
+
linesBelow: Number.POSITIVE_INFINITY,
|
3866
|
+
message,
|
3867
|
+
});
|
3868
|
+
}
|
3744
3869
|
|
3745
3870
|
exports.GraphQLRuleTester = GraphQLRuleTester;
|
3746
3871
|
exports.configs = configs;
|