场景需求:
现在前端有一个js脚本输入框,要对用户写的脚本做函数验证,要保证用户写的函数正确,必须保证有rawDataToProtocol和protocolToRawData这两个函数才正确,如果只有一个或者其它函数名均是不正确的,且函数格式不正确也会有提示;
参考:angular-expressions、expr-eval、acorn
项目技术:Vue3+Ts
最终决定使用:Acorn,原因下载量大,最合适,一直处于维护状态长达十几年,且使用配置规范合理,适合Js解析
代码实现如下:
// 异步函数,接收一个脚本作为参数
async function updateFunctionValidation(script) {
// 返回一个 Promise 对象,用于处理异步逻辑
return new Promise((resolve, reject) => {
try {
// 使用 acorn 解析传入的脚本,指定 ECMAScript 版本为 2020,模块化类型为 "module"
const ast = acorn.parse(script, { ecmaVersion: 2020, sourceType: "module" });
// 初始化两个标志,用于检测是否找到了特定的函数
let foundRawDataToProtocol = false;
let foundProtocolToRawData = false;
// 定义 AST 遍历函数
// eslint-disable-next-line no-inner-declarations
function traverse(node) {
// 检查节点类型是否为函数声明
if (node.type === "FunctionDeclaration") {
// 判断函数名是否匹配
if (node.id && node.id.name === "rawDataToProtocol") {
foundRawDataToProtocol = true;
} else if (node.id && node.id.name === "protocolToRawData") {
foundProtocolToRawData = true;
}
}
// 递归遍历子节点
for (const key in node) {
if (node[key] && typeof node[key] === "object") {
traverse(node[key]);
}
}
}
// 调用遍历函数
traverse(ast);
// 验证是否只包含指定的函数名
const isScriptValid = foundRawDataToProtocol && foundProtocolToRawData;
// 如果脚本不合法,抛出错误
if (!isScriptValid) {
throw new Error("Invalid script. It should only contain 'rawDataToProtocol' and 'protocolToRawData' functions.");
}
// 如果一切正常,解析成功,调用 resolve 完成 Promise
resolve();
} catch (error) {
// 捕获异常,打印错误信息
console.error("An error occurred while parsing script:", error.message);
// 在出现错误时,将相关验证标志置为 false,并调用 reject
functionValidation.value.rawDataToProtocol = false;
functionValidation.value.protocolToRawData = false;
reject(error);
}
});
}