@hadss/hmrouter-plugin 1.0.0-rc.1 → 1.0.0-rc.10

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 (59) hide show
  1. package/README.md +240 -17
  2. package/dist/HMRouterAnalyzer.d.ts +31 -0
  3. package/dist/HMRouterAnalyzer.js +293 -0
  4. package/dist/HMRouterHvigorPlugin.d.ts +15 -0
  5. package/dist/HMRouterHvigorPlugin.js +139 -0
  6. package/dist/HMRouterPluginConfig.d.ts +39 -0
  7. package/dist/HMRouterPluginConfig.js +73 -0
  8. package/dist/HMRouterPluginHandle.d.ts +23 -0
  9. package/dist/HMRouterPluginHandle.js +231 -0
  10. package/dist/Index.d.ts +4 -0
  11. package/dist/Index.js +124 -0
  12. package/dist/common/Constant.d.ts +27 -0
  13. package/{lib → dist}/common/Constant.js +0 -15
  14. package/dist/common/Logger.d.ts +13 -0
  15. package/{lib → dist}/common/Logger.js +17 -24
  16. package/dist/common/PluginModel.d.ts +50 -0
  17. package/{lib → dist}/common/PluginModel.js +2 -16
  18. package/dist/constants/CommonConstants.d.ts +39 -0
  19. package/dist/constants/CommonConstants.js +46 -0
  20. package/dist/constants/ConfigConstants.d.ts +11 -0
  21. package/dist/constants/ConfigConstants.js +15 -0
  22. package/dist/constants/TaskConstants.d.ts +9 -0
  23. package/dist/constants/TaskConstants.js +13 -0
  24. package/dist/store/PluginStore.d.ts +12 -0
  25. package/dist/store/PluginStore.js +19 -0
  26. package/dist/utils/ConfusionUtil.d.ts +4 -0
  27. package/dist/utils/ConfusionUtil.js +27 -0
  28. package/dist/utils/FileUtil.d.ts +11 -0
  29. package/dist/utils/FileUtil.js +20 -0
  30. package/dist/utils/ObfuscationUtil.d.ts +4 -0
  31. package/dist/utils/ObfuscationUtil.js +34 -0
  32. package/dist/utils/StringUtil.d.ts +3 -0
  33. package/dist/utils/StringUtil.js +18 -0
  34. package/dist/utils/TsAstUtil.d.ts +9 -0
  35. package/dist/utils/TsAstUtil.js +89 -0
  36. package/package.json +27 -13
  37. package/{viewBuilder.tpl → viewBuilder.ejs} +18 -11
  38. package/lib/HMRouterAnalyzer.js +0 -351
  39. package/lib/HMRouterAnalyzer.js.map +0 -1
  40. package/lib/HMRouterHvigorPlugin.js +0 -177
  41. package/lib/HMRouterHvigorPlugin.js.map +0 -1
  42. package/lib/HMRouterPluginConfig.js +0 -31
  43. package/lib/HMRouterPluginConfig.js.map +0 -1
  44. package/lib/HMRouterPluginHandle.js +0 -148
  45. package/lib/HMRouterPluginHandle.js.map +0 -1
  46. package/lib/Index.js +0 -143
  47. package/lib/Index.js.map +0 -1
  48. package/lib/common/Constant.js.map +0 -1
  49. package/lib/common/Logger.js.map +0 -1
  50. package/lib/common/PluginModel.js.map +0 -1
  51. package/src/HMRouterAnalyzer.ts +0 -375
  52. package/src/HMRouterHvigorPlugin.ts +0 -199
  53. package/src/HMRouterPluginConfig.ts +0 -44
  54. package/src/HMRouterPluginHandle.ts +0 -188
  55. package/src/Index.ts +0 -151
  56. package/src/common/Constant.ts +0 -49
  57. package/src/common/Logger.ts +0 -73
  58. package/src/common/PluginModel.ts +0 -83
  59. package/tsconfig.json +0 -14
package/README.md CHANGED
@@ -1,37 +1,260 @@
1
1
  ## 编译插件配置
2
2
 
3
- 1. 修改项目的`hvigor/hvigor-config.json`文件,加入路由编译插件
3
+ 1.修改项目的`hvigor/hvigor-config.json`文件,加入路由编译插件
4
+
4
5
  ```json5
5
6
  {
6
7
  "dependencies": {
7
- "@hadss/hmrouter-plugin": "^1.0.0-rc.0" // 使用npm仓版本号
8
+ "@hadss/hmrouter-plugin": "^1.0.0-rc.10" // 使用npm仓版本号
8
9
  },
9
10
  // ...其他配置
10
11
  }
11
12
  ```
12
13
 
13
- 2. 项目根目录或者模块目录创建路由编译插件配置文件`hmrouter_config.json`(推荐)
14
+ 2.在模块中引入路由编译插件,修改`hvigorfile.ts`
15
+
16
+ ```typescript
17
+ import { hapTasks } from "@ohos/hvigor-ohos-plugin";
18
+ import { hapPlugin } from "@hadss/hmrouter-plugin";
19
+
20
+ export default {
21
+ system: hapTasks,
22
+ plugins: [hapPlugin()], // 使用HMRouter框架中标签的模块均需要配置,与模块类型保持一致
23
+ };
24
+ ```
25
+
26
+ > 请使用与模块类型一致的插件方法,如果模块是 Har 则使用`harPlugin()`, 模块是 Hsp 则使用`hspPlugin()`
27
+
28
+ 3.项目根目录或者模块目录创建路由编译插件配置文件`hmrouter_config.json`(推荐)
29
+
14
30
  ```json5
15
31
  {
16
- "scanDir": ["src/main/ets/components","src/main/ets/interceptors"],
17
- // 如果不传则扫描src/main/ets目录,对代码进行全量扫描,建议配置可缩短编译耗时
18
- "saveGeneratedFile": false, // 默认为false,改成true则不删除编译产物
32
+ // 如果不配置则扫描src/main/ets目录,对代码进行全量扫描,如果配置则数组不能为空,建议配置指定目录可缩短编译耗时
33
+ "scanDir": [
34
+ "src/main/ets/components",
35
+ "src/main/ets/interceptors"
36
+ ],
37
+ // 默认为false,调试排除错误时可以改成true,不删除编译产物
38
+ "saveGeneratedFile": false,
39
+ // 默认为false,不自动配置混淆规则,只会生成hmrouter_obfuscation_rules.txt文件帮助开发者配置混淆文件;如果设置为true,会自动配置混淆规则,并删除hmrouter_obfuscation_rules.txt文件
40
+ "autoObfuscation": false,
41
+ // 默认模板文件,不配置时使用插件内置模板
42
+ "defaultPageTemplate": "./templates/defaultTemplate.ejs",
43
+ // 特殊页面模版文件,匹配原则支持文件通配符
44
+ "customPageTemplate": [
45
+ {
46
+ "srcPath": ["**/component/Home/**/*.ets"],
47
+ "templatePath": "./templates/home_shopping_template.ejs"
48
+ },
49
+ {
50
+ "srcPath": ["**/common/**/*.ets"],
51
+ "templatePath": "./templates/common_template.ejs"
52
+ },
53
+ {
54
+ "srcPath": ["**/live/**/*.ets"],
55
+ "templatePath": "./templates/live_template.ejs"
56
+ }
57
+ ]
19
58
  }
20
59
  ```
21
60
 
22
- 3. 在模块中引入路由编译插件,修改`hvigorfile.ts`
23
- ```typescript
24
- import { hapTasks } from '@ohos/hvigor-ohos-plugin';
25
- import { hapPlugin } from "@hadss/hmrouter-plugin";
61
+ ### hmrouter_config.json 配置
26
62
 
27
- export default {
28
- system: hapTasks,
29
- plugins: [hapPlugin()]
63
+ `hmrouter_config.json`文件用于配置该插件在编译期的行为
64
+
65
+ > 配置文件读取规则为 模块 > 工程 > 默认
66
+ >
67
+ > 优先使用本模块内的配置,如果没有配置,则找模块目录的上级目录(最多找三层目录,找到则停止),若找不到则使用默认配置
68
+
69
+ > 1.0.0-rc.6 版本开始,支持混淆配置`autoObfuscation`
70
+ >
71
+ > 1.0.0-rc.9 版本开始,支持自定义模版配置`customPageTemplate`
72
+
73
+ | 配置项 | 类型 | 是否必填 | 说明 |
74
+ |---------------------|---------|------|----------------------------------------------------------------------------------------------------------------------------------------|
75
+ | scanDir | array | 否 | 指定扫描当前模块路径,默认值为`src/main/ets` |
76
+ | saveGeneratedFile | boolean | 否 | 默认为 false,不保留插件自动生成的代码,如果需要保留,需要设置为 true |
77
+ | autoObfuscation | boolean | 否 | 默认为 false,不自动配置混淆规则,只会生成`hmrouter_obfuscation_rules.txt`文件帮助开发者配置混淆规则;如果设置为 true,会自动配置混淆规则,并在编译完成后删除`hmrouter_obfuscation_rules.txt`文件 |
78
+ | defaultPageTemplate | string | 否 | 默认模版路径,相对于hmrouter_config.json文件,例如:`./templates/defaultTemplate.ejs` |
79
+ | customPageTemplate | object | 否 | srcPath为匹配的代码文件路径,支持通配符,templatePath为模版路径,可以实现不同的代码使用不同的模版来生成 |
80
+
81
+ ## 自定义模板使用
82
+
83
+ 在 `HMRouterPlugin` 中,EJS模板用于生成动态页面或组件,你可以在模板文件中使用 EJS 的语法
84
+
85
+ > EJS语法可阅读参考[官网链接](https://github.com/mde/ejs)
86
+
87
+ **模板文件示例:**
88
+
89
+ ```ejs
90
+ import { <%= componentName %> } from '<%= importPath %>'
91
+
92
+ @Builder
93
+ export function <%= componentName %>Builder(name: string, param: Object) {
94
+ <%= componentName %>Generated()
95
+ }
96
+
97
+ @Component
98
+ export struct <%= componentName %>Generated {
99
+ private pageUrl: string = '<%= pageUrl %>'
100
+
101
+ build() {
102
+ NavDestination() {
103
+ <%= componentName %>()
104
+ }
105
+ <% if(dialog){ %>.mode(NavDestinationMode.DIALOG)<% } %>
106
+ .hideTitleBar(true)
107
+ }
30
108
  }
31
109
  ```
32
110
 
33
- > 如果是Har则使用`harPlugin()`, Hsp则使用`hspPlugin()`
34
-
35
- ## HMRouter使用
111
+ > 模板文件中至少需要包含`NavDestination`组件代码和相对应的`build`函数,缺少会导致编译失败或者页面白屏,插件中会内置一套默认模板,其中包含了**页面展示、生命周期注册、转场动画注册**
112
+
113
+ ### 默认模板介绍
114
+
115
+ 插件默认会根据如下模板来生成`NavDestination`页面代码,如有自定义模板的需求,建议先阅读内置模板的介绍在做更改,书写自定义模板时建议在内置模板基础上添加代码,删除内置模板相关代码可能会导致**编译失败、生命周期生效、转场动画失效等问题**。
116
+
117
+ **默认模版`viewBuilder.ejs`:**
118
+
119
+ ```ejs
120
+ import { <%= componentName %> } from '<%= importPath %>'
121
+ import { TemplateService, TranslateOption, ScaleOption, OpacityOption } from '@hadss/hmrouter'
122
+
123
+ @Builder
124
+ export function <%= componentName %>Builder(name: string, param: Object) {
125
+ <%= componentName %>Generated()
126
+ }
127
+
128
+ @Component
129
+ export struct <%= componentName %>Generated {
130
+ @State translateOption: TranslateOption = new TranslateOption()
131
+ @State scaleOption: ScaleOption = new ScaleOption()
132
+ @State opacityOption: OpacityOption = new OpacityOption()
133
+ private pageUrl: string = '<%= pageUrl %>'
134
+ private ndId: string = ''
135
+ private navigationId: string = ''
136
+
137
+ aboutToAppear(): void {
138
+ this.navigationId = this.queryNavigationInfo()!.navigationId;
139
+ TemplateService.aboutToAppear(this.navigationId, this.pageUrl, <%= dialog %>,
140
+ this.translateOption, this.scaleOption, this.opacityOption)
141
+ }
142
+
143
+ aboutToDisappear(): void {
144
+ TemplateService.aboutToDisappear(this.navigationId, this.pageUrl, this.ndId)
145
+ }
146
+
147
+ build() {
148
+ NavDestination() {
149
+ <%= componentName %>()
150
+ }
151
+ <% if(dialog){ %>.mode(NavDestinationMode.DIALOG)<% } %>
152
+ .hideTitleBar(true)
153
+ .gesture(PanGesture()
154
+ .onActionStart((event: GestureEvent) => {
155
+ TemplateService.interactiveStart(this.navigationId, this.ndId, event)
156
+ })
157
+ .onActionUpdate((event: GestureEvent) =>{
158
+ TemplateService.interactiveProgress(this.navigationId, this.ndId, event)
159
+ })
160
+ .onActionEnd((event: GestureEvent) =>{
161
+ TemplateService.interactiveFinish(this.navigationId, this.ndId, event)
162
+ })
163
+ )
164
+ .translate(this.translateOption)
165
+ .scale(this.scaleOption)
166
+ .opacity(this.opacityOption.opacity)
167
+ .onAppear(() => {
168
+ TemplateService.onAppear(this.navigationId, this.pageUrl, this.ndId)
169
+ })
170
+ .onDisAppear(() => {
171
+ TemplateService.onDisAppear(this.navigationId, this.pageUrl, this.ndId)
172
+ })
173
+ .onShown(() => {
174
+ TemplateService.onShown(this.navigationId, this.pageUrl, this.ndId)
175
+ })
176
+ .onHidden(() => {
177
+ TemplateService.onHidden(this.navigationId, this.pageUrl, this.ndId)
178
+ })
179
+ .onWillAppear(() => {
180
+ TemplateService.onWillAppear(this.navigationId, this.pageUrl)
181
+ })
182
+ .onWillDisappear(() => {
183
+ TemplateService.onWillDisappear(this.navigationId, this.pageUrl, this.ndId)
184
+ })
185
+ .onWillShow(() => {
186
+ TemplateService.onWillShow(this.navigationId, this.pageUrl, this.ndId)
187
+ })
188
+ .onWillHide(() => {
189
+ TemplateService.onWillHide(this.navigationId, this.pageUrl, this.ndId)
190
+ })
191
+ .onReady((navContext: NavDestinationContext) => {
192
+ this.ndId = navContext.navDestinationId!
193
+ TemplateService.onReady(this.navigationId, this.pageUrl, navContext)
194
+ })
195
+ .onBackPressed(() => {
196
+ return TemplateService.onBackPressed(this.navigationId, this.pageUrl, this.ndId)
197
+ })
198
+ }
199
+ }
200
+ ```
201
+
202
+ ### 模板变量
203
+
204
+ | 属性 | 描述 |
205
+ |-------------------|-----------------|
206
+ | pageUrl | 标签中配置的pageUrl的值 |
207
+ | importPath | 原组件的导入路径 |
208
+ | componentName | 原组件名 |
209
+ | dialog | 是否dialog页面 |
210
+ | generatorViewName | 生成的文件名 |
211
+
212
+ ### TemplateService内置模版方法
213
+
214
+ 该类中封装了一系列在模板中需要用到的注册、初始化、事件回调接口
215
+
216
+ | 接口 | 参数 | 返回值 | 接口描述 |
217
+ |----------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------|------|-----------------------|
218
+ | static aboutToAppear | navigationId: string,pageUrl: string, dialog: boolean, translateOption: TranslateOption, scaleOption: ScaleOption, opacityOption: OpacityOption | void | 注册接口,用于模板代码中注册动画与生命周期 |
219
+ | static aboutToDisappear | navigationId: string, pageUrl: string, ndId: string | void | 销毁,用户销毁一个页面的动画与生命周期实例 |
220
+ | static onDisAppear | navigationId: string, pageUrl: string, navId: string | void | NavDestination生命周期 |
221
+ | static onAppear | navigationId: string, pageUrl: string, navId: string | void | NavDestination生命周期 |
222
+ | static onShown | navigationId: string, pageUrl: string, navId: string | void | NavDestination生命周期 |
223
+ | static onHidden | navigationId: string, pageUrl: string, navId: string | void | NavDestination生命周期 |
224
+ | static onWillAppear | navigationId: string, pageUrl: string | void | NavDestination生命周期 |
225
+ | static onWillDisappear | navigationId: string, pageUrl: string, navId: string | void | NavDestination生命周期 |
226
+ | static onWillShow | navigationId: string, pageUrl: string, navId: string | void | NavDestination生命周期 |
227
+ | static onWillHide | navigationId: string, pageUrl: string, navId: string | void | NavDestination生命周期 |
228
+ | static onReady | navigationId: string, pageUrl: string, navContext: NavDestinationContext | void | NavDestination生命周期 |
229
+ | static onBackPressed | navigationId: string, pageUrl: string, navId: string | void | NavDestination生命周期 |
230
+ | static interactiveStart | navigationId: string, ndId: string, event: GestureEvent | void | 手势转场动画触发 |
231
+ | static interactiveFinish | navigationId: string, ndId: string, event: GestureEvent | void | 手势转场动画更新 |
232
+ | static interactiveProgress | navigationId: string, ndId: string, event: GestureEvent | void | 手势转场动画结束 |
233
+
234
+ ## 依赖系统版本
235
+
236
+ 最低版本
237
+
238
+ ```text
239
+ HarmonyOS NEXT Developer Beta5版本及以上
240
+ DevEco Studio 5.0.3.700及以上
241
+ hvigor 5.5.1及以上
242
+ ```
243
+
244
+ **推荐使用**
245
+
246
+ ```text
247
+ HarmonyOS NEXT Beta1版本及以上
248
+ DevEco Studio 5.0.3.800及以上
249
+ hvigor 5.7.3及以上
250
+ ```
251
+
252
+ > 低于DevEco Studio 5.0.3.800(hvigor 5.7.3)可能会导致远程的 HSP 中定义的 HMRouter 标签与路由表失效,会有如下 WARN 日志
253
+ >
254
+ > ```
255
+ > [HMRouterPlugin] Your DevEco Studio version less than 5.0.3.800, may cause remote hsp dependencies get failed
256
+ > ```
257
+
258
+ ## HMRouter路由框架使用
36
259
 
37
- 详见[@hadss/hmrouter](https://ohpm.openharmony.cn/#/cn/detail/@hadss%2Fhmrouter)
260
+ 详见[@hadss/hmrouter](https://ohpm.openharmony.cn/#/cn/detail/@hadss%2Fhmrouter)
@@ -0,0 +1,31 @@
1
+ import { AnalyzerResultLike } from './common/PluginModel';
2
+ import { HMRouterPluginConfig } from './HMRouterPluginConfig';
3
+ export declare class AnalyzerController {
4
+ private analyzeResult;
5
+ analyzeFile(sourceFilePath: string, config: HMRouterPluginConfig): void;
6
+ parseConstants(): void;
7
+ private parsePropertyValue;
8
+ getAnalyzeResultSet(): Set<AnalyzerResultLike>;
9
+ clearAnalyzeResultSet(): void;
10
+ }
11
+ export declare class AnalyzerService {
12
+ private readonly sourceFilePath;
13
+ private sourceFile;
14
+ private config;
15
+ private analyzerResultSet;
16
+ private importMap;
17
+ constructor(sourceFilePath: string, config: HMRouterPluginConfig);
18
+ start(): void;
19
+ getResult(): Set<AnalyzerResultLike>;
20
+ private analyzeImport;
21
+ private analyzeRouter;
22
+ private parseFileByLineOrder;
23
+ private analyzeComponent;
24
+ private addToResultSet;
25
+ private parseDecorator;
26
+ private parseDecoratorArguments;
27
+ private parseIdentifierPropertyValue;
28
+ private parsePropertyValue;
29
+ private getVariableFilePath;
30
+ private getOtherModuleVariableFilePath;
31
+ }
@@ -0,0 +1,293 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.AnalyzerService = exports.AnalyzerController = void 0;
7
+ const ts_morph_1 = require("ts-morph");
8
+ const Logger_1 = require("./common/Logger");
9
+ const TsAstUtil_1 = require("./utils/TsAstUtil");
10
+ const CommonConstants_1 = __importDefault(require("./constants/CommonConstants"));
11
+ const FileUtil_1 = __importDefault(require("./utils/FileUtil"));
12
+ const ConfigConstants_1 = __importDefault(require("./constants/ConfigConstants"));
13
+ const PluginStore_1 = __importDefault(require("./store/PluginStore"));
14
+ class AnalyzerController {
15
+ constructor() {
16
+ this.analyzeResult = new Set();
17
+ }
18
+ analyzeFile(sourceFilePath, config) {
19
+ let analyzerService = new AnalyzerService(sourceFilePath, config);
20
+ analyzerService.start();
21
+ analyzerService.getResult().forEach(item => {
22
+ item.pageSourceFile = sourceFilePath;
23
+ this.analyzeResult.add(item);
24
+ });
25
+ this.parseConstants();
26
+ }
27
+ parseConstants() {
28
+ this.analyzeResult.forEach((item) => {
29
+ Object.getOwnPropertyNames(item).forEach((key) => {
30
+ let propertyValue = Reflect.get(item, key);
31
+ propertyValue = this.parsePropertyValue(propertyValue);
32
+ if (propertyValue === '') {
33
+ Logger_1.Logger.error(Logger_1.PluginError.ERR_NOT_EMPTY_STRING);
34
+ throw new Error('constants value cannot be an empty string, filePath:' + item.pageSourceFile);
35
+ }
36
+ Reflect.set(item, key, propertyValue);
37
+ });
38
+ });
39
+ }
40
+ parsePropertyValue(propertyValue) {
41
+ if (propertyValue.type === 'constant') {
42
+ return TsAstUtil_1.TsAstUtil.parseConstantValue(TsAstUtil_1.TsAstUtil.getSourceFile(propertyValue.variableFilePath), propertyValue.variableName);
43
+ }
44
+ else if (propertyValue.type === 'object') {
45
+ return TsAstUtil_1.TsAstUtil.parseConstantValue(TsAstUtil_1.TsAstUtil.getSourceFile(propertyValue.variableFilePath), propertyValue.variableName, propertyValue.propertyName);
46
+ }
47
+ else if (propertyValue.type === 'array') {
48
+ return propertyValue.value.map((item) => {
49
+ return this.parsePropertyValue(item);
50
+ });
51
+ }
52
+ else {
53
+ return propertyValue;
54
+ }
55
+ }
56
+ getAnalyzeResultSet() {
57
+ return this.analyzeResult;
58
+ }
59
+ clearAnalyzeResultSet() {
60
+ this.analyzeResult.clear();
61
+ }
62
+ }
63
+ exports.AnalyzerController = AnalyzerController;
64
+ class AnalyzerService {
65
+ constructor(sourceFilePath, config) {
66
+ this.analyzerResultSet = new Set();
67
+ this.importMap = new Map();
68
+ this.sourceFilePath = sourceFilePath;
69
+ this.sourceFile = TsAstUtil_1.TsAstUtil.getSourceFile(sourceFilePath);
70
+ this.config = config;
71
+ }
72
+ start() {
73
+ this.analyzeImport();
74
+ this.analyzeRouter();
75
+ this.analyzeComponent();
76
+ this.parseFileByLineOrder();
77
+ }
78
+ getResult() {
79
+ let HMRouterNum = 0;
80
+ this.analyzerResultSet.forEach((analyzerResult) => {
81
+ if (analyzerResult.annotation === CommonConstants_1.default.ROUTER_ANNOTATION) {
82
+ HMRouterNum++;
83
+ }
84
+ });
85
+ if (HMRouterNum > 1) {
86
+ Logger_1.Logger.error(Logger_1.PluginError.ERR_REPEAT_ANNOTATION, this.sourceFilePath);
87
+ throw new Error(`File:${this.sourceFilePath} exists more than one @HMRouter annotation`);
88
+ }
89
+ return this.analyzerResultSet;
90
+ }
91
+ analyzeImport() {
92
+ this.sourceFile.getImportDeclarations().forEach(importDeclaration => {
93
+ const moduleSpecifier = importDeclaration.getModuleSpecifierValue();
94
+ const namedImports = importDeclaration.getNamedImports().map(namedImport => namedImport.getName());
95
+ const defaultImport = importDeclaration.getDefaultImport()?.getText();
96
+ const namespaceImport = importDeclaration.getNamespaceImport()?.getText();
97
+ const importNames = [];
98
+ if (namedImports.length > 0) {
99
+ importNames.push(...namedImports);
100
+ }
101
+ if (defaultImport) {
102
+ importNames.push(defaultImport);
103
+ }
104
+ if (namespaceImport) {
105
+ importNames.push(namespaceImport);
106
+ }
107
+ if (importNames.length > 0) {
108
+ this.importMap.set(moduleSpecifier, importNames);
109
+ }
110
+ });
111
+ }
112
+ analyzeRouter() {
113
+ let viewNameArr = this.sourceFile.getChildrenOfKind(ts_morph_1.SyntaxKind.ExpressionStatement).map((node) => {
114
+ return node.getText();
115
+ }).filter((text) => {
116
+ return text != 'struct';
117
+ });
118
+ this.sourceFile.getChildrenOfKind(ts_morph_1.SyntaxKind.MissingDeclaration).forEach((node, index) => {
119
+ node.getChildrenOfKind(ts_morph_1.SyntaxKind.Decorator).forEach((decorator) => {
120
+ this.addToResultSet(decorator, viewNameArr[index]);
121
+ });
122
+ });
123
+ this.sourceFile.getExportAssignments().forEach((exportAssignment, index) => {
124
+ exportAssignment.getDescendantsOfKind(ts_morph_1.SyntaxKind.Decorator).forEach((decorator) => {
125
+ let result = this.addToResultSet(decorator, viewNameArr[index]);
126
+ result.isDefaultExport = true;
127
+ });
128
+ });
129
+ }
130
+ parseFileByLineOrder() {
131
+ const statements = this.sourceFile.getStatements();
132
+ const sortedStatements = statements.sort((a, b) => a.getStart() - b.getStart());
133
+ let HMRouterExists = false;
134
+ sortedStatements.forEach(statement => {
135
+ if (statement.getKind() === ts_morph_1.SyntaxKind.MissingDeclaration && statement.getText().includes('HMRouter')) {
136
+ HMRouterExists = true;
137
+ }
138
+ if (statement.getKind() === ts_morph_1.SyntaxKind.Block && HMRouterExists) {
139
+ HMRouterExists = false;
140
+ let reg = new RegExp(/NavDestination\s*\(\s*\)/);
141
+ let text = statement.getText();
142
+ const cleanedCodeBlock = text
143
+ .replace(/(["'`]).*?\1/g, '')
144
+ .replace(/\/\/.*|\/\*[\s\S]*?\*\//g, '');
145
+ if (reg.test(cleanedCodeBlock)) {
146
+ Logger_1.Logger.error(Logger_1.PluginError.ERR_WRONG_DECORATION);
147
+ throw new Error('NavDestination is not allowed in HMRouter, filePath:' + this.sourceFilePath);
148
+ }
149
+ }
150
+ });
151
+ }
152
+ analyzeComponent() {
153
+ this.sourceFile.getClasses().forEach((cls) => {
154
+ cls.getDecorators().forEach((decorator) => {
155
+ if (this.config.annotation.includes(decorator.getName())) {
156
+ this.addToResultSet(decorator, cls.getName());
157
+ }
158
+ });
159
+ cls.getMethods().forEach((method) => {
160
+ method.getDecorators().forEach((decorator) => {
161
+ let serviceResult = this.addToResultSet(decorator, cls.getName());
162
+ serviceResult.functionName = method.getName();
163
+ });
164
+ });
165
+ });
166
+ }
167
+ addToResultSet(decorator, componentName) {
168
+ let decoratorResult = this.parseDecorator(decorator);
169
+ decoratorResult.name = componentName;
170
+ if (decoratorResult.annotation) {
171
+ this.analyzerResultSet.add(decoratorResult);
172
+ }
173
+ return decoratorResult;
174
+ }
175
+ parseDecorator(decorator) {
176
+ let decoratorResult = {};
177
+ let decoratorName = decorator.getName();
178
+ if (this.config.annotation.includes(decoratorName)) {
179
+ decoratorResult.annotation = decoratorName;
180
+ let args = this.parseDecoratorArguments(decorator);
181
+ Object.assign(decoratorResult, args);
182
+ }
183
+ return decoratorResult;
184
+ }
185
+ parseDecoratorArguments(decorator) {
186
+ let argResult = {};
187
+ decorator.getArguments().map(arg => {
188
+ const objLiteral = arg.asKind(ts_morph_1.SyntaxKind.ObjectLiteralExpression);
189
+ if (objLiteral) {
190
+ objLiteral.getProperties().forEach((prop) => {
191
+ let propertyName = prop.getName();
192
+ let propertyValue = this.parseIdentifierPropertyValue(prop.getInitializer());
193
+ Reflect.set(argResult, propertyName, propertyValue);
194
+ });
195
+ }
196
+ });
197
+ return argResult;
198
+ }
199
+ parseIdentifierPropertyValue(value) {
200
+ switch (value.getKind()) {
201
+ case ts_morph_1.SyntaxKind.Identifier:
202
+ return {
203
+ type: 'constant',
204
+ variableName: value.getText(),
205
+ variableFilePath: this.getVariableFilePath(value.getText())
206
+ };
207
+ case ts_morph_1.SyntaxKind.PropertyAccessExpression:
208
+ return {
209
+ type: 'object',
210
+ variableName: value.getExpression().getText(),
211
+ propertyName: value?.getName(),
212
+ variableFilePath: this.getVariableFilePath(value?.getExpression().getText())
213
+ };
214
+ case ts_morph_1.SyntaxKind.ArrayLiteralExpression:
215
+ return {
216
+ type: 'array',
217
+ value: value.asKind(ts_morph_1.SyntaxKind.ArrayLiteralExpression)?.getElements()
218
+ .map(item => this.parseIdentifierPropertyValue(item))
219
+ };
220
+ default:
221
+ return this.parsePropertyValue(value);
222
+ }
223
+ }
224
+ parsePropertyValue(value) {
225
+ let propertyValue;
226
+ switch (value.getKind()) {
227
+ case ts_morph_1.SyntaxKind.StringLiteral:
228
+ propertyValue = value.asKind(ts_morph_1.SyntaxKind.StringLiteral)?.getLiteralValue();
229
+ break;
230
+ case ts_morph_1.SyntaxKind.NumericLiteral:
231
+ propertyValue = value.asKind(ts_morph_1.SyntaxKind.NumericLiteral)?.getLiteralValue();
232
+ break;
233
+ case ts_morph_1.SyntaxKind.TrueKeyword:
234
+ propertyValue = true;
235
+ break;
236
+ case ts_morph_1.SyntaxKind.FalseKeyword:
237
+ propertyValue = false;
238
+ break;
239
+ case ts_morph_1.SyntaxKind.ArrayLiteralExpression:
240
+ propertyValue = value.asKind(ts_morph_1.SyntaxKind.ArrayLiteralExpression)?.getElements()
241
+ .map(item => item.asKind(ts_morph_1.SyntaxKind.StringLiteral)?.getLiteralValue());
242
+ break;
243
+ }
244
+ return propertyValue;
245
+ }
246
+ getVariableFilePath(variableName) {
247
+ let filePath = '';
248
+ let classesNames = this.sourceFile.getClasses().map((classes) => {
249
+ return classes.getName();
250
+ });
251
+ let variableNames = this.sourceFile.getVariableDeclarations().map((variableDeclaration) => {
252
+ return variableDeclaration.getName();
253
+ });
254
+ if (classesNames.includes(variableName) || variableNames.includes(variableName)) {
255
+ return this.sourceFilePath;
256
+ }
257
+ this.importMap.forEach((importNames, importPath) => {
258
+ if (importNames.includes(variableName)) {
259
+ let currentDir = FileUtil_1.default.pathResolve(this.sourceFilePath, CommonConstants_1.default.PARENT_DELIMITER);
260
+ let tempFilePath = FileUtil_1.default.pathResolve(currentDir, importPath + CommonConstants_1.default.ETS_SUFFIX);
261
+ if (FileUtil_1.default.exist(tempFilePath)) {
262
+ filePath = tempFilePath;
263
+ }
264
+ else {
265
+ filePath = this.getOtherModuleVariableFilePath(importPath, variableName);
266
+ }
267
+ }
268
+ });
269
+ return filePath;
270
+ }
271
+ getOtherModuleVariableFilePath(moduleName, variableName) {
272
+ let moduleFilePath = FileUtil_1.default.pathResolve(this.config.modulePath, CommonConstants_1.default.OH_MODULE_PATH, moduleName, ConfigConstants_1.default.DEFAULT_SCAN_DIR);
273
+ if (!FileUtil_1.default.exist(moduleFilePath)) {
274
+ moduleFilePath = FileUtil_1.default.pathResolve(PluginStore_1.default.getInstance().projectFilePath, CommonConstants_1.default.OH_MODULE_PATH, moduleName, ConfigConstants_1.default.DEFAULT_SCAN_DIR);
275
+ }
276
+ let variableMap;
277
+ if (PluginStore_1.default.getInstance().variableCache.has(moduleName)) {
278
+ variableMap = PluginStore_1.default.getInstance().variableCache.get(moduleName);
279
+ }
280
+ else {
281
+ variableMap =
282
+ TsAstUtil_1.TsAstUtil.parseCrossModuleVariable(moduleFilePath);
283
+ PluginStore_1.default.getInstance().variableCache.set(moduleName, variableMap);
284
+ }
285
+ for (let [key, value] of variableMap) {
286
+ if (value.includes(variableName)) {
287
+ return key;
288
+ }
289
+ }
290
+ throw new Error(`Unknown variable ${variableName} in ${moduleName}`);
291
+ }
292
+ }
293
+ exports.AnalyzerService = AnalyzerService;
@@ -0,0 +1,15 @@
1
+ import { AnalyzerResultLike, HMRouterResult, RouterInfo } from './common/PluginModel';
2
+ import { CustomPageTemplateImpl, HMRouterPluginConfig } from './HMRouterPluginConfig';
3
+ export declare class HMRouterHvigorPlugin {
4
+ config: HMRouterPluginConfig;
5
+ routerMap: RouterInfo[];
6
+ scanFiles: string[];
7
+ private analyzerController;
8
+ constructor(config: HMRouterPluginConfig);
9
+ analyzeAnnotation(): void;
10
+ generateRouterMap(): void;
11
+ matchedPath(filePath: string, customPageTemplate: CustomPageTemplateImpl[], defaultTplFilePath: string): string;
12
+ pushRouterInfo(analyzeResult: AnalyzerResultLike): void;
13
+ generateBuilder(analyzeResult: HMRouterResult, pageSourceFile: string, tempFilePath: string): string;
14
+ deepScan(scanPath: string, filePath: string): void;
15
+ }