@graphql-eslint/eslint-plugin 3.19.3 → 3.20.0-alpha-20230703155329-a20556f
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/{typings → cjs}/cache.d.ts +3 -1
- package/cjs/cache.js +57 -26
- package/{typings → cjs}/configs/index.d.ts +3 -1
- package/cjs/configs/index.js +47 -14
- package/{typings → cjs}/configs/operations-all.d.ts +2 -1
- package/cjs/configs/operations-all.js +47 -30
- package/{typings → cjs}/configs/operations-recommended.d.ts +2 -1
- package/cjs/configs/operations-recommended.js +72 -55
- package/{typings → cjs}/configs/relay.d.ts +2 -1
- package/cjs/configs/relay.js +31 -11
- package/{typings → cjs}/configs/schema-all.d.ts +2 -1
- package/cjs/configs/schema-all.js +41 -24
- package/{typings → cjs}/configs/schema-recommended.d.ts +2 -1
- package/cjs/configs/schema-recommended.js +68 -51
- package/{typings → cjs}/documents.d.ts +4 -1
- package/cjs/documents.js +74 -43
- package/cjs/estree-converter/converter.d.ts +8 -0
- package/cjs/estree-converter/converter.js +80 -59
- package/cjs/estree-converter/index.d.ts +8 -0
- package/cjs/estree-converter/index.js +25 -5
- package/{typings → cjs}/estree-converter/types.d.ts +7 -5
- package/cjs/estree-converter/types.js +16 -0
- package/cjs/estree-converter/utils.d.ts +18 -0
- package/cjs/estree-converter/utils.js +119 -95
- package/{typings → cjs}/flat-configs.d.ts +12 -1
- package/cjs/flat-configs.js +56 -32
- package/cjs/graphql-config.d.ts +13 -0
- package/cjs/graphql-config.js +78 -47
- package/cjs/index.d.ts +22 -0
- package/cjs/index.js +50 -18
- package/cjs/parser.d.ts +12 -0
- package/cjs/parser.js +96 -76
- package/cjs/processor.d.ts +9 -0
- package/cjs/processor.js +119 -88
- package/{typings → cjs}/rules/alphabetize.d.ts +12 -4
- package/cjs/rules/alphabetize.js +337 -295
- package/{typings → cjs}/rules/description-style.d.ts +12 -4
- package/cjs/rules/description-style.js +96 -66
- package/cjs/rules/graphql-js-validation.d.ts +12 -0
- package/cjs/rules/graphql-js-validation.js +595 -433
- package/cjs/rules/index.d.ts +125 -0
- package/cjs/rules/index.js +97 -76
- package/{typings → cjs}/rules/input-name.d.ts +12 -4
- package/cjs/rules/input-name.js +153 -123
- package/{typings → cjs}/rules/lone-executable-definition.d.ts +12 -4
- package/cjs/rules/lone-executable-definition.js +101 -72
- package/{typings → cjs}/rules/match-document-filename.d.ts +14 -6
- package/cjs/rules/match-document-filename.js +229 -182
- package/{typings → cjs}/rules/naming-convention.d.ts +12 -4
- package/cjs/rules/naming-convention.js +380 -316
- package/cjs/rules/no-anonymous-operations.d.ts +12 -0
- package/cjs/rules/no-anonymous-operations.js +88 -57
- package/cjs/rules/no-case-insensitive-enum-values-duplicates.d.ts +12 -0
- package/cjs/rules/no-case-insensitive-enum-values-duplicates.js +82 -50
- package/cjs/rules/no-deprecated.d.ts +12 -0
- package/cjs/rules/no-deprecated.js +106 -75
- package/cjs/rules/no-duplicate-fields.d.ts +12 -0
- package/cjs/rules/no-duplicate-fields.js +116 -82
- package/cjs/rules/no-hashtag-description.d.ts +13 -0
- package/cjs/rules/no-hashtag-description.js +119 -82
- package/cjs/rules/no-one-place-fragments.d.ts +12 -0
- package/cjs/rules/no-one-place-fragments.js +88 -58
- package/{typings → cjs}/rules/no-root-type.d.ts +12 -4
- package/cjs/rules/no-root-type.js +101 -74
- package/cjs/rules/no-scalar-result-type-on-mutation.d.ts +12 -0
- package/cjs/rules/no-scalar-result-type-on-mutation.js +90 -60
- package/cjs/rules/no-typename-prefix.d.ts +12 -0
- package/cjs/rules/no-typename-prefix.js +88 -55
- package/cjs/rules/no-unreachable-types.d.ts +12 -0
- package/cjs/rules/no-unreachable-types.js +169 -134
- package/cjs/rules/no-unused-fields.d.ts +12 -0
- package/cjs/rules/no-unused-fields.js +117 -92
- package/{typings → cjs}/rules/relay-arguments.d.ts +12 -4
- package/cjs/rules/relay-arguments.js +136 -110
- package/cjs/rules/relay-connection-types.d.ts +13 -0
- package/cjs/rules/relay-connection-types.js +123 -94
- package/{typings → cjs}/rules/relay-edge-types.d.ts +12 -4
- package/cjs/rules/relay-edge-types.js +196 -179
- package/cjs/rules/relay-page-info.d.ts +12 -0
- package/cjs/rules/relay-page-info.js +108 -89
- package/{typings → cjs}/rules/require-deprecation-date.d.ts +12 -4
- package/cjs/rules/require-deprecation-date.js +143 -112
- package/cjs/rules/require-deprecation-reason.d.ts +12 -0
- package/cjs/rules/require-deprecation-reason.js +80 -46
- package/{typings → cjs}/rules/require-description.d.ts +13 -5
- package/cjs/rules/require-description.js +170 -132
- package/cjs/rules/require-field-of-type-query-in-mutation-result.d.ts +12 -0
- package/cjs/rules/require-field-of-type-query-in-mutation-result.js +81 -51
- package/{typings → cjs}/rules/require-id-when-available.d.ts +12 -4
- package/cjs/rules/require-id-when-available.js +196 -173
- package/cjs/rules/require-import-fragment.d.ts +12 -0
- package/cjs/rules/require-import-fragment.js +138 -88
- package/cjs/rules/require-nullable-fields-with-oneof.d.ts +12 -0
- package/cjs/rules/require-nullable-fields-with-oneof.js +80 -50
- package/cjs/rules/require-nullable-result-in-root.d.ts +12 -0
- package/cjs/rules/require-nullable-result-in-root.js +97 -68
- package/cjs/rules/require-type-pattern-with-oneof.d.ts +12 -0
- package/cjs/rules/require-type-pattern-with-oneof.js +70 -42
- package/{typings → cjs}/rules/selection-set-depth.d.ts +12 -4
- package/cjs/rules/selection-set-depth.js +147 -107
- package/{typings → cjs}/rules/strict-id-in-types.d.ts +12 -4
- package/cjs/rules/strict-id-in-types.js +143 -122
- package/cjs/rules/unique-fragment-name.d.ts +13 -0
- package/cjs/rules/unique-fragment-name.js +88 -62
- package/cjs/rules/unique-operation-name.d.ts +12 -0
- package/cjs/rules/unique-operation-name.js +65 -35
- package/cjs/schema.d.ts +12 -0
- package/cjs/schema.js +62 -30
- package/cjs/siblings.d.ts +8 -0
- package/cjs/siblings.js +124 -106
- package/cjs/testkit.d.ts +8 -0
- package/cjs/testkit.js +165 -144
- package/cjs/types-e3367e3c.d.ts +129 -0
- package/cjs/types.d.ts +8 -0
- package/cjs/types.js +16 -0
- package/cjs/utils.d.ts +44 -0
- package/cjs/utils.js +181 -124
- package/{typings/cache.d.cts → esm/cache.d.mts} +3 -1
- package/esm/cache.js +25 -23
- package/{typings/configs/index.d.cts → esm/configs/index.d.mts} +3 -1
- package/esm/configs/index.js +14 -11
- package/{typings/configs/operations-all.d.cts → esm/configs/operations-all.d.mts} +2 -1
- package/esm/configs/operations-all.js +28 -28
- package/{typings/configs/operations-recommended.d.cts → esm/configs/operations-recommended.d.mts} +2 -1
- package/esm/configs/operations-recommended.js +53 -53
- package/{typings/configs/relay.d.cts → esm/configs/relay.d.mts} +2 -1
- package/esm/configs/relay.js +12 -9
- package/{typings/configs/schema-all.d.cts → esm/configs/schema-all.d.mts} +2 -1
- package/esm/configs/schema-all.js +22 -22
- package/{typings/configs/schema-recommended.d.cts → esm/configs/schema-recommended.d.mts} +2 -1
- package/esm/configs/schema-recommended.js +49 -49
- package/{typings/documents.d.cts → esm/documents.d.mts} +4 -1
- package/esm/documents.js +41 -39
- package/esm/estree-converter/converter.d.mts +8 -0
- package/esm/estree-converter/converter.js +63 -57
- package/esm/estree-converter/index.d.mts +8 -0
- package/esm/estree-converter/index.js +3 -3
- package/{typings/estree-converter/types.d.cts → esm/estree-converter/types.d.mts} +7 -5
- package/esm/estree-converter/utils.d.mts +18 -0
- package/esm/estree-converter/utils.js +102 -93
- package/{typings/flat-configs.d.cts → esm/flat-configs.d.mts} +13 -2
- package/esm/flat-configs.js +33 -30
- package/esm/graphql-config.d.mts +13 -0
- package/esm/graphql-config.js +49 -44
- package/esm/index.d.mts +22 -0
- package/esm/index.js +18 -9
- package/esm/package.json +1 -1
- package/esm/parser.d.mts +12 -0
- package/esm/parser.js +64 -73
- package/esm/processor.d.mts +9 -0
- package/esm/processor.js +98 -86
- package/{typings/rules/alphabetize.d.cts → esm/rules/alphabetize.d.mts} +12 -4
- package/esm/rules/alphabetize.js +304 -290
- package/{typings/rules/description-style.d.cts → esm/rules/description-style.d.mts} +12 -4
- package/esm/rules/description-style.js +73 -64
- package/esm/rules/graphql-js-validation.d.mts +12 -0
- package/esm/rules/graphql-js-validation.js +580 -429
- package/esm/rules/index.d.mts +125 -0
- package/esm/rules/index.js +74 -74
- package/{typings/rules/input-name.d.cts → esm/rules/input-name.d.mts} +12 -4
- package/esm/rules/input-name.js +132 -121
- package/{typings/rules/lone-executable-definition.d.cts → esm/rules/lone-executable-definition.d.mts} +12 -4
- package/esm/rules/lone-executable-definition.js +78 -70
- package/{typings/rules/match-document-filename.d.cts → esm/rules/match-document-filename.d.mts} +14 -6
- package/esm/rules/match-document-filename.js +210 -180
- package/{typings/rules/naming-convention.d.cts → esm/rules/naming-convention.d.mts} +12 -4
- package/esm/rules/naming-convention.js +363 -314
- package/esm/rules/no-anonymous-operations.d.mts +12 -0
- package/esm/rules/no-anonymous-operations.js +65 -55
- package/esm/rules/no-case-insensitive-enum-values-duplicates.d.mts +12 -0
- package/esm/rules/no-case-insensitive-enum-values-duplicates.js +59 -48
- package/esm/rules/no-deprecated.d.mts +12 -0
- package/esm/rules/no-deprecated.js +83 -73
- package/esm/rules/no-duplicate-fields.d.mts +12 -0
- package/esm/rules/no-duplicate-fields.js +93 -80
- package/esm/rules/no-hashtag-description.d.mts +13 -0
- package/esm/rules/no-hashtag-description.js +95 -80
- package/esm/rules/no-one-place-fragments.d.mts +12 -0
- package/esm/rules/no-one-place-fragments.js +65 -56
- package/{typings/rules/no-root-type.d.cts → esm/rules/no-root-type.d.mts} +12 -4
- package/esm/rules/no-root-type.js +78 -72
- package/esm/rules/no-scalar-result-type-on-mutation.d.mts +12 -0
- package/esm/rules/no-scalar-result-type-on-mutation.js +67 -58
- package/esm/rules/no-typename-prefix.d.mts +12 -0
- package/esm/rules/no-typename-prefix.js +65 -53
- package/esm/rules/no-unreachable-types.d.mts +12 -0
- package/esm/rules/no-unreachable-types.js +141 -131
- package/esm/rules/no-unused-fields.d.mts +12 -0
- package/esm/rules/no-unused-fields.js +94 -90
- package/{typings/rules/relay-arguments.d.cts → esm/rules/relay-arguments.d.mts} +12 -4
- package/esm/rules/relay-arguments.js +113 -108
- package/esm/rules/relay-connection-types.d.mts +13 -0
- package/esm/rules/relay-connection-types.js +98 -91
- package/{typings/rules/relay-edge-types.d.cts → esm/rules/relay-edge-types.d.mts} +12 -4
- package/esm/rules/relay-edge-types.js +178 -177
- package/esm/rules/relay-page-info.d.mts +12 -0
- package/esm/rules/relay-page-info.js +84 -86
- package/{typings/rules/require-deprecation-date.d.cts → esm/rules/require-deprecation-date.d.mts} +12 -4
- package/esm/rules/require-deprecation-date.js +120 -110
- package/esm/rules/require-deprecation-reason.d.mts +12 -0
- package/esm/rules/require-deprecation-reason.js +57 -44
- package/{typings/rules/require-description.d.cts → esm/rules/require-description.d.mts} +13 -5
- package/esm/rules/require-description.js +151 -130
- package/esm/rules/require-field-of-type-query-in-mutation-result.d.mts +12 -0
- package/esm/rules/require-field-of-type-query-in-mutation-result.js +58 -49
- package/{typings/rules/require-id-when-available.d.cts → esm/rules/require-id-when-available.d.mts} +12 -4
- package/esm/rules/require-id-when-available.js +186 -171
- package/esm/rules/require-import-fragment.d.mts +12 -0
- package/esm/rules/require-import-fragment.js +105 -85
- package/esm/rules/require-nullable-fields-with-oneof.d.mts +12 -0
- package/esm/rules/require-nullable-fields-with-oneof.js +57 -48
- package/esm/rules/require-nullable-result-in-root.d.mts +12 -0
- package/esm/rules/require-nullable-result-in-root.js +74 -66
- package/esm/rules/require-type-pattern-with-oneof.d.mts +12 -0
- package/esm/rules/require-type-pattern-with-oneof.js +47 -40
- package/{typings/rules/selection-set-depth.d.cts → esm/rules/selection-set-depth.d.mts} +12 -4
- package/esm/rules/selection-set-depth.js +114 -104
- package/{typings/rules/strict-id-in-types.d.cts → esm/rules/strict-id-in-types.d.mts} +12 -4
- package/esm/rules/strict-id-in-types.js +125 -119
- package/esm/rules/unique-fragment-name.d.mts +13 -0
- package/esm/rules/unique-fragment-name.js +65 -60
- package/esm/rules/unique-operation-name.d.mts +12 -0
- package/esm/rules/unique-operation-name.js +42 -33
- package/esm/schema.d.mts +12 -0
- package/esm/schema.js +29 -26
- package/esm/siblings.d.mts +8 -0
- package/esm/siblings.js +105 -104
- package/esm/testkit.d.mts +8 -0
- package/esm/testkit.js +132 -141
- package/esm/types-2e1afd7c.d.ts +129 -0
- package/esm/types.d.mts +8 -0
- package/esm/utils.d.mts +44 -0
- package/esm/utils.js +138 -116
- package/package.json +40 -30
- package/typings/estree-converter/converter.d.cts +0 -3
- package/typings/estree-converter/converter.d.ts +0 -3
- package/typings/estree-converter/index.d.cts +0 -3
- package/typings/estree-converter/index.d.ts +0 -3
- package/typings/estree-converter/utils.d.cts +0 -13
- package/typings/estree-converter/utils.d.ts +0 -13
- package/typings/graphql-config.d.cts +0 -4
- package/typings/graphql-config.d.ts +0 -4
- package/typings/index.d.cts +0 -10
- package/typings/index.d.ts +0 -10
- package/typings/parser.d.cts +0 -2
- package/typings/parser.d.ts +0 -2
- package/typings/processor.d.cts +0 -6
- package/typings/processor.d.ts +0 -6
- package/typings/rules/graphql-js-validation.d.cts +0 -2
- package/typings/rules/graphql-js-validation.d.ts +0 -2
- package/typings/rules/index.d.cts +0 -111
- package/typings/rules/index.d.ts +0 -111
- package/typings/rules/no-anonymous-operations.d.cts +0 -2
- package/typings/rules/no-anonymous-operations.d.ts +0 -2
- package/typings/rules/no-case-insensitive-enum-values-duplicates.d.cts +0 -2
- package/typings/rules/no-case-insensitive-enum-values-duplicates.d.ts +0 -2
- package/typings/rules/no-deprecated.d.cts +0 -2
- package/typings/rules/no-deprecated.d.ts +0 -2
- package/typings/rules/no-duplicate-fields.d.cts +0 -2
- package/typings/rules/no-duplicate-fields.d.ts +0 -2
- package/typings/rules/no-hashtag-description.d.cts +0 -3
- package/typings/rules/no-hashtag-description.d.ts +0 -3
- package/typings/rules/no-one-place-fragments.d.cts +0 -2
- package/typings/rules/no-one-place-fragments.d.ts +0 -2
- package/typings/rules/no-scalar-result-type-on-mutation.d.cts +0 -2
- package/typings/rules/no-scalar-result-type-on-mutation.d.ts +0 -2
- package/typings/rules/no-typename-prefix.d.cts +0 -2
- package/typings/rules/no-typename-prefix.d.ts +0 -2
- package/typings/rules/no-unreachable-types.d.cts +0 -2
- package/typings/rules/no-unreachable-types.d.ts +0 -2
- package/typings/rules/no-unused-fields.d.cts +0 -2
- package/typings/rules/no-unused-fields.d.ts +0 -2
- package/typings/rules/relay-connection-types.d.cts +0 -4
- package/typings/rules/relay-connection-types.d.ts +0 -4
- package/typings/rules/relay-page-info.d.cts +0 -2
- package/typings/rules/relay-page-info.d.ts +0 -2
- package/typings/rules/require-deprecation-reason.d.cts +0 -2
- package/typings/rules/require-deprecation-reason.d.ts +0 -2
- package/typings/rules/require-field-of-type-query-in-mutation-result.d.cts +0 -2
- package/typings/rules/require-field-of-type-query-in-mutation-result.d.ts +0 -2
- package/typings/rules/require-import-fragment.d.cts +0 -2
- package/typings/rules/require-import-fragment.d.ts +0 -2
- package/typings/rules/require-nullable-fields-with-oneof.d.cts +0 -2
- package/typings/rules/require-nullable-fields-with-oneof.d.ts +0 -2
- package/typings/rules/require-nullable-result-in-root.d.cts +0 -2
- package/typings/rules/require-nullable-result-in-root.d.ts +0 -2
- package/typings/rules/require-type-pattern-with-oneof.d.cts +0 -2
- package/typings/rules/require-type-pattern-with-oneof.d.ts +0 -2
- package/typings/rules/unique-fragment-name.d.cts +0 -5
- package/typings/rules/unique-fragment-name.d.ts +0 -5
- package/typings/rules/unique-operation-name.d.cts +0 -2
- package/typings/rules/unique-operation-name.d.ts +0 -2
- package/typings/schema.d.cts +0 -3
- package/typings/schema.d.ts +0 -3
- package/typings/siblings.d.cts +0 -22
- package/typings/siblings.d.ts +0 -22
- package/typings/testkit.d.cts +0 -29
- package/typings/testkit.d.ts +0 -29
- package/typings/types.d.cts +0 -83
- package/typings/types.d.ts +0 -83
- package/typings/utils.d.cts +0 -40
- package/typings/utils.d.ts +0 -40
@@ -1,119 +1,124 @@
|
|
1
|
-
import { isScalarType, Kind } from
|
2
|
-
import { requireGraphQLSchemaFromContext } from
|
3
|
-
const RULE_ID =
|
4
|
-
const MISSING_ARGUMENTS =
|
1
|
+
import { isScalarType, Kind } from "graphql";
|
2
|
+
import { requireGraphQLSchemaFromContext } from "../utils.js";
|
3
|
+
const RULE_ID = "relay-arguments";
|
4
|
+
const MISSING_ARGUMENTS = "MISSING_ARGUMENTS";
|
5
5
|
const schema = {
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
6
|
+
type: "array",
|
7
|
+
maxItems: 1,
|
8
|
+
items: {
|
9
|
+
type: "object",
|
10
|
+
additionalProperties: false,
|
11
|
+
minProperties: 1,
|
12
|
+
properties: {
|
13
|
+
includeBoth: {
|
14
|
+
type: "boolean",
|
15
|
+
default: true,
|
16
|
+
description: "Enforce including both forward and backward pagination arguments"
|
17
|
+
}
|
18
|
+
}
|
19
|
+
}
|
20
20
|
};
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
21
|
+
const rule = {
|
22
|
+
meta: {
|
23
|
+
type: "problem",
|
24
|
+
docs: {
|
25
|
+
category: "Schema",
|
26
|
+
description: [
|
27
|
+
"Set of rules to follow Relay specification for Arguments.",
|
28
|
+
"",
|
29
|
+
"- A field that returns a Connection type must include forward pagination arguments (`first` and `after`), backward pagination arguments (`last` and `before`), or both",
|
30
|
+
"",
|
31
|
+
"Forward pagination arguments",
|
32
|
+
"",
|
33
|
+
"- `first` takes a non-negative integer",
|
34
|
+
"- `after` takes the Cursor type",
|
35
|
+
"",
|
36
|
+
"Backward pagination arguments",
|
37
|
+
"",
|
38
|
+
"- `last` takes a non-negative integer",
|
39
|
+
"- `before` takes the Cursor type"
|
40
|
+
].join("\n"),
|
41
|
+
url: `https://the-guild.dev/graphql/eslint/rules/${RULE_ID}`,
|
42
|
+
examples: [
|
43
|
+
{
|
44
|
+
title: "Incorrect",
|
45
|
+
code: (
|
46
|
+
/* GraphQL */
|
47
|
+
`
|
46
48
|
type User {
|
47
49
|
posts: PostConnection
|
48
50
|
}
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
51
|
+
`
|
52
|
+
)
|
53
|
+
},
|
54
|
+
{
|
55
|
+
title: "Correct",
|
56
|
+
code: (
|
57
|
+
/* GraphQL */
|
58
|
+
`
|
54
59
|
type User {
|
55
60
|
posts(after: String, first: Int, before: String, last: Int): PostConnection
|
56
61
|
}
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
messages: {
|
63
|
-
[MISSING_ARGUMENTS]: 'A field that returns a Connection type must include forward pagination arguments (`first` and `after`), backward pagination arguments (`last` and `before`), or both.',
|
64
|
-
},
|
65
|
-
schema,
|
62
|
+
`
|
63
|
+
)
|
64
|
+
}
|
65
|
+
],
|
66
|
+
isDisabledForAllConfig: true
|
66
67
|
},
|
67
|
-
|
68
|
-
|
69
|
-
const { includeBoth = true } = context.options[0] || {};
|
70
|
-
return {
|
71
|
-
'FieldDefinition > .gqlType Name[value=/Connection$/]'(node) {
|
72
|
-
var _a;
|
73
|
-
let fieldNode = node.parent;
|
74
|
-
while (fieldNode.kind !== Kind.FIELD_DEFINITION) {
|
75
|
-
fieldNode = fieldNode.parent;
|
76
|
-
}
|
77
|
-
const args = Object.fromEntries(((_a = fieldNode.arguments) === null || _a === void 0 ? void 0 : _a.map(argument => [argument.name.value, argument])) || []);
|
78
|
-
const hasForwardPagination = !!(args.first && args.after);
|
79
|
-
const hasBackwardPagination = !!(args.last && args.before);
|
80
|
-
if (!hasForwardPagination && !hasBackwardPagination) {
|
81
|
-
context.report({
|
82
|
-
node: fieldNode.name,
|
83
|
-
messageId: MISSING_ARGUMENTS,
|
84
|
-
});
|
85
|
-
return;
|
86
|
-
}
|
87
|
-
function checkField(typeName, argumentName) {
|
88
|
-
const argument = args[argumentName];
|
89
|
-
const hasArgument = !!argument;
|
90
|
-
let type = argument;
|
91
|
-
if (hasArgument && type.gqlType.kind === Kind.NON_NULL_TYPE) {
|
92
|
-
type = type.gqlType;
|
93
|
-
}
|
94
|
-
const isAllowedNonNullType = hasArgument &&
|
95
|
-
type.gqlType.kind === Kind.NAMED_TYPE &&
|
96
|
-
(type.gqlType.name.value === typeName ||
|
97
|
-
(typeName === 'String' && isScalarType(schema.getType(type.gqlType.name.value))));
|
98
|
-
if (!isAllowedNonNullType) {
|
99
|
-
const returnType = typeName === 'String' ? 'String or Scalar' : typeName;
|
100
|
-
context.report({
|
101
|
-
node: (argument || fieldNode).name,
|
102
|
-
message: hasArgument
|
103
|
-
? `Argument \`${argumentName}\` must return ${returnType}.`
|
104
|
-
: `Field \`${fieldNode.name.value}\` must contain an argument \`${argumentName}\`, that return ${returnType}.`,
|
105
|
-
});
|
106
|
-
}
|
107
|
-
}
|
108
|
-
if (includeBoth || args.first || args.after) {
|
109
|
-
checkField('Int', 'first');
|
110
|
-
checkField('String', 'after');
|
111
|
-
}
|
112
|
-
if (includeBoth || args.last || args.before) {
|
113
|
-
checkField('Int', 'last');
|
114
|
-
checkField('String', 'before');
|
115
|
-
}
|
116
|
-
},
|
117
|
-
};
|
68
|
+
messages: {
|
69
|
+
[MISSING_ARGUMENTS]: "A field that returns a Connection type must include forward pagination arguments (`first` and `after`), backward pagination arguments (`last` and `before`), or both."
|
118
70
|
},
|
71
|
+
schema
|
72
|
+
},
|
73
|
+
create(context) {
|
74
|
+
const schema2 = requireGraphQLSchemaFromContext(RULE_ID, context);
|
75
|
+
const { includeBoth = true } = context.options[0] || {};
|
76
|
+
return {
|
77
|
+
"FieldDefinition > .gqlType Name[value=/Connection$/]"(node) {
|
78
|
+
let fieldNode = node.parent;
|
79
|
+
while (fieldNode.kind !== Kind.FIELD_DEFINITION) {
|
80
|
+
fieldNode = fieldNode.parent;
|
81
|
+
}
|
82
|
+
const args = Object.fromEntries(
|
83
|
+
fieldNode.arguments?.map((argument) => [argument.name.value, argument]) || []
|
84
|
+
);
|
85
|
+
const hasForwardPagination = !!(args.first && args.after);
|
86
|
+
const hasBackwardPagination = !!(args.last && args.before);
|
87
|
+
if (!hasForwardPagination && !hasBackwardPagination) {
|
88
|
+
context.report({
|
89
|
+
node: fieldNode.name,
|
90
|
+
messageId: MISSING_ARGUMENTS
|
91
|
+
});
|
92
|
+
return;
|
93
|
+
}
|
94
|
+
function checkField(typeName, argumentName) {
|
95
|
+
const argument = args[argumentName];
|
96
|
+
const hasArgument = !!argument;
|
97
|
+
let type = argument;
|
98
|
+
if (hasArgument && type.gqlType.kind === Kind.NON_NULL_TYPE) {
|
99
|
+
type = type.gqlType;
|
100
|
+
}
|
101
|
+
const isAllowedNonNullType = hasArgument && type.gqlType.kind === Kind.NAMED_TYPE && (type.gqlType.name.value === typeName || typeName === "String" && isScalarType(schema2.getType(type.gqlType.name.value)));
|
102
|
+
if (!isAllowedNonNullType) {
|
103
|
+
const returnType = typeName === "String" ? "String or Scalar" : typeName;
|
104
|
+
context.report({
|
105
|
+
node: (argument || fieldNode).name,
|
106
|
+
message: hasArgument ? `Argument \`${argumentName}\` must return ${returnType}.` : `Field \`${fieldNode.name.value}\` must contain an argument \`${argumentName}\`, that return ${returnType}.`
|
107
|
+
});
|
108
|
+
}
|
109
|
+
}
|
110
|
+
if (includeBoth || args.first || args.after) {
|
111
|
+
checkField("Int", "first");
|
112
|
+
checkField("String", "after");
|
113
|
+
}
|
114
|
+
if (includeBoth || args.last || args.before) {
|
115
|
+
checkField("Int", "last");
|
116
|
+
checkField("String", "before");
|
117
|
+
}
|
118
|
+
}
|
119
|
+
};
|
120
|
+
}
|
121
|
+
};
|
122
|
+
export {
|
123
|
+
rule
|
119
124
|
};
|
@@ -0,0 +1,13 @@
|
|
1
|
+
import { Kind } from 'graphql';
|
2
|
+
import { i as GraphQLESLintRule } from '../types-2e1afd7c.js';
|
3
|
+
import '@graphql-tools/utils';
|
4
|
+
import 'eslint';
|
5
|
+
import 'estree';
|
6
|
+
import 'graphql-config';
|
7
|
+
import 'json-schema-to-ts';
|
8
|
+
import '../estree-converter/types.mjs';
|
9
|
+
|
10
|
+
declare const NON_OBJECT_TYPES: Kind[];
|
11
|
+
declare const rule: GraphQLESLintRule;
|
12
|
+
|
13
|
+
export { NON_OBJECT_TYPES, rule };
|
@@ -1,104 +1,111 @@
|
|
1
|
-
import { Kind } from
|
2
|
-
const MUST_BE_OBJECT_TYPE =
|
3
|
-
const MUST_CONTAIN_FIELD_EDGES =
|
4
|
-
const MUST_CONTAIN_FIELD_PAGE_INFO =
|
5
|
-
const MUST_HAVE_CONNECTION_SUFFIX =
|
6
|
-
const EDGES_FIELD_MUST_RETURN_LIST_TYPE =
|
7
|
-
const PAGE_INFO_FIELD_MUST_RETURN_NON_NULL_TYPE =
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
1
|
+
import { Kind } from "graphql";
|
2
|
+
const MUST_BE_OBJECT_TYPE = "MUST_BE_OBJECT_TYPE";
|
3
|
+
const MUST_CONTAIN_FIELD_EDGES = "MUST_CONTAIN_FIELD_EDGES";
|
4
|
+
const MUST_CONTAIN_FIELD_PAGE_INFO = "MUST_CONTAIN_FIELD_PAGE_INFO";
|
5
|
+
const MUST_HAVE_CONNECTION_SUFFIX = "MUST_HAVE_CONNECTION_SUFFIX";
|
6
|
+
const EDGES_FIELD_MUST_RETURN_LIST_TYPE = "EDGES_FIELD_MUST_RETURN_LIST_TYPE";
|
7
|
+
const PAGE_INFO_FIELD_MUST_RETURN_NON_NULL_TYPE = "PAGE_INFO_FIELD_MUST_RETURN_NON_NULL_TYPE";
|
8
|
+
const NON_OBJECT_TYPES = [
|
9
|
+
Kind.SCALAR_TYPE_DEFINITION,
|
10
|
+
Kind.UNION_TYPE_DEFINITION,
|
11
|
+
Kind.UNION_TYPE_EXTENSION,
|
12
|
+
Kind.INPUT_OBJECT_TYPE_DEFINITION,
|
13
|
+
Kind.INPUT_OBJECT_TYPE_EXTENSION,
|
14
|
+
Kind.ENUM_TYPE_DEFINITION,
|
15
|
+
Kind.ENUM_TYPE_EXTENSION,
|
16
|
+
Kind.INTERFACE_TYPE_DEFINITION,
|
17
|
+
Kind.INTERFACE_TYPE_EXTENSION
|
18
18
|
];
|
19
19
|
const notConnectionTypesSelector = `:matches(${NON_OBJECT_TYPES})[name.value=/Connection$/] > .name`;
|
20
|
-
const hasEdgesField = (node) =>
|
21
|
-
const hasPageInfoField = (node) =>
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
20
|
+
const hasEdgesField = (node) => node.fields?.some((field) => field.name.value === "edges");
|
21
|
+
const hasPageInfoField = (node) => node.fields?.some((field) => field.name.value === "pageInfo");
|
22
|
+
const rule = {
|
23
|
+
meta: {
|
24
|
+
type: "problem",
|
25
|
+
docs: {
|
26
|
+
category: "Schema",
|
27
|
+
description: [
|
28
|
+
"Set of rules to follow Relay specification for Connection types.",
|
29
|
+
"",
|
30
|
+
'- Any type whose name ends in "Connection" is considered by spec to be a `Connection type`',
|
31
|
+
"- Connection type must be an Object type",
|
32
|
+
"- Connection type must contain a field `edges` that return a list type that wraps an edge type",
|
33
|
+
"- Connection type must contain a field `pageInfo` that return a non-null `PageInfo` Object type"
|
34
|
+
].join("\n"),
|
35
|
+
url: "https://the-guild.dev/graphql/eslint/rules/relay-connection-types",
|
36
|
+
isDisabledForAllConfig: true,
|
37
|
+
examples: [
|
38
|
+
{
|
39
|
+
title: "Incorrect",
|
40
|
+
code: (
|
41
|
+
/* GraphQL */
|
42
|
+
`
|
41
43
|
type UserPayload { # should be an Object type with \`Connection\` suffix
|
42
44
|
edges: UserEdge! # should return a list type
|
43
45
|
pageInfo: PageInfo # should return a non-null \`PageInfo\` Object type
|
44
46
|
}
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
47
|
+
`
|
48
|
+
)
|
49
|
+
},
|
50
|
+
{
|
51
|
+
title: "Correct",
|
52
|
+
code: (
|
53
|
+
/* GraphQL */
|
54
|
+
`
|
50
55
|
type UserConnection {
|
51
56
|
edges: [UserEdge]
|
52
57
|
pageInfo: PageInfo!
|
53
58
|
}
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
messages: {
|
59
|
-
// Connection types
|
60
|
-
[MUST_BE_OBJECT_TYPE]: 'Connection type must be an Object type.',
|
61
|
-
[MUST_HAVE_CONNECTION_SUFFIX]: 'Connection type must have `Connection` suffix.',
|
62
|
-
[MUST_CONTAIN_FIELD_EDGES]: 'Connection type must contain a field `edges` that return a list type.',
|
63
|
-
[MUST_CONTAIN_FIELD_PAGE_INFO]: 'Connection type must contain a field `pageInfo` that return a non-null `PageInfo` Object type.',
|
64
|
-
[EDGES_FIELD_MUST_RETURN_LIST_TYPE]: '`edges` field must return a list type.',
|
65
|
-
[PAGE_INFO_FIELD_MUST_RETURN_NON_NULL_TYPE]: '`pageInfo` field must return a non-null `PageInfo` Object type.',
|
66
|
-
},
|
67
|
-
schema: [],
|
59
|
+
`
|
60
|
+
)
|
61
|
+
}
|
62
|
+
]
|
68
63
|
},
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
}
|
78
|
-
},
|
79
|
-
':matches(ObjectTypeDefinition, ObjectTypeExtension)[name.value=/Connection$/]'(node) {
|
80
|
-
if (!hasEdgesField(node)) {
|
81
|
-
context.report({ node: node.name, messageId: MUST_CONTAIN_FIELD_EDGES });
|
82
|
-
}
|
83
|
-
if (!hasPageInfoField(node)) {
|
84
|
-
context.report({ node: node.name, messageId: MUST_CONTAIN_FIELD_PAGE_INFO });
|
85
|
-
}
|
86
|
-
},
|
87
|
-
':matches(ObjectTypeDefinition, ObjectTypeExtension)[name.value=/Connection$/] > FieldDefinition[name.value=edges] > .gqlType'(node) {
|
88
|
-
const isListType = node.kind === Kind.LIST_TYPE ||
|
89
|
-
(node.kind === Kind.NON_NULL_TYPE && node.gqlType.kind === Kind.LIST_TYPE);
|
90
|
-
if (!isListType) {
|
91
|
-
context.report({ node, messageId: EDGES_FIELD_MUST_RETURN_LIST_TYPE });
|
92
|
-
}
|
93
|
-
},
|
94
|
-
':matches(ObjectTypeDefinition, ObjectTypeExtension)[name.value=/Connection$/] > FieldDefinition[name.value=pageInfo] > .gqlType'(node) {
|
95
|
-
const isNonNullPageInfoType = node.kind === Kind.NON_NULL_TYPE &&
|
96
|
-
node.gqlType.kind === Kind.NAMED_TYPE &&
|
97
|
-
node.gqlType.name.value === 'PageInfo';
|
98
|
-
if (!isNonNullPageInfoType) {
|
99
|
-
context.report({ node, messageId: PAGE_INFO_FIELD_MUST_RETURN_NON_NULL_TYPE });
|
100
|
-
}
|
101
|
-
},
|
102
|
-
};
|
64
|
+
messages: {
|
65
|
+
// Connection types
|
66
|
+
[MUST_BE_OBJECT_TYPE]: "Connection type must be an Object type.",
|
67
|
+
[MUST_HAVE_CONNECTION_SUFFIX]: "Connection type must have `Connection` suffix.",
|
68
|
+
[MUST_CONTAIN_FIELD_EDGES]: "Connection type must contain a field `edges` that return a list type.",
|
69
|
+
[MUST_CONTAIN_FIELD_PAGE_INFO]: "Connection type must contain a field `pageInfo` that return a non-null `PageInfo` Object type.",
|
70
|
+
[EDGES_FIELD_MUST_RETURN_LIST_TYPE]: "`edges` field must return a list type.",
|
71
|
+
[PAGE_INFO_FIELD_MUST_RETURN_NON_NULL_TYPE]: "`pageInfo` field must return a non-null `PageInfo` Object type."
|
103
72
|
},
|
73
|
+
schema: []
|
74
|
+
},
|
75
|
+
create(context) {
|
76
|
+
return {
|
77
|
+
[notConnectionTypesSelector](node) {
|
78
|
+
context.report({ node, messageId: MUST_BE_OBJECT_TYPE });
|
79
|
+
},
|
80
|
+
":matches(ObjectTypeDefinition, ObjectTypeExtension)[name.value!=/Connection$/]"(node) {
|
81
|
+
if (hasEdgesField(node) && hasPageInfoField(node)) {
|
82
|
+
context.report({ node: node.name, messageId: MUST_HAVE_CONNECTION_SUFFIX });
|
83
|
+
}
|
84
|
+
},
|
85
|
+
":matches(ObjectTypeDefinition, ObjectTypeExtension)[name.value=/Connection$/]"(node) {
|
86
|
+
if (!hasEdgesField(node)) {
|
87
|
+
context.report({ node: node.name, messageId: MUST_CONTAIN_FIELD_EDGES });
|
88
|
+
}
|
89
|
+
if (!hasPageInfoField(node)) {
|
90
|
+
context.report({ node: node.name, messageId: MUST_CONTAIN_FIELD_PAGE_INFO });
|
91
|
+
}
|
92
|
+
},
|
93
|
+
":matches(ObjectTypeDefinition, ObjectTypeExtension)[name.value=/Connection$/] > FieldDefinition[name.value=edges] > .gqlType"(node) {
|
94
|
+
const isListType = node.kind === Kind.LIST_TYPE || node.kind === Kind.NON_NULL_TYPE && node.gqlType.kind === Kind.LIST_TYPE;
|
95
|
+
if (!isListType) {
|
96
|
+
context.report({ node, messageId: EDGES_FIELD_MUST_RETURN_LIST_TYPE });
|
97
|
+
}
|
98
|
+
},
|
99
|
+
":matches(ObjectTypeDefinition, ObjectTypeExtension)[name.value=/Connection$/] > FieldDefinition[name.value=pageInfo] > .gqlType"(node) {
|
100
|
+
const isNonNullPageInfoType = node.kind === Kind.NON_NULL_TYPE && node.gqlType.kind === Kind.NAMED_TYPE && node.gqlType.name.value === "PageInfo";
|
101
|
+
if (!isNonNullPageInfoType) {
|
102
|
+
context.report({ node, messageId: PAGE_INFO_FIELD_MUST_RETURN_NON_NULL_TYPE });
|
103
|
+
}
|
104
|
+
}
|
105
|
+
};
|
106
|
+
}
|
107
|
+
};
|
108
|
+
export {
|
109
|
+
NON_OBJECT_TYPES,
|
110
|
+
rule
|
104
111
|
};
|
@@ -1,5 +1,12 @@
|
|
1
1
|
import { FromSchema } from 'json-schema-to-ts';
|
2
|
-
import { GraphQLESLintRule } from '../types.
|
2
|
+
import { i as GraphQLESLintRule } from '../types-2e1afd7c.js';
|
3
|
+
import '@graphql-tools/utils';
|
4
|
+
import 'eslint';
|
5
|
+
import 'estree';
|
6
|
+
import 'graphql';
|
7
|
+
import 'graphql-config';
|
8
|
+
import '../estree-converter/types.mjs';
|
9
|
+
|
3
10
|
declare const schema: {
|
4
11
|
readonly type: "array";
|
5
12
|
readonly maxItems: 1;
|
@@ -26,6 +33,7 @@ declare const schema: {
|
|
26
33
|
};
|
27
34
|
};
|
28
35
|
};
|
29
|
-
|
30
|
-
|
31
|
-
|
36
|
+
type RuleOptions = FromSchema<typeof schema>;
|
37
|
+
declare const rule: GraphQLESLintRule<RuleOptions, true>;
|
38
|
+
|
39
|
+
export { RuleOptions, rule };
|