goja 支持es6的一种方法
goja 对于es6 的module 模式是不支持的,但是我们可以通过扩展模式支持
基本原理
k6 是利用了goja 的js 能力,但是为了支持es6,使用了babel (standalone),同时为了方便扩展ls 的能力,使用了core.js
同时利用了js可以直接转换为golang 方法的模式,直接使用了golang 方法进行es6 编译处理
- 核心代码
使用babel 的transform 方法,转换es6 为es6,同时暴露为一个golang 方法
func newBabel() (*babel, error) {
var err error
once.Do(func() {
conf := rice.Config{
LocateOrder: []rice.LocateMethod{rice.LocateEmbedded},
}
babelSrc := conf.MustFindBox("lib").MustString("babel.min.js")
vm := goja.New()
if _, err = vm.RunString(babelSrc); err != nil {
return
}
this := vm.Get("Babel")
bObj := this.ToObject(vm)
globalBabel = &babel{vm: vm, this: this}
if err = vm.ExportTo(bObj.Get("transform"), &globalBabel.transform); err != nil {
return
}
})
return globalBabel, err
}
globalBabel.transform 的定义
使用了goja 的Callable
transform goja.Callable
- 进行es6代码编译处理
func (b *babel) Transform(logger logrus.FieldLogger, src, filename string) (string, *SourceMap, error) {
b.mutex.Lock()
defer b.mutex.Unlock()
opts := make(map[string]interface{})
for k, v := range DefaultOpts {
opts[k] = v
}
opts["filename"] = filename
startTime := time.Now()
v, err := b.transform(b.this, b.vm.ToValue(src), b.vm.ToValue(opts))
if err != nil {
return "", nil, err
}
logger.WithField("t", time.Since(startTime)).Debug("Babel: Transformed")
vO := v.ToObject(b.vm)
var code string
if err = b.vm.ExportTo(vO.Get("code"), &code); err != nil {
return code, nil, err
}
var rawMap map[string]interface{}
if err = b.vm.ExportTo(vO.Get("map"), &rawMap); err != nil {
return code, nil, err
}
var srcMap SourceMap
if err = mapstructure.Decode(rawMap, &srcMap); err != nil {
return code, &srcMap, err
}
return code, &srcMap, err
}
一个参考集成babel 的demo
- 参考代码
package main
import (
"demoapp-goja/pkg"
"log"
"github.com/dop251/goja"
)
const (
bablename = "node_modules/babel.min.js"
corejsname = "node_modules/index.js"
demomodule = "node_modules/app.js"
)
var convert func(string) string
var jsCompilerVM *goja.Runtime = goja.New()
func main() {
jsCompilerVM = goja.New()
// 使用go-bindata
bableContent, _ := pkg.Asset(bablename)
corejsContent, _ := pkg.Asset(corejsname)
mydemomodule, _ := pkg.Asset(demomodule)
corejs, _ := goja.Compile(corejsname, string(corejsContent), false)
bable, _ := goja.Compile(bablename, string(bableContent), false)
// 预加载babel 以及core-js
jsCompilerVM.RunProgram(bable)
jsCompilerVM.RunProgram(corejs)
// es6 编译es5 转换为golang 代码
jsCompilerVM.RunString(`var convert = function(es6code) {
return Babel.transform(es6code, { presets: ['env'] }).code;
}
`)
err := jsCompilerVM.ExportTo(jsCompilerVM.Get("convert"), &convert)
if err != nil {
panic(err)
}
// 调用转换
log.Println(convert(string(mydemomodule)))
}
node_modules/app.js 内容
import rong from "./rong.js"
var demo = new rong("dalong",222)
console.log(demo.printInfo())
效果
说明
实践方法参考自k6一个很不错的基于golang 开发的压力测试工具,k6集成goja js 引擎还是比较巧妙的,后边说明下方法
参考资料
https://github.com/loadimpact/k6/blob/master/js/lib/lib.go
https://github.com/loadimpact/k6/blob/master/js/compiler/compiler.go#L85
https://github.com/dop251/goja
https://github.com/dop251/goja/blob/master/runtime.go#L2017
https://github.com/loadimpact/k6/blob/master/js/compiler/compiler.go#L116
https://k6.io/docs/using-k6/modules