@nest-boot/eslint-plugin 7.0.5 → 7.0.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nest-boot/eslint-plugin",
3
- "version": "7.0.5",
3
+ "version": "7.0.7",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/nest-boot/nest-boot.git",
@@ -10,7 +10,7 @@
10
10
  "main": "./dist/index.js",
11
11
  "exports": "./dist/index.js",
12
12
  "dependencies": {
13
- "@typescript-eslint/utils": "^8.51.0"
13
+ "@typescript-eslint/utils": "^8.56.1"
14
14
  },
15
15
  "peerDependencies": {
16
16
  "@typescript-eslint/parser": "^8.0.0",
@@ -21,17 +21,17 @@
21
21
  "@eslint/eslintrc": "^3.3.3",
22
22
  "@eslint/js": "^9.39.2",
23
23
  "@jest/globals": "^30.2.0",
24
- "@types/node": "^22.18.6",
25
- "@typescript-eslint/parser": "^8.51.0",
26
- "@typescript-eslint/rule-tester": "^8.51.0",
27
- "eslint": "^9.39.2",
24
+ "@types/node": "^24.12.4",
25
+ "@typescript-eslint/parser": "^8.56.1",
26
+ "@typescript-eslint/rule-tester": "^8.56.1",
27
+ "eslint": "^9.39.3",
28
28
  "eslint-config-prettier": "^10.1.8",
29
29
  "eslint-plugin-simple-import-sort": "^12.1.1",
30
30
  "jest": "^29.7.0",
31
31
  "ts-jest": "^29.4.4",
32
32
  "typescript": "^5.9.3",
33
- "typescript-eslint": "^8.46.2",
34
- "@nest-boot/tsconfig": "^7.0.1"
33
+ "typescript-eslint": "^8.56.1",
34
+ "@nest-boot/tsconfig": "^7.0.3"
35
35
  },
36
36
  "publishConfig": {
37
37
  "access": "public"
@@ -0,0 +1,8 @@
1
+ import plugin from ".";
2
+
3
+ describe("plugin public API", () => {
4
+ it("should export the rule map through the plugin entrypoint", () => {
5
+ expect(plugin.rules).toBeDefined();
6
+ expect(plugin.rules?.["graphql-field-config-from-types"]).toBeDefined();
7
+ });
8
+ });
@@ -75,6 +75,24 @@ tester.run("graphql-field-config-from-types", rule, {
75
75
  password!: string;
76
76
  }
77
77
  `,
78
+ // Configured decorators can be ignored
79
+ {
80
+ code: /* typescript */ `
81
+ @ObjectType()
82
+ class User {
83
+ @Internal()
84
+ @Field(() => String)
85
+ token!: string;
86
+ }
87
+ `,
88
+ options: [
89
+ {
90
+ decorators: {
91
+ Internal: "ignore" as const,
92
+ },
93
+ },
94
+ ],
95
+ },
78
96
  // Non-GraphQL model class is not checked
79
97
  /* typescript */ `
80
98
  class NotAGraphQLModel {
@@ -96,6 +114,31 @@ tester.run("graphql-field-config-from-types", rule, {
96
114
  query?: string;
97
115
  }
98
116
  `,
117
+ // Methods are ignored
118
+ /* typescript */ `
119
+ @ObjectType()
120
+ class User {
121
+ method() {
122
+ return "value";
123
+ }
124
+ }
125
+ `,
126
+ // Properties with no usable type information are skipped
127
+ /* typescript */ `
128
+ @ObjectType()
129
+ class User {
130
+ @Field(() => String)
131
+ settings = {};
132
+ }
133
+ `,
134
+ // Computed property names fall back to normal type inference
135
+ /* typescript */ `
136
+ @ObjectType()
137
+ class User {
138
+ @Field(() => String)
139
+ ["name"]!: string;
140
+ }
141
+ `,
99
142
  ],
100
143
  invalid: [
101
144
  // @Field type mismatch
@@ -238,5 +281,211 @@ tester.run("graphql-field-config-from-types", rule, {
238
281
  `,
239
282
  errors: [{ messageId: "alignFieldDecoratorWithTsType" }],
240
283
  },
284
+ // Missing @Field decorator should infer scalar type from the property type
285
+ {
286
+ code: /* typescript */ `@ObjectType()
287
+ class User {
288
+ name!: string;
289
+ }`,
290
+ output: /* typescript */ `@ObjectType()
291
+ class User {
292
+ @Field(() => String)
293
+ name!: string;
294
+ }`,
295
+ errors: [{ messageId: "alignFieldDecoratorWithTsType" }],
296
+ },
297
+ // Missing @Field decorator should add required ID import
298
+ {
299
+ code: /* typescript */ `@ObjectType()
300
+ class User {
301
+ id!: string;
302
+ }`,
303
+ output: /* typescript */ `import { ID } from "@nestjs/graphql";
304
+ @ObjectType()
305
+ class User {
306
+ @Field(() => ID)
307
+ id!: string;
308
+ }`,
309
+ errors: [{ messageId: "alignFieldDecoratorWithTsType" }],
310
+ },
311
+ // Existing scalar imports should be reused
312
+ {
313
+ code: /* typescript */ `import { ID } from "@nestjs/graphql";
314
+ @ObjectType()
315
+ class User {
316
+ ownerID!: string;
317
+ }`,
318
+ output: /* typescript */ `import { ID } from "@nestjs/graphql";
319
+ @ObjectType()
320
+ class User {
321
+ @Field(() => ID)
322
+ ownerID!: string;
323
+ }`,
324
+ errors: [{ messageId: "alignFieldDecoratorWithTsType" }],
325
+ },
326
+ // Record types should use GraphQLJSONObject and add its import
327
+ {
328
+ code: /* typescript */ `@ObjectType()
329
+ class User {
330
+ @Field(() => String)
331
+ metadata!: Record<string, unknown>;
332
+ }`,
333
+ output: /* typescript */ `import { GraphQLJSONObject } from "graphql-type-json";
334
+ @ObjectType()
335
+ class User {
336
+ @Field(() => GraphQLJSONObject)
337
+ metadata!: Record<string, unknown>;
338
+ }`,
339
+ errors: [{ messageId: "alignFieldDecoratorWithTsType" }],
340
+ },
341
+ // Existing GraphQLJSONObject imports should be reused
342
+ {
343
+ code: /* typescript */ `import { GraphQLJSONObject } from "graphql-type-json";
344
+ @ObjectType()
345
+ class User {
346
+ metadata!: Record<string, unknown>;
347
+ }`,
348
+ output: /* typescript */ `import { GraphQLJSONObject } from "graphql-type-json";
349
+ @ObjectType()
350
+ class User {
351
+ @Field(() => GraphQLJSONObject)
352
+ metadata!: Record<string, unknown>;
353
+ }`,
354
+ errors: [{ messageId: "alignFieldDecoratorWithTsType" }],
355
+ },
356
+ // Ref<T | null> should unwrap the type and mark the field nullable
357
+ {
358
+ code: /* typescript */ `@ObjectType()
359
+ class User {
360
+ friend!: Ref<User | null>;
361
+ }`,
362
+ output: /* typescript */ `@ObjectType()
363
+ class User {
364
+ @Field(() => User, { nullable: true })
365
+ friend!: Ref<User | null>;
366
+ }`,
367
+ errors: [{ messageId: "alignFieldDecoratorWithTsType" }],
368
+ },
369
+ // Ref<T | undefined> should also mark the field nullable
370
+ {
371
+ code: /* typescript */ `@ObjectType()
372
+ class User {
373
+ friend!: Ref<User | undefined>;
374
+ }`,
375
+ output: /* typescript */ `@ObjectType()
376
+ class User {
377
+ @Field(() => User, { nullable: true })
378
+ friend!: Ref<User | undefined>;
379
+ }`,
380
+ errors: [{ messageId: "alignFieldDecoratorWithTsType" }],
381
+ },
382
+ // Union types without nullish members use the first concrete type
383
+ {
384
+ code: /* typescript */ `@ObjectType()
385
+ class User {
386
+ value!: string | number;
387
+ }`,
388
+ output: /* typescript */ `@ObjectType()
389
+ class User {
390
+ @Field(() => String)
391
+ value!: string | number;
392
+ }`,
393
+ errors: [{ messageId: "alignFieldDecoratorWithTsType" }],
394
+ },
395
+ // Undefined unions should be nullable
396
+ {
397
+ code: /* typescript */ `@ObjectType()
398
+ class User {
399
+ nickname!: string | undefined;
400
+ }`,
401
+ output: /* typescript */ `@ObjectType()
402
+ class User {
403
+ @Field(() => String, { nullable: true })
404
+ nickname!: string | undefined;
405
+ }`,
406
+ errors: [{ messageId: "alignFieldDecoratorWithTsType" }],
407
+ },
408
+ // Array<T> should be emitted as a GraphQL array type
409
+ {
410
+ code: /* typescript */ `@ObjectType()
411
+ class User {
412
+ tags!: Array<string>;
413
+ }`,
414
+ output: /* typescript */ `@ObjectType()
415
+ class User {
416
+ @Field(() => [String])
417
+ tags!: Array<string>;
418
+ }`,
419
+ errors: [{ messageId: "alignFieldDecoratorWithTsType" }],
420
+ },
421
+ // Bare Array references should still be handled as identifier types
422
+ {
423
+ code: /* typescript */ `@ObjectType()
424
+ class User {
425
+ values!: Array;
426
+ }`,
427
+ output: /* typescript */ `@ObjectType()
428
+ class User {
429
+ @Field(() => Array)
430
+ values!: Array;
431
+ }`,
432
+ errors: [{ messageId: "alignFieldDecoratorWithTsType" }],
433
+ },
434
+ // Literal initializers should be used when no type annotation exists
435
+ {
436
+ code: /* typescript */ `@ObjectType()
437
+ class User {
438
+ published = true;
439
+ }`,
440
+ output: /* typescript */ `@ObjectType()
441
+ class User {
442
+ @Field(() => Boolean)
443
+ published = true;
444
+ }`,
445
+ errors: [{ messageId: "alignFieldDecoratorWithTsType" }],
446
+ },
447
+ // Number literal initializers should infer Float
448
+ {
449
+ code: /* typescript */ `import { Float } from "@nestjs/graphql";
450
+ @ObjectType()
451
+ class User {
452
+ score = 1;
453
+ }`,
454
+ output: /* typescript */ `import { Float } from "@nestjs/graphql";
455
+ @ObjectType()
456
+ class User {
457
+ @Field(() => Float)
458
+ score = 1;
459
+ }`,
460
+ errors: [{ messageId: "alignFieldDecoratorWithTsType" }],
461
+ },
462
+ // String literal initializers should infer String
463
+ {
464
+ code: /* typescript */ `@ObjectType()
465
+ class User {
466
+ title = "hello";
467
+ }`,
468
+ output: /* typescript */ `@ObjectType()
469
+ class User {
470
+ @Field(() => String)
471
+ title = "hello";
472
+ }`,
473
+ errors: [{ messageId: "alignFieldDecoratorWithTsType" }],
474
+ },
475
+ // Relation decorators configured for removal should remove @Field
476
+ {
477
+ code: /* typescript */ `@ObjectType()
478
+ class User {
479
+ @HideField()
480
+ @Field(() => String)
481
+ password!: string;
482
+ }`,
483
+ output: /* typescript */ `@ObjectType()
484
+ class User {
485
+ @HideField()
486
+ password!: string;
487
+ }`,
488
+ errors: [{ messageId: "removeFieldDecorator" }],
489
+ },
241
490
  ],
242
491
  });
@@ -161,7 +161,7 @@ export default createRule<
161
161
  baseTypeNode.typeName.name === "Opt")
162
162
  ) {
163
163
  let inner = baseTypeNode.typeArguments?.params[0] ?? null;
164
- if (inner && inner.type === AST_NODE_TYPES.TSUnionType) {
164
+ if (inner?.type === AST_NODE_TYPES.TSUnionType) {
165
165
  const hasNullish = inner.types.some((t: TSESTree.TypeNode) => {
166
166
  return (
167
167
  t.type === AST_NODE_TYPES.TSNullKeyword ||
@@ -324,16 +324,12 @@ export default createRule<
324
324
  callExpr.arguments.length > 0
325
325
  ) {
326
326
  // If the first argument is an object expression (no type function)
327
- if (
328
- callExpr.arguments[0] &&
329
- callExpr.arguments[0].type === AST_NODE_TYPES.ObjectExpression
330
- ) {
327
+ if (callExpr.arguments[0]?.type === AST_NODE_TYPES.ObjectExpression) {
331
328
  optionsArg = callExpr.arguments[0];
332
329
  }
333
330
  // If the second argument is an object expression (has type function)
334
331
  else if (
335
- callExpr.arguments[1] &&
336
- callExpr.arguments[1].type === AST_NODE_TYPES.ObjectExpression
332
+ callExpr.arguments[1]?.type === AST_NODE_TYPES.ObjectExpression
337
333
  ) {
338
334
  optionsArg = callExpr.arguments[1];
339
335
  }
@@ -57,6 +57,23 @@ tester.run("graphql-field-definite-assignment", rule, {
57
57
  field: string;
58
58
  }
59
59
  `,
60
+ // Methods are ignored
61
+ /* typescript */ `
62
+ @ObjectType()
63
+ class User {
64
+ method() {
65
+ return "value";
66
+ }
67
+ }
68
+ `,
69
+ // Computed property names are skipped
70
+ /* typescript */ `
71
+ @ObjectType()
72
+ class User {
73
+ @Field()
74
+ ["name"]: string;
75
+ }
76
+ `,
60
77
  ],
61
78
  invalid: [
62
79
  // No initializer and no !
@@ -0,0 +1,15 @@
1
+ import { rules } from ".";
2
+
3
+ describe("rules", () => {
4
+ it("should expose all packaged rules", () => {
5
+ expect(Object.keys(rules).sort()).toEqual([
6
+ "entity-field-definite-assignment",
7
+ "entity-property-config-from-types",
8
+ "graphql-field-config-from-types",
9
+ "graphql-field-definite-assignment",
10
+ "import-bullmq",
11
+ "import-graphql",
12
+ "import-mikro-orm",
13
+ ]);
14
+ });
15
+ });
@@ -49,6 +49,23 @@ tester.run("entity-field-definite-assignment", rule, {
49
49
  field: string;
50
50
  }
51
51
  `,
52
+ // Methods are ignored
53
+ /* typescript */ `
54
+ @Entity()
55
+ class User {
56
+ method() {
57
+ return "value";
58
+ }
59
+ }
60
+ `,
61
+ // Computed property names are skipped
62
+ /* typescript */ `
63
+ @Entity()
64
+ class User {
65
+ @Property()
66
+ ["name"]: string;
67
+ }
68
+ `,
52
69
  // @Enum decorator - with !
53
70
  /* typescript */ `
54
71
  @Entity()