-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
G2 Spec 的序列化,便于 SSR 的时候纯 JSON #6542
Comments
认领 |
具体是实现是怎么做? |
安全检验函数 function isSafeExpression(expr) {
try {
const ast = acorn.parseExpressionAt(expr, 0, { ecmaVersion: 'latest' });
let isSafe = true;
acorn.walk.simple(ast, {
CallExpression() { isSafe = false; }, // 禁止函数调用
NewExpression() { isSafe = false; }, // 禁止 new
ThisExpression() { isSafe = false; }, // 禁止 this
Identifier(node) {
if (node.name !== 'd') isSafe = false; // 仅允许变量 d
},
MemberExpression(node) {
// 确保对象链仅以比如说 d 开头(如 d.a.b)
let obj = node.object;
while (obj.type === 'MemberExpression') obj = obj.object;
if (obj.type !== 'Identifier' || obj.name !== 'd') {
isSafe = false;
}
},
});
return isSafe;
} catch {
return false; // 解析失败视为非法
}
} 递归处理 Spec 配置 function parseSpec(spec) {
const processed = Array.isArray(spec) ? [] : {};
for (const key in spec) {
const value = spec[key];
if (typeof value === 'string' && /^{.*}$/.test(value)) {
const expr = value.slice(1, -1).trim();
processed[key] = createSafeFunction(expr);
} else if (typeof value === 'object' && value !== null) {
processed[key] = parseSpec(value); // 递归处理嵌套对象
} else {
processed[key] = value;
}
}
return processed;
} 反序列化函数生成 function createSafeFunction(expr) {
if (!isSafeExpression(expr)) {
throw new Error(`Unsafe expression: ${expr}`);
}
// 返回一个类似于下面的生成后的函数
return new Function('d', `return ${expr};`);
} |
检验函数引入 acorn 会带来不小的包大小吧,而且安全风险感觉不一定能完全规避。 |
如果能确保 |
我感觉要避免使用 new Function,而是自行实现表达式的识别,也可以省去检验。另外,我们不仅仅考虑 ssr,即便非 ssr 也可以使用这套 api 语法。 |
👍 明白了,就是基于 js 独立的实现一个模版语法的简单语法解释器,这个解释器包含一些方法语法塘,包括以下几个方面
{
encode: {
x: "{d.type}",
y: "{d.value * @sum(d.values)}",
color: "{d.status ? 'green' : 'red'}"
}
} // 下面是一个简单的使用 demo
import { parseExpression } from '@antv/expression-engine';
function processSpec(userSpec, data) {
const processed = {};
for (const key in userSpec) {
const value = userSpec[key];
if (typeof value === 'string' && value.startsWith('{')) {
processed[key] = parseExpression(value.slice(1, -1), data);
} else {
processed[key] = value;
}
}
return processed;
}
// 使用示例
const parsedSpec = processSpec(userSpec, {
type: 'category',
value: 10,
values: [1, 2, 3],
status: true
});
console.log(parsedSpec.encode.y); // 输出: 60 {10 * (1+2+3)} 解释库内部自行解释带有语法糖的模版语法,并且返回模版语法的结果。 |
我觉得可以新建一个库来提供这个模版语法解释功能,这样可以方便后期对语法糖扩展以提供更强的功能,而且同时也可以为其他的 graph lib 提供 ssr 方案 |
是的,是一个单独的库,未来 G6 也会使用。 |
https://github.com/BQXBQX/graph-secure-eval 基础功能已在此仓库中实现,可以基于此再次封装后使用 // simple encapsulation
import { Tokenizer, Parser, Interpreter } from "@antv/graph-secure-eval";
const tokenizer = new Tokenizer();
const parser = new Parser();
function evaluate(input: string, context = {}, functions = {}) {
const tokens = tokenizer.tokenize(input);
const ast = parser.parse(tokens);
const interpreter = new Interpreter(context, functions);
return interpreter.evaluate(ast);
} // simple demo
const context = {
data: {
values: [1, 2, 3],
status: "active",
},
};
const functions = {
sum: (arr: number[]) => arr.reduce((a, b) => a + b, 0),
};
const input = '@sum(data.values) > 5 ? data["status"] : "inactive"';
console.log(evaluate(input, context, functions)); // "active"; |
几个小建议:
|
嗯,好的,我再打磨打磨,感谢意见🙏🏻 |
AntV Open Source Contribution Plan(可选)
Issue 类型
中级任务
任务介绍
我们在做 G2 SSR 的时候,会只用一个 json 配置描述图表,然后这个 json 会由用户输入并存储到数据库中。
但是 G2 Spec 有一些配置时使用回调函数实现,没有办法序列,所以需要有一个方案:用一个规范的字符串去描述简单函数。
改成使用(仅做示意,不干扰方案设计):
原则:
参考说明
可以参考 echarts、highcharts 等如何处理。
The text was updated successfully, but these errors were encountered: