UNPKG

router-register-plugin

Version:

鸿蒙ZRouter动态路由框架插件

509 lines (456 loc) 24 kB
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}