mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-12-19 05:05:44 +08:00
* fix * Refactor PacketApi status checks and fix typos Replaced all usages of PacketApi.available with PacketApi.packetStatus for more accurate status checking. Fixed a typo in getFullQQVesion to getFullQQVersion. Updated plugin_onmessage to fetch and parse messages for a user. These changes improve reliability and consistency in API status handling. * Fix typo in getFullQQVersion method name Corrected the method name from getFullQQVesion to getFullQQVersion in multiple locations to ensure consistency and prevent potential runtime errors. * Remove performance CLI and demo, fix typos, update proto Deleted the performance CLI and demo files. Fixed a typo in getFullQQVesion to getFullQQVersion across multiple files. Changed the 'time' field type from UINT32 to UINT64 in Oidb.0x9067_202 proto. Commented out performanceMonitorPlugin in vite.config.ts. Removed an unimplemented log statement in NodeIKernelBuddyListener. * Comment out default plugin adapter registration The default registration of OB11PluginAdapter in NapCatOneBot11Adapter was commented out, likely to prevent automatic plugin loading or to allow for more flexible plugin management. Also, removed an unnecessary blank line in the plugin_onmessage function. * fix * Add shell-analysis mode with performance monitoring Introduces a new .env.shell-analysis file and a dev:shell-analysis npm script for building in shell-analysis mode. Updates vite.config.ts to support the new mode, enabling the performance monitor plugin with an updated exclude list. Also extends the plugin's exclude patterns to filter out 'packet' files. * Delete performance-api.ts * Add commented export for performance-monitor Added a commented-out export statement for '@/common/performance-monitor' in napcat.ts, possibly for future use or reference. No functional changes to the file.
307 lines
11 KiB
TypeScript
307 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$/, /packet/];
|
||
|
||
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) {
|
||
if (path.node.source.value.includes('performance-monitor')) {
|
||
hasMonitorImport = true;
|
||
}
|
||
},
|
||
|
||
// 检查是否已经导出了性能监控器
|
||
ExportNamedDeclaration(path) {
|
||
if (path.node.declaration && t.isVariableDeclaration(path.node.declaration)) {
|
||
path.node.declaration.declarations.forEach(declarator => {
|
||
if (t.isIdentifier(declarator.id) && declarator.id.name === 'performanceMonitor') {
|
||
hasMonitorExport = true;
|
||
}
|
||
});
|
||
}
|
||
},
|
||
|
||
// 检查变量声明
|
||
VariableDeclaration(path) {
|
||
path.node.declarations.forEach(declarator => {
|
||
if (t.isIdentifier(declarator.id) && declarator.id.name === 'performanceMonitor') {
|
||
hasMonitorExport = true;
|
||
}
|
||
});
|
||
},
|
||
|
||
// 处理函数声明
|
||
FunctionDeclaration(path) {
|
||
const functionName = path.node.id?.name || 'anonymous';
|
||
const lineNumber = path.node.loc?.start.line || 0;
|
||
|
||
instrumentFunction(path, functionName, fileName, lineNumber);
|
||
needsMonitor = true;
|
||
},
|
||
|
||
// 处理箭头函数
|
||
ArrowFunctionExpression(path) {
|
||
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) {
|
||
const functionName = path.node.id?.name || 'anonymous';
|
||
const lineNumber = path.node.loc?.start.line || 0;
|
||
|
||
instrumentFunction(path, functionName, fileName, lineNumber);
|
||
needsMonitor = true;
|
||
},
|
||
|
||
// 处理类方法
|
||
ClassMethod(path) {
|
||
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) {
|
||
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;
|