@graphql-eslint/eslint-plugin 2.5.0-alpha-b1fd519.0 → 2.5.0-alpha-e34c5d6.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 +69 -60
- package/configs/all.d.ts +3 -0
- package/configs/index.d.ts +3 -0
- package/docs/README.md +1 -0
- package/docs/custom-rules.md +36 -36
- package/docs/rules/no-deprecated.md +2 -2
- package/docs/rules/no-root-type.md +56 -0
- package/index.js +121 -23
- package/index.mjs +121 -23
- package/package.json +9 -8
- package/rules/index.d.ts +3 -0
- package/rules/no-root-type.d.ts +7 -0
- package/testkit.d.ts +1 -1
package/README.md
CHANGED
@@ -6,18 +6,18 @@ This project integrates GraphQL and ESLint, for a better developer experience.
|
|
6
6
|
|
7
7
|
[](https://badge.fury.io/js/%40graphql-eslint%2Feslint-plugin)
|
8
8
|
|
9
|
-
> Created and maintained by [The Guild](http://the-guild.dev
|
9
|
+
> Created and maintained by [The Guild](http://the-guild.dev)
|
10
10
|
|
11
11
|
## Key Features
|
12
12
|
|
13
|
-
- 🚀 Integrates with ESLint core (as a ESTree parser)
|
14
|
-
- 🚀 Works on `.graphql` files, `gql` usages and `/* GraphQL */` magic comments
|
15
|
-
- 🚀 Lints both GraphQL schema and GraphQL operations
|
13
|
+
- 🚀 Integrates with ESLint core (as a ESTree parser)
|
14
|
+
- 🚀 Works on `.graphql` files, `gql` usages and `/* GraphQL */` magic comments
|
15
|
+
- 🚀 Lints both GraphQL schema and GraphQL operations
|
16
16
|
- 🚀 Extended type info for more advanced usages
|
17
|
-
- 🚀 Supports ESLint directives (for example: `disable-next-line`)
|
18
|
-
- 🚀 Easily extendable - supports custom rules based on GraphQL's AST and ESLint API
|
19
|
-
- 🚀 Validates, lints, prettifies and checks for best practices across GraphQL schema and GraphQL operations
|
20
|
-
- 🚀 Integrates with [`graphql-config`](https://graphql-config.com
|
17
|
+
- 🚀 Supports ESLint directives (for example: `eslint-disable-next-line`)
|
18
|
+
- 🚀 Easily extendable - supports custom rules based on GraphQL's AST and ESLint API
|
19
|
+
- 🚀 Validates, lints, prettifies and checks for best practices across GraphQL schema and GraphQL operations
|
20
|
+
- 🚀 Integrates with [`graphql-config`](https://graphql-config.com)
|
21
21
|
- 🚀 Integrates and visualizes lint issues in popular IDEs (VSCode / WebStorm)
|
22
22
|
|
23
23
|
> Special thanks to [ilyavolodin](https://github.com/ilyavolodin) for his work on a similar project!
|
@@ -42,11 +42,13 @@ Or, with NPM:
|
|
42
42
|
npm install --save-dev @graphql-eslint/eslint-plugin
|
43
43
|
```
|
44
44
|
|
45
|
-
>
|
45
|
+
> Make sure you have `graphql` dependency in your project.
|
46
46
|
|
47
47
|
### Configuration
|
48
48
|
|
49
|
-
To get started,
|
49
|
+
To get started, define an override in your ESLint config to apply this plugin to `.graphql` files. Add the [rules](docs/README.md) you want applied.
|
50
|
+
|
51
|
+
> 🚨 Important! This step is necessary even if you are declaring operations and/or schema in code files.
|
50
52
|
|
51
53
|
```json
|
52
54
|
{
|
@@ -56,74 +58,90 @@ To get started, create an override configuration for your ESLint, while applying
|
|
56
58
|
"parser": "@graphql-eslint/eslint-plugin",
|
57
59
|
"plugins": ["@graphql-eslint"],
|
58
60
|
"rules": {
|
59
|
-
|
61
|
+
"@graphql-eslint/known-type-names": "error"
|
60
62
|
}
|
61
63
|
}
|
62
64
|
]
|
63
65
|
}
|
64
66
|
```
|
65
67
|
|
66
|
-
If
|
68
|
+
If your GraphQL definitions are defined only in `.graphql` files, and you're only using rules that apply to individual files, you should be good to go 👍. If you would like use a remote schema or use rules that apply across the entire collection of definitions at once, see [here](#using-a-remote-schema-or-rules-with-constraints-that-span-the-entire-schema).
|
67
69
|
|
68
|
-
|
70
|
+
#### Tell ESLint to apply this plugin to GraphQL definitions defined in code files
|
71
|
+
|
72
|
+
If you are defining GraphQL schema or GraphQL operations in code files, you'll want to define an additional override to extend the functionality of this plugin to the schema and operations in those files.
|
73
|
+
|
74
|
+
```diff
|
69
75
|
{
|
70
76
|
"overrides": [
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
77
|
+
+ {
|
78
|
+
+ "files": ["*.js"],
|
79
|
+
+ "processor": "@graphql-eslint/graphql"
|
80
|
+
+ },
|
75
81
|
{
|
76
82
|
"files": ["*.graphql"],
|
77
83
|
"parser": "@graphql-eslint/eslint-plugin",
|
78
84
|
"plugins": ["@graphql-eslint"],
|
79
85
|
"rules": {
|
80
|
-
|
86
|
+
"@graphql-eslint/known-type-names": "error"
|
81
87
|
}
|
82
88
|
}
|
83
89
|
]
|
84
90
|
}
|
85
91
|
```
|
86
92
|
|
87
|
-
|
93
|
+
Under the hood, specifying the `@graphql-eslint/graphql` processor for code files will cause `graphql-eslint/graphql` to extract the schema and operation definitions from these files into virtual GraphQL documents with `.graphql` extensions. This will allow the overrides you've defined for `.graphql` files, via `"files": ["*.graphql"]`, to get applied to the definitions defined in your code files.
|
88
94
|
|
89
|
-
|
95
|
+
#### Using a remote schema or rules with constraints that span the entire schema
|
90
96
|
|
91
|
-
|
97
|
+
Some rules require an understanding of the entire schema at once. For example, [no-unreachable-types](https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/no-unreachable-types.md#no-unreachable-types) checks that all types are reachable by root-level fields.
|
92
98
|
|
93
|
-
|
99
|
+
To use these rules, you'll need to tell ESLint how to identify the entire set of schema definitions.
|
94
100
|
|
95
|
-
|
101
|
+
If you are using [`graphql-config`](https://graphql-config.com), you are good to go. `graphql-eslint` integrates with it automatically and will use it to load your schema!
|
102
|
+
|
103
|
+
Alternatively, you can define `parserOptions.schema` in the `*.graphql` override in your ESLint config.
|
104
|
+
|
105
|
+
The parser allows you to specify a json file / graphql files(s) / url / raw string to locate your schema (We are using `graphql-tools` to do that). Just add `parserOptions.schema` to your configuration file:
|
106
|
+
|
107
|
+
```diff
|
96
108
|
{
|
97
109
|
"files": ["*.graphql"],
|
98
110
|
"parser": "@graphql-eslint/eslint-plugin",
|
99
111
|
"plugins": ["@graphql-eslint"],
|
100
|
-
"
|
101
|
-
"
|
102
|
-
}
|
112
|
+
"rules": {
|
113
|
+
"@graphql-eslint/no-unreachable-types": "error"
|
114
|
+
},
|
115
|
+
+ "parserOptions": {
|
116
|
+
+ "schema": "./schema.graphql"
|
117
|
+
+ }
|
103
118
|
}
|
104
119
|
```
|
105
120
|
|
106
|
-
> You can find a complete [documentation of the `parserOptions` here](
|
121
|
+
> You can find a complete [documentation of the `parserOptions` here](docs/parser-options.md).
|
107
122
|
|
108
|
-
> Some rules
|
123
|
+
> Some rules require type information to operate, it's marked in the docs for each rule!
|
109
124
|
|
110
125
|
#### Extended linting rules with siblings operations
|
111
126
|
|
112
127
|
While implementing this tool, we had to find solutions for a better integration of the GraphQL ecosystem and ESLint core.
|
113
128
|
|
114
|
-
GraphQL operations can be distributed across many files, while ESLint operates on one file at a time. If you are using GraphQL fragments in separate files, some rules might yield incorrect results, due the
|
129
|
+
GraphQL operations can be distributed across many files, while ESLint operates on one file at a time. If you are using GraphQL fragments in separate files, some rules might yield incorrect results, due the missing information.
|
115
130
|
|
116
131
|
To workaround that, we allow you to provide additional information on your GraphQL operations, making it available for rules while doing the actual linting.
|
117
132
|
|
118
|
-
To provide that, we are using
|
133
|
+
To provide that, we are using `graphql-tools` loaders to load your sibling operations and fragments, just specify a glob expression(s) that points to your code/`.graphql` files:
|
119
134
|
|
120
|
-
```
|
135
|
+
```diff
|
121
136
|
{
|
122
137
|
"files": ["*.graphql"],
|
123
138
|
"parser": "@graphql-eslint/eslint-plugin",
|
124
139
|
"plugins": ["@graphql-eslint"],
|
140
|
+
"rules": {
|
141
|
+
"@graphql-eslint/unique-operation-name": "error"
|
142
|
+
},
|
125
143
|
"parserOptions": {
|
126
|
-
|
144
|
+
+ "operations": "./src/**/*.graphql",
|
127
145
|
"schema": "./schema.graphql"
|
128
146
|
}
|
129
147
|
}
|
@@ -131,13 +149,13 @@ To provide that, we are using `@graphql-tools` loaders to load your sibling oper
|
|
131
149
|
|
132
150
|
### VSCode Integration
|
133
151
|
|
134
|
-
By default, [ESLint VSCode plugin](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) will not lint files with extensions other
|
152
|
+
By default, [ESLint VSCode plugin](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) will not lint files with extensions other than `js`, `jsx`, `ts` and `tsx`.
|
135
153
|
|
136
154
|
In order to enable it processing other extensions, add the following section in `settings.json` or workspace configuration.
|
137
155
|
|
138
156
|
```json
|
139
157
|
{
|
140
|
-
"eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact", "graphql"]
|
158
|
+
"eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact", "graphql"]
|
141
159
|
}
|
142
160
|
```
|
143
161
|
|
@@ -156,13 +174,13 @@ type Query {
|
|
156
174
|
}
|
157
175
|
```
|
158
176
|
|
159
|
-
You can also specify specific rules to disable, apply it over the entire file, `next-line` or
|
177
|
+
You can also specify specific rules to disable, apply it over the entire file, `eslint-disable-next-line` or current `eslint-disable-line`.
|
160
178
|
|
161
179
|
You can find a list of [ESLint directives here](https://eslint.org/docs/2.13.1/user-guide/configuring#disabling-rules-with-inline-comments).
|
162
180
|
|
163
181
|
## Available Rules
|
164
182
|
|
165
|
-
You can find a complete list of [all available rules here](
|
183
|
+
You can find a complete list of [all available rules here](docs/README.md).
|
166
184
|
|
167
185
|
## Available Configs
|
168
186
|
|
@@ -177,19 +195,13 @@ Enable it in your `.eslintrc` file with the `extends` option.
|
|
177
195
|
"overrides": [
|
178
196
|
{
|
179
197
|
"files": ["*.js"],
|
180
|
-
"processor": "@graphql-eslint/graphql"
|
181
|
-
"rules": {
|
182
|
-
// your rules for JavaScript files
|
183
|
-
}
|
198
|
+
"processor": "@graphql-eslint/graphql"
|
184
199
|
},
|
185
200
|
{
|
186
201
|
"files": ["*.graphql"],
|
187
202
|
- "parser": "@graphql-eslint/eslint-plugin",
|
188
203
|
- "plugins": ["@graphql-eslint"],
|
189
|
-
+ "extends": "plugin:@graphql-eslint/recommended"
|
190
|
-
"rules": {
|
191
|
-
// your rules for GraphQL files
|
192
|
-
}
|
204
|
+
+ "extends": "plugin:@graphql-eslint/recommended" // or plugin:@graphql-eslint/all
|
193
205
|
}
|
194
206
|
]
|
195
207
|
}
|
@@ -197,26 +209,23 @@ Enable it in your `.eslintrc` file with the `extends` option.
|
|
197
209
|
|
198
210
|
### `prettier` rule
|
199
211
|
|
200
|
-
|
201
|
-
|
202
|
-
All you need to do is like the following for now:
|
212
|
+
`eslint-plugin-prettier` supports `.graphql` files. You need to do the following:
|
203
213
|
|
204
214
|
```js
|
205
|
-
// .eslintrc.js
|
206
215
|
module.exports = {
|
207
216
|
overrides: [
|
208
217
|
{
|
209
218
|
files: ['*.js'],
|
210
219
|
processor: '@graphql-eslint/graphql',
|
211
|
-
extends: ['plugin:prettier/recommended']
|
220
|
+
extends: ['plugin:prettier/recommended']
|
212
221
|
},
|
213
222
|
{
|
214
223
|
files: ['*.graphql'],
|
215
224
|
parser: '@graphql-eslint/eslint-plugin',
|
216
225
|
plugins: ['@graphql-eslint'],
|
217
226
|
rules: {
|
218
|
-
'prettier/prettier': 'error'
|
219
|
-
}
|
227
|
+
'prettier/prettier': 'error'
|
228
|
+
}
|
220
229
|
},
|
221
230
|
// the following is required for `eslint-plugin-prettier@<=3.4.0` temporarily
|
222
231
|
// after https://github.com/prettier/eslint-plugin-prettier/pull/415
|
@@ -224,22 +233,22 @@ module.exports = {
|
|
224
233
|
{
|
225
234
|
files: ['*.js/*.graphql'],
|
226
235
|
rules: {
|
227
|
-
'prettier/prettier': 'off'
|
228
|
-
}
|
229
|
-
}
|
230
|
-
]
|
231
|
-
}
|
236
|
+
'prettier/prettier': 'off'
|
237
|
+
}
|
238
|
+
}
|
239
|
+
]
|
240
|
+
}
|
232
241
|
```
|
233
242
|
|
234
243
|
You can take [`examples/prettier`](examples/prettier/.eslintrc.js) as example.
|
235
244
|
|
236
|
-
It could be better to remove the unnecessary `*.js/*.graphql`
|
245
|
+
It could be better to remove the unnecessary `*.js/*.graphql` override setting if <https://github.com/prettier/eslint-plugin-prettier/pull/415> will be merged and released.
|
237
246
|
|
238
247
|
Please help to vote up if you want to speed up the progress.
|
239
248
|
|
240
249
|
## Further Reading
|
241
250
|
|
242
|
-
If you wish to learn more about this project, how the parser works, how to add custom rules and more, [please refer to the docs directory](
|
251
|
+
If you wish to learn more about this project, how the parser works, how to add custom rules and more, [please refer to the docs directory](docs/README.md).
|
243
252
|
|
244
253
|
## Contributions
|
245
254
|
|
@@ -249,8 +258,8 @@ And if this is your first time contributing to this project, please do read our
|
|
249
258
|
|
250
259
|
### Code of Conduct
|
251
260
|
|
252
|
-
Help us keep GraphQL ESLint open and inclusive. Please read and follow our [Code of Conduct](https://github.com/the-guild-org/Stack/blob/master/CODE_OF_CONDUCT.md) as adopted from [Contributor Covenant](https://
|
261
|
+
Help us keep GraphQL ESLint open and inclusive. Please read and follow our [Code of Conduct](https://github.com/the-guild-org/Stack/blob/master/CODE_OF_CONDUCT.md) as adopted from [Contributor Covenant](https://contributor-covenant.org).
|
253
262
|
|
254
263
|
## License
|
255
264
|
|
256
|
-
Released under the [MIT license](
|
265
|
+
Released under the [MIT license](LICENSE).
|
package/configs/all.d.ts
CHANGED
@@ -15,6 +15,9 @@ export declare const allConfig: {
|
|
15
15
|
'@graphql-eslint/match-document-filename': string;
|
16
16
|
'@graphql-eslint/no-deprecated': string;
|
17
17
|
'@graphql-eslint/no-hashtag-description': string;
|
18
|
+
'@graphql-eslint/no-root-type': (string | {
|
19
|
+
disallow: string[];
|
20
|
+
})[];
|
18
21
|
'@graphql-eslint/no-unreachable-types': string;
|
19
22
|
'@graphql-eslint/no-unused-fields': string;
|
20
23
|
'@graphql-eslint/require-deprecation-date': string;
|
package/configs/index.d.ts
CHANGED
@@ -16,6 +16,9 @@ export declare const configs: {
|
|
16
16
|
'@graphql-eslint/match-document-filename': string;
|
17
17
|
'@graphql-eslint/no-deprecated': string;
|
18
18
|
'@graphql-eslint/no-hashtag-description': string;
|
19
|
+
'@graphql-eslint/no-root-type': (string | {
|
20
|
+
disallow: string[];
|
21
|
+
})[];
|
19
22
|
'@graphql-eslint/no-unreachable-types': string;
|
20
23
|
'@graphql-eslint/no-unused-fields': string;
|
21
24
|
'@graphql-eslint/require-deprecation-date': string;
|
package/docs/README.md
CHANGED
@@ -35,6 +35,7 @@ Name &nbs
|
|
35
35
|
[no-fragment-cycles](rules/no-fragment-cycles.md)|A GraphQL fragment is only valid when it does not have cycles in fragments usage.|🔮||✅
|
36
36
|
[no-hashtag-description](rules/no-hashtag-description.md)|Requires to use `"""` or `"` for adding a GraphQL description instead of `#`.|🚀||
|
37
37
|
[no-operation-name-suffix](rules/no-operation-name-suffix.md)|Makes sure you are not adding the operation type to the name of the operation.|🚀|🔧|✅
|
38
|
+
[no-root-type](rules/no-root-type.md)|Disallow using root types for `read-only` or `write-only` schemas.|🚀||
|
38
39
|
[no-undefined-variables](rules/no-undefined-variables.md)|A GraphQL operation is only valid if all variables encountered, both directly and via fragment spreads, are defined by that operation.|🔮||✅
|
39
40
|
[no-unreachable-types](rules/no-unreachable-types.md)|Requires all types to be reachable at some level by root level fields.|🚀|🔧|
|
40
41
|
[no-unused-fields](rules/no-unused-fields.md)|Requires all fields to be used at some level by siblings operations.|🚀|🔧|
|
package/docs/custom-rules.md
CHANGED
@@ -17,22 +17,22 @@ The `graphql-eslint` comes with a TypeScript wrapper for ESLint rules, and provi
|
|
17
17
|
Here's an example for a simple rule that reports on anonymous GraphQL operations:
|
18
18
|
|
19
19
|
```ts
|
20
|
-
import { GraphQLESLintRule } from '@graphql-eslint/eslint-plugin'
|
20
|
+
import { GraphQLESLintRule } from '@graphql-eslint/eslint-plugin'
|
21
21
|
|
22
22
|
const rule: GraphQLESLintRule = {
|
23
23
|
create(context) {
|
24
24
|
return {
|
25
25
|
OperationDefinition(node) {
|
26
|
-
if (
|
26
|
+
if (!node.name || !node.name.value) {
|
27
27
|
context.report({
|
28
|
-
node
|
29
|
-
message:
|
30
|
-
})
|
28
|
+
node,
|
29
|
+
message: 'Oops, name is required!'
|
30
|
+
})
|
31
31
|
}
|
32
|
-
}
|
33
|
-
}
|
34
|
-
}
|
35
|
-
}
|
32
|
+
}
|
33
|
+
}
|
34
|
+
}
|
35
|
+
}
|
36
36
|
```
|
37
37
|
|
38
38
|
So what happens here?
|
@@ -58,23 +58,23 @@ This is useful if you wish to use other GraphQL tools that works with the origin
|
|
58
58
|
Here's an example for using original `graphql-js` validate method to validate `OperationDefinition`:
|
59
59
|
|
60
60
|
```ts
|
61
|
-
import { validate } from 'graphql'
|
62
|
-
import { requireGraphQLSchemaFromContext } from '@graphql-eslint/eslint-plugin'
|
61
|
+
import { validate } from 'graphql'
|
62
|
+
import { requireGraphQLSchemaFromContext } from '@graphql-eslint/eslint-plugin'
|
63
63
|
|
64
64
|
export const rule = {
|
65
65
|
create(context) {
|
66
66
|
return {
|
67
67
|
OperationDefinition(node) {
|
68
|
-
const schema = requireGraphQLSchemaFromContext(context)
|
68
|
+
const schema = requireGraphQLSchemaFromContext(context)
|
69
69
|
|
70
70
|
validate(context, schema, {
|
71
71
|
kind: Kind.DOCUMENT,
|
72
|
-
definitions: [node.rawNode()]
|
73
|
-
})
|
74
|
-
}
|
75
|
-
}
|
76
|
-
}
|
77
|
-
}
|
72
|
+
definitions: [node.rawNode()]
|
73
|
+
})
|
74
|
+
}
|
75
|
+
}
|
76
|
+
}
|
77
|
+
}
|
78
78
|
```
|
79
79
|
|
80
80
|
## `TypeInfo` / `GraphQLSchema`
|
@@ -89,7 +89,7 @@ If you provide GraphQL schema in your ESLint configuration, it will get loaded a
|
|
89
89
|
To mark your ESLint rules as a rule that needs access to GraphQL schema, start by running `requireGraphQLSchemaFromContext` from the plugin package, it will make sure to return a schema, or throw an error for the user about the missing schema.
|
90
90
|
|
91
91
|
```ts
|
92
|
-
const schema = requireGraphQLSchemaFromContext(context)
|
92
|
+
const schema = requireGraphQLSchemaFromContext(context)
|
93
93
|
```
|
94
94
|
|
95
95
|
#### Accessing TypeInfo
|
@@ -100,23 +100,23 @@ If your plugin requires `typeInfo` in order to operate and run, make sure to cal
|
|
100
100
|
`typeInfo` is provided on every node, based on the type of that node, for example, to access the `GraphQLOutputType` while you are visiting a `SelectionSet` node, you can do:
|
101
101
|
|
102
102
|
```ts
|
103
|
-
import { requireGraphQLSchemaFromContext } from '@graphql-eslint/eslint-plugin'
|
103
|
+
import { requireGraphQLSchemaFromContext } from '@graphql-eslint/eslint-plugin'
|
104
104
|
|
105
105
|
export const rule = {
|
106
106
|
create(context) {
|
107
|
-
requireGraphQLSchemaFromContext(context)
|
107
|
+
requireGraphQLSchemaFromContext(context)
|
108
108
|
|
109
109
|
return {
|
110
110
|
SelectionSet(node) {
|
111
|
-
const typeInfo = node.typeInfo()
|
111
|
+
const typeInfo = node.typeInfo()
|
112
112
|
|
113
113
|
if (typeInfo && typeInfo.gqlType) {
|
114
|
-
console.log(`The GraphQLOutputType is: ${typeInfo.gqlType}`)
|
114
|
+
console.log(`The GraphQLOutputType is: ${typeInfo.gqlType}`)
|
115
115
|
}
|
116
|
-
}
|
117
|
-
}
|
118
|
-
}
|
119
|
-
}
|
116
|
+
}
|
117
|
+
}
|
118
|
+
}
|
119
|
+
}
|
120
120
|
```
|
121
121
|
|
122
122
|
The structure of the return value of `.typeInfo()` is [defined here](https://github.com/dotansimha/graphql-eslint/blob/master/packages/plugin/src/estree-parser/converter.ts#L38-L46). So based on the `node` you are using, you'll get a different values on `.typeInfo()` result.
|
@@ -128,21 +128,21 @@ To test your rules, you can either use the wrapped `GraphQLRuleTester` from this
|
|
128
128
|
The wrapped `GraphQLRuleTester` provides built-in configured parser, and a schema loader, if you need to test your rule with a loaded schema.
|
129
129
|
|
130
130
|
```ts
|
131
|
-
import { GraphQLRuleTester } from '@graphql-eslint/eslint-plugin'
|
132
|
-
import { rule } from './my-rule'
|
131
|
+
import { GraphQLRuleTester } from '@graphql-eslint/eslint-plugin'
|
132
|
+
import { rule } from './my-rule'
|
133
133
|
|
134
|
-
const ruleTester = new GraphQLRuleTester()
|
134
|
+
const ruleTester = new GraphQLRuleTester()
|
135
135
|
|
136
136
|
ruleTester.runGraphQLTests('my-rule', rule, {
|
137
137
|
valid: [
|
138
138
|
{
|
139
|
-
code:
|
140
|
-
}
|
139
|
+
code: 'query something { foo }'
|
140
|
+
}
|
141
141
|
],
|
142
142
|
invalid: [
|
143
143
|
{
|
144
|
-
code:
|
145
|
-
}
|
146
|
-
]
|
147
|
-
})
|
144
|
+
code: 'query invalid { foo }'
|
145
|
+
}
|
146
|
+
]
|
147
|
+
})
|
148
148
|
```
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# `no-root-type`
|
2
|
+
|
3
|
+
- Category: `Validation`
|
4
|
+
- Rule name: `@graphql-eslint/no-root-type`
|
5
|
+
- Requires GraphQL Schema: `true` [ℹ️](../../README.md#extended-linting-rules-with-graphql-schema)
|
6
|
+
- Requires GraphQL Operations: `false` [ℹ️](../../README.md#extended-linting-rules-with-siblings-operations)
|
7
|
+
|
8
|
+
Disallow using root types for `read-only` or `write-only` schemas.
|
9
|
+
|
10
|
+
## Usage Examples
|
11
|
+
|
12
|
+
### Incorrect (`read-only` schema)
|
13
|
+
|
14
|
+
```graphql
|
15
|
+
# eslint @graphql-eslint/no-root-type: ['error', { disallow: ['mutation', 'subscription'] }]
|
16
|
+
|
17
|
+
type Mutation {
|
18
|
+
createUser(input: CreateUserInput!): User!
|
19
|
+
}
|
20
|
+
```
|
21
|
+
|
22
|
+
### Incorrect (`write-only` schema)
|
23
|
+
|
24
|
+
```graphql
|
25
|
+
# eslint @graphql-eslint/no-root-type: ['error', { disallow: ['query'] }]
|
26
|
+
|
27
|
+
type Query {
|
28
|
+
users: [User!]!
|
29
|
+
}
|
30
|
+
```
|
31
|
+
|
32
|
+
### Correct (`read-only` schema)
|
33
|
+
|
34
|
+
```graphql
|
35
|
+
# eslint @graphql-eslint/no-root-type: ['error', { disallow: ['mutation', 'subscription'] }]
|
36
|
+
|
37
|
+
type Query {
|
38
|
+
users: [User!]!
|
39
|
+
}
|
40
|
+
```
|
41
|
+
|
42
|
+
## Config Schema
|
43
|
+
|
44
|
+
The schema defines the following properties:
|
45
|
+
|
46
|
+
### `disallow` (array, required)
|
47
|
+
|
48
|
+
Additional restrictions:
|
49
|
+
|
50
|
+
* Minimum items: `1`
|
51
|
+
* Unique items: `true`
|
52
|
+
|
53
|
+
## Resources
|
54
|
+
|
55
|
+
- [Rule source](../../packages/plugin/src/rules/no-root-type.ts)
|
56
|
+
- [Test source](../../packages/plugin/tests/no-root-type.spec.ts)
|
package/index.js
CHANGED
@@ -91,6 +91,7 @@ const allConfig = {
|
|
91
91
|
'@graphql-eslint/match-document-filename': 'error',
|
92
92
|
'@graphql-eslint/no-deprecated': 'error',
|
93
93
|
'@graphql-eslint/no-hashtag-description': 'error',
|
94
|
+
'@graphql-eslint/no-root-type': ['error', { disallow: ['subscription'] }],
|
94
95
|
'@graphql-eslint/no-unreachable-types': 'error',
|
95
96
|
'@graphql-eslint/no-unused-fields': 'error',
|
96
97
|
'@graphql-eslint/require-deprecation-date': 'error',
|
@@ -2065,9 +2066,100 @@ const rule$d = {
|
|
2065
2066
|
},
|
2066
2067
|
};
|
2067
2068
|
|
2069
|
+
const ROOT_TYPES = ['query', 'mutation', 'subscription'];
|
2070
|
+
const rule$e = {
|
2071
|
+
meta: {
|
2072
|
+
type: 'suggestion',
|
2073
|
+
docs: {
|
2074
|
+
category: 'Validation',
|
2075
|
+
description: 'Disallow using root types for `read-only` or `write-only` schemas.',
|
2076
|
+
url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/no-root-type.md',
|
2077
|
+
requiresSchema: true,
|
2078
|
+
examples: [
|
2079
|
+
{
|
2080
|
+
title: 'Incorrect (`read-only` schema)',
|
2081
|
+
usage: [{ disallow: ['mutation', 'subscription'] }],
|
2082
|
+
code: /* GraphQL */ `
|
2083
|
+
type Mutation {
|
2084
|
+
createUser(input: CreateUserInput!): User!
|
2085
|
+
}
|
2086
|
+
`,
|
2087
|
+
},
|
2088
|
+
{
|
2089
|
+
title: 'Incorrect (`write-only` schema)',
|
2090
|
+
usage: [{ disallow: ['query'] }],
|
2091
|
+
code: /* GraphQL */ `
|
2092
|
+
type Query {
|
2093
|
+
users: [User!]!
|
2094
|
+
}
|
2095
|
+
`,
|
2096
|
+
},
|
2097
|
+
{
|
2098
|
+
title: 'Correct (`read-only` schema)',
|
2099
|
+
usage: [{ disallow: ['mutation', 'subscription'] }],
|
2100
|
+
code: /* GraphQL */ `
|
2101
|
+
type Query {
|
2102
|
+
users: [User!]!
|
2103
|
+
}
|
2104
|
+
`,
|
2105
|
+
},
|
2106
|
+
],
|
2107
|
+
optionsForConfig: [{ disallow: ['subscription'] }],
|
2108
|
+
},
|
2109
|
+
schema: {
|
2110
|
+
type: 'array',
|
2111
|
+
minItems: 1,
|
2112
|
+
maxItems: 1,
|
2113
|
+
items: {
|
2114
|
+
type: 'object',
|
2115
|
+
additionalProperties: false,
|
2116
|
+
required: ['disallow'],
|
2117
|
+
properties: {
|
2118
|
+
disallow: {
|
2119
|
+
type: 'array',
|
2120
|
+
uniqueItems: true,
|
2121
|
+
minItems: 1,
|
2122
|
+
items: {
|
2123
|
+
enum: ROOT_TYPES,
|
2124
|
+
},
|
2125
|
+
},
|
2126
|
+
},
|
2127
|
+
},
|
2128
|
+
},
|
2129
|
+
},
|
2130
|
+
create(context) {
|
2131
|
+
const schema = requireGraphQLSchemaFromContext('no-root-type', context);
|
2132
|
+
const disallow = new Set(context.options[0].disallow);
|
2133
|
+
const rootTypeNames = [
|
2134
|
+
disallow.has('query') && schema.getQueryType(),
|
2135
|
+
disallow.has('mutation') && schema.getMutationType(),
|
2136
|
+
disallow.has('subscription') && schema.getSubscriptionType(),
|
2137
|
+
]
|
2138
|
+
.filter(Boolean)
|
2139
|
+
.map(type => type.name);
|
2140
|
+
if (rootTypeNames.length === 0) {
|
2141
|
+
return {};
|
2142
|
+
}
|
2143
|
+
const selector = [
|
2144
|
+
`:matches(${graphql.Kind.OBJECT_TYPE_DEFINITION}, ${graphql.Kind.OBJECT_TYPE_EXTENSION})`,
|
2145
|
+
'>',
|
2146
|
+
`${graphql.Kind.NAME}[value=/^(${rootTypeNames.join('|')})$/]`,
|
2147
|
+
].join(' ');
|
2148
|
+
return {
|
2149
|
+
[selector](node) {
|
2150
|
+
const typeName = node.value;
|
2151
|
+
context.report({
|
2152
|
+
loc: getLocation(node.loc, typeName),
|
2153
|
+
message: `Root type "${typeName}" is forbidden`,
|
2154
|
+
});
|
2155
|
+
},
|
2156
|
+
};
|
2157
|
+
},
|
2158
|
+
};
|
2159
|
+
|
2068
2160
|
const UNREACHABLE_TYPE = 'UNREACHABLE_TYPE';
|
2069
2161
|
const RULE_NAME = 'no-unreachable-types';
|
2070
|
-
const rule$
|
2162
|
+
const rule$f = {
|
2071
2163
|
meta: {
|
2072
2164
|
messages: {
|
2073
2165
|
[UNREACHABLE_TYPE]: `Type "{{ typeName }}" is unreachable`,
|
@@ -2143,7 +2235,7 @@ const rule$e = {
|
|
2143
2235
|
|
2144
2236
|
const UNUSED_FIELD = 'UNUSED_FIELD';
|
2145
2237
|
const RULE_NAME$1 = 'no-unused-fields';
|
2146
|
-
const rule$
|
2238
|
+
const rule$g = {
|
2147
2239
|
meta: {
|
2148
2240
|
messages: {
|
2149
2241
|
[UNUSED_FIELD]: `Field "{{fieldName}}" is unused`,
|
@@ -2331,7 +2423,7 @@ const MESSAGE_REQUIRE_DATE = 'MESSAGE_REQUIRE_DATE';
|
|
2331
2423
|
const MESSAGE_INVALID_FORMAT = 'MESSAGE_INVALID_FORMAT';
|
2332
2424
|
const MESSAGE_INVALID_DATE = 'MESSAGE_INVALID_DATE';
|
2333
2425
|
const MESSAGE_CAN_BE_REMOVED = 'MESSAGE_CAN_BE_REMOVED';
|
2334
|
-
const rule$
|
2426
|
+
const rule$h = {
|
2335
2427
|
meta: {
|
2336
2428
|
type: 'suggestion',
|
2337
2429
|
docs: {
|
@@ -2434,7 +2526,7 @@ const rule$g = {
|
|
2434
2526
|
},
|
2435
2527
|
};
|
2436
2528
|
|
2437
|
-
const rule$
|
2529
|
+
const rule$i = {
|
2438
2530
|
meta: {
|
2439
2531
|
docs: {
|
2440
2532
|
description: `Require all deprecation directives to specify a reason.`,
|
@@ -2514,7 +2606,7 @@ function verifyRule(context, node) {
|
|
2514
2606
|
}
|
2515
2607
|
}
|
2516
2608
|
}
|
2517
|
-
const rule$
|
2609
|
+
const rule$j = {
|
2518
2610
|
meta: {
|
2519
2611
|
docs: {
|
2520
2612
|
category: 'Best Practices',
|
@@ -2579,7 +2671,7 @@ const rule$i = {
|
|
2579
2671
|
};
|
2580
2672
|
|
2581
2673
|
const RULE_NAME$2 = 'require-field-of-type-query-in-mutation-result';
|
2582
|
-
const rule$
|
2674
|
+
const rule$k = {
|
2583
2675
|
meta: {
|
2584
2676
|
type: 'suggestion',
|
2585
2677
|
docs: {
|
@@ -2747,7 +2839,7 @@ const convertNode = (typeInfo) => (node, key, parent) => {
|
|
2747
2839
|
|
2748
2840
|
const REQUIRE_ID_WHEN_AVAILABLE = 'REQUIRE_ID_WHEN_AVAILABLE';
|
2749
2841
|
const DEFAULT_ID_FIELD_NAME = 'id';
|
2750
|
-
const rule$
|
2842
|
+
const rule$l = {
|
2751
2843
|
meta: {
|
2752
2844
|
type: 'suggestion',
|
2753
2845
|
docs: {
|
@@ -2883,7 +2975,7 @@ const rule$k = {
|
|
2883
2975
|
},
|
2884
2976
|
};
|
2885
2977
|
|
2886
|
-
const rule$
|
2978
|
+
const rule$m = {
|
2887
2979
|
meta: {
|
2888
2980
|
docs: {
|
2889
2981
|
category: 'Best Practices',
|
@@ -3005,7 +3097,7 @@ const shouldIgnoreNode = ({ node, exceptions }) => {
|
|
3005
3097
|
}
|
3006
3098
|
return false;
|
3007
3099
|
};
|
3008
|
-
const rule$
|
3100
|
+
const rule$n = {
|
3009
3101
|
meta: {
|
3010
3102
|
type: 'suggestion',
|
3011
3103
|
docs: {
|
@@ -3182,7 +3274,7 @@ const checkNode = (context, node, ruleName, messageId) => {
|
|
3182
3274
|
});
|
3183
3275
|
}
|
3184
3276
|
};
|
3185
|
-
const rule$
|
3277
|
+
const rule$o = {
|
3186
3278
|
meta: {
|
3187
3279
|
type: 'suggestion',
|
3188
3280
|
docs: {
|
@@ -3241,7 +3333,7 @@ const rule$n = {
|
|
3241
3333
|
|
3242
3334
|
const RULE_NAME$4 = 'unique-operation-name';
|
3243
3335
|
const UNIQUE_OPERATION_NAME = 'UNIQUE_OPERATION_NAME';
|
3244
|
-
const rule$
|
3336
|
+
const rule$p = {
|
3245
3337
|
meta: {
|
3246
3338
|
type: 'suggestion',
|
3247
3339
|
docs: {
|
@@ -3321,17 +3413,18 @@ const rules = {
|
|
3321
3413
|
'no-deprecated': rule$b,
|
3322
3414
|
'no-hashtag-description': rule$c,
|
3323
3415
|
'no-operation-name-suffix': rule$d,
|
3324
|
-
'no-
|
3325
|
-
'no-
|
3326
|
-
'
|
3327
|
-
'require-deprecation-
|
3328
|
-
'require-
|
3329
|
-
'require-
|
3330
|
-
'require-
|
3331
|
-
'
|
3332
|
-
'
|
3333
|
-
'
|
3334
|
-
'unique-
|
3416
|
+
'no-root-type': rule$e,
|
3417
|
+
'no-unreachable-types': rule$f,
|
3418
|
+
'no-unused-fields': rule$g,
|
3419
|
+
'require-deprecation-date': rule$h,
|
3420
|
+
'require-deprecation-reason': rule$i,
|
3421
|
+
'require-description': rule$j,
|
3422
|
+
'require-field-of-type-query-in-mutation-result': rule$k,
|
3423
|
+
'require-id-when-available': rule$l,
|
3424
|
+
'selection-set-depth': rule$m,
|
3425
|
+
'strict-id-in-types': rule$n,
|
3426
|
+
'unique-fragment-name': rule$o,
|
3427
|
+
'unique-operation-name': rule$p,
|
3335
3428
|
};
|
3336
3429
|
|
3337
3430
|
const RELEVANT_KEYWORDS = ['gql', 'graphql', '/* GraphQL */'];
|
@@ -3634,7 +3727,12 @@ function getReachableTypes(schema) {
|
|
3634
3727
|
Directive: collect,
|
3635
3728
|
NamedType: collect,
|
3636
3729
|
};
|
3637
|
-
for (const type of [
|
3730
|
+
for (const type of [
|
3731
|
+
schema,
|
3732
|
+
schema.getQueryType(),
|
3733
|
+
schema.getMutationType(),
|
3734
|
+
schema.getSubscriptionType(),
|
3735
|
+
]) {
|
3638
3736
|
if (type) {
|
3639
3737
|
graphql.visit(type.astNode, visitor);
|
3640
3738
|
}
|
package/index.mjs
CHANGED
@@ -85,6 +85,7 @@ const allConfig = {
|
|
85
85
|
'@graphql-eslint/match-document-filename': 'error',
|
86
86
|
'@graphql-eslint/no-deprecated': 'error',
|
87
87
|
'@graphql-eslint/no-hashtag-description': 'error',
|
88
|
+
'@graphql-eslint/no-root-type': ['error', { disallow: ['subscription'] }],
|
88
89
|
'@graphql-eslint/no-unreachable-types': 'error',
|
89
90
|
'@graphql-eslint/no-unused-fields': 'error',
|
90
91
|
'@graphql-eslint/require-deprecation-date': 'error',
|
@@ -2059,9 +2060,100 @@ const rule$d = {
|
|
2059
2060
|
},
|
2060
2061
|
};
|
2061
2062
|
|
2063
|
+
const ROOT_TYPES = ['query', 'mutation', 'subscription'];
|
2064
|
+
const rule$e = {
|
2065
|
+
meta: {
|
2066
|
+
type: 'suggestion',
|
2067
|
+
docs: {
|
2068
|
+
category: 'Validation',
|
2069
|
+
description: 'Disallow using root types for `read-only` or `write-only` schemas.',
|
2070
|
+
url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/no-root-type.md',
|
2071
|
+
requiresSchema: true,
|
2072
|
+
examples: [
|
2073
|
+
{
|
2074
|
+
title: 'Incorrect (`read-only` schema)',
|
2075
|
+
usage: [{ disallow: ['mutation', 'subscription'] }],
|
2076
|
+
code: /* GraphQL */ `
|
2077
|
+
type Mutation {
|
2078
|
+
createUser(input: CreateUserInput!): User!
|
2079
|
+
}
|
2080
|
+
`,
|
2081
|
+
},
|
2082
|
+
{
|
2083
|
+
title: 'Incorrect (`write-only` schema)',
|
2084
|
+
usage: [{ disallow: ['query'] }],
|
2085
|
+
code: /* GraphQL */ `
|
2086
|
+
type Query {
|
2087
|
+
users: [User!]!
|
2088
|
+
}
|
2089
|
+
`,
|
2090
|
+
},
|
2091
|
+
{
|
2092
|
+
title: 'Correct (`read-only` schema)',
|
2093
|
+
usage: [{ disallow: ['mutation', 'subscription'] }],
|
2094
|
+
code: /* GraphQL */ `
|
2095
|
+
type Query {
|
2096
|
+
users: [User!]!
|
2097
|
+
}
|
2098
|
+
`,
|
2099
|
+
},
|
2100
|
+
],
|
2101
|
+
optionsForConfig: [{ disallow: ['subscription'] }],
|
2102
|
+
},
|
2103
|
+
schema: {
|
2104
|
+
type: 'array',
|
2105
|
+
minItems: 1,
|
2106
|
+
maxItems: 1,
|
2107
|
+
items: {
|
2108
|
+
type: 'object',
|
2109
|
+
additionalProperties: false,
|
2110
|
+
required: ['disallow'],
|
2111
|
+
properties: {
|
2112
|
+
disallow: {
|
2113
|
+
type: 'array',
|
2114
|
+
uniqueItems: true,
|
2115
|
+
minItems: 1,
|
2116
|
+
items: {
|
2117
|
+
enum: ROOT_TYPES,
|
2118
|
+
},
|
2119
|
+
},
|
2120
|
+
},
|
2121
|
+
},
|
2122
|
+
},
|
2123
|
+
},
|
2124
|
+
create(context) {
|
2125
|
+
const schema = requireGraphQLSchemaFromContext('no-root-type', context);
|
2126
|
+
const disallow = new Set(context.options[0].disallow);
|
2127
|
+
const rootTypeNames = [
|
2128
|
+
disallow.has('query') && schema.getQueryType(),
|
2129
|
+
disallow.has('mutation') && schema.getMutationType(),
|
2130
|
+
disallow.has('subscription') && schema.getSubscriptionType(),
|
2131
|
+
]
|
2132
|
+
.filter(Boolean)
|
2133
|
+
.map(type => type.name);
|
2134
|
+
if (rootTypeNames.length === 0) {
|
2135
|
+
return {};
|
2136
|
+
}
|
2137
|
+
const selector = [
|
2138
|
+
`:matches(${Kind.OBJECT_TYPE_DEFINITION}, ${Kind.OBJECT_TYPE_EXTENSION})`,
|
2139
|
+
'>',
|
2140
|
+
`${Kind.NAME}[value=/^(${rootTypeNames.join('|')})$/]`,
|
2141
|
+
].join(' ');
|
2142
|
+
return {
|
2143
|
+
[selector](node) {
|
2144
|
+
const typeName = node.value;
|
2145
|
+
context.report({
|
2146
|
+
loc: getLocation(node.loc, typeName),
|
2147
|
+
message: `Root type "${typeName}" is forbidden`,
|
2148
|
+
});
|
2149
|
+
},
|
2150
|
+
};
|
2151
|
+
},
|
2152
|
+
};
|
2153
|
+
|
2062
2154
|
const UNREACHABLE_TYPE = 'UNREACHABLE_TYPE';
|
2063
2155
|
const RULE_NAME = 'no-unreachable-types';
|
2064
|
-
const rule$
|
2156
|
+
const rule$f = {
|
2065
2157
|
meta: {
|
2066
2158
|
messages: {
|
2067
2159
|
[UNREACHABLE_TYPE]: `Type "{{ typeName }}" is unreachable`,
|
@@ -2137,7 +2229,7 @@ const rule$e = {
|
|
2137
2229
|
|
2138
2230
|
const UNUSED_FIELD = 'UNUSED_FIELD';
|
2139
2231
|
const RULE_NAME$1 = 'no-unused-fields';
|
2140
|
-
const rule$
|
2232
|
+
const rule$g = {
|
2141
2233
|
meta: {
|
2142
2234
|
messages: {
|
2143
2235
|
[UNUSED_FIELD]: `Field "{{fieldName}}" is unused`,
|
@@ -2325,7 +2417,7 @@ const MESSAGE_REQUIRE_DATE = 'MESSAGE_REQUIRE_DATE';
|
|
2325
2417
|
const MESSAGE_INVALID_FORMAT = 'MESSAGE_INVALID_FORMAT';
|
2326
2418
|
const MESSAGE_INVALID_DATE = 'MESSAGE_INVALID_DATE';
|
2327
2419
|
const MESSAGE_CAN_BE_REMOVED = 'MESSAGE_CAN_BE_REMOVED';
|
2328
|
-
const rule$
|
2420
|
+
const rule$h = {
|
2329
2421
|
meta: {
|
2330
2422
|
type: 'suggestion',
|
2331
2423
|
docs: {
|
@@ -2428,7 +2520,7 @@ const rule$g = {
|
|
2428
2520
|
},
|
2429
2521
|
};
|
2430
2522
|
|
2431
|
-
const rule$
|
2523
|
+
const rule$i = {
|
2432
2524
|
meta: {
|
2433
2525
|
docs: {
|
2434
2526
|
description: `Require all deprecation directives to specify a reason.`,
|
@@ -2508,7 +2600,7 @@ function verifyRule(context, node) {
|
|
2508
2600
|
}
|
2509
2601
|
}
|
2510
2602
|
}
|
2511
|
-
const rule$
|
2603
|
+
const rule$j = {
|
2512
2604
|
meta: {
|
2513
2605
|
docs: {
|
2514
2606
|
category: 'Best Practices',
|
@@ -2573,7 +2665,7 @@ const rule$i = {
|
|
2573
2665
|
};
|
2574
2666
|
|
2575
2667
|
const RULE_NAME$2 = 'require-field-of-type-query-in-mutation-result';
|
2576
|
-
const rule$
|
2668
|
+
const rule$k = {
|
2577
2669
|
meta: {
|
2578
2670
|
type: 'suggestion',
|
2579
2671
|
docs: {
|
@@ -2741,7 +2833,7 @@ const convertNode = (typeInfo) => (node, key, parent) => {
|
|
2741
2833
|
|
2742
2834
|
const REQUIRE_ID_WHEN_AVAILABLE = 'REQUIRE_ID_WHEN_AVAILABLE';
|
2743
2835
|
const DEFAULT_ID_FIELD_NAME = 'id';
|
2744
|
-
const rule$
|
2836
|
+
const rule$l = {
|
2745
2837
|
meta: {
|
2746
2838
|
type: 'suggestion',
|
2747
2839
|
docs: {
|
@@ -2877,7 +2969,7 @@ const rule$k = {
|
|
2877
2969
|
},
|
2878
2970
|
};
|
2879
2971
|
|
2880
|
-
const rule$
|
2972
|
+
const rule$m = {
|
2881
2973
|
meta: {
|
2882
2974
|
docs: {
|
2883
2975
|
category: 'Best Practices',
|
@@ -2999,7 +3091,7 @@ const shouldIgnoreNode = ({ node, exceptions }) => {
|
|
2999
3091
|
}
|
3000
3092
|
return false;
|
3001
3093
|
};
|
3002
|
-
const rule$
|
3094
|
+
const rule$n = {
|
3003
3095
|
meta: {
|
3004
3096
|
type: 'suggestion',
|
3005
3097
|
docs: {
|
@@ -3176,7 +3268,7 @@ const checkNode = (context, node, ruleName, messageId) => {
|
|
3176
3268
|
});
|
3177
3269
|
}
|
3178
3270
|
};
|
3179
|
-
const rule$
|
3271
|
+
const rule$o = {
|
3180
3272
|
meta: {
|
3181
3273
|
type: 'suggestion',
|
3182
3274
|
docs: {
|
@@ -3235,7 +3327,7 @@ const rule$n = {
|
|
3235
3327
|
|
3236
3328
|
const RULE_NAME$4 = 'unique-operation-name';
|
3237
3329
|
const UNIQUE_OPERATION_NAME = 'UNIQUE_OPERATION_NAME';
|
3238
|
-
const rule$
|
3330
|
+
const rule$p = {
|
3239
3331
|
meta: {
|
3240
3332
|
type: 'suggestion',
|
3241
3333
|
docs: {
|
@@ -3315,17 +3407,18 @@ const rules = {
|
|
3315
3407
|
'no-deprecated': rule$b,
|
3316
3408
|
'no-hashtag-description': rule$c,
|
3317
3409
|
'no-operation-name-suffix': rule$d,
|
3318
|
-
'no-
|
3319
|
-
'no-
|
3320
|
-
'
|
3321
|
-
'require-deprecation-
|
3322
|
-
'require-
|
3323
|
-
'require-
|
3324
|
-
'require-
|
3325
|
-
'
|
3326
|
-
'
|
3327
|
-
'
|
3328
|
-
'unique-
|
3410
|
+
'no-root-type': rule$e,
|
3411
|
+
'no-unreachable-types': rule$f,
|
3412
|
+
'no-unused-fields': rule$g,
|
3413
|
+
'require-deprecation-date': rule$h,
|
3414
|
+
'require-deprecation-reason': rule$i,
|
3415
|
+
'require-description': rule$j,
|
3416
|
+
'require-field-of-type-query-in-mutation-result': rule$k,
|
3417
|
+
'require-id-when-available': rule$l,
|
3418
|
+
'selection-set-depth': rule$m,
|
3419
|
+
'strict-id-in-types': rule$n,
|
3420
|
+
'unique-fragment-name': rule$o,
|
3421
|
+
'unique-operation-name': rule$p,
|
3329
3422
|
};
|
3330
3423
|
|
3331
3424
|
const RELEVANT_KEYWORDS = ['gql', 'graphql', '/* GraphQL */'];
|
@@ -3628,7 +3721,12 @@ function getReachableTypes(schema) {
|
|
3628
3721
|
Directive: collect,
|
3629
3722
|
NamedType: collect,
|
3630
3723
|
};
|
3631
|
-
for (const type of [
|
3724
|
+
for (const type of [
|
3725
|
+
schema,
|
3726
|
+
schema.getQueryType(),
|
3727
|
+
schema.getMutationType(),
|
3728
|
+
schema.getSubscriptionType(),
|
3729
|
+
]) {
|
3632
3730
|
if (type) {
|
3633
3731
|
visit(type.astNode, visitor);
|
3634
3732
|
}
|
package/package.json
CHANGED
@@ -1,18 +1,19 @@
|
|
1
1
|
{
|
2
2
|
"name": "@graphql-eslint/eslint-plugin",
|
3
|
-
"version": "2.5.0-alpha-
|
3
|
+
"version": "2.5.0-alpha-e34c5d6.0",
|
4
4
|
"sideEffects": false,
|
5
5
|
"peerDependencies": {
|
6
|
-
"graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0"
|
6
|
+
"graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0"
|
7
7
|
},
|
8
8
|
"dependencies": {
|
9
|
-
"@
|
10
|
-
"@graphql-tools/
|
11
|
-
"@graphql-tools/
|
12
|
-
"@graphql-tools/
|
13
|
-
"graphql-
|
9
|
+
"@babel/code-frame": "7.16.0",
|
10
|
+
"@graphql-tools/code-file-loader": "^7.0.2",
|
11
|
+
"@graphql-tools/graphql-tag-pluck": "^7.0.2",
|
12
|
+
"@graphql-tools/import": "^6.3.1",
|
13
|
+
"@graphql-tools/utils": "^8.0.2",
|
14
|
+
"graphql-config": "^4.0.1",
|
14
15
|
"graphql-depth-limit": "1.1.0",
|
15
|
-
"lodash.lowercase": "4.3.0"
|
16
|
+
"lodash.lowercase": "^4.3.0"
|
16
17
|
},
|
17
18
|
"repository": "https://github.com/dotansimha/graphql-eslint",
|
18
19
|
"author": "Dotan Simha <dotansimha@gmail.com>",
|
package/rules/index.d.ts
CHANGED
@@ -59,6 +59,9 @@ export declare const rules: {
|
|
59
59
|
'no-deprecated': import("..").GraphQLESLintRule<[], true>;
|
60
60
|
'no-hashtag-description': import("..").GraphQLESLintRule<any[], false>;
|
61
61
|
'no-operation-name-suffix': import("..").GraphQLESLintRule<any[], false>;
|
62
|
+
'no-root-type': import("..").GraphQLESLintRule<[{
|
63
|
+
disallow: ("subscription" | "query" | "mutation")[];
|
64
|
+
}], false>;
|
62
65
|
'no-unreachable-types': import("..").GraphQLESLintRule<any[], false>;
|
63
66
|
'no-unused-fields': import("..").GraphQLESLintRule<any[], false>;
|
64
67
|
'require-deprecation-date': import("..").GraphQLESLintRule<[{
|
@@ -0,0 +1,7 @@
|
|
1
|
+
import { GraphQLESLintRule } from '../types';
|
2
|
+
declare const ROOT_TYPES: ('query' | 'mutation' | 'subscription')[];
|
3
|
+
declare type NoRootTypeConfig = {
|
4
|
+
disallow: typeof ROOT_TYPES;
|
5
|
+
};
|
6
|
+
declare const rule: GraphQLESLintRule<[NoRootTypeConfig]>;
|
7
|
+
export default rule;
|
package/testkit.d.ts
CHANGED
@@ -6,7 +6,7 @@ export declare type GraphQLESLintRuleListener<WithTypeInfo extends boolean = fal
|
|
6
6
|
[K in keyof ASTKindToNode]?: (node: GraphQLESTreeNode<ASTKindToNode[K], WithTypeInfo>) => void;
|
7
7
|
} & Record<string, any>;
|
8
8
|
export declare type GraphQLValidTestCase<Options> = Omit<RuleTester.ValidTestCase, 'options' | 'parserOptions'> & {
|
9
|
-
name
|
9
|
+
name?: string;
|
10
10
|
options?: Options;
|
11
11
|
parserOptions?: ParserOptions;
|
12
12
|
};
|