@nest-boot/eslint-plugin 7.0.0-beta.1 → 7.0.0-beta.2

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 (80) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/CHANGELOG.md +2 -20
  3. package/dist/index.d.ts +2 -0
  4. package/dist/index.js +2 -5
  5. package/dist/index.js.map +1 -1
  6. package/dist/rules/graphql/graphql-field-config-from-types.d.ts +6 -0
  7. package/dist/rules/graphql/graphql-field-config-from-types.js +417 -0
  8. package/dist/rules/graphql/graphql-field-config-from-types.js.map +1 -0
  9. package/dist/rules/graphql/graphql-field-definite-assignment.d.ts +2 -0
  10. package/dist/rules/graphql/graphql-field-definite-assignment.js +125 -0
  11. package/dist/rules/graphql/graphql-field-definite-assignment.js.map +1 -0
  12. package/dist/rules/import/import-bullmq.d.ts +2 -0
  13. package/dist/rules/import/import-bullmq.js +36 -0
  14. package/dist/rules/import/import-bullmq.js.map +1 -0
  15. package/dist/rules/import/import-graphql.d.ts +2 -0
  16. package/dist/rules/import/import-graphql.js +36 -0
  17. package/dist/rules/import/import-graphql.js.map +1 -0
  18. package/dist/rules/import/import-mikro-orm.d.ts +2 -0
  19. package/dist/rules/import/import-mikro-orm.js +36 -0
  20. package/dist/rules/import/import-mikro-orm.js.map +1 -0
  21. package/dist/rules/index.d.ts +9 -0
  22. package/dist/rules/index.js +16 -11
  23. package/dist/rules/index.js.map +1 -1
  24. package/dist/rules/mikro-orm/entity-field-definite-assignment.d.ts +2 -0
  25. package/dist/rules/mikro-orm/entity-field-definite-assignment.js +125 -0
  26. package/dist/rules/mikro-orm/entity-field-definite-assignment.js.map +1 -0
  27. package/dist/rules/mikro-orm/entity-property-config-from-types.d.ts +3 -0
  28. package/dist/rules/mikro-orm/entity-property-config-from-types.js +881 -0
  29. package/dist/rules/mikro-orm/entity-property-config-from-types.js.map +1 -0
  30. package/dist/tsconfig.build.tsbuildinfo +1 -0
  31. package/dist/utils/createRule.d.ts +2 -0
  32. package/dist/utils/createRule.js +1 -1
  33. package/dist/utils/createRule.js.map +1 -1
  34. package/dist/utils/decorators.d.ts +29 -0
  35. package/dist/utils/decorators.js +74 -0
  36. package/dist/utils/decorators.js.map +1 -0
  37. package/dist/utils/tester.d.ts +2 -0
  38. package/dist/utils/tester.js +27 -0
  39. package/dist/utils/tester.js.map +1 -0
  40. package/eslint.config.mjs +38 -2
  41. package/jest.config.ts +12 -0
  42. package/package.json +22 -14
  43. package/src/index.ts +1 -1
  44. package/src/rules/graphql/graphql-field-config-from-types.spec.ts +242 -0
  45. package/src/rules/graphql/graphql-field-config-from-types.ts +557 -0
  46. package/src/rules/graphql/graphql-field-definite-assignment.spec.ts +135 -0
  47. package/src/rules/graphql/graphql-field-definite-assignment.ts +147 -0
  48. package/src/rules/import/import-bullmq.spec.ts +69 -0
  49. package/src/rules/import/import-bullmq.ts +35 -0
  50. package/src/rules/import/import-graphql.spec.ts +65 -0
  51. package/src/rules/import/import-graphql.ts +36 -0
  52. package/src/rules/import/import-mikro-orm.spec.ts +65 -0
  53. package/src/rules/import/import-mikro-orm.ts +36 -0
  54. package/src/rules/index.ts +15 -13
  55. package/src/rules/mikro-orm/entity-field-definite-assignment.spec.ts +91 -0
  56. package/src/rules/mikro-orm/entity-field-definite-assignment.ts +141 -0
  57. package/src/rules/mikro-orm/entity-property-config-from-types.spec.ts +262 -0
  58. package/src/rules/mikro-orm/entity-property-config-from-types.ts +1111 -0
  59. package/src/utils/createRule.ts +3 -1
  60. package/src/utils/decorators.spec.ts +214 -0
  61. package/src/utils/decorators.ts +93 -0
  62. package/src/utils/tester.ts +22 -0
  63. package/tsconfig.build.json +5 -0
  64. package/tsconfig.json +6 -7
  65. package/dist/rules/entity-constructor.js +0 -78
  66. package/dist/rules/entity-constructor.js.map +0 -1
  67. package/dist/rules/entity-property-no-optional-or-non-null-assertion.js +0 -63
  68. package/dist/rules/entity-property-no-optional-or-non-null-assertion.js.map +0 -1
  69. package/dist/rules/entity-property-nullable.js +0 -81
  70. package/dist/rules/entity-property-nullable.js.map +0 -1
  71. package/dist/rules/graphql-field-arguments-match-property-type.js +0 -118
  72. package/dist/rules/graphql-field-arguments-match-property-type.js.map +0 -1
  73. package/dist/rules/graphql-resolver-method-return-type.js +0 -145
  74. package/dist/rules/graphql-resolver-method-return-type.js.map +0 -1
  75. package/dist/tsconfig.tsbuildinfo +0 -1
  76. package/src/rules/entity-constructor.ts +0 -97
  77. package/src/rules/entity-property-no-optional-or-non-null-assertion.ts +0 -81
  78. package/src/rules/entity-property-nullable.ts +0 -112
  79. package/src/rules/graphql-field-arguments-match-property-type.ts +0 -186
  80. package/src/rules/graphql-resolver-method-return-type.ts +0 -207
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hasClassDecorator = hasClassDecorator;
4
+ exports.hasPropertyDecorator = hasPropertyDecorator;
5
+ exports.getClassDecorator = getClassDecorator;
6
+ exports.getPropertyDecorator = getPropertyDecorator;
7
+ const utils_1 = require("@typescript-eslint/utils");
8
+ /**
9
+ * 检查类是否有指定的装饰器
10
+ * @param classDeclaration 类声明节点
11
+ * @param decoratorNames 装饰器名称(可以是字符串或字符串数组)
12
+ * @returns 是否有指定的装饰器
13
+ */
14
+ function hasClassDecorator(classDeclaration, decoratorNames) {
15
+ const names = Array.isArray(decoratorNames)
16
+ ? decoratorNames
17
+ : [decoratorNames];
18
+ return classDeclaration.decorators.some((decorator) => {
19
+ if (decorator.expression.type !== utils_1.AST_NODE_TYPES.CallExpression) {
20
+ return false;
21
+ }
22
+ if (decorator.expression.callee.type !== utils_1.AST_NODE_TYPES.Identifier) {
23
+ return false;
24
+ }
25
+ return names.includes(decorator.expression.callee.name);
26
+ });
27
+ }
28
+ /**
29
+ * 检查属性是否有指定的装饰器
30
+ * @param propertyDefinition 属性定义节点
31
+ * @param decoratorNames 装饰器名称(可以是字符串或字符串数组)
32
+ * @returns 是否有指定的装饰器
33
+ */
34
+ function hasPropertyDecorator(propertyDefinition, decoratorNames) {
35
+ const names = Array.isArray(decoratorNames)
36
+ ? decoratorNames
37
+ : [decoratorNames];
38
+ return propertyDefinition.decorators.some((decorator) => {
39
+ if (decorator.expression.type !== utils_1.AST_NODE_TYPES.CallExpression) {
40
+ return false;
41
+ }
42
+ if (decorator.expression.callee.type !== utils_1.AST_NODE_TYPES.Identifier) {
43
+ return false;
44
+ }
45
+ return names.includes(decorator.expression.callee.name);
46
+ });
47
+ }
48
+ /**
49
+ * 获取类的装饰器
50
+ * @param classDeclaration 类声明节点
51
+ * @param decoratorName 装饰器名称
52
+ * @returns 装饰器节点,如果不存在则返回 null
53
+ */
54
+ function getClassDecorator(classDeclaration, decoratorName) {
55
+ return (classDeclaration.decorators.find((decorator) => {
56
+ return (decorator.expression.type === utils_1.AST_NODE_TYPES.CallExpression &&
57
+ decorator.expression.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
58
+ decorator.expression.callee.name === decoratorName);
59
+ }) ?? null);
60
+ }
61
+ /**
62
+ * 获取属性的装饰器
63
+ * @param propertyDefinition 属性定义节点
64
+ * @param decoratorName 装饰器名称
65
+ * @returns 装饰器节点,如果不存在则返回 null
66
+ */
67
+ function getPropertyDecorator(propertyDefinition, decoratorName) {
68
+ return (propertyDefinition.decorators.find((decorator) => {
69
+ return (decorator.expression.type === utils_1.AST_NODE_TYPES.CallExpression &&
70
+ decorator.expression.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
71
+ decorator.expression.callee.name === decoratorName);
72
+ }) ?? null);
73
+ }
74
+ //# sourceMappingURL=decorators.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decorators.js","sourceRoot":"","sources":["../../src/utils/decorators.ts"],"names":[],"mappings":";;AAQA,8CAiBC;AAQD,oDAiBC;AAQD,8CAaC;AAQD,oDAaC;AA5FD,oDAAoE;AAEpE;;;;;GAKG;AACH,SAAgB,iBAAiB,CAC/B,gBAA2C,EAC3C,cAAiC;IAEjC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC;QACzC,CAAC,CAAC,cAAc;QAChB,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC;IAErB,OAAO,gBAAgB,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,SAA6B,EAAE,EAAE;QACxE,IAAI,SAAS,CAAC,UAAU,CAAC,IAAI,KAAK,sBAAc,CAAC,cAAc,EAAE,CAAC;YAChE,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI,SAAS,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,KAAK,sBAAc,CAAC,UAAU,EAAE,CAAC;YACnE,OAAO,KAAK,CAAC;QACf,CAAC;QACD,OAAO,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,SAAgB,oBAAoB,CAClC,kBAA+C,EAC/C,cAAiC;IAEjC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC;QACzC,CAAC,CAAC,cAAc;QAChB,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC;IAErB,OAAO,kBAAkB,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,SAA6B,EAAE,EAAE;QAC1E,IAAI,SAAS,CAAC,UAAU,CAAC,IAAI,KAAK,sBAAc,CAAC,cAAc,EAAE,CAAC;YAChE,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI,SAAS,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,KAAK,sBAAc,CAAC,UAAU,EAAE,CAAC;YACnE,OAAO,KAAK,CAAC;QACf,CAAC;QACD,OAAO,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,SAAgB,iBAAiB,CAC/B,gBAA2C,EAC3C,aAAqB;IAErB,OAAO,CACL,gBAAgB,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,SAA6B,EAAE,EAAE;QACjE,OAAO,CACL,SAAS,CAAC,UAAU,CAAC,IAAI,KAAK,sBAAc,CAAC,cAAc;YAC3D,SAAS,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,KAAK,sBAAc,CAAC,UAAU;YAC9D,SAAS,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,KAAK,aAAa,CACnD,CAAC;IACJ,CAAC,CAAC,IAAI,IAAI,CACX,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAgB,oBAAoB,CAClC,kBAA+C,EAC/C,aAAqB;IAErB,OAAO,CACL,kBAAkB,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,SAA6B,EAAE,EAAE;QACnE,OAAO,CACL,SAAS,CAAC,UAAU,CAAC,IAAI,KAAK,sBAAc,CAAC,cAAc;YAC3D,SAAS,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,KAAK,sBAAc,CAAC,UAAU;YAC9D,SAAS,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,KAAK,aAAa,CACnD,CAAC;IACJ,CAAC,CAAC,IAAI,IAAI,CACX,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { RuleTester } from "@typescript-eslint/rule-tester";
2
+ export declare const tester: RuleTester;
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.tester = void 0;
7
+ const globals_1 = require("@jest/globals");
8
+ const parser_1 = __importDefault(require("@typescript-eslint/parser"));
9
+ const rule_tester_1 = require("@typescript-eslint/rule-tester");
10
+ const path_1 = __importDefault(require("path"));
11
+ rule_tester_1.RuleTester.afterAll = globals_1.afterAll;
12
+ rule_tester_1.RuleTester.it = globals_1.it;
13
+ rule_tester_1.RuleTester.itOnly = globals_1.it.only;
14
+ rule_tester_1.RuleTester.describe = globals_1.describe;
15
+ exports.tester = new rule_tester_1.RuleTester({
16
+ languageOptions: {
17
+ parser: parser_1.default,
18
+ parserOptions: {
19
+ projectService: {
20
+ allowDefaultProject: ["*.ts*"],
21
+ defaultProject: "tsconfig.json",
22
+ },
23
+ tsconfigRootDir: path_1.default.join(__dirname, "../.."),
24
+ },
25
+ },
26
+ });
27
+ //# sourceMappingURL=tester.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tester.js","sourceRoot":"","sources":["../../src/utils/tester.ts"],"names":[],"mappings":";;;;;;AAAA,2CAAuD;AACvD,uEAA+C;AAC/C,gEAA4D;AAC5D,gDAAwB;AAExB,wBAAU,CAAC,QAAQ,GAAG,kBAAQ,CAAC;AAC/B,wBAAU,CAAC,EAAE,GAAG,YAAE,CAAC;AACnB,wBAAU,CAAC,MAAM,GAAG,YAAE,CAAC,IAAI,CAAC;AAC5B,wBAAU,CAAC,QAAQ,GAAG,kBAAQ,CAAC;AAElB,QAAA,MAAM,GAAG,IAAI,wBAAU,CAAC;IACnC,eAAe,EAAE;QACf,MAAM,EAAN,gBAAM;QACN,aAAa,EAAE;YACb,cAAc,EAAE;gBACd,mBAAmB,EAAE,CAAC,OAAO,CAAC;gBAC9B,cAAc,EAAE,eAAe;aAChC;YACD,eAAe,EAAE,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC;SAC/C;KACF;CACF,CAAC,CAAC"}
package/eslint.config.mjs CHANGED
@@ -1,13 +1,49 @@
1
- import config from "@nest-boot/eslint-config";
1
+ import { FlatCompat } from "@eslint/eslintrc";
2
+ import js from "@eslint/js";
3
+ import parser from "@typescript-eslint/parser";
4
+ import prettierConfig from "eslint-config-prettier";
5
+ import simpleImportSort from "eslint-plugin-simple-import-sort";
6
+ import { dirname } from "path";
7
+ import { configs } from "typescript-eslint";
8
+ import { fileURLToPath } from "url";
9
+
10
+ const compat = new FlatCompat({
11
+ baseDirectory: dirname(fileURLToPath(import.meta.url)),
12
+ });
2
13
 
3
14
  /** @type {import('eslint').Linter.Config[]} */
4
15
  export default [
5
- ...config,
16
+ js.configs.recommended,
17
+
18
+ // 使用 FlatCompat 包装 Standard 配置
19
+ ...compat.extends("standard"),
20
+
21
+ // TypeScript 严格类型检查配置
22
+ ...configs.strictTypeChecked,
23
+ ...configs.stylisticTypeChecked,
24
+
25
+ // Prettier 配置
26
+ prettierConfig,
27
+
28
+ // TypeScript 配置
6
29
  {
7
30
  languageOptions: {
31
+ parser,
8
32
  parserOptions: {
9
33
  project: ["tsconfig.json"],
10
34
  },
11
35
  },
12
36
  },
37
+
38
+ // Simple Import Sort 配置
39
+ {
40
+ plugins: {
41
+ "simple-import-sort": simpleImportSort,
42
+ },
43
+ rules: {
44
+ "import/order": "off",
45
+ "simple-import-sort/imports": "error",
46
+ "simple-import-sort/exports": "error",
47
+ },
48
+ },
13
49
  ];
package/jest.config.ts ADDED
@@ -0,0 +1,12 @@
1
+ import type { Config } from "jest";
2
+
3
+ export default {
4
+ moduleFileExtensions: ["js", "json", "ts"],
5
+ testRegex: ".spec.ts$",
6
+ transform: {
7
+ "^.+.(t|j)s$": "ts-jest",
8
+ },
9
+ coverageDirectory: "./coverage",
10
+ collectCoverageFrom: ["src/**/*"],
11
+ testEnvironment: "node",
12
+ } satisfies Config;
package/package.json CHANGED
@@ -1,19 +1,26 @@
1
1
  {
2
2
  "name": "@nest-boot/eslint-plugin",
3
- "version": "7.0.0-beta.1",
3
+ "version": "7.0.0-beta.2",
4
4
  "license": "MIT",
5
5
  "main": "./dist/index.js",
6
6
  "exports": "./dist/index.js",
7
7
  "dependencies": {
8
- "@typescript-eslint/utils": "^8.18.0"
8
+ "@typescript-eslint/utils": "^8.44.1"
9
9
  },
10
10
  "devDependencies": {
11
- "eslint": "^9.28.0",
12
- "eslint-doc-generator": "^2.1.2",
13
- "mocha": "^10.8.2",
14
- "npm-run-all": "^4.1.5",
15
- "typescript": "^5.8.3",
16
- "@nest-boot/tsconfig": "7.0.0-beta.0"
11
+ "@eslint/eslintrc": "^3.3.1",
12
+ "@eslint/js": "^9.36.0",
13
+ "@jest/globals": "^30.2.0",
14
+ "@types/node": "^22.18.6",
15
+ "@typescript-eslint/rule-tester": "^8.44.1",
16
+ "eslint": "^9.36.0",
17
+ "eslint-config-prettier": "^10.1.8",
18
+ "eslint-plugin-simple-import-sort": "^12.1.1",
19
+ "jest": "^29.7.0",
20
+ "ts-jest": "^29.4.4",
21
+ "typescript": "^5.9.2",
22
+ "typescript-eslint": "^8.44.1",
23
+ "@nest-boot/tsconfig": "7.0.0-beta.1"
17
24
  },
18
25
  "peerDependencies": {
19
26
  "eslint": "^9.0.0"
@@ -25,12 +32,13 @@
25
32
  "extends": "../../package.json"
26
33
  },
27
34
  "scripts": {
28
- "build": "tsc",
35
+ "build": "tsc -p tsconfig.build.json",
29
36
  "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist",
30
- "dev": "tsc --watch",
31
- "lint:eslint-docs": "npm-run-all \"update:eslint-docs -- --check\"",
32
- "lint:js": "eslint .",
33
- "test": "mocha tests --recursive",
34
- "update:eslint-docs": "eslint-doc-generator"
37
+ "dev": "tsc -p tsconfig.build.json --watch",
38
+ "lint": "eslint \"{src,test}/**/*.ts\" --fix",
39
+ "test": "jest",
40
+ "test:cov": "jest --coverage",
41
+ "test:watch": "jest --watch",
42
+ "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand"
35
43
  }
36
44
  }
package/src/index.ts CHANGED
@@ -1,3 +1,3 @@
1
- import rules from "./rules";
1
+ import { rules } from "./rules";
2
2
 
3
3
  export { rules };
@@ -0,0 +1,242 @@
1
+ import { tester } from "../../utils/tester";
2
+ import rule from "./graphql-field-config-from-types";
3
+
4
+ tester.run("graphql-field-config-from-types", rule, {
5
+ valid: [
6
+ // 正确的 string 类型
7
+ /* typescript */ `
8
+ @ObjectType()
9
+ class User {
10
+ @Field(() => String)
11
+ name!: string;
12
+ }
13
+ `,
14
+ // 正确的 number 类型(Float)
15
+ /* typescript */ `
16
+ @ObjectType()
17
+ class User {
18
+ @Field(() => Float)
19
+ score!: number;
20
+ }
21
+ `,
22
+ // 正确的 number 类型(Int)
23
+ /* typescript */ `
24
+ @ObjectType()
25
+ class User {
26
+ @Field(() => Int)
27
+ age!: number;
28
+ }
29
+ `,
30
+ // 正确的 boolean 类型
31
+ /* typescript */ `
32
+ @ObjectType()
33
+ class User {
34
+ @Field(() => Boolean)
35
+ isActive!: boolean;
36
+ }
37
+ `,
38
+ // 正确的 nullable 配置
39
+ /* typescript */ `
40
+ @ObjectType()
41
+ class User {
42
+ @Field(() => String, { nullable: true })
43
+ name?: string;
44
+ }
45
+ `,
46
+ // 正确的数组类型
47
+ /* typescript */ `
48
+ @ObjectType()
49
+ class User {
50
+ @Field(() => [String])
51
+ tags!: string[];
52
+ }
53
+ `,
54
+ // ID 类型
55
+ /* typescript */ `
56
+ @ObjectType()
57
+ class User {
58
+ @Field(() => ID)
59
+ id!: string;
60
+ }
61
+ `,
62
+ // 自定义类型
63
+ /* typescript */ `
64
+ @ObjectType()
65
+ class User {
66
+ @Field(() => Profile)
67
+ profile!: Profile;
68
+ }
69
+ `,
70
+ // 带有 HideField 装饰器的属性不需要 @Field
71
+ /* typescript */ `
72
+ @ObjectType()
73
+ class User {
74
+ @HideField()
75
+ password!: string;
76
+ }
77
+ `,
78
+ // 非 GraphQL 模型类不检查
79
+ /* typescript */ `
80
+ class NotAGraphQLModel {
81
+ field: string;
82
+ }
83
+ `,
84
+ // 复杂的配置对象(包含展开运算符和条件表达式)
85
+ /* typescript */ `
86
+ @ObjectType()
87
+ class Connection {
88
+ @Field(() => String, {
89
+ nullable: true,
90
+ ...(filterableFields.length > 0
91
+ ? {
92
+ description: \`Apply filters.\`,
93
+ }
94
+ : {}),
95
+ })
96
+ query?: string;
97
+ }
98
+ `,
99
+ ],
100
+ invalid: [
101
+ // @Field 类型不匹配
102
+ {
103
+ code: /* typescript */ `
104
+ @ObjectType()
105
+ class User {
106
+ @Field(() => Int)
107
+ name!: string;
108
+ }
109
+ `,
110
+ output: /* typescript */ `
111
+ @ObjectType()
112
+ class User {
113
+ @Field(() => String)
114
+ name!: string;
115
+ }
116
+ `,
117
+ errors: [{ messageId: "alignFieldDecoratorWithTsType" }],
118
+ },
119
+ // nullable 配置不匹配
120
+ {
121
+ code: /* typescript */ `
122
+ @ObjectType()
123
+ class User {
124
+ @Field(() => String)
125
+ name?: string;
126
+ }
127
+ `,
128
+ output: /* typescript */ `
129
+ @ObjectType()
130
+ class User {
131
+ @Field(() => String, { nullable: true })
132
+ name?: string;
133
+ }
134
+ `,
135
+ errors: [{ messageId: "alignFieldDecoratorWithTsType" }],
136
+ },
137
+ // 不应该有 nullable 但设置了
138
+ {
139
+ code: /* typescript */ `
140
+ @ObjectType()
141
+ class User {
142
+ @Field(() => String, { nullable: true })
143
+ name!: string;
144
+ }
145
+ `,
146
+ output: /* typescript */ `
147
+ @ObjectType()
148
+ class User {
149
+ @Field(() => String)
150
+ name!: string;
151
+ }
152
+ `,
153
+ errors: [{ messageId: "alignFieldDecoratorWithTsType" }],
154
+ },
155
+ // 数组类型不匹配
156
+ {
157
+ code: /* typescript */ `
158
+ @ObjectType()
159
+ class User {
160
+ @Field(() => String)
161
+ tags!: string[];
162
+ }
163
+ `,
164
+ output: /* typescript */ `
165
+ @ObjectType()
166
+ class User {
167
+ @Field(() => [String])
168
+ tags!: string[];
169
+ }
170
+ `,
171
+ errors: [{ messageId: "alignFieldDecoratorWithTsType" }],
172
+ },
173
+ // 缺少 type function 但有其他配置选项(应保留)
174
+ {
175
+ code: /* typescript */ `
176
+ @ObjectType()
177
+ class PageInfo {
178
+ @Field({
179
+ complexity: 0,
180
+ description: "Whether there are any pages prior to the current page.",
181
+ })
182
+ hasPreviousPage!: boolean;
183
+ }
184
+ `,
185
+ output: /* typescript */ `
186
+ @ObjectType()
187
+ class PageInfo {
188
+ @Field(() => Boolean, { complexity: 0, description: "Whether there are any pages prior to the current page." })
189
+ hasPreviousPage!: boolean;
190
+ }
191
+ `,
192
+ errors: [{ messageId: "alignFieldDecoratorWithTsType" }],
193
+ },
194
+ // 配置 nullable 时保留其他选项
195
+ {
196
+ code: /* typescript */ `
197
+ @ObjectType()
198
+ class User {
199
+ @Field(() => String, { description: "User name" })
200
+ name?: string;
201
+ }
202
+ `,
203
+ output: /* typescript */ `
204
+ @ObjectType()
205
+ class User {
206
+ @Field(() => String, { nullable: true, description: "User name" })
207
+ name?: string;
208
+ }
209
+ `,
210
+ errors: [{ messageId: "alignFieldDecoratorWithTsType" }],
211
+ },
212
+ // 复杂配置对象(包含展开运算符)缺少 type function
213
+ {
214
+ code: /* typescript */ `
215
+ @ObjectType()
216
+ class Connection {
217
+ @Field({
218
+ nullable: true,
219
+ ...(filterableFields.length > 0
220
+ ? {
221
+ description: \`Apply filters.\`,
222
+ }
223
+ : {}),
224
+ })
225
+ query?: string;
226
+ }
227
+ `,
228
+ output: /* typescript */ `
229
+ @ObjectType()
230
+ class Connection {
231
+ @Field(() => String, { nullable: true, ...(filterableFields.length > 0
232
+ ? {
233
+ description: \`Apply filters.\`,
234
+ }
235
+ : {}) })
236
+ query?: string;
237
+ }
238
+ `,
239
+ errors: [{ messageId: "alignFieldDecoratorWithTsType" }],
240
+ },
241
+ ],
242
+ });