@hadss/hmrouter-plugin 1.0.0-rc.1 → 1.0.0-rc.11
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/README.md +240 -17
- package/dist/HMRouterAnalyzer.d.ts +31 -0
- package/dist/HMRouterAnalyzer.js +296 -0
- package/dist/HMRouterHvigorPlugin.d.ts +15 -0
- package/dist/HMRouterHvigorPlugin.js +143 -0
- package/dist/HMRouterPluginConfig.d.ts +39 -0
- package/dist/HMRouterPluginConfig.js +74 -0
- package/dist/HMRouterPluginHandle.d.ts +23 -0
- package/dist/HMRouterPluginHandle.js +222 -0
- package/dist/Index.d.ts +4 -0
- package/dist/Index.js +128 -0
- package/dist/common/Constant.d.ts +27 -0
- package/{lib → dist}/common/Constant.js +0 -15
- package/dist/common/Logger.d.ts +13 -0
- package/{lib → dist}/common/Logger.js +17 -24
- package/dist/common/PluginModel.d.ts +50 -0
- package/{lib → dist}/common/PluginModel.js +2 -16
- package/dist/constants/CommonConstants.d.ts +38 -0
- package/dist/constants/CommonConstants.js +45 -0
- package/dist/constants/ConfigConstants.d.ts +12 -0
- package/dist/constants/ConfigConstants.js +16 -0
- package/dist/constants/TaskConstants.d.ts +9 -0
- package/dist/constants/TaskConstants.js +13 -0
- package/dist/store/PluginStore.d.ts +14 -0
- package/dist/store/PluginStore.js +20 -0
- package/dist/utils/ConfusionUtil.d.ts +4 -0
- package/dist/utils/ConfusionUtil.js +27 -0
- package/dist/utils/FileUtil.d.ts +11 -0
- package/dist/utils/FileUtil.js +20 -0
- package/dist/utils/ObfuscationUtil.d.ts +4 -0
- package/dist/utils/ObfuscationUtil.js +34 -0
- package/dist/utils/StringUtil.d.ts +3 -0
- package/dist/utils/StringUtil.js +18 -0
- package/dist/utils/TsAstUtil.d.ts +9 -0
- package/dist/utils/TsAstUtil.js +89 -0
- package/package.json +26 -12
- package/{viewBuilder.tpl → viewBuilder.ejs} +18 -11
- package/lib/HMRouterAnalyzer.js +0 -351
- package/lib/HMRouterAnalyzer.js.map +0 -1
- package/lib/HMRouterHvigorPlugin.js +0 -177
- package/lib/HMRouterHvigorPlugin.js.map +0 -1
- package/lib/HMRouterPluginConfig.js +0 -31
- package/lib/HMRouterPluginConfig.js.map +0 -1
- package/lib/HMRouterPluginHandle.js +0 -148
- package/lib/HMRouterPluginHandle.js.map +0 -1
- package/lib/Index.js +0 -143
- package/lib/Index.js.map +0 -1
- package/lib/common/Constant.js.map +0 -1
- package/lib/common/Logger.js.map +0 -1
- package/lib/common/PluginModel.js.map +0 -1
- package/src/HMRouterAnalyzer.ts +0 -375
- package/src/HMRouterHvigorPlugin.ts +0 -199
- package/src/HMRouterPluginConfig.ts +0 -44
- package/src/HMRouterPluginHandle.ts +0 -188
- package/src/Index.ts +0 -151
- package/src/common/Constant.ts +0 -49
- package/src/common/Logger.ts +0 -73
- package/src/common/PluginModel.ts +0 -83
- package/tsconfig.json +0 -14
package/src/HMRouterAnalyzer.ts
DELETED
|
@@ -1,375 +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 {HMRouterPluginConfig} from './HMRouterPluginConfig'
|
|
18
|
-
import {AnalyzerResultLike, HMRouterResult, HMServiceResult} from './common/PluginModel'
|
|
19
|
-
import {FileUtil} from '@ohos/hvigor'
|
|
20
|
-
import {Logger, PluginError} from './common/Logger'
|
|
21
|
-
import { HMRouterPluginConstant } from './common/Constant'
|
|
22
|
-
|
|
23
|
-
class NodeInfo {
|
|
24
|
-
value?: any
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export class Analyzer {
|
|
28
|
-
analyzeResultSet: Set<AnalyzerResultLike> = new Set()
|
|
29
|
-
private readonly sourcePath: string
|
|
30
|
-
private readonly pluginConfig: HMRouterPluginConfig
|
|
31
|
-
private analyzeResult: AnalyzerResultLike = {}
|
|
32
|
-
private classMethodAnalyzeResult: Set<AnalyzerResultLike> = new Set()
|
|
33
|
-
private keywordPos: number = 0
|
|
34
|
-
private importMap: Map<string, string[]> = new Map()
|
|
35
|
-
|
|
36
|
-
constructor(sourcePath: string, pluginConfig: HMRouterPluginConfig) {
|
|
37
|
-
this.sourcePath = sourcePath
|
|
38
|
-
this.pluginConfig = pluginConfig
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
start() {
|
|
42
|
-
const sourceCode = FileUtil.readFileSync(this.sourcePath).toString()
|
|
43
|
-
const sourceFile = ts.createSourceFile(this.sourcePath, sourceCode, ts.ScriptTarget.ES2021, false)
|
|
44
|
-
|
|
45
|
-
ts.forEachChild(sourceFile, node => {
|
|
46
|
-
this.resolveNode(node)
|
|
47
|
-
if (this.analyzeResult.annotation) {
|
|
48
|
-
this.analyzeResultSet.add(this.analyzeResult)
|
|
49
|
-
}
|
|
50
|
-
if (this.classMethodAnalyzeResult.size > 0) {
|
|
51
|
-
this.analyzeResultSet = new Set([...this.analyzeResultSet, ...this.classMethodAnalyzeResult])
|
|
52
|
-
}
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
// 遍历map查看是否还有常量类型的属性值
|
|
56
|
-
this.analyzeResultSet.forEach(value => {
|
|
57
|
-
if (value.annotation === 'HMRouter') {
|
|
58
|
-
let hmRouterResult = value as HMRouterResult
|
|
59
|
-
if (hmRouterResult.pageUrl.type === 'constant') {
|
|
60
|
-
hmRouterResult.pageUrl = this.resolveConstantValue(hmRouterResult.pageUrl.value)
|
|
61
|
-
} else if (hmRouterResult.pageUrl.type === 'object') {
|
|
62
|
-
hmRouterResult.pageUrl = this.resolveObjectValue(hmRouterResult.pageUrl.className, hmRouterResult.pageUrl.propertyName)
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
})
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
private resolveNode(node: ts.Node) {
|
|
69
|
-
if (ts.isImportDeclaration(node)) {
|
|
70
|
-
this.resolveImportDeclaration(node)
|
|
71
|
-
} else if (ts.isMissingDeclaration(node)) {
|
|
72
|
-
this.resolveMissingDeclaration(node)
|
|
73
|
-
} else if (ts.isClassDeclaration(node)) {
|
|
74
|
-
this.isDecoratedClass(node) ? this.resolveClass(node) : this.resolveClassMethod(node)
|
|
75
|
-
} else if (ts.isDecorator(node)) {
|
|
76
|
-
this.resolveDecorator(node)
|
|
77
|
-
} else if (ts.isCallExpression(node)) {
|
|
78
|
-
this.resolveCallExpression(node)
|
|
79
|
-
} else if (ts.isExpressionStatement(node)) {
|
|
80
|
-
this.resolveExpression(node)
|
|
81
|
-
} else if (ts.isBlock(node)) {
|
|
82
|
-
this.resolveBlock(node)
|
|
83
|
-
} else if (ts.isPropertyAssignment(node)) {
|
|
84
|
-
return this.resolvePropertyAccess(node)
|
|
85
|
-
} else if (ts.isIdentifier(node)) {
|
|
86
|
-
return this.resolveIdentifier(node)
|
|
87
|
-
} else if (ts.isStringLiteral(node)) {
|
|
88
|
-
return this.resolveStringLiteral(node)
|
|
89
|
-
} else if (ts.isNumericLiteral(node)) {
|
|
90
|
-
return this.resolveNumericLiteral(node)
|
|
91
|
-
} else if (ts.isArrayLiteralExpression(node)) {
|
|
92
|
-
return this.resolveArrayLiteral(node)
|
|
93
|
-
} else if (node.kind === ts.SyntaxKind.TrueKeyword) {
|
|
94
|
-
return this.resolveTrueKeyword()
|
|
95
|
-
} else if (node.kind === ts.SyntaxKind.FalseKeyword) {
|
|
96
|
-
return this.resolveFalseKeyword()
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
private resolveImportDeclaration(node: ts.ImportDeclaration) {
|
|
101
|
-
let variableArr: string[] = []
|
|
102
|
-
node.importClause?.namedBindings?.forEachChild(child => {
|
|
103
|
-
if (ts.isImportSpecifier(child)) {
|
|
104
|
-
variableArr.push(child.name.escapedText!)
|
|
105
|
-
}
|
|
106
|
-
})
|
|
107
|
-
let key = node.moduleSpecifier
|
|
108
|
-
if (ts.isModuleName(key)) {
|
|
109
|
-
this.importMap.set(key.text, variableArr)
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
private resolveMissingDeclaration(node: ts.MissingDeclaration) {
|
|
114
|
-
this.analyzeResult = {}
|
|
115
|
-
node.forEachChild(child => this.resolveNode(child))
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
private isDecoratedClass(node: ts.ClassDeclaration) {
|
|
119
|
-
// @ts-ignore
|
|
120
|
-
return node.modifiers?.some((item) => HMRouterPluginConstant.CLASS_ANNOTATION_ARR.includes(item.expression?.expression?.text))
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
private resolveClass(node: ts.ClassDeclaration) {
|
|
124
|
-
// 解析到类声明,先清空一次返回结果
|
|
125
|
-
this.analyzeResult = {}
|
|
126
|
-
node.modifiers?.forEach(modifier => {
|
|
127
|
-
// 遍历分析装饰器
|
|
128
|
-
this.resolveNode(modifier)
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
if (this.analyzeResult.annotation) {
|
|
132
|
-
this.analyzeResult.name = node.name?.text
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
private resolveClassMethod(node: ts.ClassDeclaration) {
|
|
137
|
-
this.classMethodAnalyzeResult = new Set([])
|
|
138
|
-
node.members?.forEach((member) => {
|
|
139
|
-
this.analyzeResult = {};
|
|
140
|
-
if (ts.isMethodDeclaration(member) && member.modifiers) {
|
|
141
|
-
member.modifiers?.forEach((modifier) => {
|
|
142
|
-
this.resolveNode(modifier)
|
|
143
|
-
})
|
|
144
|
-
|
|
145
|
-
// @ts-ignore
|
|
146
|
-
this.analyzeResult.functionName = member.name?.text
|
|
147
|
-
this.analyzeResult.name = node.name?.text
|
|
148
|
-
|
|
149
|
-
if (this.analyzeResult.annotation) {
|
|
150
|
-
this.classMethodAnalyzeResult.add(this.analyzeResult)
|
|
151
|
-
}
|
|
152
|
-
this.analyzeResult = {};
|
|
153
|
-
}
|
|
154
|
-
})
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
private resolveDecorator(node: ts.Decorator) {
|
|
158
|
-
if (ts.isCallExpression(node.expression)) {
|
|
159
|
-
const callExpression = node.expression as ts.CallExpression
|
|
160
|
-
if (ts.isIdentifier(callExpression.expression)) {
|
|
161
|
-
this.switchIdentifier(callExpression)
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
private switchIdentifier(callExpression: ts.CallExpression) {
|
|
167
|
-
const identifier = callExpression.expression as ts.Identifier
|
|
168
|
-
if (this.pluginConfig.annotation.some(item => item === identifier.text)) {
|
|
169
|
-
this.analyzeResult.annotation = identifier.text
|
|
170
|
-
this.resolveNode(callExpression)
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
private resolveCallExpression(node: ts.CallExpression) {
|
|
175
|
-
let identifier = this.resolveNode(node.expression)
|
|
176
|
-
this.parseAnnotation(node.arguments, identifier)
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
private resolveExpression(node: ts.ExpressionStatement) {
|
|
180
|
-
let identifier = this.resolveNode(node.expression)
|
|
181
|
-
if (identifier?.value === 'struct') {
|
|
182
|
-
this.keywordPos = node.end
|
|
183
|
-
}
|
|
184
|
-
if (this.analyzeResult.annotation === 'HMRouter' && this.keywordPos === node.pos) {
|
|
185
|
-
this.analyzeResult.name = identifier?.value
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
private resolveBlock(node: ts.Block) {
|
|
190
|
-
node.statements.forEach(statement => {
|
|
191
|
-
this.resolveNode(statement)
|
|
192
|
-
})
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
private parseAnnotation(args: ts.NodeArray<ts.Expression>, nodeInfo?: NodeInfo) {
|
|
196
|
-
if (this.pluginConfig.annotation.some(item => nodeInfo?.value === item)) {
|
|
197
|
-
args
|
|
198
|
-
.flatMap((e: ts.Expression) => (e as ts.ObjectLiteralExpression).properties)
|
|
199
|
-
.forEach((e: ts.ObjectLiteralElementLike) => {
|
|
200
|
-
this.parseConfig(e, this.analyzeResult)
|
|
201
|
-
})
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
private parseConfig(node: ts.ObjectLiteralElementLike, result: AnalyzerResultLike) {
|
|
206
|
-
let info = this.resolveNode(node)
|
|
207
|
-
Reflect.set(result, info?.value['key'], info?.value['value'])
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
private resolvePropertyAccess(node: any) {
|
|
211
|
-
let propertyName = this.resolveNode(node.name)?.value
|
|
212
|
-
let propertyValue
|
|
213
|
-
if (propertyName === 'pageUrl') {
|
|
214
|
-
if (node.initializer.kind === ts.SyntaxKind.Identifier) {
|
|
215
|
-
propertyValue = {
|
|
216
|
-
type: 'constant', // 常量
|
|
217
|
-
value: this.resolveNode(node.initializer)?.value
|
|
218
|
-
}
|
|
219
|
-
} else if (node.initializer.kind === ts.SyntaxKind.PropertyAccessExpression) {
|
|
220
|
-
propertyValue = {
|
|
221
|
-
type: 'object', // 对象
|
|
222
|
-
className: this.resolveNode(node.initializer.expression)?.value,
|
|
223
|
-
propertyName: this.resolveNode(node.initializer.name)?.value
|
|
224
|
-
}
|
|
225
|
-
} else {
|
|
226
|
-
propertyValue = this.resolveNode(node.initializer)?.value
|
|
227
|
-
}
|
|
228
|
-
} else {
|
|
229
|
-
propertyValue = this.resolveNode(node.initializer)?.value
|
|
230
|
-
}
|
|
231
|
-
let info = new NodeInfo()
|
|
232
|
-
info.value = { key: propertyName, value: propertyValue }
|
|
233
|
-
return info
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
private resolveIdentifier(node: ts.Identifier) {
|
|
237
|
-
if (node.escapedText === 'NavDestination' && this.analyzeResult.annotation === 'HMRouter') {
|
|
238
|
-
Logger.e(PluginError.ERR_WRONG_DECORATION)
|
|
239
|
-
throw new Error('NavDestination is not allowed in HMRouter' + this.sourcePath)
|
|
240
|
-
}
|
|
241
|
-
let info = new NodeInfo()
|
|
242
|
-
info.value = node.escapedText.toString()
|
|
243
|
-
return info
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
private resolveArrayLiteral(node: ts.ArrayLiteralExpression) {
|
|
247
|
-
let arrValue = node.elements.map(e => this.resolveNode(e)?.value as string)
|
|
248
|
-
let info = new NodeInfo()
|
|
249
|
-
info.value = arrValue
|
|
250
|
-
return info
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
private resolveNumericLiteral(node: ts.NumericLiteral) {
|
|
254
|
-
let info = new NodeInfo()
|
|
255
|
-
info.value = Number(node.text)
|
|
256
|
-
return info
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
private resolveStringLiteral(node: ts.StringLiteral) {
|
|
260
|
-
let info = new NodeInfo()
|
|
261
|
-
info.value = node.text
|
|
262
|
-
return info
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
private resolveTrueKeyword() {
|
|
266
|
-
let info = new NodeInfo()
|
|
267
|
-
info.value = true
|
|
268
|
-
return info
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
private resolveFalseKeyword() {
|
|
272
|
-
let info = new NodeInfo()
|
|
273
|
-
info.value = false
|
|
274
|
-
return info
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
private getSourceFile(filePath: string): ts.SourceFile | null {
|
|
278
|
-
if (!FileUtil.exist(filePath)) {
|
|
279
|
-
return null;
|
|
280
|
-
}
|
|
281
|
-
const sourceCode = FileUtil.readFileSync(filePath).toString();
|
|
282
|
-
return ts.createSourceFile(filePath, sourceCode, ts.ScriptTarget.ES2021, false);
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
private findConstantValueInSourceFile(sourceFile: ts.SourceFile, constantName: string): NodeInfo | null {
|
|
286
|
-
let nodeInfo = new NodeInfo();
|
|
287
|
-
ts.forEachChild(sourceFile, node => {
|
|
288
|
-
if (!ts.isVariableStatement(node)) {
|
|
289
|
-
return;
|
|
290
|
-
}
|
|
291
|
-
node.declarationList.declarations.forEach(declaration => {
|
|
292
|
-
let info = this.parseVariableDeclaration(declaration);
|
|
293
|
-
if (constantName === info?.value['key']) {
|
|
294
|
-
nodeInfo = info;
|
|
295
|
-
}
|
|
296
|
-
});
|
|
297
|
-
});
|
|
298
|
-
return nodeInfo.value['value'] !== '' ? nodeInfo : null;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
private findObjectValueInSourceFile(sourceFile: ts.SourceFile, className: string,
|
|
302
|
-
propertyName: string): NodeInfo | null {
|
|
303
|
-
let info = new NodeInfo();
|
|
304
|
-
ts.forEachChild(sourceFile, node => {
|
|
305
|
-
if (!ts.isClassDeclaration(node) || node.name?.escapedText.toString() !== className) {
|
|
306
|
-
return;
|
|
307
|
-
}
|
|
308
|
-
node.members.forEach(member => {
|
|
309
|
-
// @ts-ignore
|
|
310
|
-
if (member.name?.escapedText.toString() === propertyName) {
|
|
311
|
-
info = this.parseVariableDeclaration(member);
|
|
312
|
-
}
|
|
313
|
-
});
|
|
314
|
-
});
|
|
315
|
-
return info.value['value'] !== '' ? info : null;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
private resolveConstantValue(constantName: string) {
|
|
319
|
-
const sourceFile = this.getSourceFile(this.resolvePath(constantName));
|
|
320
|
-
if (!sourceFile) {
|
|
321
|
-
return null;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
const nodeInfo = this.findConstantValueInSourceFile(sourceFile, constantName);
|
|
325
|
-
if (!nodeInfo) {
|
|
326
|
-
Logger.e(PluginError.ERR_NOT_EMPTY_STRING, constantName);
|
|
327
|
-
throw new Error('constant value cannot be an empty string');
|
|
328
|
-
}
|
|
329
|
-
return nodeInfo.value['value'];
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
private resolveObjectValue(className: string, propertyName: string) {
|
|
333
|
-
const sourceFile = this.getSourceFile(this.resolvePath(className));
|
|
334
|
-
if (!sourceFile) {
|
|
335
|
-
return null;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
const info = this.findObjectValueInSourceFile(sourceFile, className, propertyName);
|
|
339
|
-
if (!info) {
|
|
340
|
-
Logger.e(PluginError.ERR_NOT_EMPTY_STRING, propertyName);
|
|
341
|
-
throw new Error('constant value cannot be an empty string');
|
|
342
|
-
}
|
|
343
|
-
return info.value['value'];
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
private resolvePath(variableName: any) {
|
|
348
|
-
let filePath: string = ''
|
|
349
|
-
this.importMap.forEach((value, key) => {
|
|
350
|
-
if (value.includes(variableName)) {
|
|
351
|
-
filePath = key
|
|
352
|
-
}
|
|
353
|
-
})
|
|
354
|
-
if (filePath) {
|
|
355
|
-
filePath =
|
|
356
|
-
FileUtil.pathResolve(
|
|
357
|
-
this.sourcePath.split(HMRouterPluginConstant.FILE_SEPARATOR).slice(0, -1)
|
|
358
|
-
.join(HMRouterPluginConstant.FILE_SEPARATOR),
|
|
359
|
-
filePath
|
|
360
|
-
) + '.ets'
|
|
361
|
-
} else {
|
|
362
|
-
filePath = this.sourcePath
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
return filePath
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
private parseVariableDeclaration(node: any) {
|
|
369
|
-
let variableName = this.resolveNode(node.name)?.value
|
|
370
|
-
let variableValue = this.resolveNode(node.initializer!)?.value
|
|
371
|
-
let info = new NodeInfo()
|
|
372
|
-
info.value = { key: variableName, value: variableValue }
|
|
373
|
-
return info
|
|
374
|
-
}
|
|
375
|
-
}
|
|
@@ -1,199 +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 { FileUtil } from '@ohos/hvigor'
|
|
17
|
-
import { HMRouterPluginConfig } from './HMRouterPluginConfig'
|
|
18
|
-
import {
|
|
19
|
-
AnalyzerResultLike,
|
|
20
|
-
HMAnimatorResult,
|
|
21
|
-
HMInterceptorResult,
|
|
22
|
-
HMLifecycleResult,
|
|
23
|
-
HMServiceResult,
|
|
24
|
-
HMRouterResult,
|
|
25
|
-
RouterInfo,
|
|
26
|
-
TemplateModel
|
|
27
|
-
} from './common/PluginModel'
|
|
28
|
-
import path from 'path'
|
|
29
|
-
import fs from 'fs'
|
|
30
|
-
import { Analyzer } from './HMRouterAnalyzer'
|
|
31
|
-
import Handlebars from 'handlebars'
|
|
32
|
-
import { Logger, PluginError } from './common/Logger'
|
|
33
|
-
import { HMRouterPluginConstant } from './common/Constant'
|
|
34
|
-
|
|
35
|
-
// 定义HMRouterHvigorPlugin类
|
|
36
|
-
export class HMRouterHvigorPlugin {
|
|
37
|
-
config: HMRouterPluginConfig
|
|
38
|
-
routerMap: RouterInfo[] = []
|
|
39
|
-
private scanFiles: string[] = []
|
|
40
|
-
private HMRouterNum: number = 0
|
|
41
|
-
|
|
42
|
-
constructor(config: HMRouterPluginConfig) {
|
|
43
|
-
this.config = config
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
analyzeAnnotation() {
|
|
47
|
-
this.config.scanDir.forEach(scanDir => {
|
|
48
|
-
const scanPath = FileUtil.pathResolve(this.config.modulePath, scanDir)
|
|
49
|
-
this.deepScan(scanPath, '')
|
|
50
|
-
})
|
|
51
|
-
Logger.i(`扫描到${this.scanFiles.length}个文件`, this.scanFiles)
|
|
52
|
-
this.scanFiles.forEach(filePath => {
|
|
53
|
-
if (filePath.endsWith(HMRouterPluginConstant.VIEW_NAME_SUFFIX)) {
|
|
54
|
-
// 创建Analyzer对象
|
|
55
|
-
const analyzer = new Analyzer(filePath, this.config)
|
|
56
|
-
// 开始分析
|
|
57
|
-
analyzer.start()
|
|
58
|
-
// 遍历分析结果
|
|
59
|
-
for (let analyzeResult of analyzer.analyzeResultSet) {
|
|
60
|
-
analyzeResult.module = this.config.moduleName
|
|
61
|
-
let pageSourceFile = path.relative(this.config.modulePath, filePath)
|
|
62
|
-
this.pushRouterInfo(analyzeResult, pageSourceFile.replaceAll(HMRouterPluginConstant.FILE_SEPARATOR, HMRouterPluginConstant.DELIMITER))
|
|
63
|
-
}
|
|
64
|
-
this.HMRouterNum = 0
|
|
65
|
-
}
|
|
66
|
-
})
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// 推送路由信息
|
|
70
|
-
private pushRouterInfo(analyzeResult: AnalyzerResultLike, pageSourceFile: string) {
|
|
71
|
-
// 判断注解类型
|
|
72
|
-
switch (analyzeResult.annotation) {
|
|
73
|
-
case HMRouterPluginConstant.ROUTER_ANNOTATION:
|
|
74
|
-
this.HMRouterNum++
|
|
75
|
-
// 判断是否存在多个HMRouter注解
|
|
76
|
-
if (this.HMRouterNum > 1) {
|
|
77
|
-
Logger.e(PluginError.ERR_REPEAT_ANNOTATION, pageSourceFile)
|
|
78
|
-
throw new Error(`文件${pageSourceFile}中存在多个HMRouter注解`)
|
|
79
|
-
}
|
|
80
|
-
let [generatorFilePath, componentName] = this.generateBuilder(analyzeResult, pageSourceFile)
|
|
81
|
-
// window环境下需要替换路径中的反斜杠
|
|
82
|
-
generatorFilePath = generatorFilePath.replaceAll(HMRouterPluginConstant.FILE_SEPARATOR, HMRouterPluginConstant.DELIMITER)
|
|
83
|
-
this.routerMap.push(
|
|
84
|
-
new RouterInfo((analyzeResult as HMRouterResult).pageUrl as string, generatorFilePath, `${componentName}Builder`, analyzeResult)
|
|
85
|
-
)
|
|
86
|
-
break
|
|
87
|
-
case HMRouterPluginConstant.ANIMATOR_ANNOTATION:
|
|
88
|
-
let animatorName = HMRouterPluginConstant.ANIMATOR_PREFIX + (analyzeResult as HMAnimatorResult).animatorName
|
|
89
|
-
this.routerMap.push(new RouterInfo(animatorName, pageSourceFile, '', analyzeResult))
|
|
90
|
-
break
|
|
91
|
-
case HMRouterPluginConstant.INTERCEPTOR_ANNOTATION:
|
|
92
|
-
let interceptorName = HMRouterPluginConstant.INTERCEPTOR_PREFIX + (analyzeResult as HMInterceptorResult).interceptorName
|
|
93
|
-
this.routerMap.push(new RouterInfo(interceptorName, pageSourceFile, '', analyzeResult))
|
|
94
|
-
break
|
|
95
|
-
case HMRouterPluginConstant.LIFECYCLE_ANNOTATION:
|
|
96
|
-
let lifecycleName = HMRouterPluginConstant.LIFECYCLE_PREFIX + (analyzeResult as HMLifecycleResult).lifecycleName
|
|
97
|
-
this.routerMap.push(new RouterInfo(lifecycleName, pageSourceFile, '', analyzeResult))
|
|
98
|
-
break
|
|
99
|
-
case HMRouterPluginConstant.SERVICE_ANNOTATION:
|
|
100
|
-
let serviceName = HMRouterPluginConstant.SERVICE_PREFIX + (analyzeResult as HMServiceResult).serviceName
|
|
101
|
-
this.routerMap.push(new RouterInfo(serviceName, pageSourceFile, '', analyzeResult))
|
|
102
|
-
break
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// 生成Builder
|
|
107
|
-
private generateBuilder(analyzeResult: HMRouterResult, pageSourceFile: string) {
|
|
108
|
-
// 获取导入路径
|
|
109
|
-
let importPath = path
|
|
110
|
-
.relative(this.config.builderDir, pageSourceFile)
|
|
111
|
-
.replaceAll(HMRouterPluginConstant.FILE_SEPARATOR, HMRouterPluginConstant.DELIMITER)
|
|
112
|
-
.replaceAll(HMRouterPluginConstant.VIEW_NAME_SUFFIX, '')
|
|
113
|
-
// 生成视图名称
|
|
114
|
-
let generatorViewName = HMRouterPluginConstant.VIEW_NAME_PREFIX + analyzeResult.name + this.stringToHashCode(analyzeResult.pageUrl!)
|
|
115
|
-
// 创建模板模型
|
|
116
|
-
const templateModel: TemplateModel = new TemplateModel(
|
|
117
|
-
analyzeResult.pageUrl,
|
|
118
|
-
importPath,
|
|
119
|
-
analyzeResult.name!,
|
|
120
|
-
!!analyzeResult.dialog,
|
|
121
|
-
generatorViewName
|
|
122
|
-
)
|
|
123
|
-
// 获取模板文件路径
|
|
124
|
-
const templateFilePath = FileUtil.pathResolve(__dirname, HMRouterPluginConstant.PARENT_DELIMITER + this.config.builderTpl)
|
|
125
|
-
// 读取模板文件
|
|
126
|
-
const tpl = FileUtil.readFileSync(templateFilePath).toString()
|
|
127
|
-
// 编译模板
|
|
128
|
-
const template = Handlebars.compile(tpl)
|
|
129
|
-
// 渲染模板
|
|
130
|
-
const output = template(templateModel)
|
|
131
|
-
// 获取生成文件路径
|
|
132
|
-
const generatorFilePath = FileUtil.pathResolve(
|
|
133
|
-
this.config.modulePath,
|
|
134
|
-
this.config.builderDir,
|
|
135
|
-
templateModel.generatorViewName + HMRouterPluginConstant.VIEW_NAME_SUFFIX
|
|
136
|
-
)
|
|
137
|
-
FileUtil.ensureFileSync(generatorFilePath)
|
|
138
|
-
FileUtil.writeFileSync(generatorFilePath, output)
|
|
139
|
-
Logger.i(`Builder ${templateModel.generatorViewName}.ets has been generated in ${generatorFilePath}`)
|
|
140
|
-
// 返回生成文件路径和组件名称
|
|
141
|
-
return [path.join(this.config.builderDir, templateModel.generatorViewName + HMRouterPluginConstant.VIEW_NAME_SUFFIX), templateModel.componentName]
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// 生成路由表文件
|
|
145
|
-
generateRouterMap() {
|
|
146
|
-
let set = new Set<string>()
|
|
147
|
-
this.routerMap.forEach(item => {
|
|
148
|
-
// 判断路由名称是否重复
|
|
149
|
-
if (set.has(item.name)) {
|
|
150
|
-
Logger.e(PluginError.ERR_DUPLICATE_NAME, item.name)
|
|
151
|
-
throw new Error(`路由${item.name}重复`)
|
|
152
|
-
} else {
|
|
153
|
-
set.add(item.name)
|
|
154
|
-
}
|
|
155
|
-
})
|
|
156
|
-
let routerMap = {
|
|
157
|
-
routerMap: this.routerMap.map(item => {
|
|
158
|
-
// 删除注解
|
|
159
|
-
if (item.customData && item.customData.annotation) {
|
|
160
|
-
delete item.customData.annotation
|
|
161
|
-
}
|
|
162
|
-
return item
|
|
163
|
-
})
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// 获取路由映射文件路径
|
|
167
|
-
const routerMapJsonStr = JSON.stringify(routerMap, null, 2)
|
|
168
|
-
const routerMapFilePath = FileUtil.pathResolve(this.config.modulePath, this.config.routerMapDir, HMRouterPluginConstant.ROUTER_MAP_NAME)
|
|
169
|
-
// 确保文件存在
|
|
170
|
-
FileUtil.ensureFileSync(routerMapFilePath)
|
|
171
|
-
FileUtil.writeFileSync(routerMapFilePath, routerMapJsonStr)
|
|
172
|
-
Logger.i(`hm_router_map.json has been generated in ${routerMapFilePath}`)
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// 深度扫描文件夹获取需要扫描的文件列表
|
|
176
|
-
private deepScan(scanPath: string, filePath: string) {
|
|
177
|
-
let resolvePath = FileUtil.pathResolve(scanPath, filePath)
|
|
178
|
-
if (FileUtil.isDictionary(resolvePath)) {
|
|
179
|
-
const files: string[] = fs.readdirSync(resolvePath)
|
|
180
|
-
files.forEach(file => {
|
|
181
|
-
this.deepScan(resolvePath, file)
|
|
182
|
-
})
|
|
183
|
-
} else {
|
|
184
|
-
this.scanFiles.push(resolvePath)
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// 字符串转哈希码
|
|
189
|
-
private stringToHashCode(str: string) {
|
|
190
|
-
let hash = 0
|
|
191
|
-
if (str.length === 0) return hash
|
|
192
|
-
for (let i = 0; i < str.length; i++) {
|
|
193
|
-
const char = str.charCodeAt(i)
|
|
194
|
-
hash = (hash << 5) - hash + char
|
|
195
|
-
hash |= 0 // 转换为32位整数
|
|
196
|
-
}
|
|
197
|
-
return hash
|
|
198
|
-
}
|
|
199
|
-
}
|
|
@@ -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 ? [...new Set(...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', 'HMService']
|
|
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
|
-
}
|