@hadss/turbo-trans-json-plugin 1.0.0-rc.1 → 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/dist/core/analyzers/ClassAnalyzer.d.ts +15 -0
- package/dist/core/analyzers/ClassAnalyzer.js +193 -144
- package/dist/core/analyzers/CustomTypeAnalyzer.d.ts +3 -0
- package/dist/core/analyzers/CustomTypeAnalyzer.js +65 -60
- package/dist/core/constants/DecoratorConstants.js +2 -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/generators/OriginalClassGenerator.d.ts +2 -0
- package/dist/core/services/CodeGenerationService/generators/OriginalClassGenerator.js +22 -15
- 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 +1 -1
- 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 +21 -2
- package/dist/core/utils/DeepCopyUtil.js +1 -1
- package/dist/core/utils/SerializationPathUtil.d.ts +1 -1
- package/dist/core/utils/TsMorphUtil.js +2 -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 +90 -90
- package/src/core/analyzers/ClassAnalyzer.ts +335 -230
- package/src/core/analyzers/CustomTypeAnalyzer.ts +145 -73
- package/src/core/constants/DecoratorConstants.ts +7 -6
- package/src/core/constants/PathConstants.ts +7 -7
- package/src/core/constants/StringConstants.ts +95 -95
- package/src/core/handlers/BaseTypeHandler.ts +11 -2
- package/src/core/handlers/CustomClassHandler.ts +4 -4
- 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 +2 -1
- package/src/core/handlers/UnionTypeHandler.ts +8 -7
- package/src/core/import-rewrite/services/BuildProfileUpdater.ts +6 -4
- package/src/core/import-rewrite/services/ImportRewriteService.ts +1 -1
- 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 +2 -1
- package/src/core/services/CodeGenerationEngine.ts +42 -42
- package/src/core/services/CodeGenerationService/generators/OriginalClassGenerator.ts +25 -22
- package/src/core/services/CodeGenerationService/generators/SendableClassGenerator.ts +261 -170
- package/src/core/services/CodeGenerationService/generators/SerializerGenerator.ts +1 -1
- 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 +39 -11
- package/src/core/utils/ConfigManager.ts +2 -1
- package/src/core/utils/DeepCopyUtil.ts +1 -1
- package/src/core/utils/GenericTypeSubstitutionUtil.ts +1 -8
- package/src/core/utils/SerializationPathUtil.ts +7 -6
- package/src/core/utils/TsMorphUtil.ts +4 -1
- 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 -3
- package/src/json-plugin/tasks/CleanTask.ts +7 -7
- package/src/json-plugin/tasks/WatchTask.ts +20 -18
- package/template/SerializerPerformanceTemplate.hbs +5 -5
- package/template/SerializerStrictTemplate.hbs +1 -1
- package/template/SerializerTemplate.hbs +59 -42
|
@@ -14,29 +14,12 @@
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
import {
|
|
17
|
-
ClassDeclaration,
|
|
18
|
-
|
|
19
|
-
Decorator,
|
|
20
|
-
InterfaceDeclaration,
|
|
21
|
-
NewExpression,
|
|
22
|
-
PropertyDeclaration,
|
|
23
|
-
PropertySignature,
|
|
24
|
-
SyntaxKind,
|
|
25
|
-
TypeNode
|
|
17
|
+
ClassDeclaration, ConstructorDeclaration, Decorator, InterfaceDeclaration, NewExpression, ObjectLiteralExpression,
|
|
18
|
+
PropertyDeclaration, PropertySignature, SyntaxKind, TypeNode
|
|
26
19
|
} from 'ts-morph';
|
|
27
20
|
import {
|
|
28
|
-
ClassAnalysis,
|
|
29
|
-
|
|
30
|
-
ConstructorParam,
|
|
31
|
-
DeserializationMode,
|
|
32
|
-
GenericInfo,
|
|
33
|
-
InheritanceInfo,
|
|
34
|
-
InterfaceAnalysis,
|
|
35
|
-
PropertyAnalysis,
|
|
36
|
-
PropertyDecorators,
|
|
37
|
-
PropertyKind,
|
|
38
|
-
PropertyVisibility,
|
|
39
|
-
SerializableOptions,
|
|
21
|
+
ClassAnalysis, ClassDecorators, ConstructorParam, DeserializationMode, GenericInfo, InheritanceInfo,
|
|
22
|
+
InterfaceAnalysis, PropertyAnalysis, PropertyDecorators, PropertyKind, PropertyVisibility, SerializableOptions,
|
|
40
23
|
TypeStructure
|
|
41
24
|
} from '../Types';
|
|
42
25
|
import { DecoratorConstants } from '../constants';
|
|
@@ -222,45 +205,69 @@ export class ClassAnalyzer {
|
|
|
222
205
|
return undefined;
|
|
223
206
|
}
|
|
224
207
|
const args = decorator.getArguments();
|
|
225
|
-
let generateSendable: boolean | undefined;
|
|
226
|
-
let _with: string | undefined;
|
|
227
|
-
let deserializationMode: DeserializationMode | undefined;
|
|
228
|
-
|
|
229
208
|
const firstArg = args[0];
|
|
230
209
|
|
|
231
|
-
if (!firstArg) {
|
|
210
|
+
if (!firstArg || firstArg.getKind() !== SyntaxKind.ObjectLiteralExpression) {
|
|
232
211
|
return {};
|
|
233
212
|
}
|
|
234
213
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
_with = initializer.getText();
|
|
253
|
-
} else if (propertyName === 'deserializationMode' && initializer) {
|
|
254
|
-
// 处理 deserializationMode 参数:'performance' | 'strict'
|
|
255
|
-
const modeText = initializer.getText().replace(/['"]/g, ''); // 去掉引号
|
|
256
|
-
if (modeText === DeserializationMode.PERFORMANCE || modeText === DeserializationMode.STRICT) {
|
|
257
|
-
deserializationMode = modeText as DeserializationMode;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
}
|
|
214
|
+
return this.extractOptionsFromObjectLiteral(firstArg.asKindOrThrow(SyntaxKind.ObjectLiteralExpression));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
private static extractOptionsFromObjectLiteral(objectLiteral: ObjectLiteralExpression): SerializableOptions {
|
|
218
|
+
const options: SerializableOptions = {};
|
|
219
|
+
const properties = objectLiteral.getProperties();
|
|
220
|
+
|
|
221
|
+
for (const prop of properties) {
|
|
222
|
+
// early return: 非属性赋值节点
|
|
223
|
+
if (prop.getKind() !== SyntaxKind.PropertyAssignment) {
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
const propAssignment = prop.asKindOrThrow(SyntaxKind.PropertyAssignment);
|
|
227
|
+
const initializer = propAssignment.getInitializer();
|
|
228
|
+
// early return: 无初始化器
|
|
229
|
+
if (!initializer) {
|
|
230
|
+
continue;
|
|
261
231
|
}
|
|
232
|
+
|
|
233
|
+
this.extractSingleOption(options, propAssignment.getName(), initializer);
|
|
234
|
+
}
|
|
235
|
+
return options;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
private static extractSingleOption(
|
|
239
|
+
options: SerializableOptions,
|
|
240
|
+
propertyName: string,
|
|
241
|
+
initializer: ReturnType<typeof Object.prototype.valueOf>
|
|
242
|
+
): void {
|
|
243
|
+
if (propertyName === 'generateSendable') {
|
|
244
|
+
options.generateSendable = this.parseBooleanInitializer(initializer);
|
|
245
|
+
} else if (propertyName === 'with') {
|
|
246
|
+
options.with = (initializer as { getText(): string }).getText();
|
|
247
|
+
} else if (propertyName === 'deserializationMode') {
|
|
248
|
+
this.parseDeserializationMode(options, initializer);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
private static parseBooleanInitializer(initializer: ReturnType<typeof Object.prototype.valueOf>): boolean | undefined {
|
|
253
|
+
const kind = (initializer as { getKind(): SyntaxKind }).getKind();
|
|
254
|
+
if (kind === SyntaxKind.TrueKeyword) {
|
|
255
|
+
return true;
|
|
256
|
+
}
|
|
257
|
+
if (kind === SyntaxKind.FalseKeyword) {
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
return undefined;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
private static parseDeserializationMode(
|
|
264
|
+
options: SerializableOptions,
|
|
265
|
+
initializer: ReturnType<typeof Object.prototype.valueOf>
|
|
266
|
+
): void {
|
|
267
|
+
const modeText = (initializer as { getText(): string }).getText().replace(/['"]/g, '');
|
|
268
|
+
if (modeText === DeserializationMode.PERFORMANCE || modeText === DeserializationMode.STRICT) {
|
|
269
|
+
options.deserializationMode = modeText as DeserializationMode;
|
|
262
270
|
}
|
|
263
|
-
return { with: _with, generateSendable, deserializationMode };
|
|
264
271
|
}
|
|
265
272
|
|
|
266
273
|
/**
|
|
@@ -269,36 +276,53 @@ export class ClassAnalyzer {
|
|
|
269
276
|
* @returns 序列化名称
|
|
270
277
|
*/
|
|
271
278
|
private static parseSerialNameOptions(decorator?: Decorator): string | undefined {
|
|
279
|
+
// early return: 无装饰器
|
|
272
280
|
if (!decorator) {
|
|
273
281
|
return undefined;
|
|
274
282
|
}
|
|
275
283
|
const args = decorator.getArguments();
|
|
284
|
+
// early return: 无参数
|
|
276
285
|
if (args.length === 0) {
|
|
277
286
|
return undefined;
|
|
278
287
|
}
|
|
279
288
|
|
|
280
289
|
const firstArg = args[0];
|
|
281
290
|
|
|
291
|
+
// early return: 非对象字面量
|
|
292
|
+
if (firstArg.getKind() !== SyntaxKind.ObjectLiteralExpression) {
|
|
293
|
+
return undefined;
|
|
294
|
+
}
|
|
295
|
+
|
|
282
296
|
// 处理对象形式:@SerialName({ name: 'xxx' })
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
297
|
+
const properties = firstArg.asKindOrThrow(SyntaxKind.ObjectLiteralExpression).getProperties();
|
|
298
|
+
return this.extractNameFromProperties(properties);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* 从属性列表中提取name属性的值
|
|
303
|
+
* @param properties 属性列表
|
|
304
|
+
* @returns name属性的字符串值
|
|
305
|
+
*/
|
|
306
|
+
private static extractNameFromProperties(properties: ReturnType<ObjectLiteralExpression['getProperties']>): string | undefined {
|
|
307
|
+
for (const prop of properties) {
|
|
308
|
+
// early return: 非属性赋值节点
|
|
309
|
+
if (prop.getKind() !== SyntaxKind.PropertyAssignment) {
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
const propAssignment = prop.asKindOrThrow(SyntaxKind.PropertyAssignment);
|
|
313
|
+
// early return: 非name属性
|
|
314
|
+
if (propAssignment.getName() !== 'name') {
|
|
315
|
+
continue;
|
|
299
316
|
}
|
|
300
|
-
}
|
|
301
317
|
|
|
318
|
+
const initializer = propAssignment.getInitializer();
|
|
319
|
+
// early return: 非字符串字面量
|
|
320
|
+
if (!initializer || initializer.getKind() !== SyntaxKind.StringLiteral) {
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return initializer.asKindOrThrow(SyntaxKind.StringLiteral).getLiteralValue();
|
|
325
|
+
}
|
|
302
326
|
return undefined;
|
|
303
327
|
}
|
|
304
328
|
|
|
@@ -323,40 +347,49 @@ export class ClassAnalyzer {
|
|
|
323
347
|
genericParameters
|
|
324
348
|
);
|
|
325
349
|
|
|
326
|
-
|
|
327
|
-
if (propertyAnalysis.type.kind === PropertyKind.UNKNOWN) {
|
|
328
|
-
const hasTransient = propertyAnalysis.decorators?.isTransient;
|
|
329
|
-
const hasCustomSerializer = !!propertyAnalysis.decorators?.with;
|
|
330
|
-
|
|
331
|
-
if (!hasTransient && !hasCustomSerializer) {
|
|
332
|
-
if (prop instanceof PropertyDeclaration) {
|
|
333
|
-
// 未知类型且没有 @Transient 或自定义序列化器,抛出清晰错误
|
|
334
|
-
throw new CustomError(
|
|
335
|
-
`不支持的类型 '${propertyAnalysis.type.sourceText}' 用于属性 '${propertyAnalysis.name
|
|
336
|
-
}' (类: ${cls.getName()})。\n` +
|
|
337
|
-
`请选择以下任一解决方案:\n` +
|
|
338
|
-
` 1. 添加 @Transient() 跳过序列化(属性仍会参与 Sendable 转换和对象拷贝):\n` +
|
|
339
|
-
` @Transient()\n` +
|
|
340
|
-
` ${propertyAnalysis.name}: ${propertyAnalysis.type.sourceText};\n\n` +
|
|
341
|
-
` 2. 添加 @Serializable({with: CustomSerializer}) 指定自定义序列化器:\n` +
|
|
342
|
-
` @Serializable({with: DateSerializer.INSTANCE})\n` +
|
|
343
|
-
` ${propertyAnalysis.name}: ${propertyAnalysis.type.sourceText};`,
|
|
344
|
-
ErrorCodes.TYPE_NOT_SUPPORT
|
|
345
|
-
);
|
|
346
|
-
} else {
|
|
347
|
-
// 未知类型且没有 @Transient 或自定义序列化器,抛出清晰错误
|
|
348
|
-
throw new CustomError(`不支持的类型 '${propertyAnalysis.type.sourceText}' 用于属性 '${propertyAnalysis.name
|
|
349
|
-
}' (接口: ${cls.getName()})`, ErrorCodes.TYPE_NOT_SUPPORT);
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
// 保留所有属性(包括 UNKNOWN 类型),生成阶段再筛选
|
|
350
|
+
this.checkUnknownType(cls, prop, propertyAnalysis);
|
|
355
351
|
ownProperties.push(propertyAnalysis);
|
|
356
352
|
}
|
|
357
353
|
return ownProperties;
|
|
358
354
|
}
|
|
359
355
|
|
|
356
|
+
private static checkUnknownType(
|
|
357
|
+
cls: ClassDeclaration | InterfaceDeclaration,
|
|
358
|
+
prop: PropertyDeclaration | PropertySignature,
|
|
359
|
+
propertyAnalysis: PropertyAnalysis
|
|
360
|
+
): void {
|
|
361
|
+
if (propertyAnalysis.type.kind !== PropertyKind.UNKNOWN) {
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const hasTransient = propertyAnalysis.decorators?.isTransient;
|
|
366
|
+
const hasCustomSerializer = !!propertyAnalysis.decorators?.with;
|
|
367
|
+
|
|
368
|
+
if (hasTransient || hasCustomSerializer) {
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (prop instanceof PropertyDeclaration) {
|
|
373
|
+
// 未知类型且没有 @Transient 或自定义序列化器,抛出清晰错误
|
|
374
|
+
throw new CustomError(
|
|
375
|
+
`不支持的类型 '${propertyAnalysis.type.sourceText}' 用于属性 '${propertyAnalysis.name
|
|
376
|
+
}' (类: ${cls.getName()})。\n` +
|
|
377
|
+
`请选择以下任一解决方案:\n` +
|
|
378
|
+
` 1. 添加 @Transient() 跳过序列化(属性仍会参与 Sendable 转换和对象拷贝):\n` +
|
|
379
|
+
` @Transient()\n` +
|
|
380
|
+
` ${propertyAnalysis.name}: ${propertyAnalysis.type.sourceText};\n\n` +
|
|
381
|
+
` 2. 添加 @Serializable({with: CustomSerializer}) 指定自定义序列化器:\n` +
|
|
382
|
+
` @Serializable({with: DateSerializer.INSTANCE})\n` +
|
|
383
|
+
` ${propertyAnalysis.name}: ${propertyAnalysis.type.sourceText};`,
|
|
384
|
+
ErrorCodes.TYPE_NOT_SUPPORT
|
|
385
|
+
);
|
|
386
|
+
} else {
|
|
387
|
+
// 未知类型且没有 @Transient 或自定义序列化器,抛出清晰错误
|
|
388
|
+
throw new CustomError(`不支持的类型 '${propertyAnalysis.type.sourceText}' 用于属性 '${propertyAnalysis.name
|
|
389
|
+
}' (接口: ${cls.getName()})`, ErrorCodes.TYPE_NOT_SUPPORT);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
360
393
|
/**
|
|
361
394
|
* 对属性进行分类,区分继承属性、重写属性和自有属性
|
|
362
395
|
* @param ownProperties 自有属性数组
|
|
@@ -373,8 +406,8 @@ export class ClassAnalyzer {
|
|
|
373
406
|
overriddenProperties: PropertyAnalysis[];
|
|
374
407
|
properties: PropertyAnalysis[];
|
|
375
408
|
} {
|
|
409
|
+
// early return: 没有继承,所有属性都是自有属性
|
|
376
410
|
if (!inheritance.isInherited || !inheritance.baseClassAnalysis) {
|
|
377
|
-
// 没有继承,所有属性都是自有属性
|
|
378
411
|
return {
|
|
379
412
|
inheritedProperties: [],
|
|
380
413
|
overriddenProperties: [],
|
|
@@ -385,22 +418,34 @@ export class ClassAnalyzer {
|
|
|
385
418
|
// 递归收集所有祖先属性 - 传递泛型参数进行替换
|
|
386
419
|
const allAncestorProperties = this.collectAllAncestorProperties(
|
|
387
420
|
inheritance.baseClassAnalysis,
|
|
388
|
-
inheritance.genericTypeArguments || [],
|
|
389
|
-
currentClassGenericParams
|
|
421
|
+
inheritance.genericTypeArguments || [],
|
|
422
|
+
currentClassGenericParams
|
|
390
423
|
);
|
|
391
424
|
|
|
425
|
+
return this.buildPropertyClassification(ownProperties, allAncestorProperties);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* 根据自有属性和祖先属性构建属性分类结果
|
|
430
|
+
* @param ownProperties 自有属性数组
|
|
431
|
+
* @param allAncestorProperties 所有祖先属性数组
|
|
432
|
+
* @returns 分类后的属性对象
|
|
433
|
+
*/
|
|
434
|
+
private static buildPropertyClassification(
|
|
435
|
+
ownProperties: PropertyAnalysis[],
|
|
436
|
+
allAncestorProperties: PropertyAnalysis[]
|
|
437
|
+
): {
|
|
438
|
+
inheritedProperties: PropertyAnalysis[];
|
|
439
|
+
overriddenProperties: PropertyAnalysis[];
|
|
440
|
+
properties: PropertyAnalysis[];
|
|
441
|
+
} {
|
|
392
442
|
// 构建自有属性名称集合,用于快速查找重写
|
|
393
443
|
const ownPropertyNames = new Set(ownProperties.map((prop) => prop.name));
|
|
394
444
|
|
|
395
|
-
//
|
|
396
|
-
const inheritedProperties
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
if (!ownPropertyNames.has(ancestorProp.name)) {
|
|
400
|
-
// 被当前类继承了
|
|
401
|
-
inheritedProperties.push(ancestorProp);
|
|
402
|
-
}
|
|
403
|
-
}
|
|
445
|
+
// 分类祖先属性:过滤出被继承的(未被重写的)
|
|
446
|
+
const inheritedProperties = allAncestorProperties.filter(
|
|
447
|
+
(ancestorProp) => !ownPropertyNames.has(ancestorProp.name)
|
|
448
|
+
);
|
|
404
449
|
|
|
405
450
|
// 确定当前类中的重写属性
|
|
406
451
|
const overriddenProperties = ownProperties.filter((prop) =>
|
|
@@ -410,11 +455,7 @@ export class ClassAnalyzer {
|
|
|
410
455
|
// 合并最终属性:继承的 + 当前类的(重写优先)
|
|
411
456
|
const properties = [...inheritedProperties, ...ownProperties];
|
|
412
457
|
|
|
413
|
-
return {
|
|
414
|
-
inheritedProperties,
|
|
415
|
-
overriddenProperties,
|
|
416
|
-
properties
|
|
417
|
-
};
|
|
458
|
+
return { inheritedProperties, overriddenProperties, properties };
|
|
418
459
|
}
|
|
419
460
|
|
|
420
461
|
/**
|
|
@@ -439,44 +480,80 @@ export class ClassAnalyzer {
|
|
|
439
480
|
const substitutedProperties = classAnalysis.ownProperties.map((property) =>
|
|
440
481
|
GenericTypeSubstitutionUtil.substitutePropertyType(property, genericMapping, currentClassGenericParams)
|
|
441
482
|
);
|
|
442
|
-
|
|
443
483
|
allProperties.push(...substitutedProperties);
|
|
444
484
|
|
|
445
|
-
// 3.
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
// 对父类的泛型参数进行替换,解决泛型参数引用问题
|
|
451
|
-
// 例如: CrudRepository<T> extends Repository<T, number>
|
|
452
|
-
// 这里的 T 需要替换为当前映射中的 UserEntity
|
|
453
|
-
const resolvedParentGenericArgs = parentGenericTypeArgs.map(typeArg => {
|
|
454
|
-
// 解析类型参数为 TypeStructure
|
|
455
|
-
const typeStructure = TypeHandlerRegistry.getInstance().parseType(typeArg, {
|
|
456
|
-
depth: 0,
|
|
457
|
-
genericParams: parentGenericParams // 使用父类的泛型参数上下文
|
|
458
|
-
});
|
|
459
|
-
|
|
460
|
-
// 如果是泛型引用,进行替换
|
|
461
|
-
if (typeStructure.kind === PropertyKind.GENERIC && genericMapping.has(typeStructure.sourceText)) {
|
|
462
|
-
return genericMapping.get(typeStructure.sourceText)!;
|
|
463
|
-
}
|
|
485
|
+
// 3. 递归处理更上层祖先
|
|
486
|
+
this.collectAncestorPropertiesRecursively(
|
|
487
|
+
classAnalysis, parentGenericParams, genericMapping, currentClassGenericParams, allProperties
|
|
488
|
+
);
|
|
464
489
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
});
|
|
490
|
+
return allProperties;
|
|
491
|
+
}
|
|
468
492
|
|
|
469
|
-
|
|
493
|
+
/**
|
|
494
|
+
* 递归收集更上层祖先的属性
|
|
495
|
+
* @param classAnalysis 当前类分析结果
|
|
496
|
+
* @param parentGenericParams 父类泛型参数
|
|
497
|
+
* @param genericMapping 泛型映射
|
|
498
|
+
* @param currentClassGenericParams 当前类泛型参数列表
|
|
499
|
+
* @param allProperties 属性收集数组
|
|
500
|
+
*/
|
|
501
|
+
private static collectAncestorPropertiesRecursively(
|
|
502
|
+
classAnalysis: ClassAnalysis | InterfaceAnalysis,
|
|
503
|
+
parentGenericParams: string[],
|
|
504
|
+
genericMapping: Map<string, TypeNode>,
|
|
505
|
+
currentClassGenericParams: string[],
|
|
506
|
+
allProperties: PropertyAnalysis[]
|
|
507
|
+
): void {
|
|
508
|
+
// early return: 没有继承关系
|
|
509
|
+
if (!classAnalysis.inheritance.isInherited || !classAnalysis.inheritance.baseClassAnalysis) {
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// 解析父类的泛型参数,将泛型引用替换为具体类型
|
|
514
|
+
const parentGenericTypeArgs = classAnalysis.inheritance.genericTypeArguments || [];
|
|
515
|
+
const resolvedParentGenericArgs = this.resolveGenericTypeArgs(
|
|
516
|
+
parentGenericTypeArgs, parentGenericParams, genericMapping
|
|
517
|
+
);
|
|
470
518
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
resolvedParentGenericArgs, // 传递解析后的泛型参数
|
|
474
|
-
currentClassGenericParams // 保持传递当前类的泛型参数
|
|
475
|
-
);
|
|
476
|
-
allProperties.push(...ancestorProperties);
|
|
477
|
-
}
|
|
519
|
+
Logger.debug(
|
|
520
|
+
`[CollectAncestors] Resolved parent generic args: ${resolvedParentGenericArgs.map(a => a.getText()).join(', ')}`);
|
|
478
521
|
|
|
479
|
-
|
|
522
|
+
const ancestorProperties = this.collectAllAncestorProperties(
|
|
523
|
+
classAnalysis.inheritance.baseClassAnalysis,
|
|
524
|
+
resolvedParentGenericArgs,
|
|
525
|
+
currentClassGenericParams
|
|
526
|
+
);
|
|
527
|
+
allProperties.push(...ancestorProperties);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* 解析泛型类型参数,将泛型引用替换为具体类型
|
|
532
|
+
* @param typeArgs 类型参数数组
|
|
533
|
+
* @param parentGenericParams 父类泛型参数
|
|
534
|
+
* @param genericMapping 泛型映射
|
|
535
|
+
* @returns 解析后的类型参数数组
|
|
536
|
+
*/
|
|
537
|
+
private static resolveGenericTypeArgs(
|
|
538
|
+
typeArgs: TypeNode[],
|
|
539
|
+
parentGenericParams: string[],
|
|
540
|
+
genericMapping: Map<string, TypeNode>
|
|
541
|
+
): TypeNode[] {
|
|
542
|
+
return typeArgs.map(typeArg => {
|
|
543
|
+
// 解析类型参数为 TypeStructure
|
|
544
|
+
const typeStructure = TypeHandlerRegistry.getInstance().parseType(typeArg, {
|
|
545
|
+
depth: 0,
|
|
546
|
+
genericParams: parentGenericParams
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
// 如果是泛型引用,进行替换
|
|
550
|
+
if (typeStructure.kind === PropertyKind.GENERIC && genericMapping.has(typeStructure.sourceText)) {
|
|
551
|
+
return genericMapping.get(typeStructure.sourceText)!;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// 否则保持原样
|
|
555
|
+
return typeArg;
|
|
556
|
+
});
|
|
480
557
|
}
|
|
481
558
|
|
|
482
559
|
/**
|
|
@@ -512,52 +589,75 @@ export class ClassAnalyzer {
|
|
|
512
589
|
genericParameters: string[]
|
|
513
590
|
): PropertyAnalysis {
|
|
514
591
|
const name = prop.getName();
|
|
592
|
+
const typeNode = this.getTypeNode(prop);
|
|
593
|
+
const type = TypeHandlerRegistry.getInstance().parseType(typeNode, { genericParams: genericParameters, depth: 0 });
|
|
594
|
+
type.isOptional = prop.hasQuestionToken();
|
|
595
|
+
|
|
596
|
+
const defaultValue = prop instanceof PropertyDeclaration ?
|
|
597
|
+
this.analyzeDefaultValue(prop, constructorParams) : undefined;
|
|
598
|
+
const decorators = prop instanceof PropertyDeclaration ? this.analyzePropertyDecorators(prop) : undefined;
|
|
599
|
+
const visibility = this.getPropertyVisibility(prop);
|
|
600
|
+
|
|
601
|
+
const constructorParam = constructorParams.find((param) => param.name === name);
|
|
602
|
+
const isMust = this.calculateIsMust(prop, type, constructorParam, decorators, defaultValue);
|
|
603
|
+
|
|
604
|
+
if (decorators?.isTransient && constructorParam) {
|
|
605
|
+
this.validateTransientConstructorParam(name, constructorParam);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
return {
|
|
609
|
+
name,
|
|
610
|
+
defaultValue,
|
|
611
|
+
decorators,
|
|
612
|
+
type,
|
|
613
|
+
isMust,
|
|
614
|
+
visibility
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
private static getTypeNode(prop: PropertyDeclaration | PropertySignature): TypeNode {
|
|
515
619
|
let typeNode = prop.getTypeNode();
|
|
516
620
|
if (!typeNode) {
|
|
517
621
|
typeNode = this.analyzePropertyType(prop);
|
|
518
622
|
}
|
|
519
|
-
|
|
520
623
|
if (!typeNode) {
|
|
521
|
-
throw new CustomError(`无法分析属性 '${
|
|
624
|
+
throw new CustomError(`无法分析属性 '${prop.getName()}' 的类型。请检查属性声明。`, ErrorCodes.TYPE_NOT_SUPPORT);
|
|
522
625
|
}
|
|
626
|
+
return typeNode;
|
|
627
|
+
}
|
|
523
628
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
prop.hasModifier(SyntaxKind.ProtectedKeyword) ? PropertyVisibility.PROTECTED :
|
|
534
|
-
PropertyVisibility.PUBLIC;
|
|
535
|
-
|
|
536
|
-
const constructorParam = constructorParams.find((param) => param.name === name);
|
|
537
|
-
|
|
538
|
-
// 计算构造函数参数的可选性(用于后续验证)
|
|
539
|
-
const isParamOptional = constructorParam
|
|
540
|
-
? constructorParam.isOptional ||
|
|
541
|
-
constructorParam.defaultValue !== undefined ||
|
|
542
|
-
this.hasUndefinedInUnion(constructorParam.type)
|
|
543
|
-
: true;
|
|
629
|
+
private static getPropertyVisibility(prop: PropertyDeclaration | PropertySignature): PropertyVisibility {
|
|
630
|
+
if (prop.hasModifier(SyntaxKind.PrivateKeyword)) {
|
|
631
|
+
return PropertyVisibility.PRIVATE;
|
|
632
|
+
}
|
|
633
|
+
if (prop.hasModifier(SyntaxKind.ProtectedKeyword)) {
|
|
634
|
+
return PropertyVisibility.PROTECTED;
|
|
635
|
+
}
|
|
636
|
+
return PropertyVisibility.PUBLIC;
|
|
637
|
+
}
|
|
544
638
|
|
|
545
|
-
|
|
639
|
+
private static calculateIsMust(
|
|
640
|
+
prop: PropertyDeclaration | PropertySignature,
|
|
641
|
+
type: TypeStructure,
|
|
642
|
+
constructorParam: ConstructorParam | undefined,
|
|
643
|
+
decorators: PropertyDecorators | undefined,
|
|
644
|
+
defaultValue: string | undefined
|
|
645
|
+
): boolean {
|
|
546
646
|
if (constructorParam) {
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
// 3. 类型字符串中包含undefined作为联合类型成员
|
|
552
|
-
isMust = !isParamOptional;
|
|
553
|
-
} else {
|
|
554
|
-
// 属性不在构造函数中,判断属性本身的必要性
|
|
555
|
-
isMust = decorators?.isRequired || !this.isOptional(prop, defaultValue, type);
|
|
647
|
+
const isParamOptional = constructorParam.isOptional ||
|
|
648
|
+
constructorParam.defaultValue !== undefined ||
|
|
649
|
+
this.hasUndefinedInUnion(constructorParam.type);
|
|
650
|
+
return !isParamOptional;
|
|
556
651
|
}
|
|
652
|
+
return decorators?.isRequired || !this.isOptional(prop, defaultValue, type);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
private static validateTransientConstructorParam(name: string, constructorParam: ConstructorParam): void {
|
|
656
|
+
const isParamOptional = constructorParam.isOptional ||
|
|
657
|
+
constructorParam.defaultValue !== undefined ||
|
|
658
|
+
this.hasUndefinedInUnion(constructorParam.type);
|
|
557
659
|
|
|
558
|
-
|
|
559
|
-
// 检查构造函数参数是否可选或有默认值
|
|
560
|
-
if (decorators?.isTransient && constructorParam && !isParamOptional) {
|
|
660
|
+
if (!isParamOptional) {
|
|
561
661
|
throw new CustomError(
|
|
562
662
|
`@Transient 属性 '${name}' 的构造函数参数必须是可选的或有默认值。\n` +
|
|
563
663
|
`因为 @Transient 属性不参与序列化,反序列化时无法从外部数据获取值。\n` +
|
|
@@ -567,15 +667,6 @@ export class ClassAnalyzer {
|
|
|
567
667
|
ErrorCodes.TRANSIENT_ATTR_OPTIONAL
|
|
568
668
|
);
|
|
569
669
|
}
|
|
570
|
-
|
|
571
|
-
return {
|
|
572
|
-
name,
|
|
573
|
-
defaultValue,
|
|
574
|
-
decorators,
|
|
575
|
-
type,
|
|
576
|
-
isMust,
|
|
577
|
-
visibility
|
|
578
|
-
};
|
|
579
670
|
}
|
|
580
671
|
|
|
581
672
|
private static analyzePropertyType(prop: PropertyDeclaration | PropertySignature): TypeNode | undefined {
|
|
@@ -806,15 +897,14 @@ export class ClassAnalyzer {
|
|
|
806
897
|
*/
|
|
807
898
|
private static analyzePropertyDecorators(prop: PropertyDeclaration): PropertyDecorators {
|
|
808
899
|
const decorators = prop.getDecorators();
|
|
809
|
-
let isRequired
|
|
810
|
-
let isTransient
|
|
811
|
-
let isPlainValue
|
|
900
|
+
let isRequired = false;
|
|
901
|
+
let isTransient = false;
|
|
902
|
+
let isPlainValue = false;
|
|
812
903
|
let serialName: string | undefined;
|
|
813
904
|
let _with: string | undefined;
|
|
814
905
|
|
|
815
906
|
decorators.forEach((decorator) => {
|
|
816
907
|
const name = decorator.getName();
|
|
817
|
-
|
|
818
908
|
switch (name) {
|
|
819
909
|
case DecoratorConstants.SERIAL_NAME:
|
|
820
910
|
serialName = this.parseSerialNameOptions(decorator);
|
|
@@ -830,51 +920,66 @@ export class ClassAnalyzer {
|
|
|
830
920
|
break;
|
|
831
921
|
case DecoratorConstants.SERIALIZABLE:
|
|
832
922
|
_with = this.parseSerializableOptions(decorator)!.with;
|
|
923
|
+
break;
|
|
833
924
|
}
|
|
834
925
|
});
|
|
835
926
|
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
const conflictingDecorators: string[] = [];
|
|
839
|
-
if (isRequired) {
|
|
840
|
-
conflictingDecorators.push('@Required');
|
|
841
|
-
}
|
|
842
|
-
if (serialName) {
|
|
843
|
-
conflictingDecorators.push('@SerialName');
|
|
844
|
-
}
|
|
845
|
-
if (_with) {
|
|
846
|
-
conflictingDecorators.push('@Serializable');
|
|
847
|
-
}
|
|
848
|
-
if (isPlainValue) {
|
|
849
|
-
conflictingDecorators.push('@PlainValue');
|
|
850
|
-
}
|
|
927
|
+
this.checkTransientConflicts(prop.getName(), isTransient, isRequired, !!serialName, !!_with, isPlainValue);
|
|
928
|
+
this.checkPlainValueConflicts(prop.getName(), isPlainValue, !!_with);
|
|
851
929
|
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
930
|
+
return {
|
|
931
|
+
isRequired,
|
|
932
|
+
isTransient,
|
|
933
|
+
isPlainValue,
|
|
934
|
+
serialName,
|
|
935
|
+
with: _with
|
|
936
|
+
};
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
private static checkTransientConflicts(
|
|
940
|
+
propName: string,
|
|
941
|
+
isTransient: boolean,
|
|
942
|
+
isRequired: boolean,
|
|
943
|
+
hasSerialName: boolean,
|
|
944
|
+
hasWith: boolean,
|
|
945
|
+
isPlainValue: boolean
|
|
946
|
+
): void {
|
|
947
|
+
if (!isTransient) {
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
const conflictingDecorators: string[] = [];
|
|
952
|
+
if (isRequired) {
|
|
953
|
+
conflictingDecorators.push('@Required');
|
|
954
|
+
}
|
|
955
|
+
if (hasSerialName) {
|
|
956
|
+
conflictingDecorators.push('@SerialName');
|
|
957
|
+
}
|
|
958
|
+
if (hasWith) {
|
|
959
|
+
conflictingDecorators.push('@Serializable');
|
|
960
|
+
}
|
|
961
|
+
if (isPlainValue) {
|
|
962
|
+
conflictingDecorators.push('@PlainValue');
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
if (conflictingDecorators.length > 0) {
|
|
966
|
+
throw new CustomError(
|
|
967
|
+
`属性 '${propName}' 的注解冲突:@Transient 与 ${conflictingDecorators.join(', ')} 不能同时使用。\n` +
|
|
968
|
+
`@Transient 表示跳过序列化,与其他序列化配置注解互斥。\n` +
|
|
969
|
+
`请移除 @Transient 或移除其他序列化注解。`,
|
|
970
|
+
ErrorCodes.ANNOTATION_CONFLICT
|
|
971
|
+
);
|
|
860
972
|
}
|
|
973
|
+
}
|
|
861
974
|
|
|
862
|
-
|
|
863
|
-
if (isPlainValue &&
|
|
975
|
+
private static checkPlainValueConflicts(propName: string, isPlainValue: boolean, hasWith: boolean): void {
|
|
976
|
+
if (isPlainValue && hasWith) {
|
|
864
977
|
throw new CustomError(
|
|
865
|
-
`属性 '${
|
|
978
|
+
`属性 '${propName}' 的注解冲突:@PlainValue 与 @Serializable(with:) 不能同时使用。\n` +
|
|
866
979
|
`@PlainValue 跳过类型转换直接使用原始值,与自定义序列化器冲突。\n` +
|
|
867
980
|
`请移除 @PlainValue 或移除自定义序列化器。`,
|
|
868
981
|
ErrorCodes.ANNOTATION_CONFLICT
|
|
869
982
|
);
|
|
870
983
|
}
|
|
871
|
-
|
|
872
|
-
return {
|
|
873
|
-
isRequired,
|
|
874
|
-
isTransient,
|
|
875
|
-
isPlainValue,
|
|
876
|
-
serialName,
|
|
877
|
-
with: _with
|
|
878
|
-
};
|
|
879
984
|
}
|
|
880
985
|
}
|