@graphql-eslint/eslint-plugin 4.3.0 → 4.3.1-alpha-20241209185034-de2d7397da8c26620a8930dd12b6dff42e43f537
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/{index.browser.js → browser.js} +1756 -1104
- 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/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/input-name/index.js +51 -38
- package/cjs/rules/lone-executable-definition/index.js +15 -6
- package/cjs/rules/match-document-filename/index.js +57 -32
- package/cjs/rules/naming-convention/index.js +76 -37
- 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.js +60 -42
- 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/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/input-name/index.js +51 -38
- package/esm/rules/lone-executable-definition/index.js +15 -6
- package/esm/rules/match-document-filename/index.js +57 -32
- package/esm/rules/naming-convention/index.js +76 -37
- 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.js +60 -42
- 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/package.json +11 -1
@@ -1,5 +1,6 @@
|
|
1
1
|
import { displayNodeName } from "../../utils.js";
|
2
|
-
const RULE_ID = "require-type-pattern-with-oneof"
|
2
|
+
const RULE_ID = "require-type-pattern-with-oneof";
|
3
|
+
const rule = {
|
3
4
|
meta: {
|
4
5
|
type: "suggestion",
|
5
6
|
docs: {
|
@@ -44,15 +45,18 @@ const RULE_ID = "require-type-pattern-with-oneof", rule = {
|
|
44
45
|
parent
|
45
46
|
}) {
|
46
47
|
const requiredFields = ["error", "ok"];
|
47
|
-
for (const fieldName of requiredFields)
|
48
|
-
parent.fields?.some((field) => field.name.value === fieldName)
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
48
|
+
for (const fieldName of requiredFields) {
|
49
|
+
if (!parent.fields?.some((field) => field.name.value === fieldName)) {
|
50
|
+
context.report({
|
51
|
+
node: parent.name,
|
52
|
+
messageId: RULE_ID,
|
53
|
+
data: {
|
54
|
+
nodeName: displayNodeName(parent),
|
55
|
+
fieldName
|
56
|
+
}
|
57
|
+
});
|
58
|
+
}
|
59
|
+
}
|
56
60
|
}
|
57
61
|
};
|
58
62
|
}
|
@@ -1,13 +1,14 @@
|
|
1
1
|
import { Kind } from "graphql";
|
2
2
|
import depthLimit from "graphql-depth-limit";
|
3
3
|
import { ARRAY_DEFAULT_OPTIONS, logger, requireGraphQLOperations } from "../../utils.js";
|
4
|
-
const RULE_ID = "selection-set-depth"
|
4
|
+
const RULE_ID = "selection-set-depth";
|
5
|
+
const schema = {
|
5
6
|
type: "array",
|
6
7
|
minItems: 1,
|
7
8
|
maxItems: 1,
|
8
9
|
items: {
|
9
10
|
type: "object",
|
10
|
-
additionalProperties:
|
11
|
+
additionalProperties: false,
|
11
12
|
required: ["maxDepth"],
|
12
13
|
properties: {
|
13
14
|
maxDepth: {
|
@@ -16,15 +17,16 @@ const RULE_ID = "selection-set-depth", schema = {
|
|
16
17
|
ignore: ARRAY_DEFAULT_OPTIONS
|
17
18
|
}
|
18
19
|
}
|
19
|
-
}
|
20
|
+
};
|
21
|
+
const rule = {
|
20
22
|
meta: {
|
21
23
|
type: "suggestion",
|
22
|
-
hasSuggestions:
|
24
|
+
hasSuggestions: true,
|
23
25
|
docs: {
|
24
26
|
category: "Operations",
|
25
27
|
description: "Limit the complexity of the GraphQL operations solely by their depth. Based on [graphql-depth-limit](https://npmjs.com/package/graphql-depth-limit).",
|
26
28
|
url: `https://the-guild.dev/graphql/eslint/rules/${RULE_ID}`,
|
27
|
-
requiresSiblings:
|
29
|
+
requiresSiblings: true,
|
28
30
|
examples: [
|
29
31
|
{
|
30
32
|
title: "Incorrect",
|
@@ -66,7 +68,7 @@ const RULE_ID = "selection-set-depth", schema = {
|
|
66
68
|
`
|
67
69
|
}
|
68
70
|
],
|
69
|
-
recommended:
|
71
|
+
recommended: true,
|
70
72
|
configOptions: [{ maxDepth: 7 }]
|
71
73
|
},
|
72
74
|
schema
|
@@ -80,18 +82,23 @@ const RULE_ID = "selection-set-depth", schema = {
|
|
80
82
|
`Rule "${RULE_ID}" works best with siblings operations loaded. See https://the-guild.dev/graphql/eslint/docs/usage#providing-operations for more info`
|
81
83
|
);
|
82
84
|
}
|
83
|
-
const { maxDepth, ignore = [] } = context.options[0]
|
85
|
+
const { maxDepth, ignore = [] } = context.options[0];
|
86
|
+
const checkFn = depthLimit(maxDepth, { ignore });
|
84
87
|
return {
|
85
88
|
"OperationDefinition, FragmentDefinition"(node) {
|
86
89
|
try {
|
87
|
-
const rawNode = node.rawNode()
|
90
|
+
const rawNode = node.rawNode();
|
91
|
+
const fragmentsInUse = siblings ? siblings.getFragmentsInUse(rawNode) : [];
|
92
|
+
const document = {
|
88
93
|
kind: Kind.DOCUMENT,
|
89
94
|
definitions: [rawNode, ...fragmentsInUse]
|
90
95
|
};
|
91
96
|
checkFn({
|
92
97
|
getDocument: () => document,
|
93
98
|
reportError(error) {
|
94
|
-
const { line, column } = error.locations[0]
|
99
|
+
const { line, column } = error.locations[0];
|
100
|
+
const ancestors = context.sourceCode.getAncestors(node);
|
101
|
+
const token = ancestors[0].tokens.find(
|
95
102
|
(token2) => token2.loc.start.line === line && token2.loc.start.column === column - 1
|
96
103
|
);
|
97
104
|
context.report({
|
@@ -106,7 +113,9 @@ const RULE_ID = "selection-set-depth", schema = {
|
|
106
113
|
{
|
107
114
|
desc: "Remove selections",
|
108
115
|
fix(fixer) {
|
109
|
-
const
|
116
|
+
const sourceCode = context.getSourceCode();
|
117
|
+
const foundNode = sourceCode.getNodeByRangeIndex(token.range[0]);
|
118
|
+
const parentNode = foundNode.parent.parent;
|
110
119
|
return fixer.remove(
|
111
120
|
foundNode.kind === "Name" ? parentNode.parent : parentNode
|
112
121
|
);
|
@@ -5,12 +5,13 @@ import {
|
|
5
5
|
englishJoinWords,
|
6
6
|
requireGraphQLSchema
|
7
7
|
} from "../../utils.js";
|
8
|
-
const RULE_ID = "strict-id-in-types"
|
8
|
+
const RULE_ID = "strict-id-in-types";
|
9
|
+
const schema = {
|
9
10
|
type: "array",
|
10
11
|
maxItems: 1,
|
11
12
|
items: {
|
12
13
|
type: "object",
|
13
|
-
additionalProperties:
|
14
|
+
additionalProperties: false,
|
14
15
|
properties: {
|
15
16
|
acceptedIdNames: {
|
16
17
|
...ARRAY_DEFAULT_OPTIONS,
|
@@ -22,7 +23,7 @@ const RULE_ID = "strict-id-in-types", schema = {
|
|
22
23
|
},
|
23
24
|
exceptions: {
|
24
25
|
type: "object",
|
25
|
-
additionalProperties:
|
26
|
+
additionalProperties: false,
|
26
27
|
properties: {
|
27
28
|
types: {
|
28
29
|
...ARRAY_DEFAULT_OPTIONS,
|
@@ -36,15 +37,16 @@ const RULE_ID = "strict-id-in-types", schema = {
|
|
36
37
|
}
|
37
38
|
}
|
38
39
|
}
|
39
|
-
}
|
40
|
+
};
|
41
|
+
const rule = {
|
40
42
|
meta: {
|
41
43
|
type: "suggestion",
|
42
44
|
docs: {
|
43
45
|
description: "Requires output types to have one unique identifier unless they do not have a logical one. Exceptions can be used to ignore output types that do not have unique identifiers.",
|
44
46
|
category: "Schema",
|
45
|
-
recommended:
|
47
|
+
recommended: true,
|
46
48
|
url: `https://the-guild.dev/graphql/eslint/rules/${RULE_ID}`,
|
47
|
-
requiresSchema:
|
49
|
+
requiresSchema: true,
|
48
50
|
examples: [
|
49
51
|
{
|
50
52
|
title: "Incorrect",
|
@@ -121,22 +123,33 @@ const RULE_ID = "strict-id-in-types", schema = {
|
|
121
123
|
acceptedIdTypes: ["ID"],
|
122
124
|
exceptions: {},
|
123
125
|
...context.options[0]
|
124
|
-
}
|
126
|
+
};
|
127
|
+
const schema2 = requireGraphQLSchema(RULE_ID, context);
|
128
|
+
const rootTypeNames = [
|
129
|
+
schema2.getQueryType(),
|
130
|
+
schema2.getMutationType(),
|
131
|
+
schema2.getSubscriptionType()
|
132
|
+
].filter((v) => !!v).map((type) => type.name);
|
133
|
+
const selector = `ObjectTypeDefinition[name.value!=/^(${rootTypeNames.join("|")})$/]`;
|
125
134
|
return {
|
126
|
-
[
|
127
|
-
schema2.getQueryType(),
|
128
|
-
schema2.getMutationType(),
|
129
|
-
schema2.getSubscriptionType()
|
130
|
-
].filter((v) => !!v).map((type) => type.name).join("|")})$/]`](node) {
|
135
|
+
[selector](node) {
|
131
136
|
const typeName = node.name.value;
|
132
|
-
|
137
|
+
const shouldIgnoreNode = options.exceptions.types?.includes(typeName) || options.exceptions.suffixes?.some((suffix) => typeName.endsWith(suffix));
|
138
|
+
if (shouldIgnoreNode) {
|
133
139
|
return;
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
+
}
|
141
|
+
const validIds = node.fields?.filter((field) => {
|
142
|
+
const fieldNode = field.rawNode();
|
143
|
+
const isValidIdName = options.acceptedIdNames.includes(fieldNode.name.value);
|
144
|
+
let isValidIdType = false;
|
145
|
+
if (fieldNode.type.kind === Kind.NON_NULL_TYPE && fieldNode.type.type.kind === Kind.NAMED_TYPE) {
|
146
|
+
isValidIdType = options.acceptedIdTypes.includes(fieldNode.type.type.name.value);
|
147
|
+
}
|
148
|
+
return isValidIdName && isValidIdType;
|
149
|
+
});
|
150
|
+
if (validIds?.length !== 1) {
|
151
|
+
const pluralNamesSuffix = options.acceptedIdNames.length > 1 ? "s" : "";
|
152
|
+
const pluralTypesSuffix = options.acceptedIdTypes.length > 1 ? "s" : "";
|
140
153
|
context.report({
|
141
154
|
node: node.name,
|
142
155
|
message: `${displayNodeName(node)} must have exactly one non-nullable unique identifier.
|
@@ -3,11 +3,11 @@ import { getNodeName } from "../../utils.js";
|
|
3
3
|
const rule = {
|
4
4
|
meta: {
|
5
5
|
type: "suggestion",
|
6
|
-
hasSuggestions:
|
6
|
+
hasSuggestions: true,
|
7
7
|
docs: {
|
8
8
|
url: "https://the-guild.dev/graphql/eslint/rules/unique-enum-value-names",
|
9
9
|
category: "Schema",
|
10
|
-
recommended:
|
10
|
+
recommended: true,
|
11
11
|
description: `A GraphQL enum type is only valid if all its values are uniquely named.
|
12
12
|
> This rule disallows case-insensitive enum values duplicates too.`,
|
13
13
|
examples: [
|
@@ -42,8 +42,9 @@ const rule = {
|
|
42
42
|
schema: []
|
43
43
|
},
|
44
44
|
create(context) {
|
45
|
+
const selector = [Kind.ENUM_TYPE_DEFINITION, Kind.ENUM_TYPE_EXTENSION].join(",");
|
45
46
|
return {
|
46
|
-
[
|
47
|
+
[selector](node) {
|
47
48
|
const duplicates = node.values?.filter(
|
48
49
|
(item, index, array) => array.findIndex((v) => v.name.value.toLowerCase() === item.name.value.toLowerCase()) !== index
|
49
50
|
);
|
@@ -1,30 +1,38 @@
|
|
1
1
|
import { relative } from "node:path";
|
2
2
|
import { Kind } from "graphql";
|
3
3
|
import { CWD, requireGraphQLOperations, slash, VIRTUAL_DOCUMENT_REGEX } from "../../utils.js";
|
4
|
-
const RULE_ID = "unique-fragment-name"
|
5
|
-
|
6
|
-
|
4
|
+
const RULE_ID = "unique-fragment-name";
|
5
|
+
const checkNode = (context, node, ruleId) => {
|
6
|
+
const documentName = node.name.value;
|
7
|
+
const siblings = requireGraphQLOperations(ruleId, context);
|
8
|
+
const siblingDocuments = node.kind === Kind.FRAGMENT_DEFINITION ? siblings.getFragment(documentName) : siblings.getOperation(documentName);
|
9
|
+
const filepath = context.filename;
|
10
|
+
const conflictingDocuments = siblingDocuments.filter((f) => {
|
11
|
+
const isSameName = f.document.name?.value === documentName;
|
12
|
+
const isSamePath = slash(f.filePath) === slash(filepath);
|
7
13
|
return isSameName && !isSamePath;
|
8
14
|
});
|
9
|
-
conflictingDocuments.length > 0
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
`)
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
}
|
15
|
+
if (conflictingDocuments.length > 0) {
|
16
|
+
context.report({
|
17
|
+
messageId: ruleId,
|
18
|
+
data: {
|
19
|
+
documentName,
|
20
|
+
summary: conflictingDocuments.map((f) => ` ${relative(CWD, f.filePath.replace(VIRTUAL_DOCUMENT_REGEX, ""))}`).join("\n")
|
21
|
+
},
|
22
|
+
// @ts-expect-error name will exist
|
23
|
+
node: node.name
|
24
|
+
});
|
25
|
+
}
|
26
|
+
};
|
27
|
+
const rule = {
|
20
28
|
meta: {
|
21
29
|
type: "suggestion",
|
22
30
|
docs: {
|
23
31
|
category: "Operations",
|
24
32
|
description: "Enforce unique fragment names across your project.",
|
25
33
|
url: `https://the-guild.dev/graphql/eslint/rules/${RULE_ID}`,
|
26
|
-
requiresSiblings:
|
27
|
-
recommended:
|
34
|
+
requiresSiblings: true,
|
35
|
+
recommended: true,
|
28
36
|
examples: [
|
29
37
|
{
|
30
38
|
title: "Incorrect",
|
@@ -67,8 +75,7 @@ const RULE_ID = "unique-fragment-name", checkNode = (context, node, ruleId) => {
|
|
67
75
|
]
|
68
76
|
},
|
69
77
|
messages: {
|
70
|
-
[RULE_ID]:
|
71
|
-
{{ summary }}`
|
78
|
+
[RULE_ID]: 'Fragment named "{{ documentName }}" already defined in:\n{{ summary }}'
|
72
79
|
},
|
73
80
|
schema: []
|
74
81
|
},
|
@@ -1,13 +1,14 @@
|
|
1
1
|
import { checkNode } from "../unique-fragment-name/index.js";
|
2
|
-
const RULE_ID = "unique-operation-name"
|
2
|
+
const RULE_ID = "unique-operation-name";
|
3
|
+
const rule = {
|
3
4
|
meta: {
|
4
5
|
type: "suggestion",
|
5
6
|
docs: {
|
6
7
|
category: "Operations",
|
7
8
|
description: "Enforce unique operation names across your project.",
|
8
9
|
url: `https://the-guild.dev/graphql/eslint/rules/${RULE_ID}`,
|
9
|
-
requiresSiblings:
|
10
|
-
recommended:
|
10
|
+
requiresSiblings: true,
|
11
|
+
recommended: true,
|
11
12
|
examples: [
|
12
13
|
{
|
13
14
|
title: "Incorrect",
|
@@ -54,8 +55,7 @@ const RULE_ID = "unique-operation-name", rule = {
|
|
54
55
|
]
|
55
56
|
},
|
56
57
|
messages: {
|
57
|
-
[RULE_ID]:
|
58
|
-
{{ summary }}`
|
58
|
+
[RULE_ID]: 'Operation named "{{ documentName }}" already defined in:\n{{ summary }}'
|
59
59
|
},
|
60
60
|
schema: []
|
61
61
|
},
|
package/esm/schema.js
CHANGED
@@ -3,19 +3,24 @@ import debugFactory from "debug";
|
|
3
3
|
import fg from "fast-glob";
|
4
4
|
import { BREAK, GraphQLSchema, visit } from "graphql";
|
5
5
|
import { ModuleCache } from "./cache.js";
|
6
|
-
const schemaCache = new ModuleCache()
|
6
|
+
const schemaCache = new ModuleCache();
|
7
|
+
const debug = debugFactory("graphql-eslint:schema");
|
8
|
+
const require2 = createRequire(import.meta.url);
|
7
9
|
function getSchema(project) {
|
8
10
|
const schemaKey = project.schema;
|
9
|
-
if (!schemaKey)
|
11
|
+
if (!schemaKey) {
|
10
12
|
return null;
|
13
|
+
}
|
11
14
|
const cache = schemaCache.get(schemaKey);
|
12
|
-
if (cache)
|
15
|
+
if (cache) {
|
13
16
|
return cache;
|
17
|
+
}
|
14
18
|
debug("Loading schema from %o", project.schema);
|
15
19
|
const opts = {
|
16
20
|
pluckConfig: project.extensions.pluckConfig
|
17
|
-
}
|
18
|
-
|
21
|
+
};
|
22
|
+
const typeDefs = project.loadSchemaSync(project.schema, "DocumentNode", opts);
|
23
|
+
let isFederation = false;
|
19
24
|
visit(typeDefs, {
|
20
25
|
SchemaExtension(node) {
|
21
26
|
const linkDirective = node.directives?.find((d) => d.name.value === "link");
|
@@ -30,14 +35,16 @@ function getSchema(project) {
|
|
30
35
|
if (isFederation) {
|
31
36
|
const { buildSubgraphSchema } = require2("@apollo/subgraph");
|
32
37
|
schema = buildSubgraphSchema({ typeDefs });
|
33
|
-
} else
|
38
|
+
} else {
|
34
39
|
schema = project.loadSchemaSync(project.schema, "GraphQLSchema", opts);
|
40
|
+
}
|
35
41
|
if (debug.enabled) {
|
36
42
|
debug("Schema loaded: %o", schema instanceof GraphQLSchema);
|
37
|
-
const schemaPaths = fg.sync(project.schema, { absolute:
|
43
|
+
const schemaPaths = fg.sync(project.schema, { absolute: true });
|
38
44
|
debug("Schema pointers %O", schemaPaths);
|
39
45
|
}
|
40
|
-
|
46
|
+
schemaCache.set(schemaKey, schema);
|
47
|
+
return schema;
|
41
48
|
}
|
42
49
|
export {
|
43
50
|
getSchema
|
package/esm/siblings.js
CHANGED
@@ -6,12 +6,18 @@ import { logger } from "./utils.js";
|
|
6
6
|
const siblingOperationsCache = /* @__PURE__ */ new Map();
|
7
7
|
function getSiblings(documents) {
|
8
8
|
if (documents.length === 0) {
|
9
|
-
let printed =
|
10
|
-
const noopWarn = () =>
|
11
|
-
|
12
|
-
|
9
|
+
let printed = false;
|
10
|
+
const noopWarn = () => {
|
11
|
+
if (!printed) {
|
12
|
+
logger.warn(
|
13
|
+
"getSiblingOperations was called without any operations. Make sure to set graphql-config `documents` field to make this feature available! See https://the-guild.dev/graphql/config/docs/user/documents for more info"
|
14
|
+
);
|
15
|
+
printed = true;
|
16
|
+
}
|
17
|
+
return [];
|
18
|
+
};
|
13
19
|
return {
|
14
|
-
available:
|
20
|
+
available: false,
|
15
21
|
getFragment: noopWarn,
|
16
22
|
getFragments: noopWarn,
|
17
23
|
getFragmentByType: noopWarn,
|
@@ -22,18 +28,23 @@ function getSiblings(documents) {
|
|
22
28
|
};
|
23
29
|
}
|
24
30
|
const value = siblingOperationsCache.get(documents);
|
25
|
-
if (value)
|
31
|
+
if (value) {
|
26
32
|
return value;
|
33
|
+
}
|
27
34
|
let fragmentsCache = null;
|
28
35
|
const getFragments = () => {
|
29
36
|
if (fragmentsCache === null) {
|
30
37
|
const result = [];
|
31
|
-
for (const source of documents)
|
32
|
-
for (const definition of source.document?.definitions || [])
|
33
|
-
definition.kind === Kind.FRAGMENT_DEFINITION
|
34
|
-
|
35
|
-
|
36
|
-
|
38
|
+
for (const source of documents) {
|
39
|
+
for (const definition of source.document?.definitions || []) {
|
40
|
+
if (definition.kind === Kind.FRAGMENT_DEFINITION) {
|
41
|
+
result.push({
|
42
|
+
filePath: source.location,
|
43
|
+
document: definition
|
44
|
+
});
|
45
|
+
}
|
46
|
+
}
|
47
|
+
}
|
37
48
|
fragmentsCache = result;
|
38
49
|
}
|
39
50
|
return fragmentsCache;
|
@@ -42,37 +53,54 @@ function getSiblings(documents) {
|
|
42
53
|
const getOperations = () => {
|
43
54
|
if (cachedOperations === null) {
|
44
55
|
const result = [];
|
45
|
-
for (const source of documents)
|
46
|
-
for (const definition of source.document?.definitions || [])
|
47
|
-
definition.kind === Kind.OPERATION_DEFINITION
|
48
|
-
|
49
|
-
|
50
|
-
|
56
|
+
for (const source of documents) {
|
57
|
+
for (const definition of source.document?.definitions || []) {
|
58
|
+
if (definition.kind === Kind.OPERATION_DEFINITION) {
|
59
|
+
result.push({
|
60
|
+
filePath: source.location,
|
61
|
+
document: definition
|
62
|
+
});
|
63
|
+
}
|
64
|
+
}
|
65
|
+
}
|
51
66
|
cachedOperations = result;
|
52
67
|
}
|
53
68
|
return cachedOperations;
|
54
|
-
}
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
);
|
61
|
-
|
69
|
+
};
|
70
|
+
const getFragment = (name) => getFragments().filter((f) => f.document.name.value === name);
|
71
|
+
const collectFragments = (selectable, recursive, collected = /* @__PURE__ */ new Map()) => {
|
72
|
+
visit(selectable, {
|
73
|
+
FragmentSpread(spread) {
|
74
|
+
const fragmentName = spread.name.value;
|
75
|
+
const [fragment] = getFragment(fragmentName);
|
76
|
+
if (!fragment) {
|
77
|
+
logger.warn(
|
78
|
+
`Unable to locate fragment named "${fragmentName}", please make sure it's loaded using "parserOptions.operations"`
|
79
|
+
);
|
80
|
+
return;
|
81
|
+
}
|
82
|
+
if (!collected.has(fragmentName)) {
|
83
|
+
collected.set(fragmentName, fragment.document);
|
84
|
+
if (recursive) {
|
85
|
+
collectFragments(fragment.document, recursive, collected);
|
86
|
+
}
|
87
|
+
}
|
62
88
|
}
|
63
|
-
|
64
|
-
|
65
|
-
}
|
66
|
-
|
89
|
+
});
|
90
|
+
return collected;
|
91
|
+
};
|
92
|
+
const siblingOperations = {
|
93
|
+
available: true,
|
67
94
|
getFragment,
|
68
95
|
getFragments,
|
69
96
|
getFragmentByType: (typeName) => getFragments().filter((f) => f.document.typeCondition.name.value === typeName),
|
70
|
-
getFragmentsInUse: (selectable, recursive =
|
97
|
+
getFragmentsInUse: (selectable, recursive = true) => Array.from(collectFragments(selectable, recursive).values()),
|
71
98
|
getOperation: (name) => getOperations().filter((o) => o.document.name?.value === name),
|
72
99
|
getOperations,
|
73
100
|
getOperationByType: (type) => getOperations().filter((o) => o.document.operation === type)
|
74
101
|
};
|
75
|
-
|
102
|
+
siblingOperationsCache.set(documents, siblingOperations);
|
103
|
+
return siblingOperations;
|
76
104
|
}
|
77
105
|
export {
|
78
106
|
getSiblings
|
package/esm/utils.js
CHANGED
@@ -2,24 +2,27 @@ import { Kind } from "graphql";
|
|
2
2
|
import lowerCase from "lodash.lowercase";
|
3
3
|
function requireGraphQLOperations(ruleId, context) {
|
4
4
|
const { siblingOperations } = context.sourceCode.parserServices;
|
5
|
-
if (!siblingOperations.available)
|
5
|
+
if (!siblingOperations.available) {
|
6
6
|
throw new Error(
|
7
7
|
`Rule \`${ruleId}\` requires graphql-config \`documents\` field to be set and loaded. See https://the-guild.dev/graphql/eslint/docs/usage#providing-operations for more info`
|
8
8
|
);
|
9
|
+
}
|
9
10
|
return siblingOperations;
|
10
11
|
}
|
11
12
|
function requireGraphQLSchema(ruleId, context) {
|
12
13
|
const { schema } = context.sourceCode.parserServices;
|
13
|
-
if (!schema)
|
14
|
+
if (!schema) {
|
14
15
|
throw new Error(
|
15
16
|
`Rule \`${ruleId}\` requires graphql-config \`schema\` field to be set and loaded. See https://the-guild.dev/graphql/eslint/docs/usage#providing-schema for more info`
|
16
17
|
);
|
18
|
+
}
|
17
19
|
return schema;
|
18
20
|
}
|
19
21
|
const chalk = {
|
20
22
|
red: (str) => `\x1B[31m${str}\x1B[39m`,
|
21
23
|
yellow: (str) => `\x1B[33m${str}\x1B[39m`
|
22
|
-
}
|
24
|
+
};
|
25
|
+
const logger = {
|
23
26
|
error: (...args) => (
|
24
27
|
// eslint-disable-next-line no-console
|
25
28
|
console.error(chalk.red("error"), "[graphql-eslint]", ...args)
|
@@ -28,17 +31,25 @@ const chalk = {
|
|
28
31
|
// eslint-disable-next-line no-console
|
29
32
|
console.warn(chalk.yellow("warning"), "[graphql-eslint]", ...args)
|
30
33
|
)
|
31
|
-
}
|
34
|
+
};
|
35
|
+
const slash = (path) => path.replaceAll("\\", "/");
|
36
|
+
const VIRTUAL_DOCUMENT_REGEX = /[/\\]\d+_document.graphql$/;
|
37
|
+
const CWD = process.cwd();
|
38
|
+
const getTypeName = (node) => "type" in node ? getTypeName(node.type) : "name" in node && node.name ? node.name.value : "";
|
39
|
+
const TYPES_KINDS = [
|
32
40
|
Kind.OBJECT_TYPE_DEFINITION,
|
33
41
|
Kind.INTERFACE_TYPE_DEFINITION,
|
34
42
|
Kind.ENUM_TYPE_DEFINITION,
|
35
43
|
Kind.SCALAR_TYPE_DEFINITION,
|
36
44
|
Kind.INPUT_OBJECT_TYPE_DEFINITION,
|
37
45
|
Kind.UNION_TYPE_DEFINITION
|
38
|
-
]
|
46
|
+
];
|
47
|
+
const pascalCase = (str) => lowerCase(str).split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
|
48
|
+
const camelCase = (str) => {
|
39
49
|
const result = pascalCase(str);
|
40
50
|
return result.charAt(0).toLowerCase() + result.slice(1);
|
41
|
-
}
|
51
|
+
};
|
52
|
+
const convertCase = (style, str) => {
|
42
53
|
switch (style) {
|
43
54
|
case "camelCase":
|
44
55
|
return camelCase(str);
|
@@ -65,14 +76,17 @@ function getLocation(start, fieldName = "") {
|
|
65
76
|
}
|
66
77
|
};
|
67
78
|
}
|
68
|
-
const REPORT_ON_FIRST_CHARACTER = { column: 0, line: 1 }
|
79
|
+
const REPORT_ON_FIRST_CHARACTER = { column: 0, line: 1 };
|
80
|
+
const ARRAY_DEFAULT_OPTIONS = {
|
69
81
|
type: "array",
|
70
|
-
uniqueItems:
|
82
|
+
uniqueItems: true,
|
71
83
|
minItems: 1,
|
72
84
|
items: {
|
73
85
|
type: "string"
|
74
86
|
}
|
75
|
-
}
|
87
|
+
};
|
88
|
+
const englishJoinWords = (words) => new Intl.ListFormat("en-US", { type: "disjunction" }).format(words);
|
89
|
+
const DisplayNodeNameMap = {
|
76
90
|
[Kind.ARGUMENT]: "argument",
|
77
91
|
[Kind.BOOLEAN]: "boolean",
|
78
92
|
[Kind.DIRECTIVE_DEFINITION]: "directive",
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@graphql-eslint/eslint-plugin",
|
3
|
-
"version": "4.3.
|
3
|
+
"version": "4.3.1-alpha-20241209185034-de2d7397da8c26620a8930dd12b6dff42e43f537",
|
4
4
|
"type": "module",
|
5
5
|
"description": "GraphQL plugin for ESLint",
|
6
6
|
"repository": "https://github.com/dimaMachina/graphql-eslint",
|
@@ -20,6 +20,16 @@
|
|
20
20
|
"types": "./esm/index.d.ts",
|
21
21
|
"default": "./esm/index.js"
|
22
22
|
}
|
23
|
+
},
|
24
|
+
"./*": {
|
25
|
+
"require": {
|
26
|
+
"types": "./cjs/*.d.cts",
|
27
|
+
"default": "./cjs/*.js"
|
28
|
+
},
|
29
|
+
"import": {
|
30
|
+
"types": "./esm/*.d.ts",
|
31
|
+
"default": "./esm/*.js"
|
32
|
+
}
|
23
33
|
}
|
24
34
|
},
|
25
35
|
"types": "esm/index.d.ts",
|