@graphql-eslint/eslint-plugin 4.3.0 → 4.4.0-alpha-20241207210859-41eb4549764dc0314b5bd4f257ea6667b178540e
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/cjs/cache.js +6 -2
- package/cjs/configs/operations-all.js +2 -2
- package/cjs/configs/schema-all.js +2 -2
- package/cjs/configs/schema-recommended.js +1 -1
- package/cjs/documents.js +13 -7
- package/cjs/estree-converter/converter.js +17 -8
- package/cjs/estree-converter/utils.js +22 -9
- package/cjs/graphql-config.js +13 -6
- package/cjs/index.d.cts +18 -4
- package/cjs/meta.js +1 -1
- package/cjs/parser.js +36 -9
- package/cjs/processor.js +48 -20
- package/cjs/rules/alphabetize/index.js +99 -47
- package/cjs/rules/description-style/index.js +10 -6
- package/cjs/rules/graphql-js-validation.js +142 -108
- package/cjs/rules/index.d.cts +18 -4
- package/cjs/rules/input-name/index.js +51 -38
- package/cjs/rules/lone-executable-definition/index.js +15 -6
- package/cjs/rules/match-document-filename/index.d.cts +4 -3
- package/cjs/rules/match-document-filename/index.js +63 -37
- package/cjs/rules/naming-convention/index.d.cts +6 -10
- package/cjs/rules/naming-convention/index.js +179 -82
- package/cjs/rules/no-anonymous-operations/index.js +8 -5
- package/cjs/rules/no-deprecated/index.js +27 -13
- package/cjs/rules/no-duplicate-fields/index.js +15 -8
- package/cjs/rules/no-hashtag-description/index.js +18 -10
- package/cjs/rules/no-one-place-fragments/index.js +17 -10
- package/cjs/rules/no-root-type/index.js +15 -8
- package/cjs/rules/no-scalar-result-type-on-mutation/index.js +20 -12
- package/cjs/rules/no-typename-prefix/index.js +25 -21
- package/cjs/rules/no-unreachable-types/index.js +34 -17
- package/cjs/rules/no-unused-fields/index.js +56 -30
- package/cjs/rules/relay-arguments/index.js +31 -13
- package/cjs/rules/relay-connection-types/index.js +31 -9
- package/cjs/rules/relay-edge-types/index.js +84 -41
- package/cjs/rules/relay-page-info/index.js +31 -14
- package/cjs/rules/require-deprecation-date/index.js +20 -9
- package/cjs/rules/require-deprecation-reason/index.js +8 -5
- package/cjs/rules/require-description/index.d.cts +79 -13
- package/cjs/rules/require-description/index.js +67 -49
- package/cjs/rules/require-field-of-type-query-in-mutation-result/index.js +21 -10
- package/cjs/rules/require-import-fragment/index.js +20 -11
- package/cjs/rules/require-nullable-fields-with-oneof/index.js +12 -5
- package/cjs/rules/require-nullable-result-in-root/index.js +32 -27
- package/cjs/rules/require-selections/index.js +88 -46
- package/cjs/rules/require-type-pattern-with-oneof/index.js +14 -10
- package/cjs/rules/selection-set-depth/index.js +19 -10
- package/cjs/rules/strict-id-in-types/index.js +32 -19
- package/cjs/rules/unique-enum-value-names/index.js +4 -3
- package/cjs/rules/unique-fragment-name/index.js +25 -18
- package/cjs/rules/unique-operation-name/index.js +5 -5
- package/cjs/schema.js +14 -8
- package/cjs/siblings.js +60 -32
- package/cjs/utils.js +23 -9
- package/esm/cache.js +6 -2
- package/esm/configs/operations-all.js +2 -2
- package/esm/configs/schema-all.js +2 -2
- package/esm/configs/schema-recommended.js +1 -1
- package/esm/documents.js +13 -7
- package/esm/estree-converter/converter.js +17 -8
- package/esm/estree-converter/utils.js +22 -9
- package/esm/graphql-config.js +13 -6
- package/esm/index.d.ts +18 -4
- package/esm/meta.js +1 -1
- package/esm/parser.js +36 -9
- package/esm/processor.js +48 -20
- package/esm/rules/alphabetize/index.js +99 -47
- package/esm/rules/description-style/index.js +10 -6
- package/esm/rules/graphql-js-validation.js +142 -108
- package/esm/rules/index.d.ts +18 -4
- package/esm/rules/input-name/index.js +51 -38
- package/esm/rules/lone-executable-definition/index.js +15 -6
- package/esm/rules/match-document-filename/index.d.ts +4 -3
- package/esm/rules/match-document-filename/index.js +63 -37
- package/esm/rules/naming-convention/index.d.ts +6 -10
- package/esm/rules/naming-convention/index.js +179 -82
- package/esm/rules/no-anonymous-operations/index.js +8 -5
- package/esm/rules/no-deprecated/index.js +27 -13
- package/esm/rules/no-duplicate-fields/index.js +15 -8
- package/esm/rules/no-hashtag-description/index.js +18 -10
- package/esm/rules/no-one-place-fragments/index.js +17 -10
- package/esm/rules/no-root-type/index.js +15 -8
- package/esm/rules/no-scalar-result-type-on-mutation/index.js +20 -12
- package/esm/rules/no-typename-prefix/index.js +25 -21
- package/esm/rules/no-unreachable-types/index.js +34 -17
- package/esm/rules/no-unused-fields/index.js +56 -30
- package/esm/rules/relay-arguments/index.js +31 -13
- package/esm/rules/relay-connection-types/index.js +31 -9
- package/esm/rules/relay-edge-types/index.js +84 -41
- package/esm/rules/relay-page-info/index.js +31 -14
- package/esm/rules/require-deprecation-date/index.js +20 -9
- package/esm/rules/require-deprecation-reason/index.js +8 -5
- package/esm/rules/require-description/index.d.ts +79 -13
- package/esm/rules/require-description/index.js +67 -49
- package/esm/rules/require-field-of-type-query-in-mutation-result/index.js +21 -10
- package/esm/rules/require-import-fragment/index.js +20 -11
- package/esm/rules/require-nullable-fields-with-oneof/index.js +12 -5
- package/esm/rules/require-nullable-result-in-root/index.js +32 -27
- package/esm/rules/require-selections/index.js +88 -46
- package/esm/rules/require-type-pattern-with-oneof/index.js +14 -10
- package/esm/rules/selection-set-depth/index.js +19 -10
- package/esm/rules/strict-id-in-types/index.js +32 -19
- package/esm/rules/unique-enum-value-names/index.js +4 -3
- package/esm/rules/unique-fragment-name/index.js +25 -18
- package/esm/rules/unique-operation-name/index.js +5 -5
- package/esm/schema.js +15 -8
- package/esm/siblings.js +60 -32
- package/esm/utils.js +23 -9
- package/index.browser.js +1871 -1160
- package/package.json +1 -1
@@ -1,9 +1,10 @@
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true});var _graphql = require('graphql');
|
2
2
|
var _utilsjs = require('../../utils.js');
|
3
|
-
const RULE_ID = "HASHTAG_COMMENT"
|
3
|
+
const RULE_ID = "HASHTAG_COMMENT";
|
4
|
+
const rule = {
|
4
5
|
meta: {
|
5
6
|
type: "suggestion",
|
6
|
-
hasSuggestions:
|
7
|
+
hasSuggestions: true,
|
7
8
|
schema: [],
|
8
9
|
messages: {
|
9
10
|
[RULE_ID]: 'Unexpected GraphQL descriptions as hashtag `#` for {{ nodeName }}.\nPrefer using `"""` for multiline, or `"` for a single line description.'
|
@@ -56,21 +57,27 @@ const RULE_ID = "HASHTAG_COMMENT", rule = exports.rule = {
|
|
56
57
|
)
|
57
58
|
}
|
58
59
|
],
|
59
|
-
recommended:
|
60
|
+
recommended: true
|
60
61
|
}
|
61
62
|
},
|
62
63
|
create(context) {
|
64
|
+
const selector = "Document[definitions.0.kind!=/^(OperationDefinition|FragmentDefinition)$/]";
|
63
65
|
return {
|
64
|
-
[
|
65
|
-
|
66
|
-
|
66
|
+
[selector](node) {
|
67
|
+
const rawNode = node.rawNode();
|
68
|
+
let token = rawNode.loc.startToken;
|
69
|
+
while (token) {
|
67
70
|
const { kind, prev, next, value, line, column } = token;
|
68
71
|
if (kind === _graphql.TokenKind.COMMENT && prev && next) {
|
69
|
-
const isEslintComment = value.trimStart().startsWith("eslint")
|
72
|
+
const isEslintComment = value.trimStart().startsWith("eslint");
|
73
|
+
const linesAfter = next.line - line;
|
70
74
|
if (!isEslintComment && line !== prev.line && next.kind === _graphql.TokenKind.NAME && linesAfter < 2) {
|
71
|
-
const sourceCode = context.getSourceCode()
|
75
|
+
const sourceCode = context.getSourceCode();
|
76
|
+
const { tokens } = sourceCode.ast;
|
77
|
+
const t = tokens.find(
|
72
78
|
(token2) => token2.loc.start.line === next.line && token2.loc.start.column === next.column - 1
|
73
|
-
)
|
79
|
+
);
|
80
|
+
const nextNode = sourceCode.getNodeByRangeIndex(t.range[1] + 1);
|
74
81
|
context.report({
|
75
82
|
messageId: RULE_ID,
|
76
83
|
data: {
|
@@ -92,8 +99,9 @@ const RULE_ID = "HASHTAG_COMMENT", rule = exports.rule = {
|
|
92
99
|
});
|
93
100
|
}
|
94
101
|
}
|
95
|
-
if (!next)
|
102
|
+
if (!next) {
|
96
103
|
break;
|
104
|
+
}
|
97
105
|
token = next;
|
98
106
|
}
|
99
107
|
}
|
@@ -1,7 +1,8 @@
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true});var _nodepath = require('node:path');
|
2
2
|
var _graphql = require('graphql');
|
3
3
|
var _utilsjs = require('../../utils.js');
|
4
|
-
const RULE_ID = "no-one-place-fragments"
|
4
|
+
const RULE_ID = "no-one-place-fragments";
|
5
|
+
const rule = {
|
5
6
|
meta: {
|
6
7
|
type: "suggestion",
|
7
8
|
docs: {
|
@@ -47,7 +48,7 @@ const RULE_ID = "no-one-place-fragments", rule = exports.rule = {
|
|
47
48
|
)
|
48
49
|
}
|
49
50
|
],
|
50
|
-
requiresSiblings:
|
51
|
+
requiresSiblings: true
|
51
52
|
},
|
52
53
|
messages: {
|
53
54
|
[RULE_ID]: 'Fragment `{{fragmentName}}` used only once. Inline him in "{{filePath}}".'
|
@@ -55,24 +56,30 @@ const RULE_ID = "no-one-place-fragments", rule = exports.rule = {
|
|
55
56
|
schema: []
|
56
57
|
},
|
57
58
|
create(context) {
|
58
|
-
const operations = _utilsjs.requireGraphQLOperations.call(void 0, RULE_ID, context)
|
59
|
+
const operations = _utilsjs.requireGraphQLOperations.call(void 0, RULE_ID, context);
|
60
|
+
const allDocuments = [...operations.getOperations(), ...operations.getFragments()];
|
61
|
+
const usedFragmentsMap = /* @__PURE__ */ Object.create(null);
|
59
62
|
for (const { document, filePath } of allDocuments) {
|
60
63
|
const relativeFilePath = _nodepath.relative.call(void 0, _utilsjs.CWD, filePath);
|
61
64
|
_graphql.visit.call(void 0, document, {
|
62
65
|
FragmentSpread({ name }) {
|
63
66
|
const spreadName = name.value;
|
64
|
-
usedFragmentsMap[spreadName] ||= []
|
67
|
+
usedFragmentsMap[spreadName] ||= [];
|
68
|
+
usedFragmentsMap[spreadName].push(relativeFilePath);
|
65
69
|
}
|
66
70
|
});
|
67
71
|
}
|
68
72
|
return {
|
69
73
|
"FragmentDefinition > Name"(node) {
|
70
|
-
const fragmentName = node.value
|
71
|
-
fragmentUsage
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
74
|
+
const fragmentName = node.value;
|
75
|
+
const fragmentUsage = usedFragmentsMap[fragmentName];
|
76
|
+
if (fragmentUsage.length === 1) {
|
77
|
+
context.report({
|
78
|
+
node,
|
79
|
+
messageId: RULE_ID,
|
80
|
+
data: { fragmentName, filePath: fragmentUsage[0] }
|
81
|
+
});
|
82
|
+
}
|
76
83
|
}
|
77
84
|
};
|
78
85
|
}
|
@@ -5,7 +5,7 @@ const schema = {
|
|
5
5
|
maxItems: 1,
|
6
6
|
items: {
|
7
7
|
type: "object",
|
8
|
-
additionalProperties:
|
8
|
+
additionalProperties: false,
|
9
9
|
required: ["disallow"],
|
10
10
|
properties: {
|
11
11
|
disallow: {
|
@@ -16,15 +16,16 @@ const schema = {
|
|
16
16
|
}
|
17
17
|
}
|
18
18
|
}
|
19
|
-
}
|
19
|
+
};
|
20
|
+
const rule = {
|
20
21
|
meta: {
|
21
22
|
type: "suggestion",
|
22
|
-
hasSuggestions:
|
23
|
+
hasSuggestions: true,
|
23
24
|
docs: {
|
24
25
|
category: "Schema",
|
25
26
|
description: "Disallow using root types `mutation` and/or `subscription`.",
|
26
27
|
url: "https://the-guild.dev/graphql/eslint/rules/no-root-type",
|
27
|
-
requiresSchema:
|
28
|
+
requiresSchema: true,
|
28
29
|
examples: [
|
29
30
|
{
|
30
31
|
title: "Incorrect",
|
@@ -56,12 +57,18 @@ const schema = {
|
|
56
57
|
schema
|
57
58
|
},
|
58
59
|
create(context) {
|
59
|
-
const schema2 = _utilsjs.requireGraphQLSchema.call(void 0, "no-root-type", context)
|
60
|
+
const schema2 = _utilsjs.requireGraphQLSchema.call(void 0, "no-root-type", context);
|
61
|
+
const disallow = new Set(context.options[0].disallow);
|
62
|
+
const rootTypeNames = [
|
60
63
|
disallow.has("mutation") && schema2.getMutationType(),
|
61
64
|
disallow.has("subscription") && schema2.getSubscriptionType()
|
62
65
|
].filter((v) => !!v).map((type) => type.name).join("|");
|
63
|
-
|
64
|
-
|
66
|
+
if (!rootTypeNames) {
|
67
|
+
return {};
|
68
|
+
}
|
69
|
+
const selector = `:matches(ObjectTypeDefinition, ObjectTypeExtension) > .name[value=/^(${rootTypeNames})$/]`;
|
70
|
+
return {
|
71
|
+
[selector](node) {
|
65
72
|
const typeName = node.value;
|
66
73
|
context.report({
|
67
74
|
node,
|
@@ -74,7 +81,7 @@ const schema = {
|
|
74
81
|
]
|
75
82
|
});
|
76
83
|
}
|
77
|
-
}
|
84
|
+
};
|
78
85
|
}
|
79
86
|
};
|
80
87
|
|
@@ -1,14 +1,15 @@
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true});var _graphql = require('graphql');
|
2
2
|
var _utilsjs = require('../../utils.js');
|
3
|
-
const RULE_ID = "no-scalar-result-type-on-mutation"
|
3
|
+
const RULE_ID = "no-scalar-result-type-on-mutation";
|
4
|
+
const rule = {
|
4
5
|
meta: {
|
5
6
|
type: "suggestion",
|
6
|
-
hasSuggestions:
|
7
|
+
hasSuggestions: true,
|
7
8
|
docs: {
|
8
9
|
category: "Schema",
|
9
10
|
description: "Avoid scalar result type on mutation type to make sure to return a valid state.",
|
10
11
|
url: `https://the-guild.dev/graphql/eslint/rules/${RULE_ID}`,
|
11
|
-
requiresSchema:
|
12
|
+
requiresSchema: true,
|
12
13
|
examples: [
|
13
14
|
{
|
14
15
|
title: "Incorrect",
|
@@ -37,17 +38,24 @@ const RULE_ID = "no-scalar-result-type-on-mutation", rule = exports.rule = {
|
|
37
38
|
schema: []
|
38
39
|
},
|
39
40
|
create(context) {
|
40
|
-
const schema = _utilsjs.requireGraphQLSchema.call(void 0, RULE_ID, context)
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
41
|
+
const schema = _utilsjs.requireGraphQLSchema.call(void 0, RULE_ID, context);
|
42
|
+
const mutationType = schema.getMutationType();
|
43
|
+
if (!mutationType) {
|
44
|
+
return {};
|
45
|
+
}
|
46
|
+
const selector = [
|
47
|
+
`:matches(ObjectTypeDefinition, ObjectTypeExtension)[name.value=${mutationType.name}]`,
|
48
|
+
"> FieldDefinition > .gqlType Name"
|
49
|
+
].join(" ");
|
50
|
+
return {
|
51
|
+
[selector](node) {
|
52
|
+
const typeName = node.value;
|
53
|
+
const graphQLType = schema.getType(typeName);
|
47
54
|
if (_graphql.isScalarType.call(void 0, graphQLType)) {
|
48
55
|
let fieldDef = node.parent;
|
49
|
-
|
56
|
+
while (fieldDef.kind !== _graphql.Kind.FIELD_DEFINITION) {
|
50
57
|
fieldDef = fieldDef.parent;
|
58
|
+
}
|
51
59
|
context.report({
|
52
60
|
node,
|
53
61
|
message: `Unexpected scalar result type \`${typeName}\` for ${_utilsjs.getNodeName.call(void 0, fieldDef)}`,
|
@@ -60,7 +68,7 @@ const RULE_ID = "no-scalar-result-type-on-mutation", rule = exports.rule = {
|
|
60
68
|
});
|
61
69
|
}
|
62
70
|
}
|
63
|
-
}
|
71
|
+
};
|
64
72
|
}
|
65
73
|
};
|
66
74
|
|
@@ -1,11 +1,12 @@
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true});const NO_TYPENAME_PREFIX = "NO_TYPENAME_PREFIX"
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true});const NO_TYPENAME_PREFIX = "NO_TYPENAME_PREFIX";
|
2
|
+
const rule = {
|
2
3
|
meta: {
|
3
4
|
type: "suggestion",
|
4
|
-
hasSuggestions:
|
5
|
+
hasSuggestions: true,
|
5
6
|
docs: {
|
6
7
|
category: "Schema",
|
7
8
|
description: "Enforces users to avoid using the type name in a field name while defining your schema.",
|
8
|
-
recommended:
|
9
|
+
recommended: true,
|
9
10
|
url: "https://the-guild.dev/graphql/eslint/rules/no-typename-prefix",
|
10
11
|
examples: [
|
11
12
|
{
|
@@ -40,26 +41,29 @@
|
|
40
41
|
create(context) {
|
41
42
|
return {
|
42
43
|
"ObjectTypeDefinition, ObjectTypeExtension, InterfaceTypeDefinition, InterfaceTypeExtension"(node) {
|
43
|
-
const typeName = node.name.value
|
44
|
+
const typeName = node.name.value;
|
45
|
+
const lowerTypeName = typeName.toLowerCase();
|
44
46
|
for (const field of node.fields || []) {
|
45
47
|
const fieldName = field.name.value;
|
46
|
-
fieldName.toLowerCase().startsWith(lowerTypeName)
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
48
|
+
if (fieldName.toLowerCase().startsWith(lowerTypeName)) {
|
49
|
+
context.report({
|
50
|
+
data: {
|
51
|
+
fieldName,
|
52
|
+
typeName
|
53
|
+
},
|
54
|
+
messageId: NO_TYPENAME_PREFIX,
|
55
|
+
node: field.name,
|
56
|
+
suggest: [
|
57
|
+
{
|
58
|
+
desc: `Remove \`${fieldName.slice(0, typeName.length)}\` prefix`,
|
59
|
+
fix: (fixer) => fixer.replaceText(
|
60
|
+
field.name,
|
61
|
+
fieldName.replace(new RegExp(`^${typeName}`, "i"), "")
|
62
|
+
)
|
63
|
+
}
|
64
|
+
]
|
65
|
+
});
|
66
|
+
}
|
63
67
|
}
|
64
68
|
}
|
65
69
|
};
|
@@ -8,7 +8,8 @@ var _graphql = require('graphql');
|
|
8
8
|
var _lodashlowercase = require('lodash.lowercase'); var _lodashlowercase2 = _interopRequireDefault(_lodashlowercase);
|
9
9
|
var _cachejs = require('../../cache.js');
|
10
10
|
var _utilsjs = require('../../utils.js');
|
11
|
-
const RULE_ID = "no-unreachable-types"
|
11
|
+
const RULE_ID = "no-unreachable-types";
|
12
|
+
const KINDS = [
|
12
13
|
_graphql.Kind.DIRECTIVE_DEFINITION,
|
13
14
|
_graphql.Kind.OBJECT_TYPE_DEFINITION,
|
14
15
|
_graphql.Kind.OBJECT_TYPE_EXTENSION,
|
@@ -22,7 +23,9 @@ const RULE_ID = "no-unreachable-types", KINDS = [
|
|
22
23
|
_graphql.Kind.UNION_TYPE_EXTENSION,
|
23
24
|
_graphql.Kind.ENUM_TYPE_DEFINITION,
|
24
25
|
_graphql.Kind.ENUM_TYPE_EXTENSION
|
25
|
-
]
|
26
|
+
];
|
27
|
+
const reachableTypesCache = new (0, _cachejs.ModuleCache)();
|
28
|
+
const RequestDirectiveLocations = /* @__PURE__ */ new Set([
|
26
29
|
_graphql.DirectiveLocation.QUERY,
|
27
30
|
_graphql.DirectiveLocation.MUTATION,
|
28
31
|
_graphql.DirectiveLocation.SUBSCRIPTION,
|
@@ -34,20 +37,27 @@ const RULE_ID = "no-unreachable-types", KINDS = [
|
|
34
37
|
]);
|
35
38
|
function getReachableTypes(schema) {
|
36
39
|
const cachedValue = reachableTypesCache.get(schema);
|
37
|
-
if (process.env.NODE_ENV !== "test" && cachedValue)
|
40
|
+
if (process.env.NODE_ENV !== "test" && cachedValue) {
|
38
41
|
return cachedValue;
|
39
|
-
|
42
|
+
}
|
43
|
+
const reachableTypes = /* @__PURE__ */ new Set();
|
44
|
+
const collect = (node) => {
|
40
45
|
const typeName = _utilsjs.getTypeName.call(void 0, node);
|
41
|
-
if (reachableTypes.has(typeName))
|
46
|
+
if (reachableTypes.has(typeName)) {
|
42
47
|
return;
|
48
|
+
}
|
43
49
|
reachableTypes.add(typeName);
|
44
50
|
const type = schema.getType(typeName) || schema.getDirective(typeName);
|
45
51
|
if (_graphql.isInterfaceType.call(void 0, type)) {
|
46
52
|
const { objects, interfaces } = schema.getImplementations(type);
|
47
|
-
for (const { astNode } of [...objects, ...interfaces])
|
53
|
+
for (const { astNode } of [...objects, ...interfaces]) {
|
48
54
|
_graphql.visit.call(void 0, astNode, visitor);
|
49
|
-
|
50
|
-
|
55
|
+
}
|
56
|
+
} else if (_optionalChain([type, 'optionalAccess', _ => _.astNode])) {
|
57
|
+
_graphql.visit.call(void 0, type.astNode, visitor);
|
58
|
+
}
|
59
|
+
};
|
60
|
+
const visitor = {
|
51
61
|
InterfaceTypeDefinition: collect,
|
52
62
|
ObjectTypeDefinition: collect,
|
53
63
|
InputValueDefinition: collect,
|
@@ -62,15 +72,21 @@ function getReachableTypes(schema) {
|
|
62
72
|
schema.getQueryType(),
|
63
73
|
schema.getMutationType(),
|
64
74
|
schema.getSubscriptionType()
|
65
|
-
])
|
66
|
-
_optionalChain([type, 'optionalAccess', _2 => _2.astNode])
|
67
|
-
|
75
|
+
]) {
|
76
|
+
if (_optionalChain([type, 'optionalAccess', _2 => _2.astNode])) {
|
77
|
+
_graphql.visit.call(void 0, type.astNode, visitor);
|
78
|
+
}
|
79
|
+
}
|
80
|
+
for (const node of schema.getDirectives()) {
|
68
81
|
if (node.locations.some((location) => RequestDirectiveLocations.has(location))) {
|
69
82
|
reachableTypes.add(node.name);
|
70
|
-
for (const arg of node.args)
|
83
|
+
for (const arg of node.args) {
|
71
84
|
reachableTypes.add(_graphql.getNamedType.call(void 0, arg.type).name);
|
85
|
+
}
|
72
86
|
}
|
73
|
-
|
87
|
+
}
|
88
|
+
reachableTypesCache.set(schema, reachableTypes);
|
89
|
+
return reachableTypes;
|
74
90
|
}
|
75
91
|
const rule = {
|
76
92
|
meta: {
|
@@ -81,7 +97,7 @@ const rule = {
|
|
81
97
|
description: "Requires all types to be reachable at some level by root level fields.",
|
82
98
|
category: "Schema",
|
83
99
|
url: `https://the-guild.dev/graphql/eslint/rules/${RULE_ID}`,
|
84
|
-
requiresSchema:
|
100
|
+
requiresSchema: true,
|
85
101
|
examples: [
|
86
102
|
{
|
87
103
|
title: "Incorrect",
|
@@ -116,14 +132,15 @@ const rule = {
|
|
116
132
|
)
|
117
133
|
}
|
118
134
|
],
|
119
|
-
recommended:
|
135
|
+
recommended: true
|
120
136
|
},
|
121
137
|
type: "suggestion",
|
122
138
|
schema: [],
|
123
|
-
hasSuggestions:
|
139
|
+
hasSuggestions: true
|
124
140
|
},
|
125
141
|
create(context) {
|
126
|
-
const schema = _utilsjs.requireGraphQLSchema.call(void 0, RULE_ID, context)
|
142
|
+
const schema = _utilsjs.requireGraphQLSchema.call(void 0, RULE_ID, context);
|
143
|
+
const reachableTypes = getReachableTypes(schema);
|
127
144
|
return {
|
128
145
|
[`:matches(${KINDS}) > .name`](node) {
|
129
146
|
const typeName = node.value;
|
@@ -1,7 +1,8 @@
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }var _graphql = require('graphql');
|
2
2
|
var _cachejs = require('../../cache.js');
|
3
3
|
var _utilsjs = require('../../utils.js');
|
4
|
-
const RULE_ID = "no-unused-fields"
|
4
|
+
const RULE_ID = "no-unused-fields";
|
5
|
+
const RELAY_SCHEMA = (
|
5
6
|
/* GraphQL */
|
6
7
|
`
|
7
8
|
# Root Query Type
|
@@ -42,7 +43,8 @@ const RULE_ID = "no-unused-fields", RELAY_SCHEMA = (
|
|
42
43
|
endCursor: String
|
43
44
|
}
|
44
45
|
`
|
45
|
-
)
|
46
|
+
);
|
47
|
+
const RELAY_QUERY = (
|
46
48
|
/* GraphQL */
|
47
49
|
`
|
48
50
|
query {
|
@@ -60,20 +62,22 @@ const RULE_ID = "no-unused-fields", RELAY_SCHEMA = (
|
|
60
62
|
}
|
61
63
|
}
|
62
64
|
`
|
63
|
-
)
|
65
|
+
);
|
66
|
+
const RELAY_DEFAULT_IGNORED_FIELD_SELECTORS = [
|
64
67
|
"[parent.name.value=PageInfo][name.value=/(endCursor|startCursor|hasNextPage|hasPreviousPage)/]",
|
65
68
|
"[parent.name.value=/Edge$/][name.value=cursor]",
|
66
69
|
"[parent.name.value=/Connection$/][name.value=pageInfo]"
|
67
|
-
]
|
70
|
+
];
|
71
|
+
const schema = {
|
68
72
|
type: "array",
|
69
73
|
maxItems: 1,
|
70
74
|
items: {
|
71
75
|
type: "object",
|
72
|
-
additionalProperties:
|
76
|
+
additionalProperties: false,
|
73
77
|
properties: {
|
74
78
|
ignoredFieldSelectors: {
|
75
79
|
type: "array",
|
76
|
-
uniqueItems:
|
80
|
+
uniqueItems: true,
|
77
81
|
minItems: 1,
|
78
82
|
description: [
|
79
83
|
"Fields that will be ignored and are allowed to be unused.",
|
@@ -83,8 +87,7 @@ const RULE_ID = "no-unused-fields", RELAY_SCHEMA = (
|
|
83
87
|
JSON.stringify(RELAY_DEFAULT_IGNORED_FIELD_SELECTORS, null, 2),
|
84
88
|
"```",
|
85
89
|
_utilsjs.eslintSelectorsTip
|
86
|
-
].join(
|
87
|
-
`),
|
90
|
+
].join("\n"),
|
88
91
|
items: {
|
89
92
|
type: "string",
|
90
93
|
pattern: "^\\[(.+)]$"
|
@@ -92,22 +95,33 @@ const RULE_ID = "no-unused-fields", RELAY_SCHEMA = (
|
|
92
95
|
}
|
93
96
|
}
|
94
97
|
}
|
95
|
-
}
|
98
|
+
};
|
99
|
+
const usedFieldsCache = new (0, _cachejs.ModuleCache)();
|
96
100
|
function getUsedFields(schema2, operations) {
|
97
101
|
const cachedValue = usedFieldsCache.get(schema2);
|
98
|
-
if (process.env.NODE_ENV !== "test" && cachedValue)
|
102
|
+
if (process.env.NODE_ENV !== "test" && cachedValue) {
|
99
103
|
return cachedValue;
|
100
|
-
|
104
|
+
}
|
105
|
+
const usedFields = /* @__PURE__ */ Object.create(null);
|
106
|
+
const typeInfo = new (0, _graphql.TypeInfo)(schema2);
|
107
|
+
const visitor = _graphql.visitWithTypeInfo.call(void 0, typeInfo, {
|
101
108
|
Field(node) {
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
109
|
+
const fieldDef = typeInfo.getFieldDef();
|
110
|
+
if (!fieldDef) {
|
111
|
+
return false;
|
112
|
+
}
|
113
|
+
const parentTypeName = typeInfo.getParentType().name;
|
114
|
+
const fieldName = node.name.value;
|
115
|
+
usedFields[parentTypeName] ??= /* @__PURE__ */ new Set();
|
116
|
+
usedFields[parentTypeName].add(fieldName);
|
106
117
|
}
|
107
|
-
})
|
108
|
-
|
118
|
+
});
|
119
|
+
const allDocuments = [...operations.getOperations(), ...operations.getFragments()];
|
120
|
+
for (const { document } of allDocuments) {
|
109
121
|
_graphql.visit.call(void 0, document, visitor);
|
110
|
-
|
122
|
+
}
|
123
|
+
usedFieldsCache.set(schema2, usedFields);
|
124
|
+
return usedFields;
|
111
125
|
}
|
112
126
|
const rule = {
|
113
127
|
meta: {
|
@@ -118,10 +132,10 @@ const rule = {
|
|
118
132
|
description: "Requires all fields to be used at some level by siblings operations.",
|
119
133
|
category: "Schema",
|
120
134
|
url: `https://the-guild.dev/graphql/eslint/rules/${RULE_ID}`,
|
121
|
-
requiresSiblings:
|
122
|
-
requiresSchema:
|
135
|
+
requiresSiblings: true,
|
136
|
+
requiresSchema: true,
|
123
137
|
// Requires documents to be set
|
124
|
-
isDisabledForAllConfig:
|
138
|
+
isDisabledForAllConfig: true,
|
125
139
|
examples: [
|
126
140
|
{
|
127
141
|
title: "Incorrect",
|
@@ -188,17 +202,26 @@ const rule = {
|
|
188
202
|
},
|
189
203
|
type: "suggestion",
|
190
204
|
schema,
|
191
|
-
hasSuggestions:
|
205
|
+
hasSuggestions: true
|
192
206
|
},
|
193
207
|
create(context) {
|
194
|
-
const schema2 = _utilsjs.requireGraphQLSchema.call(void 0, RULE_ID, context)
|
208
|
+
const schema2 = _utilsjs.requireGraphQLSchema.call(void 0, RULE_ID, context);
|
209
|
+
const siblingsOperations = _utilsjs.requireGraphQLOperations.call(void 0, RULE_ID, context);
|
210
|
+
const usedFields = getUsedFields(schema2, siblingsOperations);
|
211
|
+
const { ignoredFieldSelectors } = context.options[0] || {};
|
212
|
+
const selector = (ignoredFieldSelectors || []).reduce(
|
213
|
+
(acc, selector2) => `${acc}:not(${selector2})`,
|
214
|
+
"FieldDefinition"
|
215
|
+
);
|
195
216
|
return {
|
196
|
-
[(
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
217
|
+
[selector](node) {
|
218
|
+
const fieldName = node.name.value;
|
219
|
+
const parentTypeName = node.parent.name.value;
|
220
|
+
const isUsed = _optionalChain([usedFields, 'access', _ => _[parentTypeName], 'optionalAccess', _2 => _2.has, 'call', _3 => _3(fieldName)]);
|
221
|
+
if (isUsed) {
|
222
|
+
return;
|
223
|
+
}
|
224
|
+
context.report({
|
202
225
|
node: node.name,
|
203
226
|
messageId: RULE_ID,
|
204
227
|
data: { fieldName },
|
@@ -206,7 +229,10 @@ const rule = {
|
|
206
229
|
{
|
207
230
|
desc: `Remove \`${fieldName}\` field`,
|
208
231
|
fix(fixer) {
|
209
|
-
const sourceCode = context.getSourceCode()
|
232
|
+
const sourceCode = context.getSourceCode();
|
233
|
+
const tokenBefore = sourceCode.getTokenBefore(node);
|
234
|
+
const tokenAfter = sourceCode.getTokenAfter(node);
|
235
|
+
const isEmptyType = tokenBefore.type === "{" && tokenAfter.type === "}";
|
210
236
|
return fixer.remove(isEmptyType ? node.parent : node);
|
211
237
|
}
|
212
238
|
}
|