@hadss/hmrouter-plugin 1.0.0-rc.0 → 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 (50) hide show
  1. package/LICENSE +77 -77
  2. package/README.md +260 -37
  3. package/dist/HMRouterAnalyzer.d.ts +31 -0
  4. package/dist/HMRouterAnalyzer.js +293 -0
  5. package/dist/HMRouterHvigorPlugin.d.ts +15 -0
  6. package/dist/HMRouterHvigorPlugin.js +139 -0
  7. package/dist/HMRouterPluginConfig.d.ts +39 -0
  8. package/dist/HMRouterPluginConfig.js +73 -0
  9. package/dist/HMRouterPluginHandle.d.ts +23 -0
  10. package/dist/HMRouterPluginHandle.js +231 -0
  11. package/dist/Index.d.ts +4 -0
  12. package/dist/Index.js +124 -0
  13. package/dist/common/Constant.d.ts +27 -0
  14. package/dist/common/Constant.js +35 -0
  15. package/dist/common/Logger.d.ts +13 -0
  16. package/dist/common/Logger.js +55 -0
  17. package/dist/common/PluginModel.d.ts +50 -0
  18. package/dist/common/PluginModel.js +23 -0
  19. package/dist/constants/CommonConstants.d.ts +39 -0
  20. package/dist/constants/CommonConstants.js +46 -0
  21. package/dist/constants/ConfigConstants.d.ts +11 -0
  22. package/dist/constants/ConfigConstants.js +15 -0
  23. package/dist/constants/TaskConstants.d.ts +9 -0
  24. package/dist/constants/TaskConstants.js +13 -0
  25. package/dist/store/PluginStore.d.ts +12 -0
  26. package/dist/store/PluginStore.js +19 -0
  27. package/dist/utils/ConfusionUtil.d.ts +4 -0
  28. package/dist/utils/ConfusionUtil.js +27 -0
  29. package/dist/utils/FileUtil.d.ts +11 -0
  30. package/dist/utils/FileUtil.js +20 -0
  31. package/dist/utils/ObfuscationUtil.d.ts +4 -0
  32. package/dist/utils/ObfuscationUtil.js +34 -0
  33. package/dist/utils/StringUtil.d.ts +3 -0
  34. package/dist/utils/StringUtil.js +18 -0
  35. package/dist/utils/TsAstUtil.d.ts +9 -0
  36. package/dist/utils/TsAstUtil.js +89 -0
  37. package/package.json +46 -27
  38. package/viewBuilder.ejs +103 -0
  39. package/lib/HMRouterAnalyzer.js +0 -223
  40. package/lib/HMRouterHvigorPlugin.js +0 -175
  41. package/lib/HMRouterPluginConfig.js +0 -31
  42. package/lib/Index.js +0 -201
  43. package/lib/PluginModel.js +0 -33
  44. package/src/HMRouterAnalyzer.ts +0 -253
  45. package/src/HMRouterHvigorPlugin.ts +0 -246
  46. package/src/HMRouterPluginConfig.ts +0 -44
  47. package/src/Index.ts +0 -218
  48. package/src/PluginModel.ts +0 -50
  49. package/tsconfig.json +0 -14
  50. package/viewBuilder.tpl +0 -97
@@ -1,253 +0,0 @@
1
- /*
2
- * Copyright (c) 2024 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 ts from 'typescript'
17
- import fs from 'fs'
18
- import { HMRouterPluginConfig } from './HMRouterPluginConfig'
19
- import {
20
- AnalyzerResultLike,
21
- HMAnimatorResult,
22
- HMInterceptorResult,
23
- HMLifecycleResult,
24
- HMRouterResult
25
- } from './PluginModel'
26
- import { HvigorLogger } from '@ohos/hvigor'
27
- import { deleteGeneratorFile } from './Index'
28
-
29
- class NodeInfo {
30
- value?: any
31
- }
32
-
33
- const Logger = HvigorLogger.getLogger()
34
-
35
- export class Analyzer {
36
- customAnnotationExisted: boolean = false
37
- analyzeResultMap: Map<string, AnalyzerResultLike> = new Map()
38
- private sourcePath: string
39
- private pluginConfig: HMRouterPluginConfig
40
- private analyzeResult: AnalyzerResultLike = {}
41
- private keywordPos: number = 0
42
-
43
- constructor(sourcePath: string, pluginConfig: HMRouterPluginConfig) {
44
- this.sourcePath = sourcePath
45
- this.pluginConfig = pluginConfig
46
- }
47
-
48
- start() {
49
- const sourceCode = fs.readFileSync(this.sourcePath, 'utf-8')
50
- const sourceFile = ts.createSourceFile(
51
- this.sourcePath,
52
- sourceCode,
53
- ts.ScriptTarget.ES2021,
54
- false
55
- )
56
- ts.forEachChild(sourceFile, node => {
57
- this.resolveNode(node)
58
- // 解析完成后赋值存进Map
59
- switch (this.analyzeResult.annotation) {
60
- case 'HMRouter':
61
- this.analyzeResultMap.set(
62
- (this.analyzeResult as HMRouterResult).pageUrl!,
63
- this.analyzeResult
64
- )
65
- break
66
- case 'HMAnimator':
67
- this.analyzeResultMap.set(
68
- (this.analyzeResult as HMAnimatorResult).animatorName!,
69
- this.analyzeResult
70
- )
71
- break
72
- case 'HMInterceptor':
73
- this.analyzeResultMap.set(
74
- (this.analyzeResult as HMInterceptorResult).interceptorName!,
75
- this.analyzeResult
76
- )
77
- break
78
- case 'HMLifecycle':
79
- this.analyzeResultMap.set(
80
- (this.analyzeResult as HMLifecycleResult).lifecycleName!,
81
- this.analyzeResult
82
- )
83
- break
84
- }
85
- })
86
- }
87
-
88
- private resolveNode(node: ts.Node) {
89
- if (ts.isMissingDeclaration(node)) {
90
- this.resolveMissingDeclaration(node)
91
- } else if (ts.isClassDeclaration(node)) {
92
- this.resolveClass(node)
93
- } else if (ts.isDecorator(node)) {
94
- this.resolveDecorator(node)
95
- } else if (ts.isCallExpression(node)) {
96
- this.resolveCallExpression(node)
97
- } else if (ts.isExpressionStatement(node)) {
98
- this.resolveExpression(node)
99
- } else if (ts.isBlock(node)) {
100
- this.resolveBlock(node)
101
- } else if (ts.isPropertyAssignment(node)) {
102
- return this.resolvePropertyAccess(node)
103
- } else if (ts.isIdentifier(node)) {
104
- return this.resolveIdentifier(node)
105
- } else if (ts.isStringLiteral(node)) {
106
- return this.resolveStringLiteral(node)
107
- } else if (node.kind === ts.SyntaxKind.TrueKeyword) {
108
- let info = new NodeInfo()
109
- info.value = true
110
- return info
111
- } else if (node.kind === ts.SyntaxKind.FalseKeyword) {
112
- let info = new NodeInfo()
113
- info.value = false
114
- return info
115
- } else if (ts.isNumericLiteral(node)) {
116
- return this.resolveNumericLiteral(node)
117
- } else if (ts.isArrayLiteralExpression(node)) {
118
- let interceptors = this.resolveArrayLiteral(node)
119
- let info = new NodeInfo()
120
- info.value = interceptors
121
- return info
122
- }
123
- }
124
-
125
- private resolveMissingDeclaration(node: ts.MissingDeclaration) {
126
- this.analyzeResult = {}
127
- node.forEachChild(child => this.resolveNode(child))
128
- }
129
-
130
- private resolveClass(node: ts.ClassDeclaration) {
131
- // 解析到类声明,先清空一次返回结果
132
- this.analyzeResult = {}
133
- node.modifiers?.forEach(modifier => {
134
- // 遍历分析装饰器
135
- this.resolveNode(modifier)
136
- })
137
- if (this.customAnnotationExisted) {
138
- this.analyzeResult.name = node.name?.text
139
- }
140
- }
141
-
142
- private resolveDecorator(node: ts.Decorator) {
143
- if (ts.isCallExpression(node.expression)) {
144
- const callExpression = node.expression as ts.CallExpression
145
- if (ts.isIdentifier(callExpression.expression)) {
146
- this.switchIdentifier(callExpression)
147
- }
148
- }
149
- }
150
-
151
- private switchIdentifier(callExpression: ts.CallExpression) {
152
- const identifier = callExpression.expression as ts.Identifier
153
- if (this.pluginConfig.annotation.some(item => item === identifier.text)) {
154
- this.customAnnotationExisted = true
155
- // 区分是什么装饰器,构造不同的返回类
156
- switch (identifier.text) {
157
- case 'HMRouter':
158
- this.analyzeResult = new HMRouterResult()
159
- this.analyzeResult.annotation = 'HMRouter'
160
- break
161
- case 'HMAnimator':
162
- this.analyzeResult = new HMAnimatorResult()
163
- this.analyzeResult.annotation = 'HMAnimator'
164
- break
165
- case 'HMInterceptor':
166
- this.analyzeResult = new HMInterceptorResult()
167
- this.analyzeResult.annotation = 'HMInterceptor'
168
- break
169
- case 'HMLifecycle':
170
- this.analyzeResult = new HMLifecycleResult()
171
- this.analyzeResult.annotation = 'HMLifecycle'
172
- break
173
- }
174
- if (callExpression.arguments.length > 0) {
175
- this.resolveCallExpression(callExpression)
176
- }
177
- }
178
- }
179
-
180
- private resolveCallExpression(node: ts.CallExpression) {
181
- let identifier = this.resolveNode(node.expression)
182
- this.parseAnnotation(node.arguments, identifier)
183
- }
184
-
185
- private resolveExpression(node: ts.ExpressionStatement) {
186
- let identifier = this.resolveNode(node.expression)
187
- if (identifier?.value === 'struct') {
188
- this.keywordPos = node.end
189
- }
190
- if (this.analyzeResult.annotation === 'HMRouter' && this.keywordPos === node.pos) {
191
- this.analyzeResult.name = identifier?.value
192
- }
193
- }
194
-
195
- private resolveBlock(node: ts.Block) {
196
- node.statements.forEach(statement => {
197
- this.resolveNode(statement)
198
- })
199
- }
200
-
201
- private parseAnnotation(args: ts.NodeArray<ts.Expression>, nodeInfo?: NodeInfo) {
202
- if (this.pluginConfig.annotation.some(item => nodeInfo?.value === item)) {
203
- args
204
- .flatMap((e: ts.Expression) => (e as ts.ObjectLiteralExpression).properties)
205
- .forEach((e: ts.ObjectLiteralElementLike) => {
206
- this.parseConfig(e, this.analyzeResult)
207
- })
208
- }
209
- }
210
-
211
- private parseConfig(node: ts.ObjectLiteralElementLike, result: AnalyzerResultLike) {
212
- let info = this.resolveNode(node)
213
- Reflect.set(result, info?.value['key'], info?.value['value'])
214
- }
215
-
216
- private resolveArrayLiteral(node: ts.ArrayLiteralExpression) {
217
- return node.elements.map(e => this.resolveNode(e)?.value as string)
218
- }
219
-
220
- private resolvePropertyAccess(node: ts.PropertyAssignment) {
221
- let propertyName = this.resolveNode(node.name)?.value
222
- let propertyValue = this.resolveNode(node.initializer)?.value
223
- let info = new NodeInfo()
224
- info.value = { key: propertyName, value: propertyValue }
225
- return info
226
- }
227
-
228
- private resolveNumericLiteral(node: ts.NumericLiteral) {
229
- let info = new NodeInfo()
230
- info.value = Number(node.text)
231
- return info
232
- }
233
-
234
- private resolveStringLiteral(node: ts.StringLiteral) {
235
- let info = new NodeInfo()
236
- info.value = node.text
237
- return info
238
- }
239
-
240
- private resolveIdentifier(node: ts.Identifier) {
241
- if (node.escapedText === 'NavDestination' && this.analyzeResult.annotation === 'HMRouter') {
242
- Logger.error(
243
- 'errorCode: 40000003, errorMsg: @HMRouter 修饰的组件不能包含 NavDestination in' +
244
- this.analyzeResult.name
245
- )
246
- deleteGeneratorFile(this.pluginConfig)
247
- throw new Error('NavDestination is not allowed in HMRouter' + this.sourcePath)
248
- }
249
- let info = new NodeInfo()
250
- info.value = node.escapedText.toString()
251
- return info
252
- }
253
- }
@@ -1,246 +0,0 @@
1
- /*
2
- * Copyright (c) 2024 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 { HvigorLogger } from '@ohos/hvigor'
17
- import { HMRouterPluginConfig } from './HMRouterPluginConfig'
18
- import {
19
- AnalyzerResultLike,
20
- HMAnimatorResult,
21
- HMInterceptorResult,
22
- HMLifecycleResult,
23
- HMRouterResult
24
- } from './PluginModel'
25
- import path from 'path'
26
- import fs from 'fs'
27
- import { Analyzer } from './HMRouterAnalyzer'
28
- import Handlebars from 'handlebars'
29
- import { deleteGeneratorFile } from './Index'
30
-
31
- const Logger = HvigorLogger.getLogger()
32
-
33
- interface templateModel {
34
- pageUrl: string
35
- importPath: string
36
- componentName: string
37
- dialog: boolean
38
- generatorViewName: string
39
- }
40
-
41
- export class RouterInfo {
42
- name: string
43
- pageSourceFile: string
44
- buildFunction: string
45
- data: AnalyzerResultLike
46
-
47
- constructor(
48
- name: string,
49
- pageSourceFile: string,
50
- buildFunction: string,
51
- data: AnalyzerResultLike = {}
52
- ) {
53
- this.name = name
54
- this.pageSourceFile = pageSourceFile
55
- this.buildFunction = buildFunction
56
- this.data = data
57
- }
58
- }
59
-
60
- export class HMRouterHvigorPlugin {
61
- config: HMRouterPluginConfig
62
- routerMap: RouterInfo[] = []
63
- private scanFiles: string[] = []
64
- HMRouterNum: number = 0
65
-
66
- constructor(config: HMRouterPluginConfig) {
67
- this.config = config
68
- }
69
-
70
- analyzeAnnotation() {
71
- const templateModel = {
72
- pageUrl: '',
73
- importPath: '',
74
- componentName: '',
75
- dialog: false,
76
- generatorViewName: ''
77
- }
78
-
79
- this.config.scanDir.forEach(scanDir => {
80
- const scanPath = `${this.config.modulePath}/${scanDir}`
81
- this.deepScan(scanPath, '')
82
- })
83
-
84
- Logger.info(`扫描到${this.scanFiles.length}个文件`, this.scanFiles)
85
-
86
- this.scanFiles.forEach(filePath => {
87
- if (filePath.endsWith('.ets')) {
88
- const importPath = path
89
- .relative(`${this.config.modulePath}/${this.config.builderDir}`, filePath)
90
- .replaceAll('\\', '/')
91
- .replaceAll('.ets', '')
92
-
93
- const analyzer = new Analyzer(filePath, this.config)
94
- analyzer.start()
95
- for (let [key, value] of analyzer.analyzeResultMap) {
96
- value.module = this.config.moduleName
97
- let pageSourceFile = path.relative(this.config.modulePath, filePath).replaceAll('\\', '/')
98
- this.pushRouterInfo(value, pageSourceFile, importPath, templateModel)
99
- }
100
- this.HMRouterNum = 0
101
- }
102
- })
103
- }
104
-
105
- private stringToHashCode(str: string) {
106
- let hash = 0
107
- if (str.length === 0) return hash
108
- for (let i = 0; i < str.length; i++) {
109
- const char = str.charCodeAt(i)
110
- hash = (hash << 5) - hash + char
111
- hash |= 0 // 转换为32位整数
112
- }
113
- return hash
114
- }
115
-
116
- private pushHMRouter(
117
- value: AnalyzerResultLike,
118
- importPath: string,
119
- templateModel: templateModel
120
- ) {
121
- let analyzeResult = value as HMRouterResult
122
- templateModel.pageUrl = analyzeResult.pageUrl as string
123
- templateModel.importPath = importPath
124
- templateModel.componentName = analyzeResult.name as string
125
- templateModel.dialog = !!analyzeResult.dialog
126
- templateModel.generatorViewName = 'HM' + analyzeResult.name + this.stringToHashCode(analyzeResult.pageUrl!)
127
-
128
- this.routerMap.push(
129
- new RouterInfo(
130
- analyzeResult.pageUrl as string,
131
- `${this.config.builderDir}/${templateModel.generatorViewName}.ets`,
132
- `${templateModel.componentName}Builder`,
133
- value
134
- )
135
- )
136
-
137
- this.generateBuilder(templateModel)
138
- }
139
-
140
- private pushRouterInfo(
141
- value: AnalyzerResultLike,
142
- pageSourceFile: string,
143
- importPath: string,
144
- templateModel: templateModel
145
- ) {
146
- switch (value.annotation) {
147
- case 'HMRouter':
148
- this.HMRouterNum++
149
- if (this.HMRouterNum > 1) {
150
- Logger.error(`errorCode: 40000004, errorMsg: 文件${pageSourceFile}中存在多个HMRouter注解`)
151
- deleteGeneratorFile(this.config)
152
- throw new Error(`文件${pageSourceFile}中存在多个HMRouter注解`)
153
- }
154
- this.pushHMRouter(value, importPath, templateModel)
155
- break
156
- case 'HMAnimator':
157
- this.routerMap.push(
158
- new RouterInfo(
159
- `__animator__${(value as HMAnimatorResult).animatorName}`,
160
- pageSourceFile,
161
- '',
162
- value
163
- )
164
- )
165
- break
166
- case 'HMInterceptor':
167
- this.routerMap.push(
168
- new RouterInfo(
169
- `__interceptor__${(value as HMInterceptorResult).interceptorName}`,
170
- pageSourceFile,
171
- '',
172
- value
173
- )
174
- )
175
- break
176
- case 'HMLifecycle':
177
- this.routerMap.push(
178
- new RouterInfo(
179
- `__lifecycle__${(value as HMLifecycleResult).lifecycleName}`,
180
- pageSourceFile,
181
- '',
182
- value
183
- )
184
- )
185
- break
186
- }
187
- }
188
-
189
- generateBuilder(templateModel: templateModel) {
190
- const builderPath = path.resolve(__dirname, '../' + this.config.builderTpl)
191
- const tpl = fs.readFileSync(builderPath, { encoding: 'utf-8' })
192
- const template = Handlebars.compile(tpl)
193
- const output = template(templateModel)
194
- const routerBuilderDir = `${this.config.modulePath}/${this.config.builderDir}`
195
- if (!fs.existsSync(routerBuilderDir)) {
196
- fs.mkdirSync(routerBuilderDir)
197
- }
198
- fs.writeFileSync(`${routerBuilderDir}/${templateModel.generatorViewName}.ets`, output, {
199
- encoding: 'utf-8'
200
- })
201
- Logger.info(`Builder ${templateModel.generatorViewName}.ets has been generated in ${routerBuilderDir}`)
202
- }
203
-
204
- generateRouterMap() {
205
- let set = new Set<string>()
206
- this.routerMap.forEach(item => {
207
- if(set.has(item.name)) {
208
- Logger.error(`errorCode: 40000001, errorMsg: 重复的pageUrl、拦截器、生命周期、动画--${item.name}`)
209
- deleteGeneratorFile(this.config)
210
- throw new Error(`路由${item.name}重复`)
211
- } else {
212
- set.add(item.name)
213
- }
214
- })
215
- let routerMap = {
216
- routerMap: this.routerMap.map(item => {
217
- if (item.data && item.data.annotation) {
218
- delete item.data.annotation
219
- // @ts-ignore
220
- item.data = { data: JSON.stringify(item.data) }
221
- }
222
- return item
223
- })
224
- }
225
-
226
- const jsonOutput = JSON.stringify(routerMap, null, 2)
227
- const routerMapDir = `${this.config.modulePath}/${this.config.routerMapDir}`
228
- if (!fs.existsSync(routerMapDir)) {
229
- fs.mkdirSync(routerMapDir, { recursive: true })
230
- }
231
- fs.writeFileSync(`${routerMapDir}/hm_router_map.json`, jsonOutput, { encoding: 'utf-8' })
232
- Logger.info(`hm_router_map.json has been generated in ${routerMapDir}`)
233
- return this.routerMap
234
- }
235
-
236
- private deepScan(scanPath: string, filePath: string) {
237
- if (fs.lstatSync(`${scanPath + filePath}`).isDirectory()) {
238
- const files: string[] = fs.readdirSync(`${scanPath}${filePath}`)
239
- files.forEach(file => {
240
- this.deepScan(`${scanPath}${filePath}/`, file)
241
- })
242
- } else {
243
- this.scanFiles.push(`${scanPath}${filePath}`)
244
- }
245
- }
246
- }
@@ -1,44 +0,0 @@
1
- /*
2
- * Copyright (c) 2024 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 HMRouterPluginConfig {
17
- moduleName: string
18
- modulePath: string
19
- scanDir: string[]
20
- routerMapDir: string
21
- builderDir: string
22
- annotation: string[]
23
- builderTpl: string
24
- saveGeneratedFile: boolean
25
-
26
- constructor(moduleName: string, modulePath: string, param: HMRouterPluginConfigParam) {
27
- this.moduleName = moduleName
28
- this.modulePath = modulePath
29
- this.scanDir = param.scanDir ? param.scanDir : ['src/main/ets']
30
- this.routerMapDir = param.routerMapDir ? param.routerMapDir : 'src/main/resources/base/profile'
31
- this.builderDir = param.builderDir ? param.builderDir : 'src/main/ets/generated'
32
- this.annotation = ['HMRouter', 'HMAnimator', 'HMInterceptor', 'HMLifecycle']
33
- this.builderTpl = param.builderTpl ? param.builderTpl : 'viewBuilder.tpl'
34
- this.saveGeneratedFile = !!param.saveGeneratedFile
35
- }
36
- }
37
-
38
- export interface HMRouterPluginConfigParam {
39
- scanDir?: string[]
40
- routerMapDir?: string
41
- builderDir?: string
42
- saveGeneratedFile?: boolean // 隐藏字段仅用于调试
43
- builderTpl?: string // 隐藏字段仅用于调试
44
- }