@hadss/turbo-trans-json-plugin 1.0.0-rc.0 → 1.0.0-rc.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/README.md +1 -1
- package/dist/core/Types.d.ts +7 -0
- package/dist/core/Types.js +7 -1
- package/dist/core/analyzers/ClassAnalyzer.d.ts +15 -0
- package/dist/core/analyzers/ClassAnalyzer.js +200 -120
- package/dist/core/analyzers/CustomTypeAnalyzer.d.ts +3 -0
- package/dist/core/analyzers/CustomTypeAnalyzer.js +65 -60
- package/dist/core/constants/DecoratorConstants.d.ts +1 -0
- package/dist/core/constants/DecoratorConstants.js +3 -1
- package/dist/core/handlers/CustomClassHandler.js +0 -1
- package/dist/core/import-rewrite/services/BuildProfileUpdater.js +1 -1
- package/dist/core/import-rewrite/services/ImportRewriteService.js +1 -1
- package/dist/core/interfaces/index.d.ts +2 -2
- package/dist/core/services/CodeAnalysisService.js +2 -1
- package/dist/core/services/CodeGenerationService/CodeGenerationService.js +1 -1
- package/dist/core/services/CodeGenerationService/generators/MergedSendableClassGenerator.d.ts +0 -1
- package/dist/core/services/CodeGenerationService/generators/MergedSendableClassGenerator.js +0 -20
- package/dist/core/services/CodeGenerationService/generators/OriginalClassGenerator.d.ts +4 -4
- package/dist/core/services/CodeGenerationService/generators/OriginalClassGenerator.js +26 -45
- package/dist/core/services/CodeGenerationService/generators/SendableClassGenerator.d.ts +22 -0
- package/dist/core/services/CodeGenerationService/generators/SendableClassGenerator.js +194 -129
- package/dist/core/services/CodeGenerationService/generators/SerializerGenerator.js +20 -15
- package/dist/core/services/CodeGenerationService/generators/TempSerializerGenerator.js +2 -1
- package/dist/core/services/CodeGenerationService/shared/ImportManager.d.ts +2 -2
- package/dist/core/template/HandlebarsTemplateEngine.d.ts +2 -0
- package/dist/core/template/HandlebarsTemplateEngine.js +24 -2
- package/dist/core/utils/DeepCopyUtil.js +4 -2
- package/dist/core/utils/GenericTypeSubstitutionUtil.d.ts +1 -0
- package/dist/core/utils/GenericTypeSubstitutionUtil.js +27 -1
- package/dist/core/utils/SerializationPathUtil.d.ts +1 -1
- package/dist/core/utils/TsMorphUtil.js +6 -1
- package/dist/json-plugin/JSONExecuteController.d.ts +4 -0
- package/dist/json-plugin/JSONExecuteController.js +46 -36
- package/dist/json-plugin/interfaces/impl/TargetContext.js +4 -2
- package/dist/json-plugin/tasks/BaseTask.d.ts +2 -2
- package/dist/json-plugin/tasks/WatchTask.js +2 -1
- package/package.json +1 -1
- package/src/core/Types.ts +97 -89
- package/src/core/analyzers/ClassAnalyzer.ts +358 -197
- package/src/core/analyzers/CustomTypeAnalyzer.ts +145 -74
- package/src/core/constants/DecoratorConstants.ts +7 -5
- package/src/core/constants/PathConstants.ts +7 -7
- package/src/core/constants/StringConstants.ts +95 -97
- package/src/core/handlers/BaseTypeHandler.ts +11 -2
- package/src/core/handlers/CustomClassHandler.ts +4 -7
- package/src/core/handlers/DateHandler.ts +54 -46
- package/src/core/handlers/DecimalHandler.ts +53 -45
- package/src/core/handlers/EnumHandler.ts +2 -1
- package/src/core/handlers/GenericContainerHandler.ts +3 -1
- package/src/core/handlers/TupleHandler.ts +2 -1
- package/src/core/handlers/TypeHandlerRegistry.ts +3 -2
- package/src/core/handlers/UnionTypeHandler.ts +8 -7
- package/src/core/import-rewrite/services/BuildProfileUpdater.ts +7 -5
- package/src/core/import-rewrite/services/ImportRewriteService.ts +1 -3
- package/src/core/import-rewrite/services/ImportTransformService.ts +2 -2
- package/src/core/import-rewrite/types/ImportRewriteTypes.ts +3 -3
- package/src/core/index.ts +4 -4
- package/src/core/interfaces/ITask.ts +6 -5
- package/src/core/interfaces/ITaskContext.ts +9 -9
- package/src/core/interfaces/index.ts +2 -2
- package/src/core/logger/Logger.ts +28 -28
- package/src/core/services/CodeAnalysisService.ts +3 -2
- package/src/core/services/CodeGenerationEngine.ts +42 -42
- package/src/core/services/CodeGenerationService/CodeGenerationService.ts +1 -2
- package/src/core/services/CodeGenerationService/generators/MergedSendableClassGenerator.ts +0 -29
- package/src/core/services/CodeGenerationService/generators/OriginalClassGenerator.ts +31 -64
- package/src/core/services/CodeGenerationService/generators/SendableClassGenerator.ts +261 -170
- package/src/core/services/CodeGenerationService/generators/SerializerGenerator.ts +26 -19
- package/src/core/services/CodeGenerationService/generators/TempSerializerGenerator.ts +5 -3
- package/src/core/services/CodeGenerationService/shared/ImportManager.ts +8 -8
- package/src/core/template/HandlebarsTemplateEngine.ts +43 -10
- package/src/core/utils/ConfigManager.ts +2 -1
- package/src/core/utils/DeepCopyUtil.ts +4 -2
- package/src/core/utils/GenericTypeSubstitutionUtil.ts +45 -2
- package/src/core/utils/SerializationPathUtil.ts +7 -6
- package/src/core/utils/TsMorphUtil.ts +9 -2
- package/src/index.ts +2 -2
- package/src/json-plugin/JSONExecuteController.ts +51 -38
- package/src/json-plugin/interfaces/IModuleContext.ts +8 -8
- package/src/json-plugin/interfaces/ITargetContext.ts +6 -6
- package/src/json-plugin/interfaces/impl/ModuleContext.ts +10 -10
- package/src/json-plugin/interfaces/impl/TargetContext.ts +63 -58
- package/src/json-plugin/tasks/BaseTask.ts +5 -4
- package/src/json-plugin/tasks/CleanTask.ts +7 -7
- package/src/json-plugin/tasks/WatchTask.ts +20 -18
- package/template/SerializerPerformanceTemplate.hbs +14 -4
- package/template/SerializerStrictTemplate.hbs +9 -1
- package/template/SerializerTemplate.hbs +71 -46
package/README.md
CHANGED
package/dist/core/Types.d.ts
CHANGED
|
@@ -90,11 +90,18 @@ export interface PropertyAnalysis {
|
|
|
90
90
|
decorators?: PropertyDecorators;
|
|
91
91
|
isMust: boolean;
|
|
92
92
|
type: TypeStructure;
|
|
93
|
+
visibility: PropertyVisibility;
|
|
94
|
+
}
|
|
95
|
+
export declare enum PropertyVisibility {
|
|
96
|
+
PUBLIC = "public",
|
|
97
|
+
PRIVATE = "private",
|
|
98
|
+
PROTECTED = "protected"
|
|
93
99
|
}
|
|
94
100
|
export interface PropertyDecorators {
|
|
95
101
|
serialName?: string;
|
|
96
102
|
isRequired: boolean;
|
|
97
103
|
isTransient: boolean;
|
|
104
|
+
isPlainValue: boolean;
|
|
98
105
|
with?: string;
|
|
99
106
|
}
|
|
100
107
|
export interface TypeStructure {
|
package/dist/core/Types.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.TypeKind = exports.DeserializationMode = exports.PropertyKind = void 0;
|
|
3
|
+
exports.TypeKind = exports.PropertyVisibility = exports.DeserializationMode = exports.PropertyKind = void 0;
|
|
4
4
|
var PropertyKind;
|
|
5
5
|
(function (PropertyKind) {
|
|
6
6
|
PropertyKind["STRING"] = "string";
|
|
@@ -38,6 +38,12 @@ var DeserializationMode;
|
|
|
38
38
|
DeserializationMode["PERFORMANCE"] = "performance";
|
|
39
39
|
DeserializationMode["STRICT"] = "strict";
|
|
40
40
|
})(DeserializationMode || (exports.DeserializationMode = DeserializationMode = {}));
|
|
41
|
+
var PropertyVisibility;
|
|
42
|
+
(function (PropertyVisibility) {
|
|
43
|
+
PropertyVisibility["PUBLIC"] = "public";
|
|
44
|
+
PropertyVisibility["PRIVATE"] = "private";
|
|
45
|
+
PropertyVisibility["PROTECTED"] = "protected";
|
|
46
|
+
})(PropertyVisibility || (exports.PropertyVisibility = PropertyVisibility = {}));
|
|
41
47
|
var TypeKind;
|
|
42
48
|
(function (TypeKind) {
|
|
43
49
|
TypeKind["CLASS"] = "class";
|
|
@@ -6,12 +6,25 @@ export declare class ClassAnalyzer {
|
|
|
6
6
|
private static analyzeInheritanceInfo;
|
|
7
7
|
private static analyzeConstructorParameters;
|
|
8
8
|
private static parseSerializableOptions;
|
|
9
|
+
private static extractOptionsFromObjectLiteral;
|
|
10
|
+
private static extractSingleOption;
|
|
11
|
+
private static parseBooleanInitializer;
|
|
12
|
+
private static parseDeserializationMode;
|
|
9
13
|
private static parseSerialNameOptions;
|
|
14
|
+
private static extractNameFromProperties;
|
|
10
15
|
private static analyzeOwnProperties;
|
|
16
|
+
private static checkUnknownType;
|
|
11
17
|
private static classifyProperties;
|
|
18
|
+
private static buildPropertyClassification;
|
|
12
19
|
private static collectAllAncestorProperties;
|
|
20
|
+
private static collectAncestorPropertiesRecursively;
|
|
21
|
+
private static resolveGenericTypeArgs;
|
|
13
22
|
private static extractConstructorParams;
|
|
14
23
|
private static createPropertyAnalysis;
|
|
24
|
+
private static getTypeNode;
|
|
25
|
+
private static getPropertyVisibility;
|
|
26
|
+
private static calculateIsMust;
|
|
27
|
+
private static validateTransientConstructorParam;
|
|
15
28
|
private static analyzePropertyType;
|
|
16
29
|
private static simplifyImportType;
|
|
17
30
|
private static getTypeFromInitializer;
|
|
@@ -20,4 +33,6 @@ export declare class ClassAnalyzer {
|
|
|
20
33
|
private static hasUndefinedInUnion;
|
|
21
34
|
private static splitUnionType;
|
|
22
35
|
private static analyzePropertyDecorators;
|
|
36
|
+
private static checkTransientConflicts;
|
|
37
|
+
private static checkPlainValueConflicts;
|
|
23
38
|
}
|
|
@@ -118,41 +118,54 @@ class ClassAnalyzer {
|
|
|
118
118
|
return undefined;
|
|
119
119
|
}
|
|
120
120
|
const args = decorator.getArguments();
|
|
121
|
-
let generateSendable;
|
|
122
|
-
let _with;
|
|
123
|
-
let deserializationMode;
|
|
124
121
|
const firstArg = args[0];
|
|
125
|
-
if (!firstArg) {
|
|
122
|
+
if (!firstArg || firstArg.getKind() !== ts_morph_1.SyntaxKind.ObjectLiteralExpression) {
|
|
126
123
|
return {};
|
|
127
124
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
else if (propertyName === 'with' && initializer) {
|
|
144
|
-
_with = initializer.getText();
|
|
145
|
-
}
|
|
146
|
-
else if (propertyName === 'deserializationMode' && initializer) {
|
|
147
|
-
const modeText = initializer.getText().replace(/['"]/g, '');
|
|
148
|
-
if (modeText === Types_1.DeserializationMode.PERFORMANCE || modeText === Types_1.DeserializationMode.STRICT) {
|
|
149
|
-
deserializationMode = modeText;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
125
|
+
return this.extractOptionsFromObjectLiteral(firstArg.asKindOrThrow(ts_morph_1.SyntaxKind.ObjectLiteralExpression));
|
|
126
|
+
}
|
|
127
|
+
static extractOptionsFromObjectLiteral(objectLiteral) {
|
|
128
|
+
const options = {};
|
|
129
|
+
const properties = objectLiteral.getProperties();
|
|
130
|
+
for (const prop of properties) {
|
|
131
|
+
if (prop.getKind() !== ts_morph_1.SyntaxKind.PropertyAssignment) {
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
const propAssignment = prop.asKindOrThrow(ts_morph_1.SyntaxKind.PropertyAssignment);
|
|
135
|
+
const initializer = propAssignment.getInitializer();
|
|
136
|
+
if (!initializer) {
|
|
137
|
+
continue;
|
|
153
138
|
}
|
|
139
|
+
this.extractSingleOption(options, propAssignment.getName(), initializer);
|
|
140
|
+
}
|
|
141
|
+
return options;
|
|
142
|
+
}
|
|
143
|
+
static extractSingleOption(options, propertyName, initializer) {
|
|
144
|
+
if (propertyName === 'generateSendable') {
|
|
145
|
+
options.generateSendable = this.parseBooleanInitializer(initializer);
|
|
146
|
+
}
|
|
147
|
+
else if (propertyName === 'with') {
|
|
148
|
+
options.with = initializer.getText();
|
|
149
|
+
}
|
|
150
|
+
else if (propertyName === 'deserializationMode') {
|
|
151
|
+
this.parseDeserializationMode(options, initializer);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
static parseBooleanInitializer(initializer) {
|
|
155
|
+
const kind = initializer.getKind();
|
|
156
|
+
if (kind === ts_morph_1.SyntaxKind.TrueKeyword) {
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
if (kind === ts_morph_1.SyntaxKind.FalseKeyword) {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
return undefined;
|
|
163
|
+
}
|
|
164
|
+
static parseDeserializationMode(options, initializer) {
|
|
165
|
+
const modeText = initializer.getText().replace(/['"]/g, '');
|
|
166
|
+
if (modeText === Types_1.DeserializationMode.PERFORMANCE || modeText === Types_1.DeserializationMode.STRICT) {
|
|
167
|
+
options.deserializationMode = modeText;
|
|
154
168
|
}
|
|
155
|
-
return { with: _with, generateSendable, deserializationMode };
|
|
156
169
|
}
|
|
157
170
|
static parseSerialNameOptions(decorator) {
|
|
158
171
|
if (!decorator) {
|
|
@@ -163,21 +176,26 @@ class ClassAnalyzer {
|
|
|
163
176
|
return undefined;
|
|
164
177
|
}
|
|
165
178
|
const firstArg = args[0];
|
|
166
|
-
if (firstArg.getKind()
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
return stringLiteral.getLiteralValue();
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
179
|
+
if (firstArg.getKind() !== ts_morph_1.SyntaxKind.ObjectLiteralExpression) {
|
|
180
|
+
return undefined;
|
|
181
|
+
}
|
|
182
|
+
const properties = firstArg.asKindOrThrow(ts_morph_1.SyntaxKind.ObjectLiteralExpression).getProperties();
|
|
183
|
+
return this.extractNameFromProperties(properties);
|
|
184
|
+
}
|
|
185
|
+
static extractNameFromProperties(properties) {
|
|
186
|
+
for (const prop of properties) {
|
|
187
|
+
if (prop.getKind() !== ts_morph_1.SyntaxKind.PropertyAssignment) {
|
|
188
|
+
continue;
|
|
180
189
|
}
|
|
190
|
+
const propAssignment = prop.asKindOrThrow(ts_morph_1.SyntaxKind.PropertyAssignment);
|
|
191
|
+
if (propAssignment.getName() !== 'name') {
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
const initializer = propAssignment.getInitializer();
|
|
195
|
+
if (!initializer || initializer.getKind() !== ts_morph_1.SyntaxKind.StringLiteral) {
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
return initializer.asKindOrThrow(ts_morph_1.SyntaxKind.StringLiteral).getLiteralValue();
|
|
181
199
|
}
|
|
182
200
|
return undefined;
|
|
183
201
|
}
|
|
@@ -186,29 +204,34 @@ class ClassAnalyzer {
|
|
|
186
204
|
const props = cls.getProperties();
|
|
187
205
|
for (const prop of props) {
|
|
188
206
|
const propertyAnalysis = this.createPropertyAnalysis(prop, constructorParams, genericParameters);
|
|
189
|
-
|
|
190
|
-
const hasTransient = propertyAnalysis.decorators?.isTransient;
|
|
191
|
-
const hasCustomSerializer = !!propertyAnalysis.decorators?.with;
|
|
192
|
-
if (!hasTransient && !hasCustomSerializer) {
|
|
193
|
-
if (prop instanceof ts_morph_1.PropertyDeclaration) {
|
|
194
|
-
throw new CustomError_1.CustomError(`不支持的类型 '${propertyAnalysis.type.sourceText}' 用于属性 '${propertyAnalysis.name}' (类: ${cls.getName()})。\n` +
|
|
195
|
-
`请选择以下任一解决方案:\n` +
|
|
196
|
-
` 1. 添加 @Transient() 跳过序列化(属性仍会参与 Sendable 转换和对象拷贝):\n` +
|
|
197
|
-
` @Transient()\n` +
|
|
198
|
-
` ${propertyAnalysis.name}: ${propertyAnalysis.type.sourceText};\n\n` +
|
|
199
|
-
` 2. 添加 @Serializable({with: CustomSerializer}) 指定自定义序列化器:\n` +
|
|
200
|
-
` @Serializable({with: DateSerializer.INSTANCE})\n` +
|
|
201
|
-
` ${propertyAnalysis.name}: ${propertyAnalysis.type.sourceText};`, CustomError_1.ErrorCodes.TYPE_NOT_SUPPORT);
|
|
202
|
-
}
|
|
203
|
-
else {
|
|
204
|
-
throw new CustomError_1.CustomError(`不支持的类型 '${propertyAnalysis.type.sourceText}' 用于属性 '${propertyAnalysis.name}' (接口: ${cls.getName()})`, CustomError_1.ErrorCodes.TYPE_NOT_SUPPORT);
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
}
|
|
207
|
+
this.checkUnknownType(cls, prop, propertyAnalysis);
|
|
208
208
|
ownProperties.push(propertyAnalysis);
|
|
209
209
|
}
|
|
210
210
|
return ownProperties;
|
|
211
211
|
}
|
|
212
|
+
static checkUnknownType(cls, prop, propertyAnalysis) {
|
|
213
|
+
if (propertyAnalysis.type.kind !== Types_1.PropertyKind.UNKNOWN) {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const hasTransient = propertyAnalysis.decorators?.isTransient;
|
|
217
|
+
const hasCustomSerializer = !!propertyAnalysis.decorators?.with;
|
|
218
|
+
if (hasTransient || hasCustomSerializer) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
if (prop instanceof ts_morph_1.PropertyDeclaration) {
|
|
222
|
+
throw new CustomError_1.CustomError(`不支持的类型 '${propertyAnalysis.type.sourceText}' 用于属性 '${propertyAnalysis.name}' (类: ${cls.getName()})。\n` +
|
|
223
|
+
`请选择以下任一解决方案:\n` +
|
|
224
|
+
` 1. 添加 @Transient() 跳过序列化(属性仍会参与 Sendable 转换和对象拷贝):\n` +
|
|
225
|
+
` @Transient()\n` +
|
|
226
|
+
` ${propertyAnalysis.name}: ${propertyAnalysis.type.sourceText};\n\n` +
|
|
227
|
+
` 2. 添加 @Serializable({with: CustomSerializer}) 指定自定义序列化器:\n` +
|
|
228
|
+
` @Serializable({with: DateSerializer.INSTANCE})\n` +
|
|
229
|
+
` ${propertyAnalysis.name}: ${propertyAnalysis.type.sourceText};`, CustomError_1.ErrorCodes.TYPE_NOT_SUPPORT);
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
throw new CustomError_1.CustomError(`不支持的类型 '${propertyAnalysis.type.sourceText}' 用于属性 '${propertyAnalysis.name}' (接口: ${cls.getName()})`, CustomError_1.ErrorCodes.TYPE_NOT_SUPPORT);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
212
235
|
static classifyProperties(ownProperties, inheritance, currentClassGenericParams = []) {
|
|
213
236
|
if (!inheritance.isInherited || !inheritance.baseClassAnalysis) {
|
|
214
237
|
return {
|
|
@@ -218,20 +241,14 @@ class ClassAnalyzer {
|
|
|
218
241
|
};
|
|
219
242
|
}
|
|
220
243
|
const allAncestorProperties = this.collectAllAncestorProperties(inheritance.baseClassAnalysis, inheritance.genericTypeArguments || [], currentClassGenericParams);
|
|
244
|
+
return this.buildPropertyClassification(ownProperties, allAncestorProperties);
|
|
245
|
+
}
|
|
246
|
+
static buildPropertyClassification(ownProperties, allAncestorProperties) {
|
|
221
247
|
const ownPropertyNames = new Set(ownProperties.map((prop) => prop.name));
|
|
222
|
-
const inheritedProperties =
|
|
223
|
-
for (const ancestorProp of allAncestorProperties) {
|
|
224
|
-
if (!ownPropertyNames.has(ancestorProp.name)) {
|
|
225
|
-
inheritedProperties.push(ancestorProp);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
248
|
+
const inheritedProperties = allAncestorProperties.filter((ancestorProp) => !ownPropertyNames.has(ancestorProp.name));
|
|
228
249
|
const overriddenProperties = ownProperties.filter((prop) => allAncestorProperties.some((ancestorProp) => ancestorProp.name === prop.name));
|
|
229
250
|
const properties = [...inheritedProperties, ...ownProperties];
|
|
230
|
-
return {
|
|
231
|
-
inheritedProperties,
|
|
232
|
-
overriddenProperties,
|
|
233
|
-
properties
|
|
234
|
-
};
|
|
251
|
+
return { inheritedProperties, overriddenProperties, properties };
|
|
235
252
|
}
|
|
236
253
|
static collectAllAncestorProperties(classAnalysis, childGenericArguments = [], currentClassGenericParams = []) {
|
|
237
254
|
const allProperties = [];
|
|
@@ -239,12 +256,31 @@ class ClassAnalyzer {
|
|
|
239
256
|
const genericMapping = GenericTypeSubstitutionUtil_1.GenericTypeSubstitutionUtil.buildGenericMapping(parentGenericParams, childGenericArguments);
|
|
240
257
|
const substitutedProperties = classAnalysis.ownProperties.map((property) => GenericTypeSubstitutionUtil_1.GenericTypeSubstitutionUtil.substitutePropertyType(property, genericMapping, currentClassGenericParams));
|
|
241
258
|
allProperties.push(...substitutedProperties);
|
|
242
|
-
|
|
243
|
-
const ancestorProperties = this.collectAllAncestorProperties(classAnalysis.inheritance.baseClassAnalysis, classAnalysis.inheritance.genericTypeArguments || [], currentClassGenericParams);
|
|
244
|
-
allProperties.push(...ancestorProperties);
|
|
245
|
-
}
|
|
259
|
+
this.collectAncestorPropertiesRecursively(classAnalysis, parentGenericParams, genericMapping, currentClassGenericParams, allProperties);
|
|
246
260
|
return allProperties;
|
|
247
261
|
}
|
|
262
|
+
static collectAncestorPropertiesRecursively(classAnalysis, parentGenericParams, genericMapping, currentClassGenericParams, allProperties) {
|
|
263
|
+
if (!classAnalysis.inheritance.isInherited || !classAnalysis.inheritance.baseClassAnalysis) {
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
const parentGenericTypeArgs = classAnalysis.inheritance.genericTypeArguments || [];
|
|
267
|
+
const resolvedParentGenericArgs = this.resolveGenericTypeArgs(parentGenericTypeArgs, parentGenericParams, genericMapping);
|
|
268
|
+
Logger_1.Logger.debug(`[CollectAncestors] Resolved parent generic args: ${resolvedParentGenericArgs.map(a => a.getText()).join(', ')}`);
|
|
269
|
+
const ancestorProperties = this.collectAllAncestorProperties(classAnalysis.inheritance.baseClassAnalysis, resolvedParentGenericArgs, currentClassGenericParams);
|
|
270
|
+
allProperties.push(...ancestorProperties);
|
|
271
|
+
}
|
|
272
|
+
static resolveGenericTypeArgs(typeArgs, parentGenericParams, genericMapping) {
|
|
273
|
+
return typeArgs.map(typeArg => {
|
|
274
|
+
const typeStructure = handlers_1.TypeHandlerRegistry.getInstance().parseType(typeArg, {
|
|
275
|
+
depth: 0,
|
|
276
|
+
genericParams: parentGenericParams
|
|
277
|
+
});
|
|
278
|
+
if (typeStructure.kind === Types_1.PropertyKind.GENERIC && genericMapping.has(typeStructure.sourceText)) {
|
|
279
|
+
return genericMapping.get(typeStructure.sourceText);
|
|
280
|
+
}
|
|
281
|
+
return typeArg;
|
|
282
|
+
});
|
|
283
|
+
}
|
|
248
284
|
static extractConstructorParams(constructor) {
|
|
249
285
|
const parameters = [];
|
|
250
286
|
for (const param of constructor.getParameters()) {
|
|
@@ -259,44 +295,66 @@ class ClassAnalyzer {
|
|
|
259
295
|
}
|
|
260
296
|
static createPropertyAnalysis(prop, constructorParams, genericParameters) {
|
|
261
297
|
const name = prop.getName();
|
|
298
|
+
const typeNode = this.getTypeNode(prop);
|
|
299
|
+
const type = handlers_1.TypeHandlerRegistry.getInstance().parseType(typeNode, { genericParams: genericParameters, depth: 0 });
|
|
300
|
+
type.isOptional = prop.hasQuestionToken();
|
|
301
|
+
const defaultValue = prop instanceof ts_morph_1.PropertyDeclaration ?
|
|
302
|
+
this.analyzeDefaultValue(prop, constructorParams) : undefined;
|
|
303
|
+
const decorators = prop instanceof ts_morph_1.PropertyDeclaration ? this.analyzePropertyDecorators(prop) : undefined;
|
|
304
|
+
const visibility = this.getPropertyVisibility(prop);
|
|
305
|
+
const constructorParam = constructorParams.find((param) => param.name === name);
|
|
306
|
+
const isMust = this.calculateIsMust(prop, type, constructorParam, decorators, defaultValue);
|
|
307
|
+
if (decorators?.isTransient && constructorParam) {
|
|
308
|
+
this.validateTransientConstructorParam(name, constructorParam);
|
|
309
|
+
}
|
|
310
|
+
return {
|
|
311
|
+
name,
|
|
312
|
+
defaultValue,
|
|
313
|
+
decorators,
|
|
314
|
+
type,
|
|
315
|
+
isMust,
|
|
316
|
+
visibility
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
static getTypeNode(prop) {
|
|
262
320
|
let typeNode = prop.getTypeNode();
|
|
263
321
|
if (!typeNode) {
|
|
264
322
|
typeNode = this.analyzePropertyType(prop);
|
|
265
323
|
}
|
|
266
324
|
if (!typeNode) {
|
|
267
|
-
throw new CustomError_1.CustomError(`无法分析属性 '${
|
|
325
|
+
throw new CustomError_1.CustomError(`无法分析属性 '${prop.getName()}' 的类型。请检查属性声明。`, CustomError_1.ErrorCodes.TYPE_NOT_SUPPORT);
|
|
268
326
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
const isParamOptional = constructorParam
|
|
275
|
-
? constructorParam.isOptional ||
|
|
276
|
-
constructorParam.defaultValue !== undefined ||
|
|
277
|
-
this.hasUndefinedInUnion(constructorParam.type)
|
|
278
|
-
: true;
|
|
279
|
-
let isMust;
|
|
280
|
-
if (constructorParam) {
|
|
281
|
-
isMust = !isParamOptional;
|
|
327
|
+
return typeNode;
|
|
328
|
+
}
|
|
329
|
+
static getPropertyVisibility(prop) {
|
|
330
|
+
if (prop.hasModifier(ts_morph_1.SyntaxKind.PrivateKeyword)) {
|
|
331
|
+
return Types_1.PropertyVisibility.PRIVATE;
|
|
282
332
|
}
|
|
283
|
-
|
|
284
|
-
|
|
333
|
+
if (prop.hasModifier(ts_morph_1.SyntaxKind.ProtectedKeyword)) {
|
|
334
|
+
return Types_1.PropertyVisibility.PROTECTED;
|
|
285
335
|
}
|
|
286
|
-
|
|
336
|
+
return Types_1.PropertyVisibility.PUBLIC;
|
|
337
|
+
}
|
|
338
|
+
static calculateIsMust(prop, type, constructorParam, decorators, defaultValue) {
|
|
339
|
+
if (constructorParam) {
|
|
340
|
+
const isParamOptional = constructorParam.isOptional ||
|
|
341
|
+
constructorParam.defaultValue !== undefined ||
|
|
342
|
+
this.hasUndefinedInUnion(constructorParam.type);
|
|
343
|
+
return !isParamOptional;
|
|
344
|
+
}
|
|
345
|
+
return decorators?.isRequired || !this.isOptional(prop, defaultValue, type);
|
|
346
|
+
}
|
|
347
|
+
static validateTransientConstructorParam(name, constructorParam) {
|
|
348
|
+
const isParamOptional = constructorParam.isOptional ||
|
|
349
|
+
constructorParam.defaultValue !== undefined ||
|
|
350
|
+
this.hasUndefinedInUnion(constructorParam.type);
|
|
351
|
+
if (!isParamOptional) {
|
|
287
352
|
throw new CustomError_1.CustomError(`@Transient 属性 '${name}' 的构造函数参数必须是可选的或有默认值。\n` +
|
|
288
353
|
`因为 @Transient 属性不参与序列化,反序列化时无法从外部数据获取值。\n` +
|
|
289
354
|
`请修改构造函数参数为以下任一形式:\n` +
|
|
290
355
|
` 1. 可选参数: constructor(..., ${name}?: ${constructorParam.type})\n` +
|
|
291
356
|
` 2. 带默认值: constructor(..., ${name}: ${constructorParam.type} = defaultValue)`, CustomError_1.ErrorCodes.TRANSIENT_ATTR_OPTIONAL);
|
|
292
357
|
}
|
|
293
|
-
return {
|
|
294
|
-
name,
|
|
295
|
-
defaultValue,
|
|
296
|
-
decorators,
|
|
297
|
-
type,
|
|
298
|
-
isMust
|
|
299
|
-
};
|
|
300
358
|
}
|
|
301
359
|
static analyzePropertyType(prop) {
|
|
302
360
|
const name = prop.getName();
|
|
@@ -352,7 +410,8 @@ class ClassAnalyzer {
|
|
|
352
410
|
return undefined;
|
|
353
411
|
}
|
|
354
412
|
const exprText = expression.getText();
|
|
355
|
-
const supportedTypes = ['Map', 'Set', 'WeakMap', 'WeakSet', 'Array', 'Object', 'ArrayList', 'HashMap', 'HashSet',
|
|
413
|
+
const supportedTypes = ['Map', 'Set', 'WeakMap', 'WeakSet', 'Array', 'Object', 'ArrayList', 'HashMap', 'HashSet',
|
|
414
|
+
'LinkedList', 'Decimal', 'List'];
|
|
356
415
|
if (!supportedTypes.includes(exprText)) {
|
|
357
416
|
return undefined;
|
|
358
417
|
}
|
|
@@ -434,6 +493,7 @@ class ClassAnalyzer {
|
|
|
434
493
|
const decorators = prop.getDecorators();
|
|
435
494
|
let isRequired = false;
|
|
436
495
|
let isTransient = false;
|
|
496
|
+
let isPlainValue = false;
|
|
437
497
|
let serialName;
|
|
438
498
|
let _with;
|
|
439
499
|
decorators.forEach((decorator) => {
|
|
@@ -448,33 +508,53 @@ class ClassAnalyzer {
|
|
|
448
508
|
case constants_1.DecoratorConstants.TRANSIENT:
|
|
449
509
|
isTransient = true;
|
|
450
510
|
break;
|
|
511
|
+
case constants_1.DecoratorConstants.PLAIN_VALUE:
|
|
512
|
+
isPlainValue = true;
|
|
513
|
+
break;
|
|
451
514
|
case constants_1.DecoratorConstants.SERIALIZABLE:
|
|
452
515
|
_with = this.parseSerializableOptions(decorator).with;
|
|
516
|
+
break;
|
|
453
517
|
}
|
|
454
518
|
});
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
if (isRequired) {
|
|
458
|
-
conflictingDecorators.push('@Required');
|
|
459
|
-
}
|
|
460
|
-
if (serialName) {
|
|
461
|
-
conflictingDecorators.push('@SerialName');
|
|
462
|
-
}
|
|
463
|
-
if (_with) {
|
|
464
|
-
conflictingDecorators.push('@Serializable');
|
|
465
|
-
}
|
|
466
|
-
if (conflictingDecorators.length > 0) {
|
|
467
|
-
throw new CustomError_1.CustomError(`属性 '${prop.getName()}' 的注解冲突:@Transient 与 ${conflictingDecorators.join(', ')} 不能同时使用。\n` +
|
|
468
|
-
`@Transient 表示跳过序列化,与其他序列化配置注解互斥。\n` +
|
|
469
|
-
`请移除 @Transient 或移除其他序列化注解。`, CustomError_1.ErrorCodes.ANNOTATION_CONFLICT);
|
|
470
|
-
}
|
|
471
|
-
}
|
|
519
|
+
this.checkTransientConflicts(prop.getName(), isTransient, isRequired, !!serialName, !!_with, isPlainValue);
|
|
520
|
+
this.checkPlainValueConflicts(prop.getName(), isPlainValue, !!_with);
|
|
472
521
|
return {
|
|
473
522
|
isRequired,
|
|
474
523
|
isTransient,
|
|
524
|
+
isPlainValue,
|
|
475
525
|
serialName,
|
|
476
526
|
with: _with
|
|
477
527
|
};
|
|
478
528
|
}
|
|
529
|
+
static checkTransientConflicts(propName, isTransient, isRequired, hasSerialName, hasWith, isPlainValue) {
|
|
530
|
+
if (!isTransient) {
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
const conflictingDecorators = [];
|
|
534
|
+
if (isRequired) {
|
|
535
|
+
conflictingDecorators.push('@Required');
|
|
536
|
+
}
|
|
537
|
+
if (hasSerialName) {
|
|
538
|
+
conflictingDecorators.push('@SerialName');
|
|
539
|
+
}
|
|
540
|
+
if (hasWith) {
|
|
541
|
+
conflictingDecorators.push('@Serializable');
|
|
542
|
+
}
|
|
543
|
+
if (isPlainValue) {
|
|
544
|
+
conflictingDecorators.push('@PlainValue');
|
|
545
|
+
}
|
|
546
|
+
if (conflictingDecorators.length > 0) {
|
|
547
|
+
throw new CustomError_1.CustomError(`属性 '${propName}' 的注解冲突:@Transient 与 ${conflictingDecorators.join(', ')} 不能同时使用。\n` +
|
|
548
|
+
`@Transient 表示跳过序列化,与其他序列化配置注解互斥。\n` +
|
|
549
|
+
`请移除 @Transient 或移除其他序列化注解。`, CustomError_1.ErrorCodes.ANNOTATION_CONFLICT);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
static checkPlainValueConflicts(propName, isPlainValue, hasWith) {
|
|
553
|
+
if (isPlainValue && hasWith) {
|
|
554
|
+
throw new CustomError_1.CustomError(`属性 '${propName}' 的注解冲突:@PlainValue 与 @Serializable(with:) 不能同时使用。\n` +
|
|
555
|
+
`@PlainValue 跳过类型转换直接使用原始值,与自定义序列化器冲突。\n` +
|
|
556
|
+
`请移除 @PlainValue 或移除自定义序列化器。`, CustomError_1.ErrorCodes.ANNOTATION_CONFLICT);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
479
559
|
}
|
|
480
560
|
exports.ClassAnalyzer = ClassAnalyzer;
|
|
@@ -10,9 +10,12 @@ export declare class CustomTypeAnalyzer {
|
|
|
10
10
|
clearCache(): void;
|
|
11
11
|
private findInCurrentFile;
|
|
12
12
|
private findInImports;
|
|
13
|
+
private processImportMatch;
|
|
13
14
|
private findInCrossModule;
|
|
14
15
|
private searchInSourceFile;
|
|
15
16
|
private parseEnumDetails;
|
|
17
|
+
private parseEnumMember;
|
|
18
|
+
private createStringEnumResult;
|
|
16
19
|
private parseClassDetails;
|
|
17
20
|
private parseInterfaceDetails;
|
|
18
21
|
private parseTypeAliasDetails;
|