@hadss/turbo-trans-json-plugin 1.0.0-rc.0
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/LICENSE +177 -0
- package/README.md +253 -0
- package/dist/core/Types.d.ts +196 -0
- package/dist/core/Types.js +47 -0
- package/dist/core/analyzers/ClassAnalyzer.d.ts +23 -0
- package/dist/core/analyzers/ClassAnalyzer.js +480 -0
- package/dist/core/analyzers/CustomTypeAnalyzer.d.ts +21 -0
- package/dist/core/analyzers/CustomTypeAnalyzer.js +243 -0
- package/dist/core/analyzers/SendableMergeChecker.d.ts +9 -0
- package/dist/core/analyzers/SendableMergeChecker.js +100 -0
- package/dist/core/analyzers/SendableMergeabilityRegistry.d.ts +7 -0
- package/dist/core/analyzers/SendableMergeabilityRegistry.js +23 -0
- package/dist/core/constants/DecoratorConstants.d.ts +7 -0
- package/dist/core/constants/DecoratorConstants.js +13 -0
- package/dist/core/constants/PathConstants.d.ts +6 -0
- package/dist/core/constants/PathConstants.js +10 -0
- package/dist/core/constants/StringConstants.d.ts +83 -0
- package/dist/core/constants/StringConstants.js +87 -0
- package/dist/core/constants/index.d.ts +3 -0
- package/dist/core/constants/index.js +19 -0
- package/dist/core/handlers/BaseTypeHandler.d.ts +23 -0
- package/dist/core/handlers/BaseTypeHandler.js +57 -0
- package/dist/core/handlers/CustomClassHandler.d.ts +21 -0
- package/dist/core/handlers/CustomClassHandler.js +191 -0
- package/dist/core/handlers/DateHandler.d.ts +2 -0
- package/dist/core/handlers/DateHandler.js +60 -0
- package/dist/core/handlers/DecimalHandler.d.ts +2 -0
- package/dist/core/handlers/DecimalHandler.js +60 -0
- package/dist/core/handlers/EnumHandler.d.ts +2 -0
- package/dist/core/handlers/EnumHandler.js +89 -0
- package/dist/core/handlers/GenericContainerHandler.d.ts +2 -0
- package/dist/core/handlers/GenericContainerHandler.js +440 -0
- package/dist/core/handlers/GenericHandler.d.ts +18 -0
- package/dist/core/handlers/GenericHandler.js +92 -0
- package/dist/core/handlers/HandlerBootstrap.d.ts +2 -0
- package/dist/core/handlers/HandlerBootstrap.js +28 -0
- package/dist/core/handlers/ITypeHandler.d.ts +23 -0
- package/dist/core/handlers/ITypeHandler.js +8 -0
- package/dist/core/handlers/PrimitiveHandler.d.ts +2 -0
- package/dist/core/handlers/PrimitiveHandler.js +127 -0
- package/dist/core/handlers/TupleHandler.d.ts +2 -0
- package/dist/core/handlers/TupleHandler.js +98 -0
- package/dist/core/handlers/TypeHandlerRegistry.d.ts +20 -0
- package/dist/core/handlers/TypeHandlerRegistry.js +113 -0
- package/dist/core/handlers/UnionTypeHandler.d.ts +2 -0
- package/dist/core/handlers/UnionTypeHandler.js +263 -0
- package/dist/core/handlers/index.d.ts +2 -0
- package/dist/core/handlers/index.js +5 -0
- package/dist/core/import-rewrite/services/BuildProfileUpdater.d.ts +8 -0
- package/dist/core/import-rewrite/services/BuildProfileUpdater.js +92 -0
- package/dist/core/import-rewrite/services/ImportRewriteService.d.ts +9 -0
- package/dist/core/import-rewrite/services/ImportRewriteService.js +61 -0
- package/dist/core/import-rewrite/services/ImportTransformService.d.ts +15 -0
- package/dist/core/import-rewrite/services/ImportTransformService.js +109 -0
- package/dist/core/import-rewrite/strategies/ImportScanStrategy.d.ts +17 -0
- package/dist/core/import-rewrite/strategies/ImportScanStrategy.js +137 -0
- package/dist/core/import-rewrite/types/ImportRewriteTypes.d.ts +17 -0
- package/dist/core/import-rewrite/types/ImportRewriteTypes.js +2 -0
- package/dist/core/index.d.ts +9 -0
- package/dist/core/index.js +27 -0
- package/dist/core/interfaces/ITask.d.ts +7 -0
- package/dist/core/interfaces/ITask.js +2 -0
- package/dist/core/interfaces/ITaskContext.d.ts +11 -0
- package/dist/core/interfaces/ITaskContext.js +2 -0
- package/dist/core/interfaces/index.d.ts +2 -0
- package/dist/core/interfaces/index.js +2 -0
- package/dist/core/logger/Logger.d.ts +13 -0
- package/dist/core/logger/Logger.js +78 -0
- package/dist/core/services/CodeAnalysisService.d.ts +7 -0
- package/dist/core/services/CodeAnalysisService.js +43 -0
- package/dist/core/services/CodeGenerationEngine.d.ts +12 -0
- package/dist/core/services/CodeGenerationEngine.js +102 -0
- package/dist/core/services/CodeGenerationService/CodeGenerationService.d.ts +13 -0
- package/dist/core/services/CodeGenerationService/CodeGenerationService.js +110 -0
- package/dist/core/services/CodeGenerationService/generators/MergedSendableClassGenerator.d.ts +13 -0
- package/dist/core/services/CodeGenerationService/generators/MergedSendableClassGenerator.js +119 -0
- package/dist/core/services/CodeGenerationService/generators/OriginalClassGenerator.d.ts +21 -0
- package/dist/core/services/CodeGenerationService/generators/OriginalClassGenerator.js +224 -0
- package/dist/core/services/CodeGenerationService/generators/SendableClassGenerator.d.ts +24 -0
- package/dist/core/services/CodeGenerationService/generators/SendableClassGenerator.js +307 -0
- package/dist/core/services/CodeGenerationService/generators/SerializerGenerator.d.ts +28 -0
- package/dist/core/services/CodeGenerationService/generators/SerializerGenerator.js +259 -0
- package/dist/core/services/CodeGenerationService/generators/TempSerializerGenerator.d.ts +19 -0
- package/dist/core/services/CodeGenerationService/generators/TempSerializerGenerator.js +178 -0
- package/dist/core/services/CodeGenerationService/index.d.ts +1 -0
- package/dist/core/services/CodeGenerationService/index.js +5 -0
- package/dist/core/services/CodeGenerationService/shared/ImportManager.d.ts +27 -0
- package/dist/core/services/CodeGenerationService/shared/ImportManager.js +127 -0
- package/dist/core/template/HandlebarsTemplateEngine.d.ts +26 -0
- package/dist/core/template/HandlebarsTemplateEngine.js +226 -0
- package/dist/core/utils/ConfigManager.d.ts +10 -0
- package/dist/core/utils/ConfigManager.js +32 -0
- package/dist/core/utils/CustomError.d.ts +30 -0
- package/dist/core/utils/CustomError.js +37 -0
- package/dist/core/utils/DeepCopyUtil.d.ts +15 -0
- package/dist/core/utils/DeepCopyUtil.js +138 -0
- package/dist/core/utils/GenericTypeSubstitutionUtil.d.ts +9 -0
- package/dist/core/utils/GenericTypeSubstitutionUtil.js +68 -0
- package/dist/core/utils/SerializationPathUtil.d.ts +18 -0
- package/dist/core/utils/SerializationPathUtil.js +107 -0
- package/dist/core/utils/TsMorphUtil.d.ts +8 -0
- package/dist/core/utils/TsMorphUtil.js +34 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +57 -0
- package/dist/json-plugin/JSONExecuteController.d.ts +13 -0
- package/dist/json-plugin/JSONExecuteController.js +103 -0
- package/dist/json-plugin/constants/TaskConstants.d.ts +7 -0
- package/dist/json-plugin/constants/TaskConstants.js +11 -0
- package/dist/json-plugin/interfaces/IModuleContext.d.ts +10 -0
- package/dist/json-plugin/interfaces/IModuleContext.js +2 -0
- package/dist/json-plugin/interfaces/ITargetContext.d.ts +8 -0
- package/dist/json-plugin/interfaces/ITargetContext.js +2 -0
- package/dist/json-plugin/interfaces/impl/ModuleContext.d.ts +12 -0
- package/dist/json-plugin/interfaces/impl/ModuleContext.js +24 -0
- package/dist/json-plugin/interfaces/impl/TargetContext.d.ts +23 -0
- package/dist/json-plugin/interfaces/impl/TargetContext.js +113 -0
- package/dist/json-plugin/tasks/BaseTask.d.ts +14 -0
- package/dist/json-plugin/tasks/BaseTask.js +53 -0
- package/dist/json-plugin/tasks/CleanTask.d.ts +8 -0
- package/dist/json-plugin/tasks/CleanTask.js +25 -0
- package/dist/json-plugin/tasks/CodeProcessingTask.d.ts +8 -0
- package/dist/json-plugin/tasks/CodeProcessingTask.js +26 -0
- package/dist/json-plugin/tasks/SyncTask.d.ts +8 -0
- package/dist/json-plugin/tasks/SyncTask.js +21 -0
- package/dist/json-plugin/tasks/WatchTask.d.ts +11 -0
- package/dist/json-plugin/tasks/WatchTask.js +102 -0
- package/package.json +46 -0
- package/src/core/Types.ts +356 -0
- package/src/core/analyzers/ClassAnalyzer.ts +824 -0
- package/src/core/analyzers/CustomTypeAnalyzer.ts +337 -0
- package/src/core/analyzers/SendableMergeChecker.ts +195 -0
- package/src/core/analyzers/SendableMergeabilityRegistry.ts +72 -0
- package/src/core/constants/DecoratorConstants.ts +27 -0
- package/src/core/constants/PathConstants.ts +31 -0
- package/src/core/constants/StringConstants.ts +152 -0
- package/src/core/constants/index.ts +21 -0
- package/src/core/handlers/BaseTypeHandler.ts +121 -0
- package/src/core/handlers/CustomClassHandler.ts +278 -0
- package/src/core/handlers/DateHandler.ts +75 -0
- package/src/core/handlers/DecimalHandler.ts +75 -0
- package/src/core/handlers/EnumHandler.ts +142 -0
- package/src/core/handlers/GenericContainerHandler.ts +621 -0
- package/src/core/handlers/GenericHandler.ts +130 -0
- package/src/core/handlers/HandlerBootstrap.ts +64 -0
- package/src/core/handlers/ITypeHandler.ts +133 -0
- package/src/core/handlers/PrimitiveHandler.ts +159 -0
- package/src/core/handlers/TupleHandler.ts +145 -0
- package/src/core/handlers/TypeHandlerRegistry.ts +236 -0
- package/src/core/handlers/UnionTypeHandler.ts +400 -0
- package/src/core/handlers/index.ts +18 -0
- package/src/core/import-rewrite/services/BuildProfileUpdater.ts +145 -0
- package/src/core/import-rewrite/services/ImportRewriteService.ts +129 -0
- package/src/core/import-rewrite/services/ImportTransformService.ts +200 -0
- package/src/core/import-rewrite/strategies/ImportScanStrategy.ts +303 -0
- package/src/core/import-rewrite/types/ImportRewriteTypes.ts +100 -0
- package/src/core/index.ts +31 -0
- package/src/core/interfaces/ITask.ts +23 -0
- package/src/core/interfaces/ITaskContext.ts +94 -0
- package/src/core/interfaces/index.ts +17 -0
- package/src/core/logger/Logger.ts +149 -0
- package/src/core/services/CodeAnalysisService.ts +67 -0
- package/src/core/services/CodeGenerationEngine.ts +181 -0
- package/src/core/services/CodeGenerationService/CodeGenerationService.ts +159 -0
- package/src/core/services/CodeGenerationService/generators/MergedSendableClassGenerator.ts +189 -0
- package/src/core/services/CodeGenerationService/generators/OriginalClassGenerator.ts +314 -0
- package/src/core/services/CodeGenerationService/generators/SendableClassGenerator.ts +421 -0
- package/src/core/services/CodeGenerationService/generators/SerializerGenerator.ts +392 -0
- package/src/core/services/CodeGenerationService/generators/TempSerializerGenerator.ts +277 -0
- package/src/core/services/CodeGenerationService/index.ts +16 -0
- package/src/core/services/CodeGenerationService/shared/ImportManager.ts +191 -0
- package/src/core/template/HandlebarsTemplateEngine.ts +282 -0
- package/src/core/utils/ConfigManager.ts +49 -0
- package/src/core/utils/CustomError.ts +51 -0
- package/src/core/utils/DeepCopyUtil.ts +185 -0
- package/src/core/utils/GenericTypeSubstitutionUtil.ts +136 -0
- package/src/core/utils/SerializationPathUtil.ts +142 -0
- package/src/core/utils/TsMorphUtil.ts +50 -0
- package/src/index.ts +81 -0
- package/src/json-plugin/JSONExecuteController.ts +134 -0
- package/src/json-plugin/constants/TaskConstants.ts +22 -0
- package/src/json-plugin/interfaces/IModuleContext.ts +27 -0
- package/src/json-plugin/interfaces/ITargetContext.ts +25 -0
- package/src/json-plugin/interfaces/impl/ModuleContext.ts +45 -0
- package/src/json-plugin/interfaces/impl/TargetContext.ts +196 -0
- package/src/json-plugin/tasks/BaseTask.ts +94 -0
- package/src/json-plugin/tasks/CleanTask.ts +36 -0
- package/src/json-plugin/tasks/CodeProcessingTask.ts +41 -0
- package/src/json-plugin/tasks/SyncTask.ts +33 -0
- package/src/json-plugin/tasks/WatchTask.ts +99 -0
- package/template/SerializerPerformanceTemplate.hbs +35 -0
- package/template/SerializerRegisterTemplate.hbs +10 -0
- package/template/SerializerStrictTemplate.hbs +89 -0
- package/template/SerializerTemplate.hbs +176 -0
- package/tsconfig.json +17 -0
- package/tslint.json +3 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025 Huawei Device Co., Ltd.
|
|
3
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License.
|
|
5
|
+
* You may obtain a copy of the License at
|
|
6
|
+
*
|
|
7
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
*
|
|
9
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
* See the License for the specific language governing permissions and
|
|
13
|
+
* limitations under the License.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import SerializationPathUtil from '../../../utils/SerializationPathUtil';
|
|
17
|
+
import { ITaskContext } from '../../..';
|
|
18
|
+
|
|
19
|
+
interface ImportInfo {
|
|
20
|
+
modulePath: string;
|
|
21
|
+
importedNames: string[];
|
|
22
|
+
isDefaultImport: boolean;
|
|
23
|
+
isNamespaceImport: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export class ImportManager {
|
|
27
|
+
private imports: Map<string, ImportInfo> = new Map();
|
|
28
|
+
private defaultImports: Map<string, string> = new Map();
|
|
29
|
+
// 跟踪类型名称到模块路径的映射,用于去重
|
|
30
|
+
private typeToModulePath: Map<string, string> = new Map();
|
|
31
|
+
// 路径映射器
|
|
32
|
+
private context: ITaskContext | null = null;
|
|
33
|
+
|
|
34
|
+
registerCoreImports(interfaceNames: string[]): void {
|
|
35
|
+
const coreModule = '@hadss/turbo-trans-core';
|
|
36
|
+
|
|
37
|
+
for (const interfaceName of interfaceNames) {
|
|
38
|
+
this.registerCustomImport(coreModule, interfaceName);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
registerJsonImports(names: string[]): void {
|
|
43
|
+
const jsonModule = '@hadss/turbo-trans-json';
|
|
44
|
+
|
|
45
|
+
for (const name of names) {
|
|
46
|
+
this.registerCustomImport(jsonModule, name);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
registerArkTSImports(names: string[]) {
|
|
51
|
+
const arkTSModule = '@kit.ArkTS';
|
|
52
|
+
|
|
53
|
+
for (const name of names) {
|
|
54
|
+
this.registerCustomImport(arkTSModule, name);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
registerCustomImport(modulePath: string, importedName: string): void {
|
|
59
|
+
// 智能去重:检查是否已有同名类型的更好路径
|
|
60
|
+
if (this.typeToModulePath.has(importedName)) {
|
|
61
|
+
const existingModulePath = this.typeToModulePath.get(importedName)!;
|
|
62
|
+
|
|
63
|
+
// 如果当前路径是模块化路径,而已存在的是相对路径,则替换
|
|
64
|
+
if (this.isModularPath(modulePath) && this.isRelativePath(existingModulePath)) {
|
|
65
|
+
// 移除旧的相对路径导入
|
|
66
|
+
this.removeImportFromModule(existingModulePath, importedName);
|
|
67
|
+
// 更新映射
|
|
68
|
+
this.typeToModulePath.set(importedName, modulePath);
|
|
69
|
+
} else if (this.isRelativePath(modulePath) && this.isModularPath(existingModulePath)) {
|
|
70
|
+
// 如果当前是相对路径,已存在的是模块化路径,则忽略当前注册
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
} else {
|
|
74
|
+
// 首次注册该类型
|
|
75
|
+
this.typeToModulePath.set(importedName, modulePath);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (this.imports.has(modulePath)) {
|
|
79
|
+
const existingInfo = this.imports.get(modulePath)!;
|
|
80
|
+
const names = new Set(existingInfo.importedNames);
|
|
81
|
+
names.add(importedName);
|
|
82
|
+
|
|
83
|
+
this.imports.set(modulePath, {
|
|
84
|
+
...existingInfo,
|
|
85
|
+
importedNames: Array.from(names)
|
|
86
|
+
});
|
|
87
|
+
} else {
|
|
88
|
+
this.imports.set(modulePath, {
|
|
89
|
+
modulePath,
|
|
90
|
+
importedNames: [importedName],
|
|
91
|
+
isDefaultImport: false,
|
|
92
|
+
isNamespaceImport: false
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
registerDefaultImport(modulePath: string, importedName: string): void {
|
|
98
|
+
if (this.defaultImports.has(modulePath)) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
this.defaultImports.set(modulePath, importedName);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* 判断是否为模块化路径(不以 . 或 / 开头,不是 @开头的包名)
|
|
107
|
+
*/
|
|
108
|
+
private isModularPath(modulePath: string): boolean {
|
|
109
|
+
return !modulePath.startsWith('.') &&
|
|
110
|
+
!modulePath.startsWith('/') &&
|
|
111
|
+
!modulePath.startsWith('@') &&
|
|
112
|
+
modulePath.includes('/');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
private isRelativePath(modulePath: string): boolean {
|
|
116
|
+
return modulePath.startsWith('./') || modulePath.startsWith('../');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
private removeImportFromModule(modulePath: string, importedName: string): void {
|
|
120
|
+
if (this.imports.has(modulePath)) {
|
|
121
|
+
const existingInfo = this.imports.get(modulePath)!;
|
|
122
|
+
const filteredNames = existingInfo.importedNames.filter(name => name !== importedName);
|
|
123
|
+
|
|
124
|
+
if (filteredNames.length === 0) {
|
|
125
|
+
// 如果该模块没有其他导入,则删除整个模块
|
|
126
|
+
this.imports.delete(modulePath);
|
|
127
|
+
} else {
|
|
128
|
+
// 否则只更新导入列表
|
|
129
|
+
this.imports.set(modulePath, {
|
|
130
|
+
...existingInfo,
|
|
131
|
+
importedNames: filteredNames
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
getImportDeclarations() {
|
|
138
|
+
const imports: Array<{ specifier: string, names: string[] }> = [];
|
|
139
|
+
for (const [specifier, infos] of this.imports) {
|
|
140
|
+
imports.push({ specifier: specifier, names: infos.importedNames });
|
|
141
|
+
}
|
|
142
|
+
return imports;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
getDefaultImport(): Array<{ specifier: string, name: string }> {
|
|
146
|
+
const defaultImports: Array<{ specifier: string, name: string }> = [];
|
|
147
|
+
for (const [specifier, info] of this.defaultImports) {
|
|
148
|
+
defaultImports.push({ specifier: specifier, name: info });
|
|
149
|
+
}
|
|
150
|
+
return defaultImports;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
clear(): void {
|
|
154
|
+
this.imports.clear();
|
|
155
|
+
this.typeToModulePath.clear();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* 设置路径映射器
|
|
160
|
+
*
|
|
161
|
+
* @param context 任务上下文
|
|
162
|
+
*/
|
|
163
|
+
setContext(context: ITaskContext): void {
|
|
164
|
+
this.context = context;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* 注册自定义类型导入(使用模块化路径)
|
|
169
|
+
* @param customTypeFilePath 自定义类型文件的绝对路径
|
|
170
|
+
* @param typeName 类型名称
|
|
171
|
+
*/
|
|
172
|
+
registerCustomTypeImport(customTypeFilePath: string, typeName: string): void {
|
|
173
|
+
// 使用路径映射器计算模块化路径
|
|
174
|
+
const modularImportPath = SerializationPathUtil.calculateModularImportPath(customTypeFilePath, this.context!);
|
|
175
|
+
|
|
176
|
+
this.registerCustomImport(modularImportPath, typeName);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* 注册临时序列化器导入(从 tempSerializer/index.ets)
|
|
181
|
+
* @param serializerName 序列化器名称(如 "PersonSerializer")
|
|
182
|
+
*/
|
|
183
|
+
registerTempSerializerImport(serializerName: string): void {
|
|
184
|
+
// 计算 tempSerializer 目录的模块化路径
|
|
185
|
+
const outputRoot = this.context!.getOutputRoot();
|
|
186
|
+
const tempSerializerPath = SerializationPathUtil.pathResolve(outputRoot, 'tempSerializer');
|
|
187
|
+
const modularImportPath = SerializationPathUtil.calculateModularImportPath(tempSerializerPath, this.context!);
|
|
188
|
+
|
|
189
|
+
this.registerCustomImport(modularImportPath, serializerName);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025 Huawei Device Co., Ltd.
|
|
3
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License.
|
|
5
|
+
* You may obtain a copy of the License at
|
|
6
|
+
*
|
|
7
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
*
|
|
9
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
* See the License for the specific language governing permissions and
|
|
13
|
+
* limitations under the License.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import * as Handlebars from 'handlebars';
|
|
17
|
+
import {
|
|
18
|
+
ClassAnalysis, ConstructorParam, DecoratorConstants, DeserializationMode, GenerationContext, InterfaceAnalysis,
|
|
19
|
+
PropertyAnalysis, PropertyDecorators, PropertyGenerationInfo, PropertyKind
|
|
20
|
+
} from '..';
|
|
21
|
+
import SerializationPathUtil from '../utils/SerializationPathUtil';
|
|
22
|
+
import { CustomError, ErrorCodes } from '../utils/CustomError';
|
|
23
|
+
|
|
24
|
+
export class HandlebarsTemplateEngine {
|
|
25
|
+
private serializationTemplate?: HandlebarsTemplateDelegate;
|
|
26
|
+
private serializationRegisterTemplate?: HandlebarsTemplateDelegate;
|
|
27
|
+
private readonly registerTemplatePath: string =
|
|
28
|
+
SerializationPathUtil.pathResolve(__dirname, '../../../template/SerializerRegisterTemplate.hbs');
|
|
29
|
+
|
|
30
|
+
private readonly performanceTemplatePath: string =
|
|
31
|
+
SerializationPathUtil.pathResolve(__dirname, '../../../template/SerializerPerformanceTemplate.hbs');
|
|
32
|
+
private readonly strictTemplatePath: string =
|
|
33
|
+
SerializationPathUtil.pathResolve(__dirname, '../../../template/SerializerStrictTemplate.hbs');
|
|
34
|
+
private readonly commonTemplatePath: string =
|
|
35
|
+
SerializationPathUtil.pathResolve(__dirname, '../../../template/SerializerTemplate.hbs');
|
|
36
|
+
|
|
37
|
+
constructor() {
|
|
38
|
+
this.registerBuiltinHelpers();
|
|
39
|
+
this.registerTemplate();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
render(templateName: string, context: GenerationContext): string {
|
|
43
|
+
const template = this.serializationTemplate;
|
|
44
|
+
|
|
45
|
+
if (!template) {
|
|
46
|
+
throw new CustomError(`Template '${templateName}' not found.`, ErrorCodes.TEMPLATE_NOT_FOUND);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return template({ deserializeTemplate: templateName, ...context });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
renderRegister(context: GenerationContext): string {
|
|
53
|
+
const template = this.serializationRegisterTemplate;
|
|
54
|
+
|
|
55
|
+
if (!template) {
|
|
56
|
+
throw new CustomError(`Template not found.`, ErrorCodes.TEMPLATE_NOT_FOUND);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return template(context);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
registerTemplate(): void {
|
|
63
|
+
try {
|
|
64
|
+
// 性能模式反序列化模板
|
|
65
|
+
const performanceTemplateContent = SerializationPathUtil.readFileSync(this.performanceTemplatePath).toString();
|
|
66
|
+
Handlebars.registerPartial(DeserializationMode.PERFORMANCE, performanceTemplateContent);
|
|
67
|
+
|
|
68
|
+
// 严格模式反序列化模板
|
|
69
|
+
const strictTemplateContent = SerializationPathUtil.readFileSync(this.strictTemplatePath).toString();
|
|
70
|
+
Handlebars.registerPartial(DeserializationMode.STRICT, strictTemplateContent);
|
|
71
|
+
|
|
72
|
+
const commonTemplateContent = SerializationPathUtil.readFileSync(this.commonTemplatePath).toString();
|
|
73
|
+
this.serializationTemplate = this.loadTemplateFromString(commonTemplateContent);
|
|
74
|
+
|
|
75
|
+
const registerTemplateContent = SerializationPathUtil.readFileSync(this.registerTemplatePath).toString();
|
|
76
|
+
this.serializationRegisterTemplate = this.loadTemplateFromString(registerTemplateContent);
|
|
77
|
+
} catch (error) {
|
|
78
|
+
throw new CustomError(`Failed to register template ${error instanceof Error ? error.message : String(error)}`,
|
|
79
|
+
ErrorCodes.TEMPLATE_REGISTER_FAIL);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
registerHelper(name: string, helper: Handlebars.HelperDelegate): void {
|
|
84
|
+
Handlebars.registerHelper(name, helper);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
loadTemplateFromString(template: string): HandlebarsTemplateDelegate {
|
|
88
|
+
return Handlebars.compile(template, { noEscape: true });
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private registerBuiltinHelpers(): void {
|
|
92
|
+
this.registerComparisonHelpers();
|
|
93
|
+
this.registerTypeCheckHelpers();
|
|
94
|
+
this.registerFormattingHelpers();
|
|
95
|
+
this.registerPropertyAnalysisHelpers();
|
|
96
|
+
this.registerConstructorHelpers();
|
|
97
|
+
this.registerLogicHelpers();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private registerComparisonHelpers(): void {
|
|
101
|
+
this.registerHelper('eq', (arg1: unknown, arg2: unknown) => {
|
|
102
|
+
return (arg1 === arg2);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
this.registerHelper('eqPrimitiveType', (kind: string) => {
|
|
106
|
+
const primitives = ['string', 'number', 'boolean', 'bigint', 'undefined', 'null'];
|
|
107
|
+
return primitives.includes(kind);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
this.registerHelper('eqEmptyType', (kind: string) => {
|
|
111
|
+
return ['null', 'undefined'].includes(kind);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
private registerTypeCheckHelpers(): void {
|
|
116
|
+
this.registerHelper('isNumericPropertyName', (propertyName: string) => {
|
|
117
|
+
return /^\d+$/.test(propertyName);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
this.registerHelper('containsUndefined', (typeText: string) => {
|
|
121
|
+
return typeText.includes('undefined');
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// 条件:All properties are basic type + No undefined + No union type + No @Transient + No @SerialName
|
|
125
|
+
this.registerHelper('canReturnDirectly', (classAnalysis: ClassAnalysis) => {
|
|
126
|
+
// 检查类装饰器是否包含非@Serializable装饰器
|
|
127
|
+
if (this.hasNonSerializableClassDecorator(classAnalysis)) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// 检查属性装饰器是否包含非@Serializable 装饰器
|
|
132
|
+
if (this.hasNonSerializablePropertyDecorators(classAnalysis)) {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const properties = classAnalysis.properties;
|
|
137
|
+
|
|
138
|
+
if (!Array.isArray(properties)) {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return properties.every(prop => this.isPropertyValidForDirectReturn(prop));
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
this.registerHelper('isClassType', (cls: ClassAnalysis | InterfaceAnalysis) => {
|
|
146
|
+
return !!(cls as ClassAnalysis).originalClass;
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private hasNonSerializableClassDecorator(classAnalysis: ClassAnalysis): boolean {
|
|
151
|
+
const classDecorators = classAnalysis.originalClass?.getDecorators() || [];
|
|
152
|
+
return classDecorators.some(decorator =>
|
|
153
|
+
decorator.getName() !== 'Serializable'
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
private hasNonSerializablePropertyDecorators(classAnalysis: ClassAnalysis): boolean {
|
|
158
|
+
const props = classAnalysis.originalClass?.getProperties();
|
|
159
|
+
if (!props || !Array.isArray(props)) {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
for (const prop of props) {
|
|
164
|
+
const decorators = prop.getDecorators();
|
|
165
|
+
if (decorators && Array.isArray(decorators)) {
|
|
166
|
+
const hasNonSerializableDecorator = decorators.some(decorator =>
|
|
167
|
+
!DecoratorConstants.SERIAL_DECORATORS.includes(decorator.getName())
|
|
168
|
+
);
|
|
169
|
+
if (hasNonSerializableDecorator) {
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
private isPropertyValidForDirectReturn(prop: PropertyAnalysis): boolean {
|
|
178
|
+
// 条件4: No @Transient annotation - 如果有 @Transient 属性,不能直接返回
|
|
179
|
+
if (prop.decorators?.isTransient) {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// 条件5: No @SerialName property - 如果有 @SerialName,不能直接返回
|
|
184
|
+
if (prop.decorators?.serialName && prop.decorators.serialName !== prop.name) {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// 条件6: No with property - 如果有 with,不能直接返回
|
|
189
|
+
if (prop.decorators?.with) {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// 条件1: All properties are basic type(null, boolean, number, string, bigint)
|
|
194
|
+
const kind = prop.type?.kind;
|
|
195
|
+
const primitives = ['null', 'boolean', 'number', 'string', 'bigint'];
|
|
196
|
+
if (!primitives.includes(kind)) {
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// 条件2: No `undefined` or `null` type property
|
|
201
|
+
if (kind === 'undefined' || kind === 'null') {
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// 条件3: No union type property - 检查是否为联合类型
|
|
206
|
+
if (kind === PropertyKind.UNION) {
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
private registerFormattingHelpers(): void {
|
|
214
|
+
this.registerHelper('formatGenericArgs', (args: string[]) => {
|
|
215
|
+
return Array.isArray(args) && args.length > 0 ? `<${args.join(', ')}>` : '';
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
this.registerHelper('formatPropertyName', (property: PropertyAnalysis) => {
|
|
219
|
+
return property.decorators?.serialName || property.name;
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
this.registerHelper('toLowerCase', (str: string) => {
|
|
223
|
+
return str.toLowerCase();
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
this.registerHelper('genericParametersString', (parameters: string[]) => {
|
|
227
|
+
return parameters.join(', ');
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
this.registerHelper('genericParametersESObjString', (parameters: string[]) => {
|
|
231
|
+
return parameters.map(() => 'ESObject').join(', ');
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
private registerPropertyAnalysisHelpers(): void {
|
|
236
|
+
this.registerHelper('getPropertyIndex',
|
|
237
|
+
(property: PropertyGenerationInfo, properties: PropertyGenerationInfo[]) => {
|
|
238
|
+
return properties.findIndex(p => p.analysis.name === property.analysis.name);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
this.registerHelper('postConstructorAssignments',
|
|
242
|
+
(constructorParam?: ConstructorParam[], properties?: PropertyGenerationInfo[]) => {
|
|
243
|
+
return properties?.filter(prop => {
|
|
244
|
+
return !constructorParam?.some((cp) => cp.name === prop.analysis.name);
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
private registerConstructorHelpers(): void {
|
|
250
|
+
this.registerHelper('getCtorProperties',
|
|
251
|
+
(constructorParam?: ConstructorParam[], properties?: PropertyGenerationInfo[]) => {
|
|
252
|
+
return constructorParam?.map(param => {
|
|
253
|
+
const prop = properties?.find(p => p.analysis.name === param.name);
|
|
254
|
+
// ✅ 允许构造函数参数包含@Transient属性
|
|
255
|
+
// 如果在properties中找不到,说明该属性被@Transient过滤掉了,跳过
|
|
256
|
+
if (!prop) {
|
|
257
|
+
return null; // 返回null,稍后模板中会过滤掉
|
|
258
|
+
}
|
|
259
|
+
return prop;
|
|
260
|
+
}).filter(p => p !== null); // 过滤掉null值(@Transient属性)
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
private registerLogicHelpers(): void {
|
|
265
|
+
// 逻辑与操作
|
|
266
|
+
this.registerHelper('and', (...args: unknown[]) => {
|
|
267
|
+
// args 的最后一个元素是 Handlebars 的 options 对象,需要排除
|
|
268
|
+
const values = args.slice(0, -1);
|
|
269
|
+
return values.every(val => !!val);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
// 逻辑非操作
|
|
273
|
+
this.registerHelper('not', (value: unknown) => {
|
|
274
|
+
return !value;
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
// 检查装饰器中是否有 @with
|
|
278
|
+
this.registerHelper('hasDecoratorWith', (decorators?: PropertyDecorators) => {
|
|
279
|
+
return !!(decorators?.with);
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025 Huawei Device Co., Ltd.
|
|
3
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License.
|
|
5
|
+
* You may obtain a copy of the License at
|
|
6
|
+
*
|
|
7
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
*
|
|
9
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
* See the License for the specific language governing permissions and
|
|
13
|
+
* limitations under the License.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { DeserializationMode, TurboTransJsonPluginOptions, PathConstants, ImportRewriteOptions } from '..';
|
|
17
|
+
|
|
18
|
+
export class ConfigManager {
|
|
19
|
+
private static instance: ConfigManager;
|
|
20
|
+
private config: TurboTransJsonPluginOptions | null = null;
|
|
21
|
+
|
|
22
|
+
private constructor() {}
|
|
23
|
+
|
|
24
|
+
public static getInstance(): ConfigManager {
|
|
25
|
+
if (!ConfigManager.instance) {
|
|
26
|
+
ConfigManager.instance = new ConfigManager();
|
|
27
|
+
}
|
|
28
|
+
return ConfigManager.instance;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public setConfig(config: TurboTransJsonPluginOptions): void {
|
|
32
|
+
this.config = config;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public getConfig(): TurboTransJsonPluginOptions {
|
|
36
|
+
return this.config || {
|
|
37
|
+
ignoreModuleNames: [],
|
|
38
|
+
scanDir: PathConstants.DEFAULT_SCAN_DIRS,
|
|
39
|
+
deserializationMode: DeserializationMode.PERFORMANCE,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public getImportRewriteConfig(): ImportRewriteOptions {
|
|
44
|
+
return this.config?.importRewrite || {
|
|
45
|
+
scanPaths: ['src/main/ets'],
|
|
46
|
+
preserveOriginalImports: false
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025 Huawei Device Co., Ltd.
|
|
3
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License.
|
|
5
|
+
* You may obtain a copy of the License at
|
|
6
|
+
*
|
|
7
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
*
|
|
9
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
* See the License for the specific language governing permissions and
|
|
13
|
+
* limitations under the License.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export class CustomError extends Error {
|
|
17
|
+
name: string;
|
|
18
|
+
code: ErrorCodes;
|
|
19
|
+
|
|
20
|
+
constructor(message: string, code: ErrorCodes) {
|
|
21
|
+
super(message);
|
|
22
|
+
this.name = 'CustomError';
|
|
23
|
+
this.code = code;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export enum ErrorCodes {
|
|
28
|
+
INTERNAL_ERROR = 3000, // 内部未知错误
|
|
29
|
+
TEMPLATE_NOT_FOUND = 3011, // 没有找到模板
|
|
30
|
+
TEMPLATE_REGISTER_FAIL = 3012, // 性能和严格模式模板注册失败
|
|
31
|
+
ROOT_DIRECTROY_NOT_FOUND = 3021, // 源码根目录查找失败
|
|
32
|
+
INVALID_OUTPUT_FAIL = 3022, // 模板文件输出路径无效
|
|
33
|
+
INVALID_FILE_CONTENT = 3023, // 查找文件内容无效,例如为空的时候
|
|
34
|
+
HANDLER_NOT_FOUND = 3031, // 数据类型对应handler处理器查找失败
|
|
35
|
+
CLASS_DECLARATION_NOT_FOUND = 3032, // 未查找到类对应ClassDeclaration(ts-morph库的概念,用于获取类的基本信息)
|
|
36
|
+
IS_NOT_ENUM = 3033, // 枚举解析器解析到非枚举类型
|
|
37
|
+
ENUM_STRUCTURE_NOT_FOUND = 3034, // 未查找到枚举类型对应structure解析结构
|
|
38
|
+
TYPE_NOT_SUPPORT = 3041, // 自定义类中属性字段类型不支持
|
|
39
|
+
LITERAL_NOT_SOPPORT = 3042, // 字面量类型不支持
|
|
40
|
+
TRANSIENT_ATTR_OPTIONAL = 3043, // @Transient装饰属性必须是可选,或者存在默认值
|
|
41
|
+
ANNOTATION_CONFLICT = 3044, // 同一个属性存在多个Serialization属性装饰器
|
|
42
|
+
UNEXPECTED_GENERIC = 3051, // 不支持的泛型定义
|
|
43
|
+
GENERIC_NOT_MATCH = 3052, // 定义的泛型个数不匹配
|
|
44
|
+
UNION_STRUCTURE_NOT_FOUND = 3061, // 未查找到联合类型对应structure解析结构
|
|
45
|
+
UNION_EXPEDTED_TWO = 3062, // 当联合类型一部分undefined或者nul时,泛型长度为2,另一部分可以支持带泛型参数的自定义class
|
|
46
|
+
UNION_INNER_NOT_FOUND = 3063, // 当联合类型一部分undefined或者nul时,泛型长度为2时,未查找到另一部分非null或者undefined定义
|
|
47
|
+
UNION_UNSUPPORTED_PATTERN = 3064, // 不支持的联合类型
|
|
48
|
+
UNION_EXPEDTED_THREE = 3065, // 当联合类型一部分undefined或者nul时,泛型长度为3,另一部分可以支持带泛型参数的自定义class
|
|
49
|
+
REWRITE_IMPORT_FAIL = 3071, // 重写源文件import失败
|
|
50
|
+
NOT_SENDABLE = 3072, // sendable类定义的属性费非sendable类型
|
|
51
|
+
}
|