router-register-plugin
Version:
鸿蒙ZRouter动态路由框架插件
509 lines (456 loc) • 24 kB
text/typescript
import {AnalyzerParam, AnalyzerResult, Annotation, AnnotationType, ScanFileParam} from "./models/model";
import {logger, loggerE, loggerNode} from "./utils/logger";
import {readFileSync} from "fs";
import ts, {
isClassDeclaration,
isDecorator,
isExportSpecifier,
isIdentifier,
isImportSpecifier,
isNamespaceImport,
isPropertyAccessExpression, isPropertyDeclaration,
isStringLiteral, PropertyAccessExpression
} from "ohos-typescript";
import {isEmpty, isNotEmpty} from "./utils/string";
import * as path from "node:path";
import * as fs from "node:fs";
import Constants from "./models/constants";
import FileHelper from "./utils/file-helper";
import AnnotationMgr from "./utils/annotation-mgr";
import NodeHelper from "./utils/ats-node";
type TempAnalyzerResult = Omit<AnalyzerResult, 'name'> & { name: keyof any }
const annotation = new Annotation()
const IDENTIFIER_AS_ANNOTATION = Symbol('IDENTIFIER_AS_ANNOTATION')
/**
* 扫描一个ets文件,获取路由相关信息
*/
class Analyzer {
// 扫描的ets文件路径
private readonly filePath: string = ""
// 一个文件可能存在多个组件
results: Array<AnalyzerResult> = []
result: TempAnalyzerResult = new AnalyzerResult()
// 导入的所有文件
importedFiles: Map<string[], string> = new Map<string[], string>()
// 模块名称
modName: string = ""
// 路由上常量的相关参数
fileParam?: ScanFileParam | undefined
// 模块的绝对路径
modDir: string = ""
constructor(analyzerParam: AnalyzerParam, fileParam?: ScanFileParam) {
this.filePath = analyzerParam.scanFilePath;
this.modName = analyzerParam.modName
this.modDir = analyzerParam.modDir
this.fileParam = fileParam
}
start() {
logger('Analyzer start', this.modName, this.fileParam)
logger('Analyzer filePath: ', this.filePath)
// 读取文件内容
const sourceCode = readFileSync(this.filePath, "utf-8");
// loggerNode('Analyzer sourceCode: ', sourceCode)
// 解析文件内容,生成节点树信息
const sourceFile = ts.createSourceFile(this.filePath, sourceCode, ts.ScriptTarget.ES2021, false, ts.ScriptKind.ETS);
// logger('Analyzer sourceFile: ', sourceFile)
// 遍历节点信息
ts.forEachChild(sourceFile, (node: ts.Node) => {
// 解析节点
try {
this.resolveNode(node);
} catch (e) {
console.error('forEachChild error: ', e);
}
});
}
private resolveNode(node: ts.Node) {
logger('resolveNode: ', node.kind, ' ---start')
loggerNode('resolveNode node: ', node)
let isDefault = false
switch (node.kind) {
// class
case ts.SyntaxKind.ClassDeclaration:
this.resolveClassDeclaration(node as ts.ClassDeclaration)
break
// export
case ts.SyntaxKind.ExportDeclaration:
this.resolveExportDeclaration(node as ts.ExportDeclaration)
break
// import
case ts.SyntaxKind.ImportDeclaration:
this.resolveImportDeclaration(node as ts.ImportDeclaration)
break
case ts.SyntaxKind.StructDeclaration:
this.resolveStructDeclaration(node as ts.StructDeclaration)
break
// 装饰器节点
case ts.SyntaxKind.ExportAssignment:
case ts.SyntaxKind.MissingDeclaration:
if (node.kind === ts.SyntaxKind.ExportAssignment) {
isDefault = true
}
logger("resolveNode progress....", node.kind)
const child = node as ts.ParameterDeclaration
const modifiers = child.modifiers
// loggerNode('resolveNode modifiers: ', modifiers)
// @Component + @Route
if (modifiers && modifiers.length >= 2) {
modifiers.forEach((item) => {
try {
const result = this.resolveDecoration(item, isDefault);
logger("modifier result: ", result)
if (result.annotation !== AnnotationType.UNKNOWN) {
this.result = result
}
} catch (e) {
console.error('resolveNode error: ', e)
}
})
}
break;
default:
break
}
if (this.isNormalPage()) {
const item = this.results.find((item) => item.name === this.result.name)
if (!item) {
let r: AnalyzerResult = JSON.parse(JSON.stringify(this.result))
if (this.result.name === IDENTIFIER_AS_ANNOTATION) {
r.name = r.pageName
}
this.results.push(r)
this.result.reset()
logger('resolveNode AnalyzerResult: ', JSON.stringify(r), JSON.stringify(this.result))
// logger('results: ', JSON.stringify(this.results))
}
}
logger('resolveNode: ', node.kind, ' ---end')
}
// 解析class
private resolveClassDeclaration(node: ts.ClassDeclaration) {
const absPath = this.fileParam?.absolutePath
logger("resolveClassDeclaration result: ", this.result)
logger("resolveClassDeclaration absolutePath: ", absPath)
if (isEmpty(this.result.name) && isEmpty(absPath)) {
node.modifiers?.forEach((item) => {
if (isDecorator(item)){
const result = this.resolveDecoration(item)
if (isNotEmpty(result.name)) this.result = result
}
})
if (AnnotationMgr.isServiceAnnotation(this.result.annotation)){
// 解析服务路由注解
this.result.pageName = NodeHelper.getClassName(node)
logger("解析服务路由注解结果: " , this.result)
}
if (AnnotationMgr.isLifecycleAnnotation(this.result.annotation)){
// 解析生命周期注解
this.result.pageName = NodeHelper.getClassName(node)
logger("解析生命周期路由注解结果: " , this.result)
}
if (AnnotationMgr.isAttrAnnotation(this.result.annotation)){
// 解析属性注解
this.result.pageName = NodeHelper.getClassName(node)
logger("解析属性注解结果: ", this.result)
}
return;
}
if (isEmpty(absPath) || !node.name || !isIdentifier(node.name)) return
if (this.fileParam && node.name.escapedText == this.fileParam?.className) {
node?.members?.forEach((member) => {
if (isPropertyDeclaration(member) && member.name
&& isIdentifier(member.name)) {
if (this.fileParam?.isFindAnnotationConst()){
if (member.name.escapedText
== this.fileParam?.attrName && member.initializer && isStringLiteral(member.initializer)) {
this.fileParam.attrValue = member.initializer.text
}
}
}
})
}
logger("resolveClassDeclaration: ", this.fileParam)
}
private resolveStructDeclaration(node: ts.StructDeclaration) {
const absPath = this.fileParam?.absolutePath
logger("resolveStructDeclaration result: ", this.result)
logger("resolveStructDeclaration absolutePath: ", absPath)
if (isEmpty(this.result.name) && isEmpty(absPath)) {
node.modifiers?.forEach((item) => {
if (isDecorator(item)){
const result = this.resolveDecoration(item)
if (isNotEmpty(result.name)) this.result = result
}
if (item.kind === ts.SyntaxKind.DefaultKeyword) {
this.result.isDefaultExport = true
}
})
if (AnnotationMgr.isRouteAnnotation(this.result.annotation)){
// 解析路由注解
this.result.pageName = NodeHelper.getClassName(node)
logger("解析路由注解结果: " , this.result)
}
}
}
// 解析Export
private resolveExportDeclaration(node: ts.ExportDeclaration) {
if (!this.fileParam) return
let importPath = ""
const map = new Map<string, string[]>()
const names: string[] = []
if (node.exportClause === undefined && node.moduleSpecifier) {
// export * from '../xxx'
// 不支持使用这种方式导出路由常量
if (isStringLiteral(node.moduleSpecifier)) {
let path = node.moduleSpecifier.text
names.push("*")
map.set(path, names)
}
} else {
node.exportClause?.forEachChild((child: ts.Node) => {
if (isExportSpecifier(child)) {
if (isIdentifier(child.name)) {
names.push(child.name.escapedText ?? "")
}
}
})
if (node.moduleSpecifier && isStringLiteral(node.moduleSpecifier)) {
let path = node.moduleSpecifier.text
if (names.length > 0) map.set(path, names)
}
}
const mapArr = [...map]
mapArr.forEach(([key, value]) => {
const has = value.includes(this.fileParam?.className || "")
if (has) {
importPath = key
}
})
logger('resolveExportDeclaration importPath: ', importPath)
if (isNotEmpty(importPath)) {
if (this.fileParam.isFindModuleIndexPath()) {
logger('resolveExportDeclaration current modDir: ', this.modDir)
logger("resolveExportDeclaration fileParam: ", this.fileParam)
this.fileParam.absolutePath =
path.resolve(this.modDir, this.fileParam.moduleSrcPath ||
this.fileParam.indexModuleName, importPath)
}
}
}
// 解析import导入的信息
private resolveImportDeclaration(node: ts.ImportDeclaration) {
const key: string[] = []
if (node.importClause?.namedBindings == undefined && node.importClause?.name != undefined) {
// import MyModule from './MyModule';
if (isIdentifier(node.importClause.name)) {
key.push(node.importClause.name.escapedText ?? "")
}
} else {
node.importClause?.namedBindings?.forEachChild(child => {
if (isImportSpecifier(child)) {
// import { ExportedItem1, ExportedItem2 } from './MyModule';
// import { ExportedItem as RenamedItem } from './MyModule';
if (isIdentifier(child.name)) {
key.push(child.name.escapedText ?? "")
}
} else if (isNamespaceImport(child)) {
// import * as MyModule from './MyModule';
const node = child as ts.NamespaceImport
if (isIdentifier(node.name)) {
key.push(node.name.escapedText ?? "")
}
}
});
}
logger("resolveImportDeclaration moduleSpecifier: ", node.moduleSpecifier.kind, key)
if (isStringLiteral(node.moduleSpecifier)) {
if (key.length > 0) {
this.importedFiles.set(key, node.moduleSpecifier.text)
}
}
const mapArr = [...this.importedFiles]
mapArr.forEach(([k, v]) => {
logger(`resolveImportDeclaration importedFiles k-v: ${k} : ${v}`)
})
}
private isExistAnnotation(): boolean {
return isNotEmpty(this.result.name)
}
private isNormalPage() {
return isNotEmpty(this.result.pageName) && this.isExistAnnotation()
}
// 解析装饰器
private resolveDecoration(node: ts.Node, isDefaultExport: boolean = false, from?: number) {
const result: TempAnalyzerResult = new AnalyzerResult()
// 转换为装饰器节点类型
let decorator = node as ts.Decorator;
logger('resolveDecoration kind: ' + decorator?.kind, from)
loggerNode(`resolveDecoration Decorator: `, JSON.stringify(decorator))
logger(JSON.stringify(result))
// 判断表达式是否是标识符
if (decorator.expression && ts.isIdentifier(decorator.expression)) {
const identifier = decorator.expression
if (annotation.annotations.includes(identifier.text)) {
logger(`resolveDecoration text: ${identifier.text} ${identifier.escapedText}`)
result.isDefaultExport = isDefaultExport
result.annotation = AnnotationMgr.getAnnotation(identifier.text)
result.name = IDENTIFIER_AS_ANNOTATION
}
}
// 判断表达式是否是函数调用
if (decorator.expression?.kind === ts.SyntaxKind.CallExpression) {
const callExpression = decorator.expression as ts.CallExpression;
// 表达式类型是否是标识符
if (callExpression.expression?.kind === ts.SyntaxKind.Identifier) {
const identifier = callExpression.expression as ts.Identifier;
// 标识符是否是自定义的装饰器
logger(`resolveDecoration text: ${identifier.text} ${identifier.escapedText}`)
const args = callExpression.arguments
if (annotation.annotations.includes(identifier.text) && args) {
const arg = args[0];
result.isDefaultExport = isDefaultExport
result.annotation = AnnotationMgr.getAnnotation(identifier.text)
result.name = IDENTIFIER_AS_ANNOTATION
loggerNode(`resolveDecoration arg: `, JSON.stringify(arg))
// 调用方法的第一个参数是否是表达式
if (arg?.kind === ts.SyntaxKind.ObjectLiteralExpression) {
const properties = (arg as ts.ObjectLiteralExpression).properties;
logger(`resolveDecoration properties length: ${properties.length}`)
// 遍历装饰器中的所有参数
properties?.forEach((propertie) => {
loggerNode(`resolveDecoration properties item: `, JSON.stringify(propertie))
if (propertie?.kind === ts.SyntaxKind.PropertyAssignment) {
// 参数是否是自定义装饰器中的变量名
const text = (propertie.name as ts.Identifier).escapedText
if (text === annotation.name) {
// 将装饰器中的变量的值赋值给解析结果中的变量
if (isStringLiteral(propertie.initializer)) {
// 装饰器上的值是字符串
result.name = (propertie.initializer as ts.StringLiteral).text;
}
if (isPropertyAccessExpression(propertie.initializer)) {
// 装饰器上的常量
result.name = this.resolveConstantOnAnnotations(propertie.initializer)
}
}
if (text === annotation.description) {
result.description = (propertie.initializer as ts.StringLiteral).text;
}
if (text === annotation.extra) {
result.extra = (propertie.initializer as ts.StringLiteral).text;
}
if (text === annotation.needLogin) {
result.needLogin = NodeHelper.getBooleanByKind(propertie.initializer.kind)
}
if (text === annotation.useTemplate) {
result.useTemplate = NodeHelper.getBooleanByKind(propertie.initializer.kind)
}
if (text === annotation.title) {
result.title = (propertie.initializer as ts.StringLiteral).text;
}
if (text === annotation.useV2) {
result.useV2 = NodeHelper.getBooleanByKind(propertie.initializer.kind)
}
if (text === annotation.hideTitleBar){
result.hideTitleBar = NodeHelper.getBooleanByKind(propertie.initializer.kind)
}
if (text === annotation.lifecycleObserverAttributeName){
result.loAttributeName = (propertie.initializer as ts.StringLiteral).text;
}
// 解析路由参数
if (text === annotation.param){
this.resolveAnnotationArgs(propertie, result);
}
}
})
}
}
}
}
return result
// logger('resolveDecoration end')
}
private resolveAnnotationArgs(propertie: ts.PropertyAssignment, result: TempAnalyzerResult) {
// 路由参数是一个对象
if (propertie.initializer.kind === ts.SyntaxKind.ObjectLiteralExpression) {
const paramObject = propertie.initializer as ts.ObjectLiteralExpression;
const params: Record<string, { default: any }> = {};
const paramStrings: string[] = [];
// 遍历参数对象中的所有属性
paramObject.properties.forEach(paramProp => {
if (paramProp.kind === ts.SyntaxKind.PropertyAssignment) {
const paramProperty = paramProp as ts.PropertyAssignment;
const paramName = (paramProperty.name as ts.StringLiteral | ts.Identifier).text ||
(paramProperty.name as ts.Identifier).escapedText?.toString();
if (paramProperty.initializer.kind === ts.SyntaxKind.ObjectLiteralExpression) {
const defaultObj = paramProperty.initializer as ts.ObjectLiteralExpression;
defaultObj.properties.forEach(defaultProp => {
if (defaultProp.kind === ts.SyntaxKind.PropertyAssignment) {
const defaultProperty = defaultProp as ts.PropertyAssignment;
const propName = (defaultProperty.name as ts.Identifier).escapedText?.toString();
if (propName === 'default') {
let defaultValue: any;
// 根据类型解析默认值
if (defaultProperty.initializer.kind === ts.SyntaxKind.NumericLiteral) {
defaultValue = Number((defaultProperty.initializer as ts.NumericLiteral).text);
paramStrings.push(`${paramName}: param?.${paramName} as number || ${defaultValue}`);
} else if (defaultProperty.initializer.kind === ts.SyntaxKind.StringLiteral) {
defaultValue = (defaultProperty.initializer as ts.StringLiteral).text;
paramStrings.push(`${paramName}: param?.${paramName} as string || ${defaultValue}`);
} else if (defaultProperty.initializer.kind === ts.SyntaxKind.TrueKeyword ||
defaultProperty.initializer.kind === ts.SyntaxKind.FalseKeyword) {
defaultValue = defaultProperty.initializer.kind === ts.SyntaxKind.TrueKeyword;
paramStrings.push(`${paramName}: param?.${paramName} as boolean || ${defaultValue}`);
} else {
defaultValue = null;
paramStrings.push(`${paramName}: param?.${paramName} || null`);
}
// 保存参数信息
if (paramName) params[paramName] = {default: defaultValue};
}
}
});
}
}
});
// 保存参数对象和字符串形式
result.param = params;
result.paramStr = paramStrings.join(',\n\t\t');
logger(`Parsed param: ${JSON.stringify(params)}`);
logger(`Param string: ${result.paramStr}`);
}
}
private resolveConstantOnAnnotations(initializer: PropertyAccessExpression) {
// 装饰器上的值是常量
const fileParam = new ScanFileParam()
let constValue = ""
if (isIdentifier(initializer.expression)) {
fileParam.className = initializer.expression.escapedText ?? ""
}
if (isIdentifier(initializer.name)) {
fileParam.attrName = initializer.name.escapedText ?? ""
}
this.importedFiles.forEach((value, key) => {
const target = key.find((item) => item == fileParam.className)
if (isNotEmpty(target)) {
fileParam.importPath = value
fileParam.absolutePath = FileHelper.getImportAbsolutePathByOHPackage(value,
AnalyzerParam.create(this.filePath, this.modName, this.modDir), fileParam) + Constants.ETS_SUFFIX
}
})
if (isNotEmpty(fileParam.absolutePath) && fs.existsSync(fileParam.absolutePath)) {
fileParam.actionType = Constants.TYPE_FIND_ANNOTATION_CONST_VALUE
let analyzer = new Analyzer(AnalyzerParam.create(fileParam.absolutePath, this.modName, this.modDir), fileParam)
analyzer.start()
constValue = analyzer?.fileParam?.attrValue || ""
} else {
loggerE("路径不存在:", fileParam.absolutePath)
}
logger("常量解析结果: ", JSON.stringify(fileParam))
if (isEmpty(constValue)) {
loggerE("常量解析失败:", fileParam.className, fileParam.attrName)
}
return constValue
}
}
export {Analyzer}