From c22d2632fd33446f68fb9b48697d3c372d105bf7 Mon Sep 17 00:00:00 2001 From: Ryan Albert Date: Wed, 20 Sep 2023 15:07:41 -0700 Subject: [PATCH 1/6] feat: execute template methods directly --- engine.go | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/engine.go b/engine.go index 19ccb62..ba17121 100644 --- a/engine.go +++ b/engine.go @@ -12,6 +12,7 @@ import ( "path" "github.com/dop251/goja" + esbuild "github.com/evanw/esbuild/pkg/api" "github.com/speakeasy-api/easytemplate/internal/template" "github.com/speakeasy-api/easytemplate/internal/utils" "github.com/speakeasy-api/easytemplate/internal/vm" @@ -160,6 +161,61 @@ func (e *Engine) RunScript(scriptFile string, data any) error { return nil } +// MethodExecutor returns an execution function that enables calls to global template methods from easytemplate +func (e *Engine) MethodExecutor(scriptFile string, data any) (func(fnName string, args ...interface{}) (interface{}, error), error) { + vm, err := e.init(data) + if err != nil { + return nil, err + } + + script, err := e.readFile(scriptFile) + if err != nil { + return nil, fmt.Errorf("failed to read script file: %w", err) + } + + result := esbuild.Transform(string(script), esbuild.TransformOptions{ + Target: esbuild.ES2015, + Loader: esbuild.LoaderTS, + Sourcemap: esbuild.SourceMapExternal, + }) + if len(result.Errors) > 0 { + msg := "" + for _, errMsg := range result.Errors { + if errMsg.Location == nil { + msg += fmt.Sprintf("%v @ %v;", errMsg.Text, scriptFile) + } else { + msg += fmt.Sprintf("%v @ %v %v:%v;", errMsg.Text, scriptFile, errMsg.Location.Line, errMsg.Location.Column) + } + } + return nil, fmt.Errorf("%w: %s", errors.New("script compilation failed"), msg) + } + + if _, err := vm.Run(scriptFile, string(script)); err != nil { + return nil, err + } + + runFn := func(fnName string, args ...interface{}) (interface{}, error) { + fn, ok := goja.AssertFunction(vm.Get(fnName)) + if !ok { + return nil, fmt.Errorf("failed to find %s function", fnName) + } + + gojaArgs := make([]goja.Value, len(args)) + for i, arg := range args { + gojaArgs[i] = vm.ToValue(arg) + } + + val, err := fn(goja.Undefined(), gojaArgs...) + if err != nil { + return nil, err + } + + return val.Export(), nil + } + + return runFn, nil +} + // RunTemplate runs the provided template file, with the provided data, starting the template engine and templating the provided template to a file. func (e *Engine) RunTemplate(templateFile string, outFile string, data any) error { vm, err := e.init(data) From 17a74c32400defad65d07a45a58d42495a11b8a3 Mon Sep 17 00:00:00 2001 From: Ryan Albert Date: Wed, 20 Sep 2023 15:59:24 -0700 Subject: [PATCH 2/6] feat: execute template methods directly --- engine.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine.go b/engine.go index ba17121..678063d 100644 --- a/engine.go +++ b/engine.go @@ -187,7 +187,7 @@ func (e *Engine) MethodExecutor(scriptFile string, data any) (func(fnName string msg += fmt.Sprintf("%v @ %v %v:%v;", errMsg.Text, scriptFile, errMsg.Location.Line, errMsg.Location.Column) } } - return nil, fmt.Errorf("%w: %s", errors.New("script compilation failed"), msg) + return nil, fmt.Errorf("script compilation failed: %s", msg) } if _, err := vm.Run(scriptFile, string(script)); err != nil { From 101cb77a3717aad2bbcf0b7312aaa4878ab7becb Mon Sep 17 00:00:00 2001 From: Ryan Albert Date: Wed, 20 Sep 2023 16:04:56 -0700 Subject: [PATCH 3/6] feat: execute template methods directly --- engine.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/engine.go b/engine.go index 678063d..73c7fdf 100644 --- a/engine.go +++ b/engine.go @@ -27,6 +27,10 @@ var ( ErrInvalidArg = errors.New("invalid argument") // ErrTemplateCompilation is returned when a template fails to compile. ErrTemplateCompilation = errors.New("template compilation failed") + // ErrFunctionNotFound Function does not exist in script + ErrFunctionNotFound = errors.New("failed to find function") + // ErrScriptCompilation The provided script cannot compile + ErrScriptCompilation = errors.New("script compilation failed") ) // CallContext is the context that is passed to go functions when called from js. @@ -161,7 +165,7 @@ func (e *Engine) RunScript(scriptFile string, data any) error { return nil } -// MethodExecutor returns an execution function that enables calls to global template methods from easytemplate +// MethodExecutor returns an execution function that enables calls to global template methods from easytemplate. func (e *Engine) MethodExecutor(scriptFile string, data any) (func(fnName string, args ...interface{}) (interface{}, error), error) { vm, err := e.init(data) if err != nil { @@ -187,7 +191,7 @@ func (e *Engine) MethodExecutor(scriptFile string, data any) (func(fnName string msg += fmt.Sprintf("%v @ %v %v:%v;", errMsg.Text, scriptFile, errMsg.Location.Line, errMsg.Location.Column) } } - return nil, fmt.Errorf("script compilation failed: %s", msg) + return nil, fmt.Errorf("%w: %s", ErrScriptCompilation, msg) } if _, err := vm.Run(scriptFile, string(script)); err != nil { @@ -197,7 +201,7 @@ func (e *Engine) MethodExecutor(scriptFile string, data any) (func(fnName string runFn := func(fnName string, args ...interface{}) (interface{}, error) { fn, ok := goja.AssertFunction(vm.Get(fnName)) if !ok { - return nil, fmt.Errorf("failed to find %s function", fnName) + return nil, fmt.Errorf("%w: %s", ErrFunctionNotFound, fnName) } gojaArgs := make([]goja.Value, len(args)) From 50cbd928f49218dd7a3dd7846cf0a4964841b559 Mon Sep 17 00:00:00 2001 From: Ryan Albert Date: Wed, 20 Sep 2023 16:05:59 -0700 Subject: [PATCH 4/6] feat: execute template methods directly --- engine.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine.go b/engine.go index 73c7fdf..83c83e1 100644 --- a/engine.go +++ b/engine.go @@ -27,9 +27,9 @@ var ( ErrInvalidArg = errors.New("invalid argument") // ErrTemplateCompilation is returned when a template fails to compile. ErrTemplateCompilation = errors.New("template compilation failed") - // ErrFunctionNotFound Function does not exist in script + // ErrFunctionNotFound Function does not exist in script. ErrFunctionNotFound = errors.New("failed to find function") - // ErrScriptCompilation The provided script cannot compile + // ErrScriptCompilation The provided script cannot compile. ErrScriptCompilation = errors.New("script compilation failed") ) From 36dc324defb0b28af81c570e1820b80a681f4d3c Mon Sep 17 00:00:00 2001 From: Ryan Albert Date: Thu, 21 Sep 2023 11:11:42 -0700 Subject: [PATCH 5/6] update --- engine.go | 52 ++++++++++++++-------------------------------------- 1 file changed, 14 insertions(+), 38 deletions(-) diff --git a/engine.go b/engine.go index 83c83e1..ba8f8cb 100644 --- a/engine.go +++ b/engine.go @@ -12,7 +12,6 @@ import ( "path" "github.com/dop251/goja" - esbuild "github.com/evanw/esbuild/pkg/api" "github.com/speakeasy-api/easytemplate/internal/template" "github.com/speakeasy-api/easytemplate/internal/utils" "github.com/speakeasy-api/easytemplate/internal/vm" @@ -29,8 +28,6 @@ var ( ErrTemplateCompilation = errors.New("template compilation failed") // ErrFunctionNotFound Function does not exist in script. ErrFunctionNotFound = errors.New("failed to find function") - // ErrScriptCompilation The provided script cannot compile. - ErrScriptCompilation = errors.New("script compilation failed") ) // CallContext is the context that is passed to go functions when called from js. @@ -165,8 +162,8 @@ func (e *Engine) RunScript(scriptFile string, data any) error { return nil } -// MethodExecutor returns an execution function that enables calls to global template methods from easytemplate. -func (e *Engine) MethodExecutor(scriptFile string, data any) (func(fnName string, args ...interface{}) (interface{}, error), error) { +// RunMethod enables calls to global template methods from easytemplate. +func (e *Engine) RunMethod(scriptFile string, data any, fnName string, args ...interface{}) (goja.Value, error) { vm, err := e.init(data) if err != nil { return nil, err @@ -177,47 +174,26 @@ func (e *Engine) MethodExecutor(scriptFile string, data any) (func(fnName string return nil, fmt.Errorf("failed to read script file: %w", err) } - result := esbuild.Transform(string(script), esbuild.TransformOptions{ - Target: esbuild.ES2015, - Loader: esbuild.LoaderTS, - Sourcemap: esbuild.SourceMapExternal, - }) - if len(result.Errors) > 0 { - msg := "" - for _, errMsg := range result.Errors { - if errMsg.Location == nil { - msg += fmt.Sprintf("%v @ %v;", errMsg.Text, scriptFile) - } else { - msg += fmt.Sprintf("%v @ %v %v:%v;", errMsg.Text, scriptFile, errMsg.Location.Line, errMsg.Location.Column) - } - } - return nil, fmt.Errorf("%w: %s", ErrScriptCompilation, msg) - } - if _, err := vm.Run(scriptFile, string(script)); err != nil { return nil, err } - runFn := func(fnName string, args ...interface{}) (interface{}, error) { - fn, ok := goja.AssertFunction(vm.Get(fnName)) - if !ok { - return nil, fmt.Errorf("%w: %s", ErrFunctionNotFound, fnName) - } - - gojaArgs := make([]goja.Value, len(args)) - for i, arg := range args { - gojaArgs[i] = vm.ToValue(arg) - } + fn, ok := goja.AssertFunction(vm.Get(fnName)) + if !ok { + return nil, fmt.Errorf("%w: %s", ErrFunctionNotFound, fnName) + } - val, err := fn(goja.Undefined(), gojaArgs...) - if err != nil { - return nil, err - } + gojaArgs := make([]goja.Value, len(args)) + for i, arg := range args { + gojaArgs[i] = vm.ToValue(arg) + } - return val.Export(), nil + val, err := fn(goja.Undefined(), gojaArgs...) + if err != nil { + return nil, err } - return runFn, nil + return val, nil } // RunTemplate runs the provided template file, with the provided data, starting the template engine and templating the provided template to a file. From 111442c60517aea9c2e13869469ecc205a8619a4 Mon Sep 17 00:00:00 2001 From: Ryan Albert Date: Thu, 21 Sep 2023 13:58:16 -0700 Subject: [PATCH 6/6] update --- engine.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/engine.go b/engine.go index ba8f8cb..e618aad 100644 --- a/engine.go +++ b/engine.go @@ -163,7 +163,7 @@ func (e *Engine) RunScript(scriptFile string, data any) error { } // RunMethod enables calls to global template methods from easytemplate. -func (e *Engine) RunMethod(scriptFile string, data any, fnName string, args ...interface{}) (goja.Value, error) { +func (e *Engine) RunMethod(scriptFile string, data any, fnName string, args ...any) (goja.Value, error) { vm, err := e.init(data) if err != nil { return nil, err @@ -187,7 +187,6 @@ func (e *Engine) RunMethod(scriptFile string, data any, fnName string, args ...i for i, arg := range args { gojaArgs[i] = vm.ToValue(arg) } - val, err := fn(goja.Undefined(), gojaArgs...) if err != nil { return nil, err