@nest-boot/eslint-plugin 7.0.6 → 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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +6 -0
- package/dist/rules/mikro-orm/entity-property-config-from-types.js +51 -17
- package/dist/rules/mikro-orm/entity-property-config-from-types.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/src/index.spec.ts +8 -0
- package/src/rules/graphql/graphql-field-config-from-types.spec.ts +249 -0
- package/src/rules/graphql/graphql-field-definite-assignment.spec.ts +17 -0
- package/src/rules/index.spec.ts +15 -0
- package/src/rules/mikro-orm/entity-field-definite-assignment.spec.ts +17 -0
- package/src/rules/mikro-orm/entity-property-config-from-types.spec.ts +678 -1
- package/src/rules/mikro-orm/entity-property-config-from-types.ts +94 -17
- package/src/utils/decorators.spec.ts +52 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nest-boot/eslint-plugin",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.7",
|
|
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": "^
|
|
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.
|
|
34
|
+
"@nest-boot/tsconfig": "^7.0.3"
|
|
35
35
|
},
|
|
36
36
|
"publishConfig": {
|
|
37
37
|
"access": "public"
|
|
@@ -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()
|