@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.
Files changed (195) hide show
  1. package/LICENSE +177 -0
  2. package/README.md +253 -0
  3. package/dist/core/Types.d.ts +196 -0
  4. package/dist/core/Types.js +47 -0
  5. package/dist/core/analyzers/ClassAnalyzer.d.ts +23 -0
  6. package/dist/core/analyzers/ClassAnalyzer.js +480 -0
  7. package/dist/core/analyzers/CustomTypeAnalyzer.d.ts +21 -0
  8. package/dist/core/analyzers/CustomTypeAnalyzer.js +243 -0
  9. package/dist/core/analyzers/SendableMergeChecker.d.ts +9 -0
  10. package/dist/core/analyzers/SendableMergeChecker.js +100 -0
  11. package/dist/core/analyzers/SendableMergeabilityRegistry.d.ts +7 -0
  12. package/dist/core/analyzers/SendableMergeabilityRegistry.js +23 -0
  13. package/dist/core/constants/DecoratorConstants.d.ts +7 -0
  14. package/dist/core/constants/DecoratorConstants.js +13 -0
  15. package/dist/core/constants/PathConstants.d.ts +6 -0
  16. package/dist/core/constants/PathConstants.js +10 -0
  17. package/dist/core/constants/StringConstants.d.ts +83 -0
  18. package/dist/core/constants/StringConstants.js +87 -0
  19. package/dist/core/constants/index.d.ts +3 -0
  20. package/dist/core/constants/index.js +19 -0
  21. package/dist/core/handlers/BaseTypeHandler.d.ts +23 -0
  22. package/dist/core/handlers/BaseTypeHandler.js +57 -0
  23. package/dist/core/handlers/CustomClassHandler.d.ts +21 -0
  24. package/dist/core/handlers/CustomClassHandler.js +191 -0
  25. package/dist/core/handlers/DateHandler.d.ts +2 -0
  26. package/dist/core/handlers/DateHandler.js +60 -0
  27. package/dist/core/handlers/DecimalHandler.d.ts +2 -0
  28. package/dist/core/handlers/DecimalHandler.js +60 -0
  29. package/dist/core/handlers/EnumHandler.d.ts +2 -0
  30. package/dist/core/handlers/EnumHandler.js +89 -0
  31. package/dist/core/handlers/GenericContainerHandler.d.ts +2 -0
  32. package/dist/core/handlers/GenericContainerHandler.js +440 -0
  33. package/dist/core/handlers/GenericHandler.d.ts +18 -0
  34. package/dist/core/handlers/GenericHandler.js +92 -0
  35. package/dist/core/handlers/HandlerBootstrap.d.ts +2 -0
  36. package/dist/core/handlers/HandlerBootstrap.js +28 -0
  37. package/dist/core/handlers/ITypeHandler.d.ts +23 -0
  38. package/dist/core/handlers/ITypeHandler.js +8 -0
  39. package/dist/core/handlers/PrimitiveHandler.d.ts +2 -0
  40. package/dist/core/handlers/PrimitiveHandler.js +127 -0
  41. package/dist/core/handlers/TupleHandler.d.ts +2 -0
  42. package/dist/core/handlers/TupleHandler.js +98 -0
  43. package/dist/core/handlers/TypeHandlerRegistry.d.ts +20 -0
  44. package/dist/core/handlers/TypeHandlerRegistry.js +113 -0
  45. package/dist/core/handlers/UnionTypeHandler.d.ts +2 -0
  46. package/dist/core/handlers/UnionTypeHandler.js +263 -0
  47. package/dist/core/handlers/index.d.ts +2 -0
  48. package/dist/core/handlers/index.js +5 -0
  49. package/dist/core/import-rewrite/services/BuildProfileUpdater.d.ts +8 -0
  50. package/dist/core/import-rewrite/services/BuildProfileUpdater.js +92 -0
  51. package/dist/core/import-rewrite/services/ImportRewriteService.d.ts +9 -0
  52. package/dist/core/import-rewrite/services/ImportRewriteService.js +61 -0
  53. package/dist/core/import-rewrite/services/ImportTransformService.d.ts +15 -0
  54. package/dist/core/import-rewrite/services/ImportTransformService.js +109 -0
  55. package/dist/core/import-rewrite/strategies/ImportScanStrategy.d.ts +17 -0
  56. package/dist/core/import-rewrite/strategies/ImportScanStrategy.js +137 -0
  57. package/dist/core/import-rewrite/types/ImportRewriteTypes.d.ts +17 -0
  58. package/dist/core/import-rewrite/types/ImportRewriteTypes.js +2 -0
  59. package/dist/core/index.d.ts +9 -0
  60. package/dist/core/index.js +27 -0
  61. package/dist/core/interfaces/ITask.d.ts +7 -0
  62. package/dist/core/interfaces/ITask.js +2 -0
  63. package/dist/core/interfaces/ITaskContext.d.ts +11 -0
  64. package/dist/core/interfaces/ITaskContext.js +2 -0
  65. package/dist/core/interfaces/index.d.ts +2 -0
  66. package/dist/core/interfaces/index.js +2 -0
  67. package/dist/core/logger/Logger.d.ts +13 -0
  68. package/dist/core/logger/Logger.js +78 -0
  69. package/dist/core/services/CodeAnalysisService.d.ts +7 -0
  70. package/dist/core/services/CodeAnalysisService.js +43 -0
  71. package/dist/core/services/CodeGenerationEngine.d.ts +12 -0
  72. package/dist/core/services/CodeGenerationEngine.js +102 -0
  73. package/dist/core/services/CodeGenerationService/CodeGenerationService.d.ts +13 -0
  74. package/dist/core/services/CodeGenerationService/CodeGenerationService.js +110 -0
  75. package/dist/core/services/CodeGenerationService/generators/MergedSendableClassGenerator.d.ts +13 -0
  76. package/dist/core/services/CodeGenerationService/generators/MergedSendableClassGenerator.js +119 -0
  77. package/dist/core/services/CodeGenerationService/generators/OriginalClassGenerator.d.ts +21 -0
  78. package/dist/core/services/CodeGenerationService/generators/OriginalClassGenerator.js +224 -0
  79. package/dist/core/services/CodeGenerationService/generators/SendableClassGenerator.d.ts +24 -0
  80. package/dist/core/services/CodeGenerationService/generators/SendableClassGenerator.js +307 -0
  81. package/dist/core/services/CodeGenerationService/generators/SerializerGenerator.d.ts +28 -0
  82. package/dist/core/services/CodeGenerationService/generators/SerializerGenerator.js +259 -0
  83. package/dist/core/services/CodeGenerationService/generators/TempSerializerGenerator.d.ts +19 -0
  84. package/dist/core/services/CodeGenerationService/generators/TempSerializerGenerator.js +178 -0
  85. package/dist/core/services/CodeGenerationService/index.d.ts +1 -0
  86. package/dist/core/services/CodeGenerationService/index.js +5 -0
  87. package/dist/core/services/CodeGenerationService/shared/ImportManager.d.ts +27 -0
  88. package/dist/core/services/CodeGenerationService/shared/ImportManager.js +127 -0
  89. package/dist/core/template/HandlebarsTemplateEngine.d.ts +26 -0
  90. package/dist/core/template/HandlebarsTemplateEngine.js +226 -0
  91. package/dist/core/utils/ConfigManager.d.ts +10 -0
  92. package/dist/core/utils/ConfigManager.js +32 -0
  93. package/dist/core/utils/CustomError.d.ts +30 -0
  94. package/dist/core/utils/CustomError.js +37 -0
  95. package/dist/core/utils/DeepCopyUtil.d.ts +15 -0
  96. package/dist/core/utils/DeepCopyUtil.js +138 -0
  97. package/dist/core/utils/GenericTypeSubstitutionUtil.d.ts +9 -0
  98. package/dist/core/utils/GenericTypeSubstitutionUtil.js +68 -0
  99. package/dist/core/utils/SerializationPathUtil.d.ts +18 -0
  100. package/dist/core/utils/SerializationPathUtil.js +107 -0
  101. package/dist/core/utils/TsMorphUtil.d.ts +8 -0
  102. package/dist/core/utils/TsMorphUtil.js +34 -0
  103. package/dist/index.d.ts +4 -0
  104. package/dist/index.js +57 -0
  105. package/dist/json-plugin/JSONExecuteController.d.ts +13 -0
  106. package/dist/json-plugin/JSONExecuteController.js +103 -0
  107. package/dist/json-plugin/constants/TaskConstants.d.ts +7 -0
  108. package/dist/json-plugin/constants/TaskConstants.js +11 -0
  109. package/dist/json-plugin/interfaces/IModuleContext.d.ts +10 -0
  110. package/dist/json-plugin/interfaces/IModuleContext.js +2 -0
  111. package/dist/json-plugin/interfaces/ITargetContext.d.ts +8 -0
  112. package/dist/json-plugin/interfaces/ITargetContext.js +2 -0
  113. package/dist/json-plugin/interfaces/impl/ModuleContext.d.ts +12 -0
  114. package/dist/json-plugin/interfaces/impl/ModuleContext.js +24 -0
  115. package/dist/json-plugin/interfaces/impl/TargetContext.d.ts +23 -0
  116. package/dist/json-plugin/interfaces/impl/TargetContext.js +113 -0
  117. package/dist/json-plugin/tasks/BaseTask.d.ts +14 -0
  118. package/dist/json-plugin/tasks/BaseTask.js +53 -0
  119. package/dist/json-plugin/tasks/CleanTask.d.ts +8 -0
  120. package/dist/json-plugin/tasks/CleanTask.js +25 -0
  121. package/dist/json-plugin/tasks/CodeProcessingTask.d.ts +8 -0
  122. package/dist/json-plugin/tasks/CodeProcessingTask.js +26 -0
  123. package/dist/json-plugin/tasks/SyncTask.d.ts +8 -0
  124. package/dist/json-plugin/tasks/SyncTask.js +21 -0
  125. package/dist/json-plugin/tasks/WatchTask.d.ts +11 -0
  126. package/dist/json-plugin/tasks/WatchTask.js +102 -0
  127. package/package.json +46 -0
  128. package/src/core/Types.ts +356 -0
  129. package/src/core/analyzers/ClassAnalyzer.ts +824 -0
  130. package/src/core/analyzers/CustomTypeAnalyzer.ts +337 -0
  131. package/src/core/analyzers/SendableMergeChecker.ts +195 -0
  132. package/src/core/analyzers/SendableMergeabilityRegistry.ts +72 -0
  133. package/src/core/constants/DecoratorConstants.ts +27 -0
  134. package/src/core/constants/PathConstants.ts +31 -0
  135. package/src/core/constants/StringConstants.ts +152 -0
  136. package/src/core/constants/index.ts +21 -0
  137. package/src/core/handlers/BaseTypeHandler.ts +121 -0
  138. package/src/core/handlers/CustomClassHandler.ts +278 -0
  139. package/src/core/handlers/DateHandler.ts +75 -0
  140. package/src/core/handlers/DecimalHandler.ts +75 -0
  141. package/src/core/handlers/EnumHandler.ts +142 -0
  142. package/src/core/handlers/GenericContainerHandler.ts +621 -0
  143. package/src/core/handlers/GenericHandler.ts +130 -0
  144. package/src/core/handlers/HandlerBootstrap.ts +64 -0
  145. package/src/core/handlers/ITypeHandler.ts +133 -0
  146. package/src/core/handlers/PrimitiveHandler.ts +159 -0
  147. package/src/core/handlers/TupleHandler.ts +145 -0
  148. package/src/core/handlers/TypeHandlerRegistry.ts +236 -0
  149. package/src/core/handlers/UnionTypeHandler.ts +400 -0
  150. package/src/core/handlers/index.ts +18 -0
  151. package/src/core/import-rewrite/services/BuildProfileUpdater.ts +145 -0
  152. package/src/core/import-rewrite/services/ImportRewriteService.ts +129 -0
  153. package/src/core/import-rewrite/services/ImportTransformService.ts +200 -0
  154. package/src/core/import-rewrite/strategies/ImportScanStrategy.ts +303 -0
  155. package/src/core/import-rewrite/types/ImportRewriteTypes.ts +100 -0
  156. package/src/core/index.ts +31 -0
  157. package/src/core/interfaces/ITask.ts +23 -0
  158. package/src/core/interfaces/ITaskContext.ts +94 -0
  159. package/src/core/interfaces/index.ts +17 -0
  160. package/src/core/logger/Logger.ts +149 -0
  161. package/src/core/services/CodeAnalysisService.ts +67 -0
  162. package/src/core/services/CodeGenerationEngine.ts +181 -0
  163. package/src/core/services/CodeGenerationService/CodeGenerationService.ts +159 -0
  164. package/src/core/services/CodeGenerationService/generators/MergedSendableClassGenerator.ts +189 -0
  165. package/src/core/services/CodeGenerationService/generators/OriginalClassGenerator.ts +314 -0
  166. package/src/core/services/CodeGenerationService/generators/SendableClassGenerator.ts +421 -0
  167. package/src/core/services/CodeGenerationService/generators/SerializerGenerator.ts +392 -0
  168. package/src/core/services/CodeGenerationService/generators/TempSerializerGenerator.ts +277 -0
  169. package/src/core/services/CodeGenerationService/index.ts +16 -0
  170. package/src/core/services/CodeGenerationService/shared/ImportManager.ts +191 -0
  171. package/src/core/template/HandlebarsTemplateEngine.ts +282 -0
  172. package/src/core/utils/ConfigManager.ts +49 -0
  173. package/src/core/utils/CustomError.ts +51 -0
  174. package/src/core/utils/DeepCopyUtil.ts +185 -0
  175. package/src/core/utils/GenericTypeSubstitutionUtil.ts +136 -0
  176. package/src/core/utils/SerializationPathUtil.ts +142 -0
  177. package/src/core/utils/TsMorphUtil.ts +50 -0
  178. package/src/index.ts +81 -0
  179. package/src/json-plugin/JSONExecuteController.ts +134 -0
  180. package/src/json-plugin/constants/TaskConstants.ts +22 -0
  181. package/src/json-plugin/interfaces/IModuleContext.ts +27 -0
  182. package/src/json-plugin/interfaces/ITargetContext.ts +25 -0
  183. package/src/json-plugin/interfaces/impl/ModuleContext.ts +45 -0
  184. package/src/json-plugin/interfaces/impl/TargetContext.ts +196 -0
  185. package/src/json-plugin/tasks/BaseTask.ts +94 -0
  186. package/src/json-plugin/tasks/CleanTask.ts +36 -0
  187. package/src/json-plugin/tasks/CodeProcessingTask.ts +41 -0
  188. package/src/json-plugin/tasks/SyncTask.ts +33 -0
  189. package/src/json-plugin/tasks/WatchTask.ts +99 -0
  190. package/template/SerializerPerformanceTemplate.hbs +35 -0
  191. package/template/SerializerRegisterTemplate.hbs +10 -0
  192. package/template/SerializerStrictTemplate.hbs +89 -0
  193. package/template/SerializerTemplate.hbs +176 -0
  194. package/tsconfig.json +17 -0
  195. package/tslint.json +3 -0
@@ -0,0 +1,134 @@
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 { HvigorNode } from '@ohos/hvigor';
17
+ import { OhosHapContext, Target } from '@ohos/hvigor-ohos-plugin';
18
+ import { ITask, Logger, TurboTransJsonPluginOptions, ConfigManager, TempSerializerGenerator } from '../core';
19
+ import { TargetContextImpl } from './interfaces/impl/TargetContext';
20
+ import { ModuleContextImpl } from './interfaces/impl/ModuleContext';
21
+ import { IModuleContext } from './interfaces/IModuleContext';
22
+ import { CodeProcessingTask } from './tasks/CodeProcessingTask';
23
+ import { CleanTask } from './tasks/CleanTask';
24
+ import { SyncTask } from './tasks/SyncTask';
25
+ import { WatchTask } from './tasks/WatchTask';
26
+ import SerializationPathUtil from '../core/utils/SerializationPathUtil';
27
+ import { CustomError, ErrorCodes } from '../core/utils/CustomError';
28
+ import { TaskConstants } from './constants/TaskConstants';
29
+ import { TsMorphUtil } from '../core/utils/TsMorphUtil';
30
+ import { CustomTypeAnalyzer } from '../core/analyzers/CustomTypeAnalyzer';
31
+ import { SendableMergeabilityRegistry } from '../core/analyzers/SendableMergeabilityRegistry';
32
+
33
+ export class JSONExecuteController {
34
+ private readonly node: HvigorNode;
35
+ private readonly options: TurboTransJsonPluginOptions;
36
+ tasks: Set<ITask> = new Set();
37
+
38
+ constructor(node: HvigorNode, options: TurboTransJsonPluginOptions) {
39
+ this.node = node;
40
+ this.options = options;
41
+ }
42
+
43
+ execute(): void {
44
+ // 设置导入重写配置
45
+ const configManager = ConfigManager.getInstance();
46
+ configManager.setConfig(this.options);
47
+
48
+ // 在nodesEvaluated之后才能拿到moduleContext
49
+ const moduleContext: OhosHapContext = this.node.getContext(this.node.getAllPluginIds()[0]);
50
+
51
+ const context = new ModuleContextImpl(this.node, moduleContext);
52
+
53
+ context.initScanFiles(this.options.scanDir);
54
+
55
+ // 1. 初始化任务
56
+ this.initTasks();
57
+
58
+ // 2. 按照target注册任务
59
+ moduleContext.targets((target: Target) => {
60
+ this.registerHvigorTasks(target, context);
61
+ });
62
+ }
63
+
64
+ compile(): void {
65
+ // 编译结束后的清理逻辑由clean任务处理
66
+ }
67
+
68
+ private initTasks() {
69
+ this.tasks.add(new CodeProcessingTask());
70
+ this.tasks.add(new CleanTask());
71
+ this.tasks.add(new SyncTask());
72
+ this.tasks.add(new WatchTask());
73
+ }
74
+
75
+ private taskCleanUp(): void {
76
+ const sourceFiles = TsMorphUtil.getProject().getSourceFiles();
77
+ Logger.info(`code analysis Clearing ${sourceFiles.length} source files...`);
78
+ sourceFiles.forEach((sourceFile) => {
79
+ TsMorphUtil.getProject().removeSourceFile(sourceFile);
80
+ });
81
+ CustomTypeAnalyzer.getInstance().clearCache();
82
+ SendableMergeabilityRegistry.clear();
83
+ }
84
+
85
+ private registerHvigorTasks(target: Target, context: IModuleContext) {
86
+ const targetContext = new TargetContextImpl(target, context);
87
+ this.tasks.forEach((task) => {
88
+ this.node.registerTask({
89
+ name: task.taskName,
90
+ run: async () => {
91
+ try {
92
+ Logger.info(`${task.taskName} run`);
93
+ TempSerializerGenerator.getInstance().init(targetContext);
94
+ const result = task.run(targetContext);
95
+ // clean任务不需要生成序列化器
96
+ if (task.taskName !== TaskConstants.CLEAN_TASK) {
97
+ TempSerializerGenerator.getInstance().saveFile();
98
+ }
99
+ if (result instanceof Promise) {
100
+ await result;
101
+ }
102
+ if (task.taskName === TaskConstants.SYNC_TASK || task.taskName === TaskConstants.CODE_PROCESSING_TASK) {
103
+ this.taskCleanUp();
104
+ }
105
+ Logger.info(`${task.taskName} end`);
106
+ } catch (error) {
107
+ if (error instanceof CustomError) {
108
+ Logger.error(`Error: ${error.constructor.name}: ${error.code} ${error.message}\n${error.stack}`);
109
+ } else {
110
+ const err = error as Error;
111
+ Logger.error(`Error: ${err.constructor.name}: ${ErrorCodes.INTERNAL_ERROR} ${err.message}\n${err.stack}`);
112
+ }
113
+ this.taskCleanUp();
114
+ throw new Error('serialization execute failed');
115
+ }
116
+ },
117
+ dependencies: task.dependencies.map((dep) => `${target.getTargetName()}@${dep}`),
118
+ postDependencies: task.postDependencies.map((dep) => `${target.getTargetName()}@${dep}`)
119
+ });
120
+ });
121
+
122
+ const cleanTask = this.node.getTaskByName('clean');
123
+ if (cleanTask) {
124
+ cleanTask.beforeRun(() => {
125
+ const generatedRootDir = targetContext.getOutputRoot();
126
+
127
+ if (SerializationPathUtil.exist(generatedRootDir)) {
128
+ Logger.info(`Clean up the generated directory: ${generatedRootDir}`);
129
+ SerializationPathUtil.rmSync(generatedRootDir, { recursive: true, force: true });
130
+ }
131
+ });
132
+ }
133
+ }
134
+ }
@@ -0,0 +1,22 @@
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 TaskConstants {
17
+ static readonly SYNC_TASK = 'jsonSync';
18
+ static readonly CLEAN_TASK = 'jsonClean';
19
+ static readonly WATCH_TASK = 'jsonWatch';
20
+ static readonly CODE_PROCESSING_TASK = 'SerializationTask';
21
+ static readonly PRE_BUILD_TASK = 'PreBuild';
22
+ }
@@ -0,0 +1,27 @@
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 { HvigorNode } from '@ohos/hvigor'
17
+ import { ModuleBuildProfile } from '@ohos/hvigor-ohos-plugin/src/options/build/module-build-profile'
18
+ import { OhosModuleContext } from '@ohos/hvigor-ohos-plugin/src/plugin/context/plugin-context'
19
+
20
+ export interface IModuleContext {
21
+ readonly node: HvigorNode
22
+ readonly moduleContext: OhosModuleContext
23
+ readonly buildProfileOpt: ModuleBuildProfile.ModuleBuildOpt
24
+ readonly scanFiles: string[]
25
+
26
+ initScanFiles(scanDir: string[]): void
27
+ }
@@ -0,0 +1,25 @@
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 { Target } from '@ohos/hvigor-ohos-plugin'
17
+ import { IModuleContext } from './IModuleContext'
18
+ import { ITaskContext } from '../../core'
19
+
20
+ export interface ITargetContext extends ITaskContext {
21
+ readonly target: Target
22
+ readonly context: IModuleContext
23
+
24
+ getCurrentTargetScanFiles(): string[]
25
+ }
@@ -0,0 +1,45 @@
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 { HvigorNode } from '@ohos/hvigor'
17
+ import { IModuleContext } from '../IModuleContext'
18
+ import SerializationPathUtil from '../../../core/utils/SerializationPathUtil';
19
+ import { OhosHapContext } from '@ohos/hvigor-ohos-plugin';
20
+ import { ModuleBuildProfile } from '@ohos/hvigor-ohos-plugin/src/options/build/module-build-profile';
21
+
22
+ export class ModuleContextImpl implements IModuleContext {
23
+ readonly node: HvigorNode
24
+ readonly moduleContext: OhosHapContext
25
+ readonly scanFiles: string[] = []
26
+
27
+ constructor(node: HvigorNode, moduleContext: OhosHapContext) {
28
+ this.node = node
29
+ this.moduleContext = moduleContext
30
+ }
31
+
32
+ /**
33
+ * 获取模块的buildProfile配置
34
+ */
35
+ get buildProfileOpt(): ModuleBuildProfile.ModuleBuildOpt {
36
+ return this.moduleContext.getBuildProfileOpt();
37
+ }
38
+
39
+ initScanFiles(scanDir: string[]): void {
40
+ scanDir.forEach(dir => {
41
+ const scanPath = SerializationPathUtil.pathResolve(this.moduleContext.getModulePath(), dir)
42
+ this.scanFiles.push(...SerializationPathUtil.scanEtsFiles(scanPath))
43
+ })
44
+ }
45
+ }
@@ -0,0 +1,196 @@
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 { Target } from '@ohos/hvigor-ohos-plugin'
17
+ import { ModuleBuildProfile } from '@ohos/hvigor-ohos-plugin/src/options/build/module-build-profile'
18
+ import { PathConstants} from '../../../core';
19
+ import { IModuleContext } from '../IModuleContext'
20
+ import { ITargetContext } from '../ITargetContext'
21
+ import SerializationPathUtil from '../../../core/utils/SerializationPathUtil';
22
+ import { CustomError, ErrorCodes } from '../../../core/utils/CustomError';
23
+
24
+ export class TargetContextImpl implements ITargetContext {
25
+ readonly target: Target
26
+ readonly context: IModuleContext
27
+
28
+ private readonly DEFAULT_SOURCE_ROOT = PathConstants.DEFAULT_SOURCE_ROOT
29
+ private readonly GENERATED_ROOT = PathConstants.GENERATED_ROOT
30
+
31
+ constructor(target: Target, context: IModuleContext) {
32
+ this.target = target
33
+ this.context = context
34
+ }
35
+
36
+ private get targetOpt(): ModuleBuildProfile.ModuleTargetBuildOpt {
37
+ return this.context.buildProfileOpt.targets?.find(
38
+ (item: ModuleBuildProfile.ModuleTargetBuildOpt) => item.name === this.target.getTargetName())!;
39
+ }
40
+
41
+ /**
42
+ * 获取当前目标的扫描文件
43
+ * 从所有扫描文件中筛选出属于当前目标源代码根目录的文件
44
+ *
45
+ * @returns 当前目标的扫描文件路径数组,只包含属于当前目标源代码根目录的文件
46
+ */
47
+ getCurrentTargetScanFiles(): string[] {
48
+ // 获取当前目标的所有源代码根目录的绝对路径
49
+ const absoluteSourceRoots = this.getAbsoluteSourceRoots()
50
+
51
+ // 过滤扫描文件,只保留在当前目标源代码根目录下的文件
52
+ return this.context.scanFiles.filter(item => {
53
+ // 标准化文件路径格式,确保路径分隔符统一
54
+ const normalizedFilePath = SerializationPathUtil.normalize(item)
55
+
56
+ // 检查文件路径是否以任一源代码根目录开头
57
+ // 这样可以确保只处理属于当前目标的文件
58
+ return absoluteSourceRoots.some(sourceRoot => normalizedFilePath.startsWith(sourceRoot))
59
+ })
60
+ }
61
+
62
+ getModuleName(): string {
63
+ return this.context.moduleContext.getModuleName()
64
+ }
65
+
66
+ getModulePath(): string {
67
+ return this.context.moduleContext.getModulePath();
68
+ }
69
+
70
+ getPackageName(): string {
71
+ // 这里需要获取oh-package.json5中配置的name
72
+ const packagePath = SerializationPathUtil.pathResolve(this.getModulePath(), 'oh-package.json5')
73
+ const ohPackageObj = SerializationPathUtil.readJson5(packagePath)
74
+ return ohPackageObj.name
75
+ }
76
+
77
+ mapToOutputPath(absSourceFile: string): string {
78
+ const normalizedFilePath = SerializationPathUtil.normalize(absSourceFile)
79
+ const moduleRoot = this.context.moduleContext.getModulePath()
80
+ const srcMainAbs = SerializationPathUtil.normalize(SerializationPathUtil.pathResolve(moduleRoot, this.DEFAULT_SOURCE_ROOT))
81
+ const generatedRootAbs = this.getOutputRoot()
82
+
83
+ // 优先基于 src/main 做路径映射
84
+ if (normalizedFilePath.startsWith(srcMainAbs + SerializationPathUtil.sep)) {
85
+ const relativeToSrcMain = SerializationPathUtil.relative(srcMainAbs, normalizedFilePath)
86
+ return SerializationPathUtil.join(generatedRootAbs, relativeToSrcMain)
87
+ }
88
+
89
+ // 如果文件不在 src/main 下,退化为:找到匹配的任一 sourceRoot,映射到 generated 根下
90
+ const absoluteSourceRoots = this.getAbsoluteSourceRoots()
91
+ const matchedRoot = absoluteSourceRoots.find(root => normalizedFilePath.startsWith(root))
92
+ if (matchedRoot) {
93
+ const rel = SerializationPathUtil.relative(matchedRoot, normalizedFilePath)
94
+ return SerializationPathUtil.join(generatedRootAbs, rel)
95
+ }
96
+
97
+ // 否则直接放在 generated 根下,保持文件名
98
+ return SerializationPathUtil.join(generatedRootAbs, SerializationPathUtil.basename(normalizedFilePath))
99
+ }
100
+
101
+ getOutputRoot(): string {
102
+ const moduleRoot = this.context.moduleContext.getModulePath()
103
+ const absolutePath = SerializationPathUtil.pathResolve(moduleRoot, this.GENERATED_ROOT)
104
+ return SerializationPathUtil.normalize(absolutePath)
105
+ }
106
+
107
+ getRelativePath(absolutePath: string): string {
108
+ const outputRoot = this.getOutputRoot()
109
+ // 从输出根目录推断项目根目录
110
+ // 假设输出根目录为: <module>/src/generated
111
+ const moduleRoot = SerializationPathUtil.dirname(SerializationPathUtil.dirname(outputRoot))
112
+
113
+ try {
114
+ return SerializationPathUtil.relative(moduleRoot, absolutePath).replaceAll(SerializationPathUtil.sep, '/')
115
+ } catch (error) {
116
+ // 如果相对路径计算失败,返回原路径
117
+ return absolutePath
118
+ }
119
+ }
120
+
121
+ isValidOutputPath(outputPath: string): boolean {
122
+ try {
123
+ const outputRoot = this.getOutputRoot()
124
+ const resolvedOutputPath = SerializationPathUtil.pathResolve(outputPath)
125
+ const resolvedOutputRoot = SerializationPathUtil.pathResolve(outputRoot)
126
+
127
+ // 检查输出路径是否在输出根目录下
128
+ return resolvedOutputPath.startsWith(resolvedOutputRoot)
129
+ } catch (error) {
130
+ return false
131
+ }
132
+ }
133
+
134
+ ensureOutputDirectory(outputPath: string): void {
135
+ SerializationPathUtil.ensureDirSync(SerializationPathUtil.dirname(outputPath))
136
+ }
137
+
138
+ calculateSourceRootToModuleRoot(absolutePath: string): string {
139
+ // 处理Windows环境路径兼容性
140
+ const normalizedAbsolutePath = absolutePath.replaceAll(SerializationPathUtil.sep, '/')
141
+
142
+ // 获取所有源根路径并标准化为统一格式(以 / 结尾)
143
+ const sourceRoots = this.getAbsoluteSourceRoots().map(root =>
144
+ root.replaceAll(SerializationPathUtil.sep, '/').replace(/\/$/, '') + '/'
145
+ )
146
+
147
+ // 找到匹配的源根路径
148
+ const sourceRoot = sourceRoots.find(root => normalizedAbsolutePath.startsWith(root))
149
+
150
+ if (!sourceRoot) {
151
+ throw new CustomError(`无法找到匹配的源根路径: ${normalizedAbsolutePath}。可用的源根路径: ${sourceRoots.join(', ')}`, ErrorCodes.ROOT_DIRECTROY_NOT_FOUND)
152
+ }
153
+
154
+ // 计算相对路径,移除源根路径的尾部分隔符进行计算
155
+ const sourceRootForRelative = sourceRoot.replace(/\/$/, '')
156
+ const absolutePathForRelative = normalizedAbsolutePath.replace(/\/$/, '')
157
+
158
+ return SerializationPathUtil.relative(sourceRootForRelative, absolutePathForRelative).replaceAll(SerializationPathUtil.sep, '/')
159
+ }
160
+
161
+ private get targetSourceRoots(): string[] {
162
+ if (!this.targetOpt.source?.sourceRoots) {
163
+ return [this.DEFAULT_SOURCE_ROOT]
164
+ }
165
+ // 检查默认路径是否已包含在sourceRoots中
166
+ if (!this.targetOpt.source.sourceRoots.includes(this.DEFAULT_SOURCE_ROOT)) {
167
+ return [this.DEFAULT_SOURCE_ROOT, ...this.targetOpt.source.sourceRoots]
168
+ }
169
+ return this.targetOpt.source.sourceRoots
170
+ }
171
+
172
+ /**
173
+ * 获取当前目标的源代码根目录的绝对路径
174
+ * 将配置的相对路径源代码根目录转换为标准化的绝对路径
175
+ *
176
+ * @returns 标准化的绝对路径数组,每个路径都以路径分隔符结尾
177
+ */
178
+ private getAbsoluteSourceRoots(): string[] {
179
+ // 获取当前模块的根目录路径
180
+ const moduleRoot = this.context.moduleContext.getModulePath()
181
+
182
+ // 将每个相对路径源代码根目录转换为绝对路径
183
+ return this.targetSourceRoots.map((sourceRoot: string) => {
184
+ // 将相对路径解析为基于模块根目录的绝对路径
185
+ // path.resolve 会处理相对路径符号(如 ../ ./)
186
+ const absolutePath = SerializationPathUtil.pathResolve(moduleRoot, sourceRoot)
187
+
188
+ // 标准化路径格式,确保路径分隔符统一,处理多余的分隔符
189
+ const normalizedPath = SerializationPathUtil.normalize(absolutePath)
190
+
191
+ // 确保路径以分隔符结尾,避免路径匹配错误
192
+ // 例如:/project/src 不会错误匹配 /project/src2
193
+ return normalizedPath.endsWith(SerializationPathUtil.sep) ? normalizedPath : normalizedPath + SerializationPathUtil.sep
194
+ })
195
+ }
196
+ }
@@ -0,0 +1,94 @@
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 { ITask, Logger, ImportRewriteServiceImpl, ConfigManager, CodeGenerationEngine } from '../../core';
17
+ import { BuildProfileUpdaterImpl } from '../../core/import-rewrite/services/BuildProfileUpdater';
18
+ import { ITargetContext } from '../interfaces/ITargetContext';
19
+ import {logger} from 'handlebars';
20
+ import {TaskConstants} from '../constants/TaskConstants';
21
+ import SerializationPathUtil from '../../core/utils/SerializationPathUtil';
22
+
23
+ export abstract class BaseTask implements ITask {
24
+ abstract readonly taskName: string;
25
+ abstract readonly dependencies: string[];
26
+ abstract readonly postDependencies: string[];
27
+
28
+ protected readonly engine = new CodeGenerationEngine();
29
+ protected readonly buildProfileUpdater = new BuildProfileUpdaterImpl();
30
+
31
+ protected getScanFiles(context: ITargetContext): string[] {
32
+ const scanFiles = context.getCurrentTargetScanFiles();
33
+
34
+ if (!scanFiles || scanFiles.length === 0) {
35
+ Logger.debug('There are no source files to be processed');
36
+ return [];
37
+ }
38
+
39
+ return scanFiles;
40
+ }
41
+
42
+ protected processWithLogging(scanFiles: string[], context: ITargetContext, taskName: string) {
43
+ Logger.debug(`Start executing ${taskName} and process ${scanFiles.length} files`);
44
+
45
+ this.buildProfileUpdater.updateSourceRoots(context); // 更新builde-profile.json5中的sourceRoot
46
+
47
+ const processingResult = this.engine.processFilesUnified(scanFiles, context);
48
+
49
+ const totalGenerated = processingResult.generatedFiles.length;
50
+ const totalSkipped = processingResult.skippedFiles.length;
51
+
52
+ Logger.info(`${taskName} completed: generate ${totalGenerated} files and skip ${totalSkipped} files`);
53
+
54
+ if (totalGenerated > 0 && taskName === TaskConstants.SYNC_TASK) {
55
+ const config = context.context.buildProfileOpt;
56
+ const buildProfilePath = SerializationPathUtil.join(context.getModulePath(), 'build-profile.json5');
57
+
58
+ SerializationPathUtil.writeFileSync(buildProfilePath, JSON.stringify(config, null, 2));
59
+ }
60
+
61
+ this.executeImportRewrite(context, taskName);
62
+ return processingResult;
63
+ }
64
+
65
+ protected executeImportRewrite(context: ITargetContext, taskName: string): void {
66
+ const configManager = ConfigManager.getInstance();
67
+
68
+ Logger.debug(`Start to execute the import rewrite - ${taskName}`);
69
+
70
+ // 收集生成文件信息
71
+ const generatedFileInfos = this.engine.collectGeneratedFileInfos(context);
72
+
73
+ if (generatedFileInfos.length === 0) {
74
+ Logger.debug('No file was generated. Skip the import and rewrite');
75
+ return;
76
+ }
77
+
78
+ // 获取导入重写配置
79
+ const importRewriteConfig = configManager.getImportRewriteConfig();
80
+
81
+ // 执行导入重写
82
+ const importRewriteService = new ImportRewriteServiceImpl();
83
+ importRewriteService.rewriteImports(
84
+ context,
85
+ generatedFileInfos,
86
+ importRewriteConfig
87
+ );
88
+
89
+ Logger.info(`Import and rewrite completed - ${taskName}`);
90
+ }
91
+
92
+ abstract run(context: ITargetContext): void | Promise<void>;
93
+
94
+ }
@@ -0,0 +1,36 @@
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 { ITask, Logger } from '../../core';
17
+ import { TaskConstants } from '../constants/TaskConstants';
18
+ import { ITargetContext } from '../interfaces/ITargetContext';
19
+ import SerializationPathUtil from '../../core/utils/SerializationPathUtil';
20
+
21
+ export class CleanTask implements ITask {
22
+ readonly taskName: string = TaskConstants.CLEAN_TASK
23
+ readonly dependencies: string[] = [] // CLI任务无依赖
24
+ readonly postDependencies: string[] = [] // CLI任务无依赖
25
+
26
+ run(context: ITargetContext): void {
27
+ Logger.info('Start the code cleaning task')
28
+
29
+ const generatedRootDir = context.getOutputRoot()
30
+
31
+ if (SerializationPathUtil.exist(generatedRootDir)) {
32
+ Logger.info(`Clean up the generated directory: ${generatedRootDir}`)
33
+ SerializationPathUtil.rmSync(generatedRootDir, { recursive: true, force: true })
34
+ }
35
+ }
36
+ }
@@ -0,0 +1,41 @@
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 { Logger } from '../../core';
17
+ import { ITargetContext } from '../interfaces/ITargetContext';
18
+ import { TaskConstants } from '../constants/TaskConstants';
19
+ import { BaseTask } from './BaseTask';
20
+
21
+ export class CodeProcessingTask extends BaseTask {
22
+ readonly taskName: string = TaskConstants.CODE_PROCESSING_TASK;
23
+ readonly dependencies: string[] = [];
24
+ readonly postDependencies: string[] = [TaskConstants.PRE_BUILD_TASK];
25
+
26
+ run(context: ITargetContext): void {
27
+ const scanFiles = this.getScanFiles(context);
28
+ if (scanFiles.length === 0) {
29
+ return;
30
+ }
31
+
32
+ const processingResult = this.processWithLogging(scanFiles, context, TaskConstants.CODE_PROCESSING_TASK);
33
+
34
+ // 计算包含@Serializable类的文件统计
35
+ const serializableClassCount = processingResult.generatedFiles.reduce((sum, file) => {
36
+ return sum + (file.serializerCount || 0);
37
+ }, 0);
38
+
39
+ Logger.info(`Discovered @Serializable classes: ${serializableClassCount}`);
40
+ }
41
+ }
@@ -0,0 +1,33 @@
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 { TaskConstants } from '../constants/TaskConstants';
17
+ import { ITargetContext } from '../interfaces/ITargetContext';
18
+ import { BaseTask } from './BaseTask';
19
+
20
+ export class SyncTask extends BaseTask {
21
+ readonly taskName: string = TaskConstants.SYNC_TASK;
22
+ readonly dependencies: string[] = []; // CLI任务无依赖
23
+ readonly postDependencies: string[] = []; // CLI任务无依赖
24
+
25
+ run(context: ITargetContext): void {
26
+ const scanFiles = this.getScanFiles(context);
27
+ if (scanFiles.length === 0) {
28
+ return;
29
+ }
30
+
31
+ this.processWithLogging(scanFiles, context, TaskConstants.SYNC_TASK);
32
+ }
33
+ }