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

posted on 2021-01-04 21:54  荣锋亮  阅读(1942)  评论(0编辑  收藏  举报

导航