mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-12-18 20:30:08 +08:00
Updated the performance monitor Vite plugin to add explicit type annotations to AST traversal callback parameters, improving type safety and clarity. Also removed a duplicate import in src/plugin/index.ts.
306 lines
11 KiB
TypeScript
306 lines
11 KiB
TypeScript
import { Plugin } from 'vite';
|
||
import { parse } from '@babel/parser';
|
||
import traverseDefault from '@babel/traverse';
|
||
import generateDefault from '@babel/generator';
|
||
import * as t from '@babel/types';
|
||
import { resolve } from 'path';
|
||
|
||
// @ts-ignore
|
||
const traverse = traverseDefault.default || traverseDefault;
|
||
// @ts-ignore
|
||
const generate = generateDefault.default || generateDefault;
|
||
|
||
interface PerformancePluginOptions {
|
||
}
|
||
|
||
/**
|
||
* Vite插件:自动在函数中插入性能监控代码
|
||
*/
|
||
export function performanceMonitorPlugin(options: PerformancePluginOptions): Plugin {
|
||
const exclude = [/node_modules/, /\.min\./, /performance-monitor\.ts$/];
|
||
|
||
return {
|
||
name: 'performance-monitor',
|
||
transform(code: string, id: string) {
|
||
const fileName = id.replace(process.cwd(), '').replace(/\\/g, '/');
|
||
|
||
// 排除规则检查
|
||
if (exclude.some(pattern => pattern.test(id))) {
|
||
return null;
|
||
}
|
||
|
||
try {
|
||
// 解析AST
|
||
const ast = parse(code, {
|
||
sourceType: 'module',
|
||
plugins: [
|
||
'typescript',
|
||
'decorators-legacy',
|
||
'classProperties',
|
||
'asyncGenerators',
|
||
'bigInt',
|
||
'dynamicImport',
|
||
'exportDefaultFrom',
|
||
'exportNamespaceFrom',
|
||
'nullishCoalescingOperator',
|
||
'numericSeparator',
|
||
'optionalCatchBinding',
|
||
'optionalChaining',
|
||
'topLevelAwait'
|
||
]
|
||
});
|
||
|
||
let hasMonitorImport = false;
|
||
let hasMonitorExport = false;
|
||
let needsMonitor = false;
|
||
// 遍历AST
|
||
traverse(ast, {
|
||
// 检查是否已经导入了性能监控器
|
||
ImportDeclaration(path: { node: { source: { value: string | string[]; }; }; }) {
|
||
if (path.node.source.value.includes('performance-monitor')) {
|
||
hasMonitorImport = true;
|
||
}
|
||
},
|
||
|
||
// 检查是否已经导出了性能监控器
|
||
ExportNamedDeclaration(path: { node: { declaration: t.Node | null | undefined; }; }) {
|
||
if (path.node.declaration && t.isVariableDeclaration(path.node.declaration)) {
|
||
path.node.declaration.declarations.forEach((declarator: { id: t.Node | null | undefined; }) => {
|
||
if (t.isIdentifier(declarator.id) && declarator.id.name === 'performanceMonitor') {
|
||
hasMonitorExport = true;
|
||
}
|
||
});
|
||
}
|
||
},
|
||
|
||
// 检查变量声明
|
||
VariableDeclaration(path: { node: { declarations: any[]; }; }) {
|
||
path.node.declarations.forEach((declarator: { id: t.Node | null | undefined; }) => {
|
||
if (t.isIdentifier(declarator.id) && declarator.id.name === 'performanceMonitor') {
|
||
hasMonitorExport = true;
|
||
}
|
||
});
|
||
},
|
||
|
||
// 处理函数声明
|
||
FunctionDeclaration(path: { node: { id: { name: string; }; loc: { start: { line: number; }; }; }; }) {
|
||
const functionName = path.node.id?.name || 'anonymous';
|
||
const lineNumber = path.node.loc?.start.line || 0;
|
||
|
||
instrumentFunction(path, functionName, fileName, lineNumber);
|
||
needsMonitor = true;
|
||
},
|
||
|
||
// 处理箭头函数
|
||
ArrowFunctionExpression(path: { parent: any; node: { loc: { start: { line: number; }; }; }; }) {
|
||
const parent = path.parent;
|
||
let functionName = 'anonymous';
|
||
|
||
if (t.isVariableDeclarator(parent) && t.isIdentifier(parent.id)) {
|
||
functionName = parent.id.name;
|
||
} else if (t.isProperty(parent) && t.isIdentifier(parent.key)) {
|
||
functionName = parent.key.name;
|
||
} else if (t.isAssignmentExpression(parent) && t.isMemberExpression(parent.left)) {
|
||
const property = parent.left.property;
|
||
if (t.isIdentifier(property)) {
|
||
functionName = property.name;
|
||
}
|
||
}
|
||
|
||
const lineNumber = path.node.loc?.start.line || 0;
|
||
instrumentFunction(path, functionName, fileName, lineNumber);
|
||
needsMonitor = true;
|
||
},
|
||
|
||
// 处理函数表达式
|
||
FunctionExpression(path: { node: { id: { name: string; }; loc: { start: { line: number; }; }; }; }) {
|
||
const functionName = path.node.id?.name || 'anonymous';
|
||
const lineNumber = path.node.loc?.start.line || 0;
|
||
|
||
instrumentFunction(path, functionName, fileName, lineNumber);
|
||
needsMonitor = true;
|
||
},
|
||
|
||
// 处理类方法
|
||
ClassMethod(path: { node: { key: t.Node | null | undefined; loc: { start: { line: number; }; }; }; }) {
|
||
const methodName = t.isIdentifier(path.node.key) ? path.node.key.name : 'anonymous';
|
||
const className = getClassName(path);
|
||
const functionName = `${className}.${methodName}`;
|
||
const lineNumber = path.node.loc?.start.line || 0;
|
||
|
||
instrumentFunction(path, functionName, fileName, lineNumber);
|
||
needsMonitor = true;
|
||
},
|
||
|
||
// 处理对象方法
|
||
ObjectMethod(path: { node: { key: t.Node | null | undefined; loc: { start: { line: number; }; }; }; }) {
|
||
const methodName = t.isIdentifier(path.node.key) ? path.node.key.name : 'anonymous';
|
||
const lineNumber = path.node.loc?.start.line || 0;
|
||
|
||
instrumentFunction(path, methodName, fileName, lineNumber);
|
||
needsMonitor = true;
|
||
}
|
||
});
|
||
|
||
if (!needsMonitor) {
|
||
return null;
|
||
}
|
||
|
||
// 如果需要监控但还没有导入且没有导出,则添加导入语句
|
||
if (!hasMonitorImport && !hasMonitorExport) {
|
||
const importDeclaration = t.importDeclaration(
|
||
[t.importSpecifier(t.identifier('performanceMonitor'), t.identifier('performanceMonitor'))],
|
||
t.stringLiteral('@/common/performance-monitor')
|
||
);
|
||
ast.program.body.unshift(importDeclaration);
|
||
}
|
||
|
||
// 生成新代码
|
||
const result = generate(ast, {
|
||
retainLines: true,
|
||
compact: false
|
||
});
|
||
|
||
return {
|
||
code: result.code,
|
||
map: result.map
|
||
};
|
||
|
||
} catch (error) {
|
||
console.warn(`性能监控插件处理文件 ${id} 时出错:`, error);
|
||
return null;
|
||
}
|
||
}
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 为函数添加性能监控代码
|
||
*/
|
||
function instrumentFunction(
|
||
path: any,
|
||
functionName: string,
|
||
fileName: string,
|
||
lineNumber: number
|
||
) {
|
||
// 跳过已经被监控的函数
|
||
if (functionName.includes('__perf_monitor__')) {
|
||
return;
|
||
}
|
||
|
||
const isAsync = path.node.async;
|
||
const body = path.node.body;
|
||
|
||
// 确保函数体是块语句
|
||
if (!t.isBlockStatement(body)) {
|
||
// 对于箭头函数的表达式体,转换为块语句
|
||
const returnStatement = t.returnStatement(body);
|
||
path.node.body = t.blockStatement([returnStatement]);
|
||
}
|
||
|
||
const blockBody = path.node.body as t.BlockStatement;
|
||
|
||
// 生成唯一的调用ID变量名
|
||
const callIdVar = `__perf_monitor_${functionName.replace(/[^a-zA-Z0-9]/g, '_')}_${lineNumber}__`;
|
||
|
||
// 创建开始监控的语句
|
||
const startMonitoring = t.variableDeclaration('const', [
|
||
t.variableDeclarator(
|
||
t.identifier(callIdVar),
|
||
t.callExpression(
|
||
t.memberExpression(
|
||
t.identifier('performanceMonitor'),
|
||
t.identifier('startFunction')
|
||
),
|
||
[
|
||
t.stringLiteral(functionName),
|
||
t.stringLiteral(fileName),
|
||
t.numericLiteral(lineNumber)
|
||
]
|
||
)
|
||
)
|
||
]);
|
||
|
||
// 创建结束监控的语句
|
||
const endMonitoring = t.expressionStatement(
|
||
t.callExpression(
|
||
t.memberExpression(
|
||
t.identifier('performanceMonitor'),
|
||
t.identifier('endFunction')
|
||
),
|
||
[
|
||
t.identifier(callIdVar),
|
||
t.stringLiteral(functionName)
|
||
]
|
||
)
|
||
);
|
||
|
||
if (isAsync) {
|
||
// 对于异步函数,需要在所有可能的返回点添加监控结束
|
||
instrumentAsyncFunction(blockBody, startMonitoring, endMonitoring, callIdVar, functionName);
|
||
} else {
|
||
// 对于同步函数,使用try-finally确保监控结束
|
||
instrumentSyncFunction(blockBody, startMonitoring, endMonitoring);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 为同步函数添加监控
|
||
*/
|
||
function instrumentSyncFunction(
|
||
blockBody: t.BlockStatement,
|
||
startMonitoring: t.VariableDeclaration,
|
||
endMonitoring: t.ExpressionStatement
|
||
) {
|
||
const originalStatements = [...blockBody.body];
|
||
|
||
const tryStatement = t.tryStatement(
|
||
t.blockStatement(originalStatements),
|
||
null,
|
||
t.blockStatement([endMonitoring])
|
||
);
|
||
|
||
blockBody.body = [startMonitoring, tryStatement];
|
||
}
|
||
|
||
/**
|
||
* 为异步函数添加监控
|
||
*/
|
||
function instrumentAsyncFunction(
|
||
blockBody: t.BlockStatement,
|
||
startMonitoring: t.VariableDeclaration,
|
||
endMonitoring: t.ExpressionStatement,
|
||
callIdVar: string,
|
||
functionName: string
|
||
) {
|
||
const originalStatements = [...blockBody.body];
|
||
|
||
// 创建包装的异步执行体
|
||
const asyncTryStatement = t.tryStatement(
|
||
t.blockStatement(originalStatements),
|
||
null,
|
||
t.blockStatement([endMonitoring])
|
||
);
|
||
|
||
blockBody.body = [startMonitoring, asyncTryStatement];
|
||
}
|
||
|
||
/**
|
||
* 获取类名
|
||
*/
|
||
function getClassName(path: any): string {
|
||
let current = path;
|
||
while (current) {
|
||
if (current.isClassDeclaration && current.isClassDeclaration()) {
|
||
return current.node.id?.name || 'AnonymousClass';
|
||
} else if (current.isClassExpression && current.isClassExpression()) {
|
||
return current.node.id?.name || 'AnonymousClass';
|
||
} else if (current.node && (t.isClassDeclaration(current.node) || t.isClassExpression(current.node))) {
|
||
return current.node.id?.name || 'AnonymousClass';
|
||
}
|
||
current = current.parent;
|
||
}
|
||
return 'UnknownClass';
|
||
}
|
||
|
||
export default performanceMonitorPlugin; |