ohpm使用 开发hvigor插件 ohpm配置以及plugin仓库配置
import ts from 'typescript' , 一开始在 hvigorfile.ts 找不到,直接把,粘贴到鸿蒙项目中
import { hvigor, HvigorNode, HvigorPlugin, Json5Reader } from '@ohos/hvigor';
import { harTasks, OhosHapContext, OhosPluginId, Target, OhosHarContext, OhosHspContext } from '@ohos/hvigor-ohos-plugin';
import fs from 'fs'
import path from 'path'
import ts from 'typescript'
const appRouter = "AppRouter"
// 实现自定义插件
export function customPlugin(): HvigorPlugin {
return {
pluginId: 'customPlugin',
context() {
return {
data: 'customPlugin'
};
},
async apply(currentNode: HvigorNode): Promise<void> {
hvigor.nodesEvaluated(async () => {
// 注册模块级任务
hapTask(currentNode);
});
}
};
}
function hapTask(node: HvigorNode) {
// hvigor向外部暴露的hvigor对象 通过此对象可以操作hvigor运行中的内容
hvigor.nodesEvaluated(async () => {
// let conf = readConfig(node)
// 获取har模块上下文信息
const context = node.getContext(OhosPluginId.OHOS_HAR_PLUGIN) as OhosHarContext;
let packageJson = await Json5Reader.readJson5File(
path.resolve(node.getNodePath(), './oh-package.json5'),
'utf-8'
)
if (!context) {
throw new Error('hspContext is null')
}
let pluginId = OhosPluginId.OHOS_HAR_PLUGIN
node.getAllTasks().forEach((v,k)=>{
logUtil(`allTasks = ${ v.getName()}`)
})
context.targets((target: Target) => {
const targetName = target.getTargetName()
node.registerTask({
name: `${targetName}@MyPLugin`,
run: () => {
MyPLugin(context, node)
let taskName = 'Package' + pluginId.split('.')[2].charAt(0).toUpperCase() + pluginId.split('.')[2].slice(1)
console.error(`nzy - >targetName ${targetName}`)
console.error(`nzy - >taskName ${taskName}`)
node.getTaskByName(`${targetName}@${taskName}`)?.afterRun(() => {
// deleteGeneratorFile(conf)
})
},
dependencies: [`${targetName}@PreBuild`],
postDependencies: [`${targetName}@MergeProfile`]
})
})
})
}
function MyPLugin(
context: OhosHarContext,
node: HvigorNode
) {
let startTime = Date.now()
console.log(node.getNodeName() + ' 开始分析' + startTime)
// 遍历 node.getNodePath() 下面的所有文件
let lists: string[] = getEtsFiles(node.getNodePath() + "/src/main/ets")
node.getAllTasks().forEach((v,k)=>{
logUtil(`allTasks = ${ v.getName()}`)
})
logUtil('找到了 ' + lists.length + " 个文件---\n" + lists)
let filsPath: AppRouterFile[] = []
lists.forEach(filePath => {
const analyzer = new Analyzer(filePath)
analyzer.start()
for (let [key, value] of analyzer.analyzeResultMap) {
console.log('analyzeResult=>' + key + "----" + JSON.stringify(value))
// analyzeResult=>undefined----{"annotation":"AppRouter","path":"c1","name":"C1"}
let record = value as Record<string, string>
if (value.name) {
filsPath.push({ filePath: filePath, path: record['path'] })
}
}
})
logUtil(node.getNodePath())
// 判断是否有Index.ets
let path = node.getNodePath() + "/Index1.ets"
let exist = fs.existsSync(path)
/**
*
import { BuilderNameConstants } from '@ohos/routermodule';
export function harInit(builderName: string): void {
// 动态引入要跳转的页面
switch (builderName) {
case BuilderNameConstants.HARC_C1:
import("./src/main/ets/components/mainpage/C1");
break;
case BuilderNameConstants.HARC_C2:
import("./src/main/ets/components/mainpage/C2");
break;
default:
break;
}
}
*/
let stringBuilder = new StringBuilder()
stringBuilder.appendLine("import { BuilderNameConstants } from '@ohos/routermodule';")
stringBuilder.appendLine("")
stringBuilder.appendLine("export function harInit(builderName: string): void {")
stringBuilder.appendLine(" switch (builderName) {")
// 这里可以for循环遍历了
filsPath.forEach((router) => {
stringBuilder.append(" case ")
stringBuilder.append(`"${router.path}"`)
stringBuilder.appendLine(":")
const srcIndex = router.filePath.indexOf('/src');
// 截取路径
const result = `.${router.filePath.substring(srcIndex, router.filePath.lastIndexOf('.ets'))}`;
stringBuilder.appendLine(` import("${result}")`)
stringBuilder.appendLine(" break;")
})
stringBuilder.appendLine(" }")
stringBuilder.appendLine("}")
if (exist) {
fs.unlinkSync(path)
}
fs.writeFileSync(path, stringBuilder.toString())
let endTime = Date.now()
console.log(node.getNodeName() + ' 结束耗时 ' + (endTime - startTime) + " ms")
}
// 遍历 AST
function findClasses(node: ts.Node) {
ts.forEachChild(node, child => {
console.log(`节点类型: ${ts.SyntaxKind[node.kind]}`); // 打印节点类型
// resolveNode(child)
// 检查节点是否是类声明
// if (ts.isClassDeclaration(child)) {
// console.log(`Found class: ${child.name?.text}`);
// }
// // 递归遍历子节点
findClasses(child);
if (ts.isClassDeclaration(node)) {
const className = node.name ? node.name.text : '匿名类';
console.log(`找到类:${className}`);
// 提取装饰器
// node.decorators.forEach(decorator => {
// const decoratorName = decorator.expression.escapedText;
// console.log(` 装饰器:${decoratorName}`);
// });
}
});
}
function getEtsFiles(dir: string): string[] {
let files: string[] = [];
// 读取目录内容
const items = fs.readdirSync(dir);
for (const item of items) {
const itemPath = path.join(dir, item);
const stat = fs.statSync(itemPath);
// 如果是目录,则递归调用
if (stat.isDirectory()) {
files = files.concat(getEtsFiles(itemPath));
} else if (item.endsWith('.ets')) {
// 如果是以 .ets 结尾的文件,添加到数组中
files.push(itemPath);
}
}
return files;
}
export default {
system: harTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
plugins: [customPlugin()] /* Custom plugin to extend the functionality of Hvigor. */
};
function logUtil(content: string) {
console.log(`nzy-> ${content}`)
}
export type AnalyzerResultLike = HMRouterResult
export class BaseAnalyzeResult {
name?: string // 类名
module?: string // 模块名
annotation?: string // 注解
}
export class HMRouterResult extends BaseAnalyzeResult {
pageUrl?: string // 跳转路径
dialog?: boolean // 是否弹窗
singleton?: boolean // 是否单例
interceptors?: string[] // 拦截器
animator?: string // 动画
lifecycle?: string // 生命周期
}
class NodeInfo {
value?: any
}
export class Analyzer {
private HMRouter = "AppRouter"
customAnnotationExisted: boolean = false
analyzeResultMap: Map<string, AnalyzerResultLike> = new Map()
private sourcePath: string
private pluginConfig:string[] = [this.HMRouter]
private analyzeResult: AnalyzerResultLike = {}
private keywordPos: number = 0
constructor(sourcePath: string) {
this.sourcePath = sourcePath
}
start() {
const sourceCode = fs.readFileSync(this.sourcePath, 'utf-8')
const sourceFile = ts.createSourceFile(
this.sourcePath,
sourceCode,
ts.ScriptTarget.Latest,
false
)
ts.forEachChild(sourceFile, node => {
this.resolveNode(node)
// 解析完成后赋值存进Map
switch (this.analyzeResult.annotation) {
case this.HMRouter:
this.analyzeResultMap.set(
(this.analyzeResult as HMRouterResult).pageUrl!,
this.analyzeResult
)
break
}
})
}
private resolveNode(node: ts.Node) {
if (ts.isMissingDeclaration(node)) {
this.resolveMissingDeclaration(node)
} else if (ts.isClassDeclaration(node)) {
this.resolveClass(node)
} else if (ts.isDecorator(node)) {
this.resolveDecorator(node)
} else if (ts.isCallExpression(node)) {
this.resolveCallExpression(node)
} else if (ts.isExpressionStatement(node)) {
this.resolveExpression(node)
} else if (ts.isBlock(node)) {
this.resolveBlock(node)
} else if (ts.isPropertyAssignment(node)) {
return this.resolvePropertyAccess(node)
} else if (ts.isIdentifier(node)) {
return this.resolveIdentifier(node)
} else if (ts.isStringLiteral(node)) {
return this.resolveStringLiteral(node)
} else if (node.kind === ts.SyntaxKind.TrueKeyword) {
let info = new NodeInfo()
info.value = true
return info
} else if (node.kind === ts.SyntaxKind.FalseKeyword) {
let info = new NodeInfo()
info.value = false
return info
} else if (ts.isNumericLiteral(node)) {
return this.resolveNumericLiteral(node)
} else if (ts.isArrayLiteralExpression(node)) {
let interceptors = this.resolveArrayLiteral(node)
let info = new NodeInfo()
info.value = interceptors
return info
}
}
private resolveMissingDeclaration(node: ts.MissingDeclaration) {
this.analyzeResult = {}
node.forEachChild(child => this.resolveNode(child))
}
private resolveClass(node: ts.ClassDeclaration) {
// 解析到类声明,先清空一次返回结果
this.analyzeResult = {}
node.modifiers?.forEach(modifier => {
// 遍历分析装饰器
this.resolveNode(modifier)
})
if (this.customAnnotationExisted) {
this.analyzeResult.name = node.name?.text
}
}
private resolveDecorator(node: ts.Decorator) {
if (ts.isCallExpression(node.expression)) {
const callExpression = node.expression as ts.CallExpression
if (ts.isIdentifier(callExpression.expression)) {
this.switchIdentifier(callExpression)
}
}
}
private switchIdentifier(callExpression: ts.CallExpression) {
const identifier = callExpression.expression as ts.Identifier
if (this.pluginConfig.some(item => item === identifier.text)) {
this.customAnnotationExisted = true
// 区分是什么装饰器,构造不同的返回类
switch (identifier.text) {
case this.HMRouter:
this.analyzeResult = new HMRouterResult()
this.analyzeResult.annotation = this.HMRouter
break
}
if (callExpression.arguments.length > 0) {
this.resolveCallExpression(callExpression)
}
}
}
private resolveCallExpression(node: ts.CallExpression) {
let identifier = this.resolveNode(node.expression)
this.parseAnnotation(node.arguments, identifier)
}
private resolveExpression(node: ts.ExpressionStatement) {
let identifier = this.resolveNode(node.expression)
if (identifier?.value === 'struct') {
this.keywordPos = node.end
}
if (this.analyzeResult.annotation === this.HMRouter && this.keywordPos === node.pos) {
this.analyzeResult.name = identifier?.value
}
}
private resolveBlock(node: ts.Block) {
node.statements.forEach(statement => {
this.resolveNode(statement)
})
}
private parseAnnotation(args: ts.NodeArray<ts.Expression>, nodeInfo?: NodeInfo) {
if (this.pluginConfig.some(item => nodeInfo?.value === item)) {
args
.flatMap((e: ts.Expression) => (e as ts.ObjectLiteralExpression).properties)
.forEach((e: ts.ObjectLiteralElementLike) => {
this.parseConfig(e, this.analyzeResult)
})
}
}
private parseConfig(node: ts.ObjectLiteralElementLike, result: AnalyzerResultLike) {
let info = this.resolveNode(node)
console.log(`-----${info?.value['key']}----${info?.value['value']}----`)
Reflect.set(result, info?.value['key'], info?.value['value'])
}
private resolveArrayLiteral(node: ts.ArrayLiteralExpression) {
return node.elements.map(e => this.resolveNode(e)?.value as string)
}
private resolvePropertyAccess(node: ts.PropertyAssignment) {
let propertyName = this.resolveNode(node.name)?.value
let propertyValue = this.resolveNode(node.initializer)?.value
let info = new NodeInfo()
info.value = { key: propertyName, value: propertyValue }
return info
}
private resolveNumericLiteral(node: ts.NumericLiteral) {
let info = new NodeInfo()
info.value = Number(node.text)
console.log(`-----${node.text}----`)
return info
}
private resolveStringLiteral(node: ts.StringLiteral) {
let info = new NodeInfo()
info.value = node.text
console.log(`-----${node.text}----`)
return info
}
private resolveIdentifier(node: ts.Identifier) {
let info = new NodeInfo()
info.value = node.escapedText.toString()
console.log(`-----${node.escapedText.toString()}----`)
return info
}
}
class StringBuilder {
private strings: string[] = [];
// 添加字符串
append(str: string): void {
this.strings.push(str);
}
// 添加字符串并换行
appendLine(str: string): void {
this.strings.push(str + '\n');
}
// 获取拼接后的字符串
toString(): string {
return this.strings.join('');
}
// 清空内容
clear(): void {
this.strings = [];
}
}
interface AppRouterFile {
filePath: string,
path: string,
}