nodejs vm+ babel ast 实现类似cube.js schema 的处理能力
很简单主要是学习下cube.js 关于schema 的特殊处理了解下原理
以下部分代码参考了cube.js compiler 部分
参考项目
- package.json
{
"name": "vm-scripts",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
"@babel/core": "^7.12.10",
"@babel/generator": "^7.12.11",
"@babel/parser": "^7.12.11",
"@babel/preset-env": "^7.12.11",
"@babel/traverse": "^7.12.12",
"@babel/types": "^7.12.12"
}
}
- 仿cube.js schema 定义
app("dalong2",{
"name":{
age:333,
version:"v1",
name:"v1 rongfengliang",
full_name: `${JSON.stringify(COMPILE_CONTEXT)}---demoapp`
},
"info":`${COMPILE_}`
})
简单说明,对于info 我们认为应该一个函数,同时app 也是我们一个全局变量,类似cube.js 的cube
同时我们也提供了一个全局变量COMPILE_CONTEXT,类似cube.js 编译时的schema 处理,当然其他的也是可以扩展的
- 基于babel 的ast 进行代码转换
主要是将info 属性变为info: COMPILE_CONTEXT=>${COMPILE_},这样就和cube.js sql 是一个函数类似了
参考代码
const vm = require("vm")
const fs = require("fs")
const { parse } = require("@babel/parser")
const generate = require("@babel/generator")
const traverse = require("@babel/traverse")
const t = require("@babel/types")
// 进行数据的中间存储
const cubes = [];
const field = /^.*(info|sql|conf)$/
// 包装的基于babel ast+ vm 的处理函数
function call_vm(context) {
const code = fs.readFileSync("./cube.js", {
encoding: "utf8"
}).toString();
const ast1 = parse(
code,
{
sourceFilename: "cube.js",
sourceType: 'module',
plugins: ['objectRestSpread']
},
);
traverse.default(ast1, {
ObjectProperty(path) {
console.log("path info:",JSON.stringify(path.node))
// 进行ast类型判断处理,同时进行as的替换处理,将info 的包含为一个箭头函数
if (path.node.key.type === 'StringLiteral' && path.node.key.value.match(field)) {
path.get('value').replaceWith(
t.arrowFunctionExpression([t.identifier("COMPILE_CONTEXT")], path.node.value, false)
);
}
}
})
const output = generate.default(
ast1,
{},
code
);
console.log(output)
// 使用vm 沙箱提供数据隔离的环境
vm.runInNewContext(output.code, {
// 提供上下文的函数
app: (name, conf) => {
cubes.push({
name:name,
conf:conf
})
},
COMPILE_CONTEXT: context
})
}
context = {
name: "rongfengliangdemoapp",
appid: "app001",
auth: {
name: "dalong",
age: 22
}
}
call_vm(context)
cubes.forEach(cube => {
console.log(cube)
let result = ()
console.log("=======",result,"=======")
});
- 运行效果