@qubhq/eslint-plugin-nestjs-graphql 1.2.0 → 1.2.1

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.
Files changed (2) hide show
  1. package/README.md +172 -94
  2. package/package.json +11 -7
package/README.md CHANGED
@@ -1,15 +1,23 @@
1
- # eslint-plugin-nestjs-graphql
1
+ # @qubhq/eslint-plugin-nestjs-graphql
2
2
 
3
3
  > **Note**
4
- > This fork of `eslint-plugin-nestjs-graphql` extends the original functionality
5
- > by adding support for some [`graphql-scalars`](https://www.npmjs.com/package/graphql-scalars)
6
- types as needed.
4
+ > This fork of [`eslint-plugin-nestjs-graphql`](https://github.com/Hatko/eslint-plugin-nestjs-graphql) extends the
5
+ > original functionality by adding support for selected [`graphql-scalars`](https://www.npmjs.com/package/graphql-scalars)
6
+ > types.
7
+ >
8
+ > Right now the following scalars are supported: `GraphQLUUID`. If you want more, open an issue or make a PR.
9
+ >
7
10
  > Also adds support for `@typescript-eslint` v8
8
11
  > Introduced new rule: `args-nullable-optional`
9
12
 
10
- [![npm](https://img.shields.io/npm/v/eslint-plugin-nestjs-graphql.svg)](https://www.npmjs.com/package/eslint-plugin-nestjs-graphql)
13
+ [![NPM Version](https://img.shields.io/npm/v/%40qubhq%2Feslint-plugin-nestjs-graphql?style=for-the-badge)](https://www.npmjs.com/package/@qubhq/eslint-plugin-nestjs-graphql)
14
+ ![NPM License](https://img.shields.io/npm/l/%40qubhq%2Feslint-plugin-nestjs-graphql?style=for-the-badge)
15
+ ![NPM Downloads](https://img.shields.io/npm/dw/%40qubhq%2Feslint-plugin-nestjs-graphql?style=for-the-badge)
16
+ ![NPM Type Definitions](https://img.shields.io/npm/types/%40qubhq%2Feslint-plugin-nestjs-graphql?style=for-the-badge)
17
+ [![QubHQ](https://img.shields.io/badge/backed%20by-QubHQ-orange?style=for-the-badge&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCAyNDAwIDI0MDAiPjxwYXRoIGQ9Ik0xNTk4IDgwMkgwdjE1OTdoMTU5OFY4MDJaIiBmaWxsPSIjRkVGN0YxIi8%2BPHBhdGggZD0iTTI0MDAgMHYxNTk4TDE1OTggMjQwMFY4MDJMMjQwMCAwWk0xMzkwIDIxMDZ2LTM4MGwtMTUgMTdhMjMxIDIzMSAwIDAxLTE4MCA4MmMtNzAgMC0xMzAtMjgtMTgwLTgyLTQ3LTUzLTcwLTExNi03MC0xODcgMC03MiAyMy0xMzUgNzAtMTg4YTIzMSAyMzEgMCAwMSAxODAtODIgMjMxIDIzMSAwIDAxIDE5NSAxMDB2LTg4aDgydjgwOEgxMzkwWm0tNTItNjgyYTE3MCAxNzAgMCAwMC0xMzAtNTYgMTcwIDE3MCAwIDAwLTEzMCA1NiAxOTIgMTkyIDAgMDAgMCAyNjMgMTcwIDE3MCAwIDAwIDEzMCA1NiAxNzAgMTcwIDAgMDAgMTMwLTU2IDE5MiAxOTIgMCAwMCAwLTI2M1oiIGZpbGw9IiNFQjZEMDAiLz48bWFzayBpZD0iYSIgd2lkdGg9IjQ0MCIgaGVpZ2h0PSIxMzEwIiB4PSIxNjkwIiB5PSI3NjAiIG1hc2tVbml0cz0idXNlclNwYWNlT25Vc2UiIHN0eWxlPSJtYXNrLXR5cGU6bHVtaW5hbmNlIj48cGF0aCBkPSJtMjEyNiA3NjgtNDMwIDQzMHY4NjRsNDMwLTQzMFY3NjhaIiBmaWxsPSIjZmZmIi8%2BPC9tYXNrPjxnIGZpbGw9IiNGRUY3RjEiIG1hc2s9InVybCgjYSkiPjxwYXRoIGQ9Ik0xNzU0IDIwMDR2LTIxMmMwLTcwIDgtMTMyIDI1LTE4NmE0MjQgNDI0IDAgMDEgOTgtMTYxYzI0LTI1IDQwLTI5IDUxLTE3IDEwIDEzIDE3IDM2IDE3IDY4djMxN2w1Ny01N3YtMzE3YzAtNTgtMTItOTQtMzMtMTA5LTExLTctMjQtOC00MC0zYTE5MCAxOTAgMCAwMC02MiA0NiAzODEgMzgxIDAgMDAtNjcgOTcgNjEwIDYxMCAwIDAwLTQ2IDExNFYxMTQwbC01NyA1N1YyMDYwbDU3LTU3WiIvPjxwYXRoIGQ9Ik0xNzU0IDIwMDR2MTJsMTAtOXYtMTJsLTEwIDlabTI1LTM5OGgtNyA3Wm0zOC04NCA0IDctNC03Wm0xMjggMjkxLTEwIDEwdjEybDEwLTEwdi0xMlptNTctNTd2MTNsOS0xMHYtMTJsLTEwIDlabS0zMy00MjYgNy0xNS03IDE1Wm0tNDAtMyA0LTE1LTQgMTVaTTE4MDAgMTQ3MGw2IDUtNS01Wm0tNDUgMTE0LTEwIDEwdjQ4bDE4LTYwLTggMlptMC00NDQgMTAtMTB2LTEybC0xMCA5djEzWm0tNTcgNTd2LTEzbC0xMCAxMHYxMmwxMC0xMFptMCA4NjQtMTAgOXYxMmwxMC05di0xMlptNjYtNjZ2LTIxMmwtMTggMTh2MjEybDE4LTE4Wm0wLTIxMmMwLTcwIDgtMTI4IDI0LTE3N2gtMTVhNjM4IDYzOCAwIDAwLTI3IDE5NWwxOC0xOFptMjQtMTc3YTQxMCA0MTAgMCAwMSAzNC03N2wtOS0xNGMtMTYgMjktMzAgNTgtNDAgOTJsMTUtMVptMzQtNzhjMTMtMjIgMzItNDcgNTUtNzB2LTI2YTM4NSAzODUgMCAwMC02NCA4M2wxMCAxM1ptNTUtNzBjMjItMjIgMzYtMjUgNDUtMTVsMTUtMzBjLTEzLTE0LTMzLTgtNjAgMjB2MjVabTQ1LTE1YzEwIDExIDE1IDMyIDE1IDYybDE4LTE4YzAtMzQtNi02MC0xOC03M2wtMTUgMzBabTE1IDYydjMxN2wxOC0xOHYtMzE3bC0xOCAxOFptOSAzMjAgNTctNTZ2LTI2bC01NyA1N3YyNlptNjYtNzhWMTQzMGwtMTggMTh2MzE3bDE4LTE4Wm0wLTMxN2MwLTYwLTEyLTEwMC0zNS0xMTVsLTE0IDMwYzIwIDE0IDMwIDQ3IDMwIDEwM2wxOS0xOFptLTM1LTExNWMtMTItOC0yNi04LTQzLTNsLTggMzBjMTUtNCAyNy0zIDM3IDNsMTQtMzBabS00My0zYTIwMCAyMDAgMCAwMC02NiA0OHYyNmMyNS0yNCA0NC0zOCA1OC00M2w4LTMwWm0tNjYgNDhhNDEwIDQxMCAwIDAwLTcyIDEwNGwxMCAxMWMxOC0zNSAzOC02NSA2Mi04OVYxMzYwWm0tNzIgMTA0Yy0yMCAzOC0zNyA4MC00OSAxMjJsMTYtM2E1NzAgNTcwIDAgMDEgNDMtMTA4bC0xMC0xMVptLTMyIDExMXYtNDQ0bC0xOCAxOHY0NDRsMTgtMThabS0xMC00NDgtNTYgNTd2MjZsNTctNTd2LTI2Wm0tNjUgNzlWMjA3MGwxOC0xOHYtODY0bC0xOCAxOFptOSA4NjcgNTctNTd2LTI1bC01NyA1N3YyNVoiLz48cGF0aCBkPSJNMjA3NyAxMDAwYzAgMzItOCA3MC0yNCAxMTAtMTYgNDAtMzYgNzMtNjAgOTYtMjQgMjQtNDQgMzItNjAgMjQtMTYtOC0yNC0zMC0yNC02MiAwLTMzIDgtNzAgMjQtMTEwIDE2LTQxIDM2LTczIDYwLTk3IDI0LTI0IDQ0LTMxIDYwLTIzIDE2IDggMjQgMzAgMjQgNjFabTM4IDMyMnYtNTMwbC0zOCAzOHY2MGMtMi0zLTQtNC03LTUtMjItMTMtNTAtNC04MyAyOWE0MTAgNDEwIDAgMDAtODMgMTM3IDQ0MCA0NDAgMCAwMC0zMyAxNTZjMCA0NyAxMCA3NyAzMyA5MCAyMiAxMyA1MCAzIDgzLTMwYTM5MCAzOTAgMCAwMCA5MC0xNTV2MjUwbDM4LTM5WiIvPjxwYXRoIGQ9Im0yMDUzIDExMTAtNy0zIDcgM1ptLTEyMCAxMjAgNy0xNi03IDE2Wm0xODIgOTJ2MTNsMTAtOXYtMTNsLTEwIDEwWm0wLTUzMCAxMC05di0xMmwtMTAgOXYxMlptLTM4IDM4di0xMmwtOSA5djEybDEwLTlabTAgNTktNyAxNCAxNiAyMFY4ODBsLTkgOVptLTctNSA3LTE1LTcgMTVaTTE5MDQgMTA1MGwtNy0yIDcgMlptMTY2IDgwLTYtMyA2IDJabTctMjAgMTAtOHYtNDNsLTE3IDUyaDdabTAgMjUwLTkgMTB2MTJsMTAtOVYxMzYwWm0tOS0zNTJjMCAzMC03IDYzLTIyIDEwMGwxMyA0YTMzMCAzMzAgMCAwMCAyNy0xMjJsLTE4IDE4Wm0tMjIgMTAwYTI1MSAyNTEgMCAwMS01MyA4NnYyNWMyNi0yNiA0OS02MiA2Ny0xMDdsLTE0LTRabS01MyA4NmMtMjEgMjAtMzkgMjgtNTMgMjBsLTE0IDMxYzE4IDEwIDQwIDAgNjctMjZ2LTI1Wm0tNTQgMjBjLTE0LTYtMjAtMjUtMjAtNTVsLTE5IDE4YzAgMzYgOCA2MCAyNiA2OGwxMy0zMFptLTIwLTU1YzAtMzAgNi02MiAyMC0xMDBsLTEzLTNhMzMyIDMzMiAwIDAwLTI2IDEyMGwxOC0xN1ptMjAtMTAwYzE1LTM2IDMzLTY0IDU0LTg2di0yNWEzMTcgMzE3IDAgMDAtNjcgMTA4bDE0IDRabTU0LTg2YzIxLTIwIDM5LTI3IDUzLTIwbDE0LTMwYy0xOC0xMC00MC0xLTY3IDI1djI1Wm01My0yMGMxNSA4IDIyIDI2IDIyIDU1bDE4LTE4YzAtMzUtOS01OC0yNi02OGwtMTQgMzFabTc4IDM2MHYtNTMwbC0xOCAxOHY1MzBsMTgtMThabS05LTUzMy0zOCAzOHYyNWwzOC0zOFY3ODBabS00NyA2MHY1OGwxOC0xOHYtNjBsLTE4IDE4Wm0xNyAzNGEyMCAyMCAwIDAwLTQtM2wtNC0yLTE0IDMwIDQgMiAzIDIgMTUtMjlabS04LTZjLTI0LTEzLTU0LTMtOTAgMzJ2MjZjMzAtMzAgNTUtMzkgNzctMjdsMTMtMzBabS05MCAzMmE0MzAgNDMwIDAgMDAtOTAgMTQ4bDEzIDRjMjItNTQgNDctOTYgNzctMTI2VjkwMFptLTkwIDE0OWMtMjMgNjAtMzUgMTE2LTM1IDE2NmwxOC0xOGMwLTQ0IDEwLTkyIDMwLTE0NWwtMTMtM1ptLTM1IDE2NmMwIDUwIDEyIDgzIDM1IDk2bDEzLTMwYy0yMC0xMi0zMC00MC0zMC04NGwtMTggMThabTM1IDk2YzI0IDE1IDU0IDQgOTAtMzJ2LTI2Yy0zMCAzMC01NSA0MC03NyAyOGwtMTMgMzBabTkwLTMyYzM2LTM1IDY2LTg1IDkwLTE0OGwtMTMtM2EzNTcgMzU3IDAgMDEtNzcgMTI1djI2Wm05MC0xNDcgOC0yMi0xNSAxLTYgMTYgMTMgNVptLTktMTJ2MjUwbDE4LTE5di0yNTBsLTE4IDE5Wm0xMCAyNTMgMzctMzhWMTMxMGwtMzggMzh2MjZaIi8%2BPC9nPjwvc3ZnPgo%3D)](https://qubhq.com)
11
18
 
12
- This plugin intends to prevent issues with returning the wrong type from NestJS GraphQL resolvers. Relevant to [Code first](https://docs.nestjs.com/graphql/quick-start#code-first) approach.
19
+ This plugin intends to prevent issues with returning the wrong type from NestJS GraphQL resolvers. Relevant
20
+ to [Code first](https://docs.nestjs.com/graphql/quick-start#code-first) approach.
13
21
 
14
22
  ## Rules
15
23
 
@@ -23,18 +31,25 @@ The plugin supports rules:
23
31
 
24
32
  ### matching-return-type
25
33
 
26
- When Code first approach is used, NestJS generates schema based on the decorators such as `ResolveField`, `Query`, or `Mutation` which define the type of the returned value. However, the type of the returned value is not checked by TypeScript compiler.
34
+ When Code first approach is used, NestJS generates schema based on the decorators such as `ResolveField`, `Query`, or
35
+ `Mutation` which define the type of the returned value. However, the type of the returned value is not checked by
36
+ TypeScript compiler.
27
37
 
28
38
  A query defined as:
29
39
 
30
40
  ```typescript
31
41
  @Query(returns => Author)
32
- async author(@Args('id', { type: () => Int }) id: number) {
33
- return this.authorsService.findOneById(id);
34
- }
42
+ async
43
+ author(@Args('id', { type: () => Int })
44
+ id: number
45
+ )
46
+ {
47
+ return this.authorsService.findOneById(id);
48
+ }
35
49
  ```
36
50
 
37
- can be implemented to return any type of value, e.g. `Promise<string>`. This will not be caught by TypeScript compiler, but will result in runtime error when the GraphQL schema is generated.
51
+ can be implemented to return any type of value, e.g. `Promise<string>`. This will not be caught by TypeScript compiler,
52
+ but will result in runtime error when the GraphQL schema is generated.
38
53
 
39
54
  This rule aims to solve this issue by checking the type of the returned value.
40
55
 
@@ -42,181 +57,244 @@ This rule aims to solve this issue by checking the type of the returned value.
42
57
 
43
58
  ```typescript
44
59
  @Query(returns => Author)
45
- async author(@Args('id', { type: () => Int }) id: number): Author {
46
- return this.authorsService.findOneById(id);
47
- }
60
+ async
61
+ author(@Args('id', { type: () => Int })
62
+ id: number
63
+ ):
64
+ Author
65
+ {
66
+ return this.authorsService.findOneById(id);
67
+ }
48
68
  ```
49
69
 
50
70
  ```typescript
51
71
  @Query(returns => Author)
52
- async author(@Args('id', { type: () => Int }) id: number): Promise<Author> {
53
- return this.authorsService.findOneById(id);
54
- }
72
+ async
73
+ author(@Args('id', { type: () => Int })
74
+ id: number
75
+ ):
76
+ Promise < Author > {
77
+ return this.authorsService.findOneById(id);
78
+ }
55
79
  ```
56
80
 
57
81
  ```typescript
58
82
  @Query(returns => [Author])
59
- async author(@Args('id', { type: () => Int }) id: number): Promise<Author[]> {
60
- return this.authorsService.findOneById(id);
61
- }
83
+ async
84
+ author(@Args('id', { type: () => Int })
85
+ id: number
86
+ ):
87
+ Promise < Author[] > {
88
+ return this.authorsService.findOneById(id);
89
+ }
62
90
  ```
63
91
 
64
92
  ```typescript
65
93
  @Query(returns => [Author], { nullable: true })
66
- async author(@Args('id', { type: () => Int }) id: number): Promise<Author[] | null> {
67
- return this.authorsService.findOneById(id);
68
- }
94
+ async
95
+ author(@Args('id', { type: () => Int })
96
+ id: number
97
+ ):
98
+ Promise < Author[] | null > {
99
+ return this.authorsService.findOneById(id);
100
+ }
69
101
  ```
70
102
 
71
103
  *Invalid*
72
104
 
73
105
  ```typescript
74
106
  @Query(returns => Author)
75
- async author(@Args('id', { type: () => Int }) id: number): string {
76
- return this.authorsService.findOneById(id);
77
- }
107
+ async
108
+ author(@Args('id', { type: () => Int })
109
+ id: number
110
+ ):
111
+ string
112
+ {
113
+ return this.authorsService.findOneById(id);
114
+ }
78
115
  ```
79
116
 
80
117
  ```typescript
81
118
  @Query(returns => Author)
82
- async author(@Args('id', { type: () => Int }) id: number): Promise<Author | null> {
83
- return this.authorsService.findOneById(id);
84
- }
119
+ async
120
+ author(@Args('id', { type: () => Int })
121
+ id: number
122
+ ):
123
+ Promise < Author | null > {
124
+ return this.authorsService.findOneById(id);
125
+ }
85
126
  ```
86
127
 
87
128
  ```typescript
88
129
  @Query(returns => Author)
89
- async author(@Args('id', { type: () => Int }) id: number): Promise<Author[]> {
90
- return this.authorsService.findOneById(id);
91
- }
130
+ async
131
+ author(@Args('id', { type: () => Int })
132
+ id: number
133
+ ):
134
+ Promise < Author[] > {
135
+ return this.authorsService.findOneById(id);
136
+ }
92
137
  ```
93
138
 
94
139
  ### matching-resolve-field-parent-type
95
140
 
96
- When resolving a field, the `@Parent()` decorator's type can mismatch the type returned from the `@Resolver()` decorator of the class. This may result in runtime error or unexpected behavior.
141
+ When resolving a field, the `@Parent()` decorator's type can mismatch the type returned from the `@Resolver()` decorator
142
+ of the class. This may result in runtime error or unexpected behavior.
97
143
 
98
144
  This rule aims to solve this issue by checking the type of the `@Parent` against `@Resolver()`.
99
145
 
100
146
  *Valid*
101
147
 
102
148
  ```typescript
103
- @Resolver(() => Author)
104
- class AuthorResolver {
105
- @ResolveField(() => [Book])
106
- async books(@Parent() author: Author): Promise<Book[]> {
107
- return this.booksService.findAllByAuthorId(author.id);
108
- }
149
+
150
+ @Resolver(() => Author)
151
+ class AuthorResolver {
152
+ @ResolveField(() => [Book])
153
+ async books(@Parent() author: Author): Promise<Book[]> {
154
+ return this.booksService.findAllByAuthorId(author.id);
109
155
  }
156
+ }
110
157
  ```
111
158
 
112
159
  ```typescript
113
- @Resolver(Author)
114
- class AuthorResolver {
115
- @ResolveField(returns => [Book])
116
- async books(@Parent() author: Author): Promise<Book[]> {
117
- return this.booksService.findAllByAuthorId(author.id);
118
- }
160
+
161
+ @Resolver(Author)
162
+ class AuthorResolver {
163
+ @ResolveField(returns => [Book])
164
+ async books(@Parent() author: Author): Promise<Book[]> {
165
+ return this.booksService.findAllByAuthorId(author.id);
119
166
  }
167
+ }
120
168
  ```
121
169
 
122
170
  *Invalid*
123
171
 
124
172
  ```typescript
125
- @Resolver()
126
- class AuthorResolver {
127
- @ResolveField(returns => [Book])
128
- async books(@Parent() author: Author): Promise<Book[]> {
129
- return this.booksService.findAllByAuthorId(author.id);
130
- }
173
+
174
+ @Resolver()
175
+ class AuthorResolver {
176
+ @ResolveField(returns => [Book])
177
+ async books(@Parent() author: Author): Promise<Book[]> {
178
+ return this.booksService.findAllByAuthorId(author.id);
131
179
  }
180
+ }
132
181
  ```
133
182
 
134
183
  ```typescript
135
- @Resolver(Author)
136
- class AuthorResolver {
137
- @ResolveField(returns => [Book])
138
- async books(@Parent() author: Book): Promise<Book[]> {
139
- return this.booksService.findAllByAuthorId(author.id);
140
- }
184
+
185
+ @Resolver(Author)
186
+ class AuthorResolver {
187
+ @ResolveField(returns => [Book])
188
+ async books(@Parent() author: Book): Promise<Book[]> {
189
+ return this.booksService.findAllByAuthorId(author.id);
141
190
  }
191
+ }
142
192
  ```
143
193
 
144
194
  ### args-nullable-optional
145
195
 
146
- When using the `@Args` decorator in NestJS GraphQL resolvers, there's a common mismatch between the `nullable` property in the decorator options and the optionality of the parameter in TypeScript. If an argument is marked as `nullable: true` in GraphQL, it should be optional in TypeScript (using `?`), and vice versa.
196
+ When using the `@Args` decorator in NestJS GraphQL resolvers, there's a common mismatch between the `nullable` property
197
+ in the decorator options and the optionality of the parameter in TypeScript. If an argument is marked as`nullable: true`
198
+ in GraphQL, it should be optional in TypeScript (using `?`), and vice versa.
147
199
 
148
- This rule ensures consistency between GraphQL schema nullability and TypeScript parameter optionality to prevent runtime errors and improve type safety.
200
+ This rule ensures consistency between GraphQL schema nullability and TypeScript parameter optionality to prevent runtime
201
+ errors and improve type safety.
149
202
 
150
203
  *Valid*
151
204
 
152
205
  ```typescript
153
206
  // Correct: nullable args with optional parameter
154
- async locations(
155
- @BusinessId() businessId: string,
156
- @Args('input', { nullable: true }) input?: LocationsQueryInput
157
- ): Promise<LocationModel[]> {
158
- return this.locationsService.getAllLocationsForBusiness(businessId, input)
159
- }
207
+ async
208
+ locations(
209
+ @BusinessId()
210
+ businessId: string,
211
+ @Args('input', { nullable: true })
212
+ input ? : LocationsQueryInput
213
+ ):
214
+ Promise < LocationModel[] > {
215
+ return this.locationsService.getAllLocationsForBusiness(businessId, input)
216
+ }
160
217
  ```
161
218
 
162
219
  ```typescript
163
220
  // Correct: required args with non-optional parameter
164
- async locations(
165
- @BusinessId() businessId: string,
166
- @Args('input') input: LocationsQueryInput
167
- ): Promise<LocationModel[]> {
168
- return this.locationsService.getAllLocationsForBusiness(businessId, input)
169
- }
221
+ async
222
+ locations(
223
+ @BusinessId()
224
+ businessId: string,
225
+ @Args('input')
226
+ input: LocationsQueryInput
227
+ ):
228
+ Promise < LocationModel[] > {
229
+ return this.locationsService.getAllLocationsForBusiness(businessId, input)
230
+ }
170
231
  ```
171
232
 
172
233
  ```typescript
173
234
  // Correct: explicitly non-nullable args with non-optional parameter
174
- async locations(
175
- @BusinessId() businessId: string,
176
- @Args('input', { nullable: false }) input: LocationsQueryInput
177
- ): Promise<LocationModel[]> {
178
- return this.locationsService.getAllLocationsForBusiness(businessId, input)
179
- }
235
+ async
236
+ locations(
237
+ @BusinessId()
238
+ businessId: string,
239
+ @Args('input', { nullable: false })
240
+ input: LocationsQueryInput
241
+ ):
242
+ Promise < LocationModel[] > {
243
+ return this.locationsService.getAllLocationsForBusiness(businessId, input)
244
+ }
180
245
  ```
181
246
 
182
247
  *Invalid*
183
248
 
184
249
  ```typescript
185
250
  // Invalid: nullable args but parameter is not optional
186
- async locations(
187
- @BusinessId() businessId: string,
188
- @Args('input', { nullable: true }) input: LocationsQueryInput
189
- ): Promise<LocationModel[]> {
190
- return this.locationsService.getAllLocationsForBusiness(businessId, input)
191
- }
251
+ async
252
+ locations(
253
+ @BusinessId()
254
+ businessId: string,
255
+ @Args('input', { nullable: true })
256
+ input: LocationsQueryInput
257
+ ):
258
+ Promise < LocationModel[] > {
259
+ return this.locationsService.getAllLocationsForBusiness(businessId, input)
260
+ }
192
261
  ```
193
262
 
194
263
  ```typescript
195
264
  // Invalid: optional parameter but args is not nullable
196
- async locations(
197
- @BusinessId() businessId: string,
198
- @Args('input') input?: LocationsQueryInput
199
- ): Promise<LocationModel[]> {
200
- return this.locationsService.getAllLocationsForBusiness(businessId, input)
201
- }
265
+ async
266
+ locations(
267
+ @BusinessId()
268
+ businessId: string,
269
+ @Args('input')
270
+ input ? : LocationsQueryInput
271
+ ):
272
+ Promise < LocationModel[] > {
273
+ return this.locationsService.getAllLocationsForBusiness(businessId, input)
274
+ }
202
275
  ```
203
276
 
204
277
  ## Installation
205
278
 
206
279
  ```sh
207
280
  # inside your project's working tree
208
- npm i eslint-plugin-nestjs-graphql --save-dev
281
+ npm i @qubhq/eslint-plugin-nestjs-graphql --save-dev
209
282
  ```
210
283
 
211
284
  The rules are off by default. To turn them on, add the following to your `.eslintrc` file:
212
285
 
213
286
  ```json
214
287
  {
215
- "plugins": ["nestjs-graphql"],
288
+ "plugins": [
289
+ "@qubhq/nestjs-graphql"
290
+ ],
216
291
  "rules": {
217
- "nestjs-graphql/matching-return-type": "error", // `error` level is recommended
218
- "nestjs-graphql/matching-resolve-field-parent-type": "error", // `error` level is recommended
219
- "nestjs-graphql/args-nullable-optional": "error" // `error` level is recommended
292
+ "@qubhq/nestjs-graphql/matching-return-type": "error",
293
+ // `error` level is recommended
294
+ "@qubhq/nestjs-graphql/matching-resolve-field-parent-type": "error",
295
+ // `error` level is recommended
296
+ "@qubhq/nestjs-graphql/args-nullable-optional": "error"
297
+ // `error` level is recommended
220
298
  }
221
299
  }
222
300
  ```
package/package.json CHANGED
@@ -1,15 +1,18 @@
1
1
  {
2
2
  "name": "@qubhq/eslint-plugin-nestjs-graphql",
3
- "author": "Vladyslav Zavalykhatko",
4
- "homepage": "https://github.com/Hatko/eslint-plugin-nestjs-graphql",
3
+ "author": "QubHQ",
4
+ "contributors": [
5
+ "Vladyslav Zavalykhatko (original author)"
6
+ ],
7
+ "homepage": "https://github.com/qubhq/eslint-plugin-nestjs-graphql",
5
8
  "repository": {
6
9
  "type": "git",
7
- "url": "https://github.com/Hatko/eslint-plugin-nestjs-graphql.git"
10
+ "url": "https://github.com/qubhq/eslint-plugin-nestjs-graphql.git"
8
11
  },
9
12
  "bugs": {
10
- "url": "https://github.com/Hatko/eslint-plugin-nestjs-graphql/issues"
13
+ "url": "https://github.com/qubhq/eslint-plugin-nestjs-graphql/issues"
11
14
  },
12
- "version": "1.2.0",
15
+ "version": "1.2.1",
13
16
  "description": "Ensure correct typing for NestJS GraphQL decorated methods",
14
17
  "main": "./dist/index.js",
15
18
  "files": [
@@ -25,12 +28,13 @@
25
28
  "devDependencies": {
26
29
  "@types/estree": "^1.0.7",
27
30
  "@types/node": "^22.15.18",
28
- "@typescript-eslint/utils": "^8.32.1",
29
31
  "@typescript-eslint/types": "^8.32.1",
32
+ "@typescript-eslint/utils": "^8.32.1",
33
+ "rimraf": "^6.0.1",
30
34
  "typescript": "^5.8.3"
31
35
  },
32
36
  "scripts": {
33
37
  "bump": "pnpx npm-check-updates -u --deep && pnpm i && pnpm upgrade",
34
- "build": "pnpm tsc"
38
+ "build": "rimraf dist && pnpm tsc"
35
39
  }
36
40
  }