@nest-boot/eslint-plugin 7.0.6 → 7.0.8

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.6",
3
+ "version": "7.0.8",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/nest-boot/nest-boot.git",
@@ -21,7 +21,7 @@
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",
24
+ "@types/node": "^24.12.4",
25
25
  "@typescript-eslint/parser": "^8.56.1",
26
26
  "@typescript-eslint/rule-tester": "^8.56.1",
27
27
  "eslint": "^9.39.3",
@@ -31,7 +31,7 @@
31
31
  "ts-jest": "^29.4.4",
32
32
  "typescript": "^5.9.3",
33
33
  "typescript-eslint": "^8.56.1",
34
- "@nest-boot/tsconfig": "^7.0.2"
34
+ "@nest-boot/tsconfig": "^7.1.0"
35
35
  },
36
36
  "publishConfig": {
37
37
  "access": "public"
@@ -41,9 +41,10 @@
41
41
  },
42
42
  "scripts": {
43
43
  "build": "tsc -p tsconfig.build.json",
44
- "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist",
44
+ "clean": "rm -rf .nx && rm -rf node_modules && rm -rf dist",
45
45
  "dev": "tsc -p tsconfig.build.json --watch",
46
- "lint": "eslint \"{src,test}/**/*.ts\" --fix",
46
+ "lint": "eslint \"{src,test}/**/*.ts\"",
47
+ "lint:fix": "eslint \"{src,test}/**/*.ts\" --fix",
47
48
  "test": "jest",
48
49
  "test:cov": "jest --coverage",
49
50
  "test:watch": "jest --watch",
@@ -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
  });
@@ -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()