@graphql-eslint/eslint-plugin 3.0.0-alpha-5388f29.0 → 3.0.0-alpha-3168a9b.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 CHANGED
@@ -6,18 +6,18 @@ This project integrates GraphQL and ESLint, for a better developer experience.
6
6
 
7
7
  [![npm version](https://badge.fury.io/js/%40graphql-eslint%2Feslint-plugin.svg)](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
- > Also, make sure you have `graphql` dependency in your project.
45
+ > Make sure you have `graphql` dependency in your project.
46
46
 
47
47
  ### Configuration
48
48
 
49
- To get started, create an override configuration for your ESLint, while applying it to `.graphql` files (do that even if you are declaring your operations in code files):
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 you are using code files to store your GraphQL schema or your GraphQL operations, you can extend the behaviour of ESLint and extract those, by adding **an additional `override`** that does that extraction processes:
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
- ```json
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
- "files": ["*.tsx", "*.ts", "*.jsx", "*.js"],
73
- "processor": "@graphql-eslint/graphql"
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
- #### Extended linting rules with GraphQL Schema
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
- If you are using [`graphql-config`](https://graphql-config.com/) - you are good to go. This package integrates with it automatically, and will use it to load your schema!
95
+ #### Using a remote schema or rules with constraints that span the entire schema
90
96
 
91
- Linting process can be enriched and extended with GraphQL type information, if you are able to provide your GraphQL schema.
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
- The parser allow 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:
99
+ To use these rules, you'll need to tell ESLint how to identify the entire set of schema definitions.
94
100
 
95
- ```json
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
- "parserOptions": {
101
- "schema": "./schema.graphql"
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](./docs/parser-options.md)
121
+ > You can find a complete [documentation of the `parserOptions` here](docs/parser-options.md).
107
122
 
108
- > Some rules requires type information to operate, it's marked in the docs of each plugin!
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 the missing information.
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 `@graphql-tools` loaders to load your sibling operations and fragments, just specify a glob expression(s) that points to your code/.graphql files:
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
- ```json
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
- "operations": ["./src/**/*.graphql"],
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 then js, jsx, ts, tsx.
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 (current) `line`.
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](./docs/README.md)
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", // or plugin:@graphql-eslint/all
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
- The original `prettier` rule has been removed because `eslint-plugin-prettier` supports `.graphql` files well actually.
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` overrides setting if <https://github.com/prettier/eslint-plugin-prettier/pull/415> will be merged and released.
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](./docs/README.md))
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://www.contributor-covenant.org/)
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](./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;
@@ -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&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&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.|🚀|🔧|
@@ -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 (node && (!node.name || node.name.value === '')) {
26
+ if (!node.name || !node.name.value) {
27
27
  context.report({
28
- node: node,
29
- message: `Oops, name is required!`,
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: `query something { foo }`,
140
- },
139
+ code: 'query something { foo }'
140
+ }
141
141
  ],
142
142
  invalid: [
143
143
  {
144
- code: `query invalid { foo }`,
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)
@@ -1,7 +1,6 @@
1
1
  import { GraphQLESTreeNode } from './estree-ast';
2
2
  import { ASTNode, TypeInfo } from 'graphql';
3
- import { Comment } from 'estree';
4
3
  export declare function convertToESTree<T extends ASTNode>(node: T, typeInfo?: TypeInfo): {
5
- rootTree: GraphQLESTreeNode<T>;
6
- comments: Comment[];
4
+ rootTree: GraphQLESTreeNode<T, false>;
5
+ comments: import("estree").Comment[];
7
6
  };
package/index.js CHANGED
@@ -118,6 +118,7 @@ const allConfig = {
118
118
  '@graphql-eslint/match-document-filename': 'error',
119
119
  '@graphql-eslint/no-deprecated': 'error',
120
120
  '@graphql-eslint/no-hashtag-description': 'error',
121
+ '@graphql-eslint/no-root-type': ['error', { disallow: ['subscription'] }],
121
122
  '@graphql-eslint/no-unreachable-types': 'error',
122
123
  '@graphql-eslint/no-unused-fields': 'error',
123
124
  '@graphql-eslint/require-deprecation-date': 'error',
@@ -176,7 +177,7 @@ function getLexer(source) {
176
177
  throw new Error(`Unsupported GraphQL version! Please make sure to use GraphQL v14 or newer!`);
177
178
  }
178
179
  function extractTokens(source) {
179
- const lexer = getLexer(new graphql.Source(source));
180
+ const lexer = getLexer(source);
180
181
  const tokens = [];
181
182
  let token = lexer.advance();
182
183
  while (token && token.kind !== '<EOF>') {
@@ -1885,7 +1886,7 @@ const rule$b = {
1885
1886
  requireGraphQLSchemaFromContext('no-deprecated', context);
1886
1887
  const typeInfo = node.typeInfo();
1887
1888
  if (typeInfo && typeInfo.enumValue) {
1888
- if (typeInfo.enumValue.isDeprecated) {
1889
+ if (typeInfo.enumValue.deprecationReason) {
1889
1890
  const enumValueName = node.value;
1890
1891
  context.report({
1891
1892
  loc: getLocation(node.loc, enumValueName),
@@ -1902,7 +1903,7 @@ const rule$b = {
1902
1903
  requireGraphQLSchemaFromContext('no-deprecated', context);
1903
1904
  const typeInfo = node.typeInfo();
1904
1905
  if (typeInfo && typeInfo.fieldDef) {
1905
- if (typeInfo.fieldDef.isDeprecated) {
1906
+ if (typeInfo.fieldDef.deprecationReason) {
1906
1907
  const fieldName = node.name.value;
1907
1908
  context.report({
1908
1909
  loc: getLocation(node.loc, fieldName),
@@ -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(${graphql.Kind.OBJECT_TYPE_DEFINITION}, ${graphql.Kind.OBJECT_TYPE_EXTENSION})`,
2139
+ '>',
2140
+ `${graphql.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$e = {
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$f = {
2232
+ const rule$g = {
2141
2233
  meta: {
2142
2234
  messages: {
2143
2235
  [UNUSED_FIELD]: `Field "{{fieldName}}" is unused`,
@@ -2326,7 +2418,7 @@ const MESSAGE_REQUIRE_DATE = 'MESSAGE_REQUIRE_DATE';
2326
2418
  const MESSAGE_INVALID_FORMAT = 'MESSAGE_INVALID_FORMAT';
2327
2419
  const MESSAGE_INVALID_DATE = 'MESSAGE_INVALID_DATE';
2328
2420
  const MESSAGE_CAN_BE_REMOVED = 'MESSAGE_CAN_BE_REMOVED';
2329
- const rule$g = {
2421
+ const rule$h = {
2330
2422
  meta: {
2331
2423
  type: 'suggestion',
2332
2424
  docs: {
@@ -2429,7 +2521,7 @@ const rule$g = {
2429
2521
  },
2430
2522
  };
2431
2523
 
2432
- const rule$h = {
2524
+ const rule$i = {
2433
2525
  meta: {
2434
2526
  docs: {
2435
2527
  description: `Require all deprecation directives to specify a reason.`,
@@ -2509,7 +2601,7 @@ function verifyRule(context, node) {
2509
2601
  }
2510
2602
  }
2511
2603
  }
2512
- const rule$i = {
2604
+ const rule$j = {
2513
2605
  meta: {
2514
2606
  docs: {
2515
2607
  category: 'Best Practices',
@@ -2518,7 +2610,7 @@ const rule$i = {
2518
2610
  examples: [
2519
2611
  {
2520
2612
  title: 'Incorrect',
2521
- usage: [{ on: ['ObjectTypeDefinition', 'FieldDefinition'] }],
2613
+ usage: [{ on: [graphql.Kind.OBJECT_TYPE_DEFINITION, graphql.Kind.FIELD_DEFINITION] }],
2522
2614
  code: /* GraphQL */ `
2523
2615
  type someTypeName {
2524
2616
  name: String
@@ -2527,7 +2619,7 @@ const rule$i = {
2527
2619
  },
2528
2620
  {
2529
2621
  title: 'Correct',
2530
- usage: [{ on: ['ObjectTypeDefinition', 'FieldDefinition'] }],
2622
+ usage: [{ on: [graphql.Kind.OBJECT_TYPE_DEFINITION, graphql.Kind.FIELD_DEFINITION] }],
2531
2623
  code: /* GraphQL */ `
2532
2624
  """
2533
2625
  Some type description
@@ -2574,7 +2666,7 @@ const rule$i = {
2574
2666
  };
2575
2667
 
2576
2668
  const RULE_NAME$2 = 'require-field-of-type-query-in-mutation-result';
2577
- const rule$j = {
2669
+ const rule$k = {
2578
2670
  meta: {
2579
2671
  type: 'suggestion',
2580
2672
  docs: {
@@ -2742,7 +2834,7 @@ const convertNode = (typeInfo) => (node, key, parent) => {
2742
2834
 
2743
2835
  const REQUIRE_ID_WHEN_AVAILABLE = 'REQUIRE_ID_WHEN_AVAILABLE';
2744
2836
  const DEFAULT_ID_FIELD_NAME = 'id';
2745
- const rule$k = {
2837
+ const rule$l = {
2746
2838
  meta: {
2747
2839
  type: 'suggestion',
2748
2840
  docs: {
@@ -2878,7 +2970,7 @@ const rule$k = {
2878
2970
  },
2879
2971
  };
2880
2972
 
2881
- const rule$l = {
2973
+ const rule$m = {
2882
2974
  meta: {
2883
2975
  docs: {
2884
2976
  category: 'Best Practices',
@@ -3000,7 +3092,7 @@ const shouldIgnoreNode = ({ node, exceptions }) => {
3000
3092
  }
3001
3093
  return false;
3002
3094
  };
3003
- const rule$m = {
3095
+ const rule$n = {
3004
3096
  meta: {
3005
3097
  type: 'suggestion',
3006
3098
  docs: {
@@ -3177,7 +3269,7 @@ const checkNode = (context, node, ruleName, messageId) => {
3177
3269
  });
3178
3270
  }
3179
3271
  };
3180
- const rule$n = {
3272
+ const rule$o = {
3181
3273
  meta: {
3182
3274
  type: 'suggestion',
3183
3275
  docs: {
@@ -3236,7 +3328,7 @@ const rule$n = {
3236
3328
 
3237
3329
  const RULE_NAME$4 = 'unique-operation-name';
3238
3330
  const UNIQUE_OPERATION_NAME = 'UNIQUE_OPERATION_NAME';
3239
- const rule$o = {
3331
+ const rule$p = {
3240
3332
  meta: {
3241
3333
  type: 'suggestion',
3242
3334
  docs: {
@@ -3316,17 +3408,18 @@ const rules = {
3316
3408
  'no-deprecated': rule$b,
3317
3409
  'no-hashtag-description': rule$c,
3318
3410
  'no-operation-name-suffix': rule$d,
3319
- 'no-unreachable-types': rule$e,
3320
- 'no-unused-fields': rule$f,
3321
- 'require-deprecation-date': rule$g,
3322
- 'require-deprecation-reason': rule$h,
3323
- 'require-description': rule$i,
3324
- 'require-field-of-type-query-in-mutation-result': rule$j,
3325
- 'require-id-when-available': rule$k,
3326
- 'selection-set-depth': rule$l,
3327
- 'strict-id-in-types': rule$m,
3328
- 'unique-fragment-name': rule$n,
3329
- 'unique-operation-name': rule$o,
3411
+ 'no-root-type': rule$e,
3412
+ 'no-unreachable-types': rule$f,
3413
+ 'no-unused-fields': rule$g,
3414
+ 'require-deprecation-date': rule$h,
3415
+ 'require-deprecation-reason': rule$i,
3416
+ 'require-description': rule$j,
3417
+ 'require-field-of-type-query-in-mutation-result': rule$k,
3418
+ 'require-id-when-available': rule$l,
3419
+ 'selection-set-depth': rule$m,
3420
+ 'strict-id-in-types': rule$n,
3421
+ 'unique-fragment-name': rule$o,
3422
+ 'unique-operation-name': rule$p,
3330
3423
  };
3331
3424
 
3332
3425
  const RELEVANT_KEYWORDS = ['gql', 'graphql', '/* GraphQL */'];
@@ -3629,7 +3722,12 @@ function getReachableTypes(schema) {
3629
3722
  Directive: collect,
3630
3723
  NamedType: collect,
3631
3724
  };
3632
- for (const type of [schema.getQueryType(), schema.getMutationType(), schema.getSubscriptionType()]) {
3725
+ for (const type of [
3726
+ schema,
3727
+ schema.getQueryType(),
3728
+ schema.getMutationType(),
3729
+ schema.getSubscriptionType(),
3730
+ ]) {
3633
3731
  if (type) {
3634
3732
  graphql.visit(type.astNode, visitor);
3635
3733
  }
@@ -3688,7 +3786,7 @@ function parseForESLint(code, options = {}) {
3688
3786
  noLocation: false,
3689
3787
  });
3690
3788
  const { rootTree, comments } = convertToESTree(graphqlAst.document, schema ? new graphql.TypeInfo(schema) : null);
3691
- const tokens = extractTokens(code);
3789
+ const tokens = extractTokens(new graphql.Source(code, filePath));
3692
3790
  return {
3693
3791
  services: parserServices,
3694
3792
  parserServices,
package/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { Kind, Source, validate, isScalarType, TokenKind, isNonNullType, isListType, isObjectType as isObjectType$1, visit, visitWithTypeInfo, GraphQLObjectType, GraphQLInterfaceType, TypeInfo, isInterfaceType, GraphQLError } from 'graphql';
1
+ import { Kind, validate, isScalarType, TokenKind, isNonNullType, isListType, isObjectType as isObjectType$1, visit, visitWithTypeInfo, GraphQLObjectType, GraphQLInterfaceType, TypeInfo, isInterfaceType, Source, GraphQLError } from 'graphql';
2
2
  import { validateSDL } from 'graphql/validation/validate';
3
3
  import { processImport, parseImportLine } from '@graphql-tools/import';
4
4
  import { statSync, existsSync, readFileSync } from 'fs';
@@ -112,6 +112,7 @@ const allConfig = {
112
112
  '@graphql-eslint/match-document-filename': 'error',
113
113
  '@graphql-eslint/no-deprecated': 'error',
114
114
  '@graphql-eslint/no-hashtag-description': 'error',
115
+ '@graphql-eslint/no-root-type': ['error', { disallow: ['subscription'] }],
115
116
  '@graphql-eslint/no-unreachable-types': 'error',
116
117
  '@graphql-eslint/no-unused-fields': 'error',
117
118
  '@graphql-eslint/require-deprecation-date': 'error',
@@ -170,7 +171,7 @@ function getLexer(source) {
170
171
  throw new Error(`Unsupported GraphQL version! Please make sure to use GraphQL v14 or newer!`);
171
172
  }
172
173
  function extractTokens(source) {
173
- const lexer = getLexer(new Source(source));
174
+ const lexer = getLexer(source);
174
175
  const tokens = [];
175
176
  let token = lexer.advance();
176
177
  while (token && token.kind !== '<EOF>') {
@@ -1879,7 +1880,7 @@ const rule$b = {
1879
1880
  requireGraphQLSchemaFromContext('no-deprecated', context);
1880
1881
  const typeInfo = node.typeInfo();
1881
1882
  if (typeInfo && typeInfo.enumValue) {
1882
- if (typeInfo.enumValue.isDeprecated) {
1883
+ if (typeInfo.enumValue.deprecationReason) {
1883
1884
  const enumValueName = node.value;
1884
1885
  context.report({
1885
1886
  loc: getLocation(node.loc, enumValueName),
@@ -1896,7 +1897,7 @@ const rule$b = {
1896
1897
  requireGraphQLSchemaFromContext('no-deprecated', context);
1897
1898
  const typeInfo = node.typeInfo();
1898
1899
  if (typeInfo && typeInfo.fieldDef) {
1899
- if (typeInfo.fieldDef.isDeprecated) {
1900
+ if (typeInfo.fieldDef.deprecationReason) {
1900
1901
  const fieldName = node.name.value;
1901
1902
  context.report({
1902
1903
  loc: getLocation(node.loc, fieldName),
@@ -2053,9 +2054,100 @@ const rule$d = {
2053
2054
  },
2054
2055
  };
2055
2056
 
2057
+ const ROOT_TYPES = ['query', 'mutation', 'subscription'];
2058
+ const rule$e = {
2059
+ meta: {
2060
+ type: 'suggestion',
2061
+ docs: {
2062
+ category: 'Validation',
2063
+ description: 'Disallow using root types for `read-only` or `write-only` schemas.',
2064
+ url: 'https://github.com/dotansimha/graphql-eslint/blob/master/docs/rules/no-root-type.md',
2065
+ requiresSchema: true,
2066
+ examples: [
2067
+ {
2068
+ title: 'Incorrect (`read-only` schema)',
2069
+ usage: [{ disallow: ['mutation', 'subscription'] }],
2070
+ code: /* GraphQL */ `
2071
+ type Mutation {
2072
+ createUser(input: CreateUserInput!): User!
2073
+ }
2074
+ `,
2075
+ },
2076
+ {
2077
+ title: 'Incorrect (`write-only` schema)',
2078
+ usage: [{ disallow: ['query'] }],
2079
+ code: /* GraphQL */ `
2080
+ type Query {
2081
+ users: [User!]!
2082
+ }
2083
+ `,
2084
+ },
2085
+ {
2086
+ title: 'Correct (`read-only` schema)',
2087
+ usage: [{ disallow: ['mutation', 'subscription'] }],
2088
+ code: /* GraphQL */ `
2089
+ type Query {
2090
+ users: [User!]!
2091
+ }
2092
+ `,
2093
+ },
2094
+ ],
2095
+ optionsForConfig: [{ disallow: ['subscription'] }],
2096
+ },
2097
+ schema: {
2098
+ type: 'array',
2099
+ minItems: 1,
2100
+ maxItems: 1,
2101
+ items: {
2102
+ type: 'object',
2103
+ additionalProperties: false,
2104
+ required: ['disallow'],
2105
+ properties: {
2106
+ disallow: {
2107
+ type: 'array',
2108
+ uniqueItems: true,
2109
+ minItems: 1,
2110
+ items: {
2111
+ enum: ROOT_TYPES,
2112
+ },
2113
+ },
2114
+ },
2115
+ },
2116
+ },
2117
+ },
2118
+ create(context) {
2119
+ const schema = requireGraphQLSchemaFromContext('no-root-type', context);
2120
+ const disallow = new Set(context.options[0].disallow);
2121
+ const rootTypeNames = [
2122
+ disallow.has('query') && schema.getQueryType(),
2123
+ disallow.has('mutation') && schema.getMutationType(),
2124
+ disallow.has('subscription') && schema.getSubscriptionType(),
2125
+ ]
2126
+ .filter(Boolean)
2127
+ .map(type => type.name);
2128
+ if (rootTypeNames.length === 0) {
2129
+ return {};
2130
+ }
2131
+ const selector = [
2132
+ `:matches(${Kind.OBJECT_TYPE_DEFINITION}, ${Kind.OBJECT_TYPE_EXTENSION})`,
2133
+ '>',
2134
+ `${Kind.NAME}[value=/^(${rootTypeNames.join('|')})$/]`,
2135
+ ].join(' ');
2136
+ return {
2137
+ [selector](node) {
2138
+ const typeName = node.value;
2139
+ context.report({
2140
+ loc: getLocation(node.loc, typeName),
2141
+ message: `Root type "${typeName}" is forbidden`,
2142
+ });
2143
+ },
2144
+ };
2145
+ },
2146
+ };
2147
+
2056
2148
  const UNREACHABLE_TYPE = 'UNREACHABLE_TYPE';
2057
2149
  const RULE_NAME = 'no-unreachable-types';
2058
- const rule$e = {
2150
+ const rule$f = {
2059
2151
  meta: {
2060
2152
  messages: {
2061
2153
  [UNREACHABLE_TYPE]: `Type "{{ typeName }}" is unreachable`,
@@ -2131,7 +2223,7 @@ const rule$e = {
2131
2223
 
2132
2224
  const UNUSED_FIELD = 'UNUSED_FIELD';
2133
2225
  const RULE_NAME$1 = 'no-unused-fields';
2134
- const rule$f = {
2226
+ const rule$g = {
2135
2227
  meta: {
2136
2228
  messages: {
2137
2229
  [UNUSED_FIELD]: `Field "{{fieldName}}" is unused`,
@@ -2320,7 +2412,7 @@ const MESSAGE_REQUIRE_DATE = 'MESSAGE_REQUIRE_DATE';
2320
2412
  const MESSAGE_INVALID_FORMAT = 'MESSAGE_INVALID_FORMAT';
2321
2413
  const MESSAGE_INVALID_DATE = 'MESSAGE_INVALID_DATE';
2322
2414
  const MESSAGE_CAN_BE_REMOVED = 'MESSAGE_CAN_BE_REMOVED';
2323
- const rule$g = {
2415
+ const rule$h = {
2324
2416
  meta: {
2325
2417
  type: 'suggestion',
2326
2418
  docs: {
@@ -2423,7 +2515,7 @@ const rule$g = {
2423
2515
  },
2424
2516
  };
2425
2517
 
2426
- const rule$h = {
2518
+ const rule$i = {
2427
2519
  meta: {
2428
2520
  docs: {
2429
2521
  description: `Require all deprecation directives to specify a reason.`,
@@ -2503,7 +2595,7 @@ function verifyRule(context, node) {
2503
2595
  }
2504
2596
  }
2505
2597
  }
2506
- const rule$i = {
2598
+ const rule$j = {
2507
2599
  meta: {
2508
2600
  docs: {
2509
2601
  category: 'Best Practices',
@@ -2512,7 +2604,7 @@ const rule$i = {
2512
2604
  examples: [
2513
2605
  {
2514
2606
  title: 'Incorrect',
2515
- usage: [{ on: ['ObjectTypeDefinition', 'FieldDefinition'] }],
2607
+ usage: [{ on: [Kind.OBJECT_TYPE_DEFINITION, Kind.FIELD_DEFINITION] }],
2516
2608
  code: /* GraphQL */ `
2517
2609
  type someTypeName {
2518
2610
  name: String
@@ -2521,7 +2613,7 @@ const rule$i = {
2521
2613
  },
2522
2614
  {
2523
2615
  title: 'Correct',
2524
- usage: [{ on: ['ObjectTypeDefinition', 'FieldDefinition'] }],
2616
+ usage: [{ on: [Kind.OBJECT_TYPE_DEFINITION, Kind.FIELD_DEFINITION] }],
2525
2617
  code: /* GraphQL */ `
2526
2618
  """
2527
2619
  Some type description
@@ -2568,7 +2660,7 @@ const rule$i = {
2568
2660
  };
2569
2661
 
2570
2662
  const RULE_NAME$2 = 'require-field-of-type-query-in-mutation-result';
2571
- const rule$j = {
2663
+ const rule$k = {
2572
2664
  meta: {
2573
2665
  type: 'suggestion',
2574
2666
  docs: {
@@ -2736,7 +2828,7 @@ const convertNode = (typeInfo) => (node, key, parent) => {
2736
2828
 
2737
2829
  const REQUIRE_ID_WHEN_AVAILABLE = 'REQUIRE_ID_WHEN_AVAILABLE';
2738
2830
  const DEFAULT_ID_FIELD_NAME = 'id';
2739
- const rule$k = {
2831
+ const rule$l = {
2740
2832
  meta: {
2741
2833
  type: 'suggestion',
2742
2834
  docs: {
@@ -2872,7 +2964,7 @@ const rule$k = {
2872
2964
  },
2873
2965
  };
2874
2966
 
2875
- const rule$l = {
2967
+ const rule$m = {
2876
2968
  meta: {
2877
2969
  docs: {
2878
2970
  category: 'Best Practices',
@@ -2994,7 +3086,7 @@ const shouldIgnoreNode = ({ node, exceptions }) => {
2994
3086
  }
2995
3087
  return false;
2996
3088
  };
2997
- const rule$m = {
3089
+ const rule$n = {
2998
3090
  meta: {
2999
3091
  type: 'suggestion',
3000
3092
  docs: {
@@ -3171,7 +3263,7 @@ const checkNode = (context, node, ruleName, messageId) => {
3171
3263
  });
3172
3264
  }
3173
3265
  };
3174
- const rule$n = {
3266
+ const rule$o = {
3175
3267
  meta: {
3176
3268
  type: 'suggestion',
3177
3269
  docs: {
@@ -3230,7 +3322,7 @@ const rule$n = {
3230
3322
 
3231
3323
  const RULE_NAME$4 = 'unique-operation-name';
3232
3324
  const UNIQUE_OPERATION_NAME = 'UNIQUE_OPERATION_NAME';
3233
- const rule$o = {
3325
+ const rule$p = {
3234
3326
  meta: {
3235
3327
  type: 'suggestion',
3236
3328
  docs: {
@@ -3310,17 +3402,18 @@ const rules = {
3310
3402
  'no-deprecated': rule$b,
3311
3403
  'no-hashtag-description': rule$c,
3312
3404
  'no-operation-name-suffix': rule$d,
3313
- 'no-unreachable-types': rule$e,
3314
- 'no-unused-fields': rule$f,
3315
- 'require-deprecation-date': rule$g,
3316
- 'require-deprecation-reason': rule$h,
3317
- 'require-description': rule$i,
3318
- 'require-field-of-type-query-in-mutation-result': rule$j,
3319
- 'require-id-when-available': rule$k,
3320
- 'selection-set-depth': rule$l,
3321
- 'strict-id-in-types': rule$m,
3322
- 'unique-fragment-name': rule$n,
3323
- 'unique-operation-name': rule$o,
3405
+ 'no-root-type': rule$e,
3406
+ 'no-unreachable-types': rule$f,
3407
+ 'no-unused-fields': rule$g,
3408
+ 'require-deprecation-date': rule$h,
3409
+ 'require-deprecation-reason': rule$i,
3410
+ 'require-description': rule$j,
3411
+ 'require-field-of-type-query-in-mutation-result': rule$k,
3412
+ 'require-id-when-available': rule$l,
3413
+ 'selection-set-depth': rule$m,
3414
+ 'strict-id-in-types': rule$n,
3415
+ 'unique-fragment-name': rule$o,
3416
+ 'unique-operation-name': rule$p,
3324
3417
  };
3325
3418
 
3326
3419
  const RELEVANT_KEYWORDS = ['gql', 'graphql', '/* GraphQL */'];
@@ -3623,7 +3716,12 @@ function getReachableTypes(schema) {
3623
3716
  Directive: collect,
3624
3717
  NamedType: collect,
3625
3718
  };
3626
- for (const type of [schema.getQueryType(), schema.getMutationType(), schema.getSubscriptionType()]) {
3719
+ for (const type of [
3720
+ schema,
3721
+ schema.getQueryType(),
3722
+ schema.getMutationType(),
3723
+ schema.getSubscriptionType(),
3724
+ ]) {
3627
3725
  if (type) {
3628
3726
  visit(type.astNode, visitor);
3629
3727
  }
@@ -3682,7 +3780,7 @@ function parseForESLint(code, options = {}) {
3682
3780
  noLocation: false,
3683
3781
  });
3684
3782
  const { rootTree, comments } = convertToESTree(graphqlAst.document, schema ? new TypeInfo(schema) : null);
3685
- const tokens = extractTokens(code);
3783
+ const tokens = extractTokens(new Source(code, filePath));
3686
3784
  return {
3687
3785
  services: parserServices,
3688
3786
  parserServices,
package/package.json CHANGED
@@ -1,18 +1,19 @@
1
1
  {
2
2
  "name": "@graphql-eslint/eslint-plugin",
3
- "version": "3.0.0-alpha-5388f29.0",
3
+ "version": "3.0.0-alpha-3168a9b.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
- "@graphql-tools/code-file-loader": "^7.0.2",
10
- "@graphql-tools/graphql-tag-pluck": "^7.0.2",
11
- "@graphql-tools/import": "^6.3.1",
12
- "@graphql-tools/utils": "^8.0.2",
13
- "graphql-config": "^4.0.1",
9
+ "@babel/code-frame": "7.16.0",
10
+ "@graphql-tools/code-file-loader": "7.2.2",
11
+ "@graphql-tools/graphql-tag-pluck": "7.1.3",
12
+ "@graphql-tools/import": "6.6.1",
13
+ "@graphql-tools/utils": "8.5.3",
14
+ "graphql-config": "4.1.0",
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>",
@@ -1,9 +1,10 @@
1
+ import { Kind } from 'graphql';
1
2
  import { GraphQLESLintRule } from '../types';
2
- declare const fieldsEnum: ("ObjectTypeDefinition" | "InterfaceTypeDefinition" | "InputObjectTypeDefinition")[];
3
- declare const valuesEnum: "EnumTypeDefinition"[];
4
- declare const selectionsEnum: ("OperationDefinition" | "FragmentDefinition")[];
5
- declare const variablesEnum: "OperationDefinition"[];
6
- declare const argumentsEnum: ("Field" | "Directive" | "FieldDefinition" | "DirectiveDefinition")[];
3
+ declare const fieldsEnum: Kind[];
4
+ declare const valuesEnum: Kind[];
5
+ declare const selectionsEnum: Kind[];
6
+ declare const variablesEnum: Kind[];
7
+ declare const argumentsEnum: Kind[];
7
8
  declare type AlphabetizeConfig = [
8
9
  {
9
10
  fields?: typeof fieldsEnum;
package/rules/index.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  export declare const rules: {
2
2
  alphabetize: import("..").GraphQLESLintRule<[{
3
- fields?: ("ObjectTypeDefinition" | "InterfaceTypeDefinition" | "InputObjectTypeDefinition")[];
4
- values?: "EnumTypeDefinition"[];
5
- selections?: ("OperationDefinition" | "FragmentDefinition")[];
6
- variables?: "OperationDefinition"[];
7
- arguments?: ("Field" | "Directive" | "FieldDefinition" | "DirectiveDefinition")[];
3
+ fields?: import("graphql").Kind[];
4
+ values?: import("graphql").Kind[];
5
+ selections?: import("graphql").Kind[];
6
+ variables?: import("graphql").Kind[];
7
+ arguments?: import("graphql").Kind[];
8
8
  }], false>;
9
9
  'avoid-duplicate-fields': import("..").GraphQLESLintRule<any[], false>;
10
10
  'avoid-operation-name-prefix': import("..").GraphQLESLintRule<import("./avoid-operation-name-prefix").AvoidOperationNamePrefixConfig, false>;
@@ -161,6 +161,9 @@ export declare const rules: {
161
161
  'no-deprecated': import("..").GraphQLESLintRule<[], true>;
162
162
  'no-hashtag-description': import("..").GraphQLESLintRule<any[], false>;
163
163
  'no-operation-name-suffix': import("..").GraphQLESLintRule<any[], false>;
164
+ 'no-root-type': import("..").GraphQLESLintRule<[{
165
+ disallow: ("query" | "mutation" | "subscription")[];
166
+ }], false>;
164
167
  'no-unreachable-types': import("..").GraphQLESLintRule<any[], false>;
165
168
  'no-unused-fields': import("..").GraphQLESLintRule<any[], false>;
166
169
  'require-deprecation-date': import("..").GraphQLESLintRule<[{
@@ -168,7 +171,7 @@ export declare const rules: {
168
171
  }], false>;
169
172
  'require-deprecation-reason': import("..").GraphQLESLintRule<any[], false>;
170
173
  'require-description': import("..").GraphQLESLintRule<[{
171
- on: ("SchemaDefinition" | "ObjectTypeDefinition" | "FieldDefinition" | "InputValueDefinition" | "InterfaceTypeDefinition" | "UnionTypeDefinition" | "EnumTypeDefinition" | "EnumValueDefinition" | "InputObjectTypeDefinition" | "DirectiveDefinition")[];
174
+ on: import("graphql").Kind[];
172
175
  }], false>;
173
176
  'require-field-of-type-query-in-mutation-result': import("..").GraphQLESLintRule<any[], false>;
174
177
  'require-id-when-available': 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;
@@ -1,5 +1,6 @@
1
1
  import { GraphQLESLintRule } from '../types';
2
- declare const DESCRIBABLE_NODES: ("SchemaDefinition" | "ObjectTypeDefinition" | "FieldDefinition" | "InputValueDefinition" | "InterfaceTypeDefinition" | "UnionTypeDefinition" | "EnumTypeDefinition" | "EnumValueDefinition" | "InputObjectTypeDefinition" | "DirectiveDefinition")[];
2
+ import { Kind } from 'graphql';
3
+ declare const DESCRIBABLE_NODES: Kind[];
3
4
  declare type RequireDescriptionRuleConfig = [{
4
5
  on: typeof DESCRIBABLE_NODES;
5
6
  }];
package/utils.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { GraphQLSchema } from 'graphql';
1
+ import { GraphQLSchema, Source, Kind } from 'graphql';
2
2
  import { AST } from 'eslint';
3
3
  import { Source as LoaderSource } from '@graphql-tools/utils';
4
4
  import { GraphQLESLintRuleContext } from './types';
@@ -8,7 +8,7 @@ export declare function requireSiblingsOperations(ruleName: string, context: Gra
8
8
  export declare function requireGraphQLSchemaFromContext(ruleName: string, context: GraphQLESLintRuleContext): GraphQLSchema | never;
9
9
  export declare function requireReachableTypesFromContext(ruleName: string, context: GraphQLESLintRuleContext): ReachableTypes | never;
10
10
  export declare function requireUsedFieldsFromContext(ruleName: string, context: GraphQLESLintRuleContext): UsedFields | never;
11
- export declare function extractTokens(source: string): AST.Token[];
11
+ export declare function extractTokens(source: Source): AST.Token[];
12
12
  export declare const normalizePath: (path: string) => string;
13
13
  /**
14
14
  * https://github.com/prettier/eslint-plugin-prettier/blob/76bd45ece6d56eb52f75db6b4a1efdd2efb56392/eslint-plugin-prettier.js#L71
@@ -19,7 +19,7 @@ export declare const normalizePath: (path: string) => string;
19
19
  export declare const getOnDiskFilepath: (filepath: string) => string;
20
20
  export declare const getTypeName: (node: any) => any;
21
21
  export declare const loaderCache: Record<string, LoaderSource[]>;
22
- export declare const TYPES_KINDS: ("ScalarTypeDefinition" | "ObjectTypeDefinition" | "InterfaceTypeDefinition" | "UnionTypeDefinition" | "EnumTypeDefinition" | "InputObjectTypeDefinition")[];
22
+ export declare const TYPES_KINDS: Kind[];
23
23
  export declare enum CaseStyle {
24
24
  camelCase = "camelCase",
25
25
  pascalCase = "PascalCase",