@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.
- package/.turbo/turbo-build.log +2 -2
- package/CHANGELOG.md +2 -20
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -5
- package/dist/index.js.map +1 -1
- package/dist/rules/graphql/graphql-field-config-from-types.d.ts +6 -0
- package/dist/rules/graphql/graphql-field-config-from-types.js +417 -0
- package/dist/rules/graphql/graphql-field-config-from-types.js.map +1 -0
- package/dist/rules/graphql/graphql-field-definite-assignment.d.ts +2 -0
- package/dist/rules/graphql/graphql-field-definite-assignment.js +125 -0
- package/dist/rules/graphql/graphql-field-definite-assignment.js.map +1 -0
- package/dist/rules/import/import-bullmq.d.ts +2 -0
- package/dist/rules/import/import-bullmq.js +36 -0
- package/dist/rules/import/import-bullmq.js.map +1 -0
- package/dist/rules/import/import-graphql.d.ts +2 -0
- package/dist/rules/import/import-graphql.js +36 -0
- package/dist/rules/import/import-graphql.js.map +1 -0
- package/dist/rules/import/import-mikro-orm.d.ts +2 -0
- package/dist/rules/import/import-mikro-orm.js +36 -0
- package/dist/rules/import/import-mikro-orm.js.map +1 -0
- package/dist/rules/index.d.ts +9 -0
- package/dist/rules/index.js +16 -11
- package/dist/rules/index.js.map +1 -1
- package/dist/rules/mikro-orm/entity-field-definite-assignment.d.ts +2 -0
- package/dist/rules/mikro-orm/entity-field-definite-assignment.js +125 -0
- package/dist/rules/mikro-orm/entity-field-definite-assignment.js.map +1 -0
- package/dist/rules/mikro-orm/entity-property-config-from-types.d.ts +3 -0
- package/dist/rules/mikro-orm/entity-property-config-from-types.js +881 -0
- package/dist/rules/mikro-orm/entity-property-config-from-types.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/dist/utils/createRule.d.ts +2 -0
- package/dist/utils/createRule.js +1 -1
- package/dist/utils/createRule.js.map +1 -1
- package/dist/utils/decorators.d.ts +29 -0
- package/dist/utils/decorators.js +74 -0
- package/dist/utils/decorators.js.map +1 -0
- package/dist/utils/tester.d.ts +2 -0
- package/dist/utils/tester.js +27 -0
- package/dist/utils/tester.js.map +1 -0
- package/eslint.config.mjs +38 -2
- package/jest.config.ts +12 -0
- package/package.json +22 -14
- package/src/index.ts +1 -1
- package/src/rules/graphql/graphql-field-config-from-types.spec.ts +242 -0
- package/src/rules/graphql/graphql-field-config-from-types.ts +557 -0
- package/src/rules/graphql/graphql-field-definite-assignment.spec.ts +135 -0
- package/src/rules/graphql/graphql-field-definite-assignment.ts +147 -0
- package/src/rules/import/import-bullmq.spec.ts +69 -0
- package/src/rules/import/import-bullmq.ts +35 -0
- package/src/rules/import/import-graphql.spec.ts +65 -0
- package/src/rules/import/import-graphql.ts +36 -0
- package/src/rules/import/import-mikro-orm.spec.ts +65 -0
- package/src/rules/import/import-mikro-orm.ts +36 -0
- package/src/rules/index.ts +15 -13
- package/src/rules/mikro-orm/entity-field-definite-assignment.spec.ts +91 -0
- package/src/rules/mikro-orm/entity-field-definite-assignment.ts +141 -0
- package/src/rules/mikro-orm/entity-property-config-from-types.spec.ts +262 -0
- package/src/rules/mikro-orm/entity-property-config-from-types.ts +1111 -0
- package/src/utils/createRule.ts +3 -1
- package/src/utils/decorators.spec.ts +214 -0
- package/src/utils/decorators.ts +93 -0
- package/src/utils/tester.ts +22 -0
- package/tsconfig.build.json +5 -0
- package/tsconfig.json +6 -7
- package/dist/rules/entity-constructor.js +0 -78
- package/dist/rules/entity-constructor.js.map +0 -1
- package/dist/rules/entity-property-no-optional-or-non-null-assertion.js +0 -63
- package/dist/rules/entity-property-no-optional-or-non-null-assertion.js.map +0 -1
- package/dist/rules/entity-property-nullable.js +0 -81
- package/dist/rules/entity-property-nullable.js.map +0 -1
- package/dist/rules/graphql-field-arguments-match-property-type.js +0 -118
- package/dist/rules/graphql-field-arguments-match-property-type.js.map +0 -1
- package/dist/rules/graphql-resolver-method-return-type.js +0 -145
- package/dist/rules/graphql-resolver-method-return-type.js.map +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/src/rules/entity-constructor.ts +0 -97
- package/src/rules/entity-property-no-optional-or-non-null-assertion.ts +0 -81
- package/src/rules/entity-property-nullable.ts +0 -112
- package/src/rules/graphql-field-arguments-match-property-type.ts +0 -186
- package/src/rules/graphql-resolver-method-return-type.ts +0 -207
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import { AST_NODE_TYPES } from "@typescript-eslint/utils";
|
|
2
|
-
|
|
3
|
-
import { createRule } from "../utils/createRule";
|
|
4
|
-
|
|
5
|
-
export default createRule({
|
|
6
|
-
meta: {
|
|
7
|
-
type: "problem",
|
|
8
|
-
docs: {
|
|
9
|
-
description: "实体字段可为空时属性类型",
|
|
10
|
-
},
|
|
11
|
-
fixable: "code",
|
|
12
|
-
schema: [],
|
|
13
|
-
messages: {
|
|
14
|
-
entityPropertyNullable:
|
|
15
|
-
"@Property({ nullable: true }) 属性类型需包含 null。",
|
|
16
|
-
},
|
|
17
|
-
},
|
|
18
|
-
defaultOptions: [{}],
|
|
19
|
-
create(context) {
|
|
20
|
-
return {
|
|
21
|
-
ClassDeclaration(node) {
|
|
22
|
-
// 检查是否有 @Entity 装饰器
|
|
23
|
-
if (
|
|
24
|
-
node.decorators.some((decorator) => {
|
|
25
|
-
return (
|
|
26
|
-
decorator.expression.type === AST_NODE_TYPES.CallExpression &&
|
|
27
|
-
decorator.expression.callee.type === AST_NODE_TYPES.Identifier &&
|
|
28
|
-
decorator.expression.callee.name === "Entity"
|
|
29
|
-
);
|
|
30
|
-
})
|
|
31
|
-
) {
|
|
32
|
-
// 遍历类属性
|
|
33
|
-
node.body.body.forEach((property) => {
|
|
34
|
-
// 检查是否有 @Property() 装饰器
|
|
35
|
-
if (property.type === AST_NODE_TYPES.PropertyDefinition) {
|
|
36
|
-
const propertyDecorator = property.decorators.find(
|
|
37
|
-
(decorator) => {
|
|
38
|
-
return (
|
|
39
|
-
decorator.expression.type ===
|
|
40
|
-
AST_NODE_TYPES.CallExpression &&
|
|
41
|
-
decorator.expression.callee.type ===
|
|
42
|
-
AST_NODE_TYPES.Identifier &&
|
|
43
|
-
decorator.expression.callee.name === "Property"
|
|
44
|
-
);
|
|
45
|
-
},
|
|
46
|
-
);
|
|
47
|
-
|
|
48
|
-
if (typeof propertyDecorator === "undefined") {
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// 检查 @Property() 装饰器 nullable 参数是否为 true
|
|
53
|
-
if (
|
|
54
|
-
propertyDecorator.expression.type ===
|
|
55
|
-
AST_NODE_TYPES.CallExpression &&
|
|
56
|
-
propertyDecorator.expression.arguments[0]?.type ===
|
|
57
|
-
AST_NODE_TYPES.ObjectExpression &&
|
|
58
|
-
propertyDecorator.expression.arguments[0]?.properties?.some(
|
|
59
|
-
(prop) => {
|
|
60
|
-
return (
|
|
61
|
-
prop.type === AST_NODE_TYPES.Property &&
|
|
62
|
-
prop.key.type === AST_NODE_TYPES.Identifier &&
|
|
63
|
-
prop.key.name === "nullable" &&
|
|
64
|
-
prop.value.type === AST_NODE_TYPES.Literal &&
|
|
65
|
-
prop.value.value === true
|
|
66
|
-
);
|
|
67
|
-
},
|
|
68
|
-
)
|
|
69
|
-
) {
|
|
70
|
-
const typeAnnotation = property.typeAnnotation;
|
|
71
|
-
|
|
72
|
-
if (typeof typeAnnotation === "undefined") {
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// 检查属性类型是否包含 null
|
|
77
|
-
if (
|
|
78
|
-
!(
|
|
79
|
-
typeAnnotation.type === AST_NODE_TYPES.TSTypeAnnotation &&
|
|
80
|
-
typeAnnotation.typeAnnotation.type ===
|
|
81
|
-
AST_NODE_TYPES.TSUnionType &&
|
|
82
|
-
typeAnnotation.typeAnnotation.types.some(
|
|
83
|
-
(type) => type.type === AST_NODE_TYPES.TSNullKeyword,
|
|
84
|
-
)
|
|
85
|
-
)
|
|
86
|
-
) {
|
|
87
|
-
context.report({
|
|
88
|
-
node: property,
|
|
89
|
-
messageId: "entityPropertyNullable",
|
|
90
|
-
fix: (fixer) => {
|
|
91
|
-
const typeAnnotationStart = typeAnnotation.range[0];
|
|
92
|
-
const typeAnnotationEnd = typeAnnotation.range[1];
|
|
93
|
-
const originalType =
|
|
94
|
-
context.sourceCode.getText(typeAnnotation);
|
|
95
|
-
|
|
96
|
-
return fixer.replaceTextRange(
|
|
97
|
-
[typeAnnotationStart, typeAnnotationEnd],
|
|
98
|
-
`${originalType} | null${
|
|
99
|
-
property.value ? "" : " = null"
|
|
100
|
-
}`,
|
|
101
|
-
);
|
|
102
|
-
},
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
},
|
|
110
|
-
};
|
|
111
|
-
},
|
|
112
|
-
});
|
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
import { AST_NODE_TYPES } from "@typescript-eslint/utils";
|
|
2
|
-
|
|
3
|
-
import { createRule } from "../utils/createRule";
|
|
4
|
-
|
|
5
|
-
const scalarTypeAlias: Record<string, string> = {
|
|
6
|
-
Boolean: "boolean",
|
|
7
|
-
Int: "number",
|
|
8
|
-
Float: "number",
|
|
9
|
-
String: "string",
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
const astTypeAlias: Record<string, string> = {
|
|
13
|
-
TSBooleanKeyword: "boolean",
|
|
14
|
-
TSNumberKeyword: "number",
|
|
15
|
-
TSStringKeyword: "string",
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export default createRule({
|
|
19
|
-
meta: {
|
|
20
|
-
type: "problem",
|
|
21
|
-
docs: {
|
|
22
|
-
description: "@Field() 装饰器参数需要和字段类型相符。",
|
|
23
|
-
},
|
|
24
|
-
fixable: "code",
|
|
25
|
-
schema: [],
|
|
26
|
-
messages: {
|
|
27
|
-
fieldArgumentsMatchPropertyType:
|
|
28
|
-
"@Field() 装饰器类型需要和字段类型声明相符。",
|
|
29
|
-
propertyNoExplicitTypeDeclaration:
|
|
30
|
-
"属性未显式类型声明时 @Field() 装饰器需要提供类型。",
|
|
31
|
-
},
|
|
32
|
-
},
|
|
33
|
-
defaultOptions: [{}],
|
|
34
|
-
create(context) {
|
|
35
|
-
return {
|
|
36
|
-
ClassDeclaration(node) {
|
|
37
|
-
// 检查是否有 @ObjectType 或者 @InputType 装饰器
|
|
38
|
-
if (
|
|
39
|
-
node.decorators.some((decorator) => {
|
|
40
|
-
return (
|
|
41
|
-
decorator.expression.type === AST_NODE_TYPES.CallExpression &&
|
|
42
|
-
decorator.expression.callee.type === AST_NODE_TYPES.Identifier &&
|
|
43
|
-
["ObjectType", "InputType"].includes(
|
|
44
|
-
decorator.expression.callee.name,
|
|
45
|
-
)
|
|
46
|
-
);
|
|
47
|
-
})
|
|
48
|
-
) {
|
|
49
|
-
// 遍历类属性
|
|
50
|
-
node.body.body.forEach((property) => {
|
|
51
|
-
// 检查是否有 @Field() 装饰器
|
|
52
|
-
if (property.type === AST_NODE_TYPES.PropertyDefinition) {
|
|
53
|
-
const propertyDecorator = property.decorators.find(
|
|
54
|
-
(decorator) => {
|
|
55
|
-
return (
|
|
56
|
-
decorator.expression.type ===
|
|
57
|
-
AST_NODE_TYPES.CallExpression &&
|
|
58
|
-
decorator.expression.callee.type ===
|
|
59
|
-
AST_NODE_TYPES.Identifier &&
|
|
60
|
-
decorator.expression.callee.name === "Field"
|
|
61
|
-
);
|
|
62
|
-
},
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
if (typeof propertyDecorator === "undefined") {
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// 检查 @Field 装饰器是否声明类型
|
|
70
|
-
if (
|
|
71
|
-
propertyDecorator.expression.type ===
|
|
72
|
-
AST_NODE_TYPES.CallExpression &&
|
|
73
|
-
propertyDecorator.expression.arguments[0]?.type !==
|
|
74
|
-
AST_NODE_TYPES.ArrowFunctionExpression
|
|
75
|
-
) {
|
|
76
|
-
if (
|
|
77
|
-
typeof property.typeAnnotation === "undefined" &&
|
|
78
|
-
property.value !== null &&
|
|
79
|
-
property.value.type === AST_NODE_TYPES.Literal
|
|
80
|
-
) {
|
|
81
|
-
const propertyTypeName = typeof property.value.value;
|
|
82
|
-
|
|
83
|
-
const expectedScalarType =
|
|
84
|
-
Object.entries(scalarTypeAlias).find(
|
|
85
|
-
([, value]) => value === propertyTypeName,
|
|
86
|
-
)?.[0] ?? propertyTypeName;
|
|
87
|
-
|
|
88
|
-
if (typeof expectedScalarType !== "undefined") {
|
|
89
|
-
context.report({
|
|
90
|
-
node: property,
|
|
91
|
-
messageId: "propertyNoExplicitTypeDeclaration",
|
|
92
|
-
fix: (fixer) => {
|
|
93
|
-
const fixes = [];
|
|
94
|
-
|
|
95
|
-
if (["Int", "Float"].includes(expectedScalarType)) {
|
|
96
|
-
fixes.push(
|
|
97
|
-
fixer.insertTextBeforeRange(
|
|
98
|
-
[0, 0],
|
|
99
|
-
`import { ${expectedScalarType} } from "@nestjs/graphql";\n`,
|
|
100
|
-
),
|
|
101
|
-
);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
fixes.push(
|
|
105
|
-
fixer.replaceText(
|
|
106
|
-
propertyDecorator,
|
|
107
|
-
`@Field(() => ${expectedScalarType})`,
|
|
108
|
-
),
|
|
109
|
-
);
|
|
110
|
-
|
|
111
|
-
return fixes;
|
|
112
|
-
},
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
} else if (
|
|
116
|
-
property.typeAnnotation?.type ===
|
|
117
|
-
AST_NODE_TYPES.TSTypeAnnotation &&
|
|
118
|
-
property.typeAnnotation.typeAnnotation.type ===
|
|
119
|
-
AST_NODE_TYPES.TSUnionType
|
|
120
|
-
) {
|
|
121
|
-
const typeAnnotation =
|
|
122
|
-
property.typeAnnotation.typeAnnotation.types.find(
|
|
123
|
-
(item) =>
|
|
124
|
-
![
|
|
125
|
-
AST_NODE_TYPES.TSNullKeyword,
|
|
126
|
-
AST_NODE_TYPES.TSUndefinedKeyword,
|
|
127
|
-
].includes(item.type),
|
|
128
|
-
);
|
|
129
|
-
|
|
130
|
-
if (typeof typeAnnotation !== "undefined") {
|
|
131
|
-
const propertyTypeAnnotationHasNullType =
|
|
132
|
-
property.typeAnnotation.typeAnnotation.types.some(
|
|
133
|
-
(item) => item.type === AST_NODE_TYPES.TSNullKeyword,
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
const propertyTypeName =
|
|
137
|
-
astTypeAlias[typeAnnotation.type] ??
|
|
138
|
-
(typeAnnotation.type === AST_NODE_TYPES.TSTypeReference &&
|
|
139
|
-
typeAnnotation.typeName.type === AST_NODE_TYPES.Identifier
|
|
140
|
-
? typeAnnotation.typeName.name
|
|
141
|
-
: typeAnnotation.type);
|
|
142
|
-
|
|
143
|
-
const expectedScalarType =
|
|
144
|
-
Object.entries(scalarTypeAlias).find(
|
|
145
|
-
([, value]) => value === propertyTypeName,
|
|
146
|
-
)?.[0] ?? propertyTypeName;
|
|
147
|
-
|
|
148
|
-
context.report({
|
|
149
|
-
node: property,
|
|
150
|
-
messageId: "fieldArgumentsMatchPropertyType",
|
|
151
|
-
fix: (fixer) => {
|
|
152
|
-
const fixes = [];
|
|
153
|
-
|
|
154
|
-
if (["Int", "Float"].includes(expectedScalarType)) {
|
|
155
|
-
fixes.push(
|
|
156
|
-
fixer.insertTextBeforeRange(
|
|
157
|
-
[0, 0],
|
|
158
|
-
`import { ${expectedScalarType} } from "@nestjs/graphql";\n`,
|
|
159
|
-
),
|
|
160
|
-
);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
fixes.push(
|
|
164
|
-
fixer.replaceText(
|
|
165
|
-
propertyDecorator,
|
|
166
|
-
`@Field(() => ${expectedScalarType}${
|
|
167
|
-
propertyTypeAnnotationHasNullType
|
|
168
|
-
? ", { nullable: true }"
|
|
169
|
-
: ""
|
|
170
|
-
})`,
|
|
171
|
-
),
|
|
172
|
-
);
|
|
173
|
-
|
|
174
|
-
return fixes;
|
|
175
|
-
},
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
},
|
|
184
|
-
};
|
|
185
|
-
},
|
|
186
|
-
});
|
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
import { AST_NODE_TYPES } from "@typescript-eslint/utils";
|
|
2
|
-
|
|
3
|
-
import { createRule } from "../utils/createRule";
|
|
4
|
-
|
|
5
|
-
const scalarTypeAlias: Record<string, string> = {
|
|
6
|
-
Boolean: "boolean",
|
|
7
|
-
Int: "number",
|
|
8
|
-
Float: "number",
|
|
9
|
-
String: "string",
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
const astTypeAlias: Record<string, string> = {
|
|
13
|
-
TSBooleanKeyword: "boolean",
|
|
14
|
-
TSNumberKeyword: "number",
|
|
15
|
-
TSStringKeyword: "string",
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export default createRule({
|
|
19
|
-
meta: {
|
|
20
|
-
type: "problem",
|
|
21
|
-
docs: {
|
|
22
|
-
description: "GraphQL 解决器的方法装饰器类型需要和返回类型需要一致",
|
|
23
|
-
},
|
|
24
|
-
fixable: "code",
|
|
25
|
-
schema: [],
|
|
26
|
-
messages: {
|
|
27
|
-
resolveMethodReturnType: "解决器的方法装饰器类型需要和返回类型需要一致",
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
-
defaultOptions: [{}],
|
|
31
|
-
create(context) {
|
|
32
|
-
return {
|
|
33
|
-
ClassDeclaration(node) {
|
|
34
|
-
// 检查是否有 @Resolver 装饰器
|
|
35
|
-
if (
|
|
36
|
-
node.decorators.some((decorator) => {
|
|
37
|
-
return (
|
|
38
|
-
decorator.expression.type === AST_NODE_TYPES.CallExpression &&
|
|
39
|
-
decorator.expression.callee.type === AST_NODE_TYPES.Identifier &&
|
|
40
|
-
decorator.expression.callee.name === "Resolver"
|
|
41
|
-
);
|
|
42
|
-
})
|
|
43
|
-
) {
|
|
44
|
-
// 遍历类属性
|
|
45
|
-
node.body.body.forEach((method) => {
|
|
46
|
-
// 仅检查方法
|
|
47
|
-
if (method.type === AST_NODE_TYPES.MethodDefinition) {
|
|
48
|
-
const isAsync = method.value.async;
|
|
49
|
-
|
|
50
|
-
const methodDecorator = method.decorators.find((decorator) => {
|
|
51
|
-
return (
|
|
52
|
-
decorator.expression.type === AST_NODE_TYPES.CallExpression &&
|
|
53
|
-
decorator.expression.callee.type ===
|
|
54
|
-
AST_NODE_TYPES.Identifier &&
|
|
55
|
-
["Query", "Mutation", "ResolveField"].includes(
|
|
56
|
-
decorator.expression.callee.name,
|
|
57
|
-
)
|
|
58
|
-
);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
// 检查是否有 @Query, @Mutation, @ResolveField 装饰器
|
|
62
|
-
if (typeof methodDecorator === "undefined") {
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const methodDecoratorExpression = methodDecorator.expression;
|
|
67
|
-
|
|
68
|
-
if (
|
|
69
|
-
methodDecoratorExpression.type ===
|
|
70
|
-
AST_NODE_TYPES.CallExpression &&
|
|
71
|
-
methodDecoratorExpression.arguments[0]?.type ===
|
|
72
|
-
AST_NODE_TYPES.ArrowFunctionExpression
|
|
73
|
-
) {
|
|
74
|
-
let actualReturnTypeAnnotation =
|
|
75
|
-
method.value.returnType?.typeAnnotation ?? null;
|
|
76
|
-
const expectedReturnTypeFunctionBody =
|
|
77
|
-
methodDecoratorExpression.arguments[0].body;
|
|
78
|
-
|
|
79
|
-
const isNullable =
|
|
80
|
-
methodDecoratorExpression.arguments[1]?.type ===
|
|
81
|
-
AST_NODE_TYPES.ObjectExpression &&
|
|
82
|
-
methodDecoratorExpression.arguments[1]?.properties?.some(
|
|
83
|
-
(prop) => {
|
|
84
|
-
return (
|
|
85
|
-
prop.type === AST_NODE_TYPES.Property &&
|
|
86
|
-
prop.key.type === AST_NODE_TYPES.Identifier &&
|
|
87
|
-
prop.key.name === "nullable" &&
|
|
88
|
-
prop.value.type === AST_NODE_TYPES.Literal &&
|
|
89
|
-
prop.value.value === true
|
|
90
|
-
);
|
|
91
|
-
},
|
|
92
|
-
);
|
|
93
|
-
|
|
94
|
-
// 如果没有定义返回类型,或者返回类型不是标识符,跳过检查
|
|
95
|
-
if (
|
|
96
|
-
typeof expectedReturnTypeFunctionBody === "undefined" ||
|
|
97
|
-
expectedReturnTypeFunctionBody.type !==
|
|
98
|
-
AST_NODE_TYPES.Identifier
|
|
99
|
-
) {
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// 获取期望返回类型
|
|
104
|
-
const expectedReturnType =
|
|
105
|
-
scalarTypeAlias[expectedReturnTypeFunctionBody.name] ??
|
|
106
|
-
expectedReturnTypeFunctionBody.name;
|
|
107
|
-
|
|
108
|
-
// 获取实际返回类型
|
|
109
|
-
let actualReturnType: string | null = null;
|
|
110
|
-
let actualHasPromise = false;
|
|
111
|
-
let actualHasNullable = false;
|
|
112
|
-
|
|
113
|
-
if (
|
|
114
|
-
actualReturnTypeAnnotation?.type ===
|
|
115
|
-
AST_NODE_TYPES.TSTypeReference &&
|
|
116
|
-
actualReturnTypeAnnotation.typeName.type ===
|
|
117
|
-
AST_NODE_TYPES.Identifier &&
|
|
118
|
-
actualReturnTypeAnnotation.typeName.name === "Promise"
|
|
119
|
-
) {
|
|
120
|
-
actualHasPromise = true;
|
|
121
|
-
actualReturnTypeAnnotation =
|
|
122
|
-
actualReturnTypeAnnotation.typeArguments?.params[0] ?? null;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (
|
|
126
|
-
actualReturnTypeAnnotation?.type ===
|
|
127
|
-
AST_NODE_TYPES.TSUnionType
|
|
128
|
-
) {
|
|
129
|
-
actualHasNullable = actualReturnTypeAnnotation.types.some(
|
|
130
|
-
({ type }) => type === AST_NODE_TYPES.TSNullKeyword,
|
|
131
|
-
);
|
|
132
|
-
|
|
133
|
-
actualReturnTypeAnnotation =
|
|
134
|
-
actualReturnTypeAnnotation.types.find(
|
|
135
|
-
({ type }) => type !== AST_NODE_TYPES.TSNullKeyword,
|
|
136
|
-
) ?? null;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
if (actualReturnTypeAnnotation !== null) {
|
|
140
|
-
actualReturnType =
|
|
141
|
-
astTypeAlias[actualReturnTypeAnnotation.type] ??
|
|
142
|
-
(actualReturnTypeAnnotation.type ===
|
|
143
|
-
AST_NODE_TYPES.TSTypeReference &&
|
|
144
|
-
actualReturnTypeAnnotation.typeName.type ===
|
|
145
|
-
AST_NODE_TYPES.Identifier
|
|
146
|
-
? actualReturnTypeAnnotation.typeName.name
|
|
147
|
-
: actualReturnTypeAnnotation.type);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// 如果期望返回类型和实际返回类型不一致,则报告错误。
|
|
151
|
-
if (
|
|
152
|
-
expectedReturnType !== actualReturnType ||
|
|
153
|
-
isNullable !== actualHasNullable ||
|
|
154
|
-
isAsync !== actualHasPromise
|
|
155
|
-
) {
|
|
156
|
-
context.report({
|
|
157
|
-
node: method,
|
|
158
|
-
messageId: "resolveMethodReturnType",
|
|
159
|
-
fix: (fixer) => {
|
|
160
|
-
const fixes = [];
|
|
161
|
-
|
|
162
|
-
let returnTypeString = expectedReturnType;
|
|
163
|
-
|
|
164
|
-
if (isNullable) {
|
|
165
|
-
returnTypeString += " | null";
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
if (isAsync) {
|
|
169
|
-
returnTypeString = `Promise<${returnTypeString}>`;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
if (
|
|
173
|
-
typeof method.value.returnType === "undefined" ||
|
|
174
|
-
actualReturnType === null
|
|
175
|
-
) {
|
|
176
|
-
if (
|
|
177
|
-
method.value.type ===
|
|
178
|
-
AST_NODE_TYPES.FunctionExpression
|
|
179
|
-
) {
|
|
180
|
-
fixes.push(
|
|
181
|
-
fixer.insertTextBefore(
|
|
182
|
-
method.value.body,
|
|
183
|
-
`: ${returnTypeString} `,
|
|
184
|
-
),
|
|
185
|
-
);
|
|
186
|
-
}
|
|
187
|
-
} else {
|
|
188
|
-
fixes.push(
|
|
189
|
-
fixer.replaceText(
|
|
190
|
-
method.value.returnType.typeAnnotation,
|
|
191
|
-
returnTypeString,
|
|
192
|
-
),
|
|
193
|
-
);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
return fixes;
|
|
197
|
-
},
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
},
|
|
205
|
-
};
|
|
206
|
-
},
|
|
207
|
-
});
|