@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
|
@@ -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
|
-
if (initializer.getKind() === ts_morph_1.SyntaxKind.TrueKeyword) {
|
|
137
|
-
generateSendable = true;
|
|
138
|
-
}
|
|
139
|
-
else if (initializer.getKind() === ts_morph_1.SyntaxKind.FalseKeyword) {
|
|
140
|
-
generateSendable = false;
|
|
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;
|
|
153
133
|
}
|
|
134
|
+
const propAssignment = prop.asKindOrThrow(ts_morph_1.SyntaxKind.PropertyAssignment);
|
|
135
|
+
const initializer = propAssignment.getInitializer();
|
|
136
|
+
if (!initializer) {
|
|
137
|
+
continue;
|
|
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
|
-
|
|
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;
|
|
189
|
+
}
|
|
190
|
+
const propAssignment = prop.asKindOrThrow(ts_morph_1.SyntaxKind.PropertyAssignment);
|
|
191
|
+
if (propAssignment.getName() !== 'name') {
|
|
192
|
+
continue;
|
|
180
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,24 +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 parentGenericTypeArgs = classAnalysis.inheritance.genericTypeArguments || [];
|
|
244
|
-
const resolvedParentGenericArgs = parentGenericTypeArgs.map(typeArg => {
|
|
245
|
-
const typeStructure = handlers_1.TypeHandlerRegistry.getInstance().parseType(typeArg, {
|
|
246
|
-
depth: 0,
|
|
247
|
-
genericParams: parentGenericParams
|
|
248
|
-
});
|
|
249
|
-
if (typeStructure.kind === Types_1.PropertyKind.GENERIC && genericMapping.has(typeStructure.sourceText)) {
|
|
250
|
-
return genericMapping.get(typeStructure.sourceText);
|
|
251
|
-
}
|
|
252
|
-
return typeArg;
|
|
253
|
-
});
|
|
254
|
-
Logger_1.Logger.debug(`[CollectAncestors] Resolved parent generic args: ${resolvedParentGenericArgs.map(a => a.getText()).join(', ')}`);
|
|
255
|
-
const ancestorProperties = this.collectAllAncestorProperties(classAnalysis.inheritance.baseClassAnalysis, resolvedParentGenericArgs, currentClassGenericParams);
|
|
256
|
-
allProperties.push(...ancestorProperties);
|
|
257
|
-
}
|
|
259
|
+
this.collectAncestorPropertiesRecursively(classAnalysis, parentGenericParams, genericMapping, currentClassGenericParams, allProperties);
|
|
258
260
|
return allProperties;
|
|
259
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
|
+
}
|
|
260
284
|
static extractConstructorParams(constructor) {
|
|
261
285
|
const parameters = [];
|
|
262
286
|
for (const param of constructor.getParameters()) {
|
|
@@ -271,49 +295,66 @@ class ClassAnalyzer {
|
|
|
271
295
|
}
|
|
272
296
|
static createPropertyAnalysis(prop, constructorParams, genericParameters) {
|
|
273
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) {
|
|
274
320
|
let typeNode = prop.getTypeNode();
|
|
275
321
|
if (!typeNode) {
|
|
276
322
|
typeNode = this.analyzePropertyType(prop);
|
|
277
323
|
}
|
|
278
324
|
if (!typeNode) {
|
|
279
|
-
throw new CustomError_1.CustomError(`无法分析属性 '${
|
|
325
|
+
throw new CustomError_1.CustomError(`无法分析属性 '${prop.getName()}' 的类型。请检查属性声明。`, CustomError_1.ErrorCodes.TYPE_NOT_SUPPORT);
|
|
280
326
|
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
const visibility = prop.hasModifier(ts_morph_1.SyntaxKind.PrivateKeyword) ? Types_1.PropertyVisibility.PRIVATE :
|
|
287
|
-
prop.hasModifier(ts_morph_1.SyntaxKind.ProtectedKeyword) ? Types_1.PropertyVisibility.PROTECTED :
|
|
288
|
-
Types_1.PropertyVisibility.PUBLIC;
|
|
289
|
-
const constructorParam = constructorParams.find((param) => param.name === name);
|
|
290
|
-
const isParamOptional = constructorParam
|
|
291
|
-
? constructorParam.isOptional ||
|
|
292
|
-
constructorParam.defaultValue !== undefined ||
|
|
293
|
-
this.hasUndefinedInUnion(constructorParam.type)
|
|
294
|
-
: true;
|
|
295
|
-
let isMust;
|
|
296
|
-
if (constructorParam) {
|
|
297
|
-
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;
|
|
298
332
|
}
|
|
299
|
-
|
|
300
|
-
|
|
333
|
+
if (prop.hasModifier(ts_morph_1.SyntaxKind.ProtectedKeyword)) {
|
|
334
|
+
return Types_1.PropertyVisibility.PROTECTED;
|
|
301
335
|
}
|
|
302
|
-
|
|
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) {
|
|
303
352
|
throw new CustomError_1.CustomError(`@Transient 属性 '${name}' 的构造函数参数必须是可选的或有默认值。\n` +
|
|
304
353
|
`因为 @Transient 属性不参与序列化,反序列化时无法从外部数据获取值。\n` +
|
|
305
354
|
`请修改构造函数参数为以下任一形式:\n` +
|
|
306
355
|
` 1. 可选参数: constructor(..., ${name}?: ${constructorParam.type})\n` +
|
|
307
356
|
` 2. 带默认值: constructor(..., ${name}: ${constructorParam.type} = defaultValue)`, CustomError_1.ErrorCodes.TRANSIENT_ATTR_OPTIONAL);
|
|
308
357
|
}
|
|
309
|
-
return {
|
|
310
|
-
name,
|
|
311
|
-
defaultValue,
|
|
312
|
-
decorators,
|
|
313
|
-
type,
|
|
314
|
-
isMust,
|
|
315
|
-
visibility
|
|
316
|
-
};
|
|
317
358
|
}
|
|
318
359
|
static analyzePropertyType(prop) {
|
|
319
360
|
const name = prop.getName();
|
|
@@ -472,33 +513,11 @@ class ClassAnalyzer {
|
|
|
472
513
|
break;
|
|
473
514
|
case constants_1.DecoratorConstants.SERIALIZABLE:
|
|
474
515
|
_with = this.parseSerializableOptions(decorator).with;
|
|
516
|
+
break;
|
|
475
517
|
}
|
|
476
518
|
});
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
if (isRequired) {
|
|
480
|
-
conflictingDecorators.push('@Required');
|
|
481
|
-
}
|
|
482
|
-
if (serialName) {
|
|
483
|
-
conflictingDecorators.push('@SerialName');
|
|
484
|
-
}
|
|
485
|
-
if (_with) {
|
|
486
|
-
conflictingDecorators.push('@Serializable');
|
|
487
|
-
}
|
|
488
|
-
if (isPlainValue) {
|
|
489
|
-
conflictingDecorators.push('@PlainValue');
|
|
490
|
-
}
|
|
491
|
-
if (conflictingDecorators.length > 0) {
|
|
492
|
-
throw new CustomError_1.CustomError(`属性 '${prop.getName()}' 的注解冲突:@Transient 与 ${conflictingDecorators.join(', ')} 不能同时使用。\n` +
|
|
493
|
-
`@Transient 表示跳过序列化,与其他序列化配置注解互斥。\n` +
|
|
494
|
-
`请移除 @Transient 或移除其他序列化注解。`, CustomError_1.ErrorCodes.ANNOTATION_CONFLICT);
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
if (isPlainValue && _with) {
|
|
498
|
-
throw new CustomError_1.CustomError(`属性 '${prop.getName()}' 的注解冲突:@PlainValue 与 @Serializable(with:) 不能同时使用。\n` +
|
|
499
|
-
`@PlainValue 跳过类型转换直接使用原始值,与自定义序列化器冲突。\n` +
|
|
500
|
-
`请移除 @PlainValue 或移除自定义序列化器。`, CustomError_1.ErrorCodes.ANNOTATION_CONFLICT);
|
|
501
|
-
}
|
|
519
|
+
this.checkTransientConflicts(prop.getName(), isTransient, isRequired, !!serialName, !!_with, isPlainValue);
|
|
520
|
+
this.checkPlainValueConflicts(prop.getName(), isPlainValue, !!_with);
|
|
502
521
|
return {
|
|
503
522
|
isRequired,
|
|
504
523
|
isTransient,
|
|
@@ -507,5 +526,35 @@ class ClassAnalyzer {
|
|
|
507
526
|
with: _with
|
|
508
527
|
};
|
|
509
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
|
+
}
|
|
510
559
|
}
|
|
511
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;
|
|
@@ -80,36 +80,44 @@ class CustomTypeAnalyzer {
|
|
|
80
80
|
const imports = sourceFile.getImportDeclarations();
|
|
81
81
|
for (const importDecl of imports) {
|
|
82
82
|
const namedImports = importDecl.getNamedImports();
|
|
83
|
+
const matchedImport = namedImports.find(ni => ni.getName() === targetName);
|
|
84
|
+
if (!matchedImport) {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
83
87
|
const specifierValue = importDecl.getModuleSpecifierValue();
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const moduleSpecifier = importDecl.getModuleSpecifierValue();
|
|
88
|
-
const resolvedPath = this.resolveImportPath(moduleSpecifier, sourceFile);
|
|
89
|
-
if (resolvedPath && SerializationPathUtil_1.default.exist(resolvedPath)) {
|
|
90
|
-
const externalSourceFile = this.getOrLoadSourceFile(resolvedPath);
|
|
91
|
-
if (externalSourceFile) {
|
|
92
|
-
const typeDetail = this.searchInSourceFile(targetName, externalSourceFile);
|
|
93
|
-
if (typeDetail) {
|
|
94
|
-
return {
|
|
95
|
-
typeName: targetName,
|
|
96
|
-
typeKind: typeDetail.kind,
|
|
97
|
-
importPath: specifierValue,
|
|
98
|
-
source: {
|
|
99
|
-
type: 'imported',
|
|
100
|
-
sourceFilePath: externalSourceFile.getFilePath(),
|
|
101
|
-
originalImport: specifierValue
|
|
102
|
-
},
|
|
103
|
-
details: typeDetail
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
88
|
+
const result = this.processImportMatch(importDecl, targetName, sourceFile, specifierValue);
|
|
89
|
+
if (result) {
|
|
90
|
+
return result;
|
|
109
91
|
}
|
|
110
92
|
}
|
|
111
93
|
return null;
|
|
112
94
|
}
|
|
95
|
+
processImportMatch(importDecl, targetName, sourceFile, specifierValue) {
|
|
96
|
+
const moduleSpecifier = importDecl.getModuleSpecifierValue();
|
|
97
|
+
const resolvedPath = this.resolveImportPath(moduleSpecifier, sourceFile);
|
|
98
|
+
if (!resolvedPath || !SerializationPathUtil_1.default.exist(resolvedPath)) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
const externalSourceFile = this.getOrLoadSourceFile(resolvedPath);
|
|
102
|
+
if (!externalSourceFile) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
const typeDetail = this.searchInSourceFile(targetName, externalSourceFile);
|
|
106
|
+
if (!typeDetail) {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
typeName: targetName,
|
|
111
|
+
typeKind: typeDetail.kind,
|
|
112
|
+
importPath: specifierValue,
|
|
113
|
+
source: {
|
|
114
|
+
type: 'imported',
|
|
115
|
+
sourceFilePath: externalSourceFile.getFilePath(),
|
|
116
|
+
originalImport: specifierValue
|
|
117
|
+
},
|
|
118
|
+
details: typeDetail
|
|
119
|
+
};
|
|
120
|
+
}
|
|
113
121
|
findInCrossModule(targetName, sourceFile) {
|
|
114
122
|
return null;
|
|
115
123
|
}
|
|
@@ -146,49 +154,46 @@ class CustomTypeAnalyzer {
|
|
|
146
154
|
let numericValue = 0;
|
|
147
155
|
const enumMembers = enumDecl.getMembers();
|
|
148
156
|
for (const member of enumMembers) {
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
if (initializer) {
|
|
154
|
-
const initText = initializer.getText().trim();
|
|
155
|
-
if (initText.startsWith('"') && initText.endsWith('"')) {
|
|
156
|
-
memberValue = initText.slice(1, -1);
|
|
157
|
-
literalValue = initText;
|
|
158
|
-
valueType = 'string';
|
|
159
|
-
}
|
|
160
|
-
else if (initText.startsWith("'") && initText.endsWith("'")) {
|
|
161
|
-
memberValue = initText.slice(1, -1);
|
|
162
|
-
literalValue = `"${memberValue}"`;
|
|
163
|
-
valueType = 'string';
|
|
164
|
-
}
|
|
165
|
-
else {
|
|
166
|
-
const numValue = parseInt(initText, 10);
|
|
167
|
-
if (!isNaN(numValue)) {
|
|
168
|
-
memberValue = numValue;
|
|
169
|
-
literalValue = numValue.toString();
|
|
170
|
-
numericValue = numValue + 1;
|
|
171
|
-
}
|
|
172
|
-
else {
|
|
173
|
-
memberValue = initText;
|
|
174
|
-
literalValue = `"${initText}"`;
|
|
175
|
-
valueType = 'string';
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
else {
|
|
180
|
-
memberValue = numericValue;
|
|
181
|
-
literalValue = numericValue.toString();
|
|
182
|
-
numericValue++;
|
|
157
|
+
const { memberValue, literalValue, type, nextValue } = this.parseEnumMember(member, numericValue);
|
|
158
|
+
numericValue = nextValue;
|
|
159
|
+
if (type === 'string') {
|
|
160
|
+
valueType = 'string';
|
|
183
161
|
}
|
|
184
162
|
members.push({
|
|
185
|
-
name:
|
|
163
|
+
name: member.getName(),
|
|
186
164
|
value: memberValue,
|
|
187
165
|
literalValue
|
|
188
166
|
});
|
|
189
167
|
}
|
|
190
168
|
return { kind: __1.TypeKind.ENUM, valueType, members };
|
|
191
169
|
}
|
|
170
|
+
parseEnumMember(member, numericValue) {
|
|
171
|
+
const initializer = member.getInitializer();
|
|
172
|
+
if (!initializer) {
|
|
173
|
+
return {
|
|
174
|
+
memberValue: numericValue,
|
|
175
|
+
literalValue: numericValue.toString(),
|
|
176
|
+
type: 'number',
|
|
177
|
+
nextValue: numericValue + 1
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
const initText = initializer.getText().trim();
|
|
181
|
+
if (initText.startsWith('"') && initText.endsWith('"')) {
|
|
182
|
+
return this.createStringEnumResult(initText.slice(1, -1), initText, numericValue);
|
|
183
|
+
}
|
|
184
|
+
if (initText.startsWith("'") && initText.endsWith("'")) {
|
|
185
|
+
const val = initText.slice(1, -1);
|
|
186
|
+
return this.createStringEnumResult(val, `"${val}"`, numericValue);
|
|
187
|
+
}
|
|
188
|
+
const numValue = parseInt(initText, 10);
|
|
189
|
+
if (!isNaN(numValue)) {
|
|
190
|
+
return { memberValue: numValue, literalValue: numValue.toString(), type: 'number', nextValue: numValue + 1 };
|
|
191
|
+
}
|
|
192
|
+
return this.createStringEnumResult(initText, `"${initText}"`, numericValue);
|
|
193
|
+
}
|
|
194
|
+
createStringEnumResult(memberValue, literalValue, numericValue) {
|
|
195
|
+
return { memberValue, literalValue, type: 'string', nextValue: numericValue };
|
|
196
|
+
}
|
|
192
197
|
parseClassDetails(classDecl) {
|
|
193
198
|
return {
|
|
194
199
|
kind: __1.TypeKind.CLASS,
|
|
@@ -11,4 +11,5 @@ DecoratorConstants.SERIAL_NAME = 'SerialName';
|
|
|
11
11
|
DecoratorConstants.REQUIRED = 'Required';
|
|
12
12
|
DecoratorConstants.TRANSIENT = 'Transient';
|
|
13
13
|
DecoratorConstants.PLAIN_VALUE = 'PlainValue';
|
|
14
|
-
DecoratorConstants.SERIAL_DECORATORS = [_a.SERIALIZABLE, _a.SERIAL_NAME, _a.REQUIRED, _a.TRANSIENT,
|
|
14
|
+
DecoratorConstants.SERIAL_DECORATORS = [_a.SERIALIZABLE, _a.SERIAL_NAME, _a.REQUIRED, _a.TRANSIENT,
|
|
15
|
+
_a.PLAIN_VALUE];
|
|
@@ -64,7 +64,7 @@ class BuildProfileUpdaterImpl {
|
|
|
64
64
|
}
|
|
65
65
|
updateSourceRootsConfig(config) {
|
|
66
66
|
let updated = false;
|
|
67
|
-
const generatedSourceRoot =
|
|
67
|
+
const generatedSourceRoot = './src/generated';
|
|
68
68
|
__1.Logger.info(`target.source.sourceRoots start`);
|
|
69
69
|
if (config.targets && Array.isArray(config.targets)) {
|
|
70
70
|
for (const target of config.targets) {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { ITask } from './ITask';
|
|
2
|
-
export { ITaskContext } from './ITaskContext';
|
|
1
|
+
export type { ITask } from './ITask';
|
|
2
|
+
export type { ITaskContext } from './ITaskContext';
|