pre migrate
This commit is contained in:
@@ -5,10 +5,13 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/evanw/esbuild/pkg/api"
|
||||
"modernc.org/quickjs"
|
||||
|
||||
"reichard.io/poiesis/internal/functions"
|
||||
_ "reichard.io/poiesis/internal/stdlib"
|
||||
)
|
||||
|
||||
type Runtime struct {
|
||||
@@ -19,7 +22,7 @@ type Runtime struct {
|
||||
stderr io.Writer
|
||||
}
|
||||
|
||||
func New(ctx context.Context) (*Runtime, error) {
|
||||
func New(ctx context.Context, opts ...RuntimeOption) (*Runtime, error) {
|
||||
// Create VM
|
||||
vm, err := quickjs.NewVM()
|
||||
if err != nil {
|
||||
@@ -33,6 +36,11 @@ func New(ctx context.Context) (*Runtime, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Apply Options
|
||||
for _, opt := range opts {
|
||||
opt(r)
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
@@ -59,27 +67,18 @@ func (r *Runtime) populateGlobals() error {
|
||||
}
|
||||
|
||||
// Register Custom Functions
|
||||
for name, builtin := range functions.GetRegisteredFunctions() {
|
||||
for name, fn := range functions.GetRegisteredFunctions() {
|
||||
// Register Main Function
|
||||
if err := r.vm.RegisterFunc(name, builtin.WrapFn(r.ctx), false); err != nil {
|
||||
if err := r.vm.RegisterFunc(name, func(allArgs ...any) (any, error) {
|
||||
return fn.Call(r.ctx, allArgs)
|
||||
}, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Wrap Exception - The QuickJS library does not allow us to throw exceptions, so we
|
||||
// wrap the function with native JS to appropriately throw on error.
|
||||
if _, err := r.vm.Eval(fmt.Sprintf(`
|
||||
(function() {
|
||||
const original = globalThis[%q];
|
||||
|
||||
globalThis[%q] = function(...args) {
|
||||
const [result, error] = original.apply(this, args);
|
||||
if (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
`, name, name), quickjs.EvalGlobal); err != nil {
|
||||
wrappedFunc := wrapFunc(fn.Name(), fn.IsAsync())
|
||||
if _, err := r.vm.Eval(wrappedFunc, quickjs.EvalGlobal); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -87,89 +86,80 @@ func (r *Runtime) populateGlobals() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Runtime) RunFile(filePath string, stdout, stderr io.Writer) error {
|
||||
r.stdout = stdout
|
||||
r.stderr = stderr
|
||||
|
||||
content, err := r.transformFile(filePath)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(stderr, "Error: %v\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if len(content.errors) > 0 {
|
||||
_, _ = fmt.Fprintf(stderr, "Transpilation errors:\n")
|
||||
for _, err := range content.errors {
|
||||
_, _ = fmt.Fprintf(stderr, " %s\n", err.Text)
|
||||
}
|
||||
return fmt.Errorf("transpilation failed")
|
||||
}
|
||||
|
||||
_, err = r.vm.Eval(content.code, quickjs.EvalGlobal)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(stderr, "Execution error: %v\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Runtime) RunCode(tsCode string, stdout, stderr io.Writer) error {
|
||||
r.stdout = stdout
|
||||
r.stderr = stderr
|
||||
|
||||
content := r.transformCode(tsCode)
|
||||
|
||||
if len(content.errors) > 0 {
|
||||
_, _ = fmt.Fprintf(stderr, "Transpilation errors:\n")
|
||||
for _, err := range content.errors {
|
||||
_, _ = fmt.Fprintf(stderr, " %s\n", err.Text)
|
||||
}
|
||||
return fmt.Errorf("transpilation failed")
|
||||
}
|
||||
|
||||
_, err := r.vm.Eval(content.code, quickjs.EvalGlobal)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(stderr, "Execution error: %v\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type transformResult struct {
|
||||
code string
|
||||
errors []api.Message
|
||||
}
|
||||
|
||||
func (r *Runtime) transformFile(filePath string) (*transformResult, error) {
|
||||
func (r *Runtime) RunFile(filePath string) error {
|
||||
tsFileContent, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading file: %w", err)
|
||||
return fmt.Errorf("error reading file: %w", err)
|
||||
}
|
||||
|
||||
return r.transformCode(string(tsFileContent)), nil
|
||||
return r.RunCode(string(tsFileContent))
|
||||
}
|
||||
|
||||
func (r *Runtime) transformCode(tsCode string) *transformResult {
|
||||
// wrappedCode := `(async () => {
|
||||
// try {
|
||||
// ` + tsCode + `
|
||||
// } catch (err) {
|
||||
// console.error(err);
|
||||
// }
|
||||
// })()`
|
||||
func (r *Runtime) RunCode(tsCode string) error {
|
||||
transformedCode, err := r.transformCode(tsCode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = r.vm.Eval(string(transformedCode), quickjs.EvalGlobal)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *Runtime) transformCode(tsCode string) ([]byte, error) {
|
||||
result := api.Transform(tsCode, api.TransformOptions{
|
||||
Loader: api.LoaderTS,
|
||||
Target: api.ES2022,
|
||||
Format: api.FormatIIFE,
|
||||
Loader: api.LoaderTS,
|
||||
Target: api.ES2022,
|
||||
// Format: api.FormatIIFE,
|
||||
Sourcemap: api.SourceMapNone,
|
||||
TreeShaking: api.TreeShakingFalse,
|
||||
})
|
||||
|
||||
return &transformResult{
|
||||
code: string(result.Code),
|
||||
errors: result.Errors,
|
||||
if len(result.Errors) > 0 {
|
||||
var allErrs []string
|
||||
for _, e := range result.Errors {
|
||||
allErrs = append(allErrs, e.Text)
|
||||
}
|
||||
return nil, fmt.Errorf("transpilation failed: %s", strings.Join(allErrs, ", "))
|
||||
}
|
||||
|
||||
return result.Code, nil
|
||||
}
|
||||
|
||||
func wrapFunc(funcName string, isAsync bool) string {
|
||||
if isAsync {
|
||||
return fmt.Sprintf(`
|
||||
(function() {
|
||||
const original = globalThis[%q];
|
||||
|
||||
globalThis[%q] = function(...args) {
|
||||
console.log("calling")
|
||||
return new Promise((resolve, reject) => {
|
||||
const [result, error] = original.apply(this, args);
|
||||
console.log("result", result)
|
||||
if (error) {
|
||||
console.log("reject")
|
||||
reject(new Error(error));
|
||||
} else {
|
||||
console.log("resolve")
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
};
|
||||
})();
|
||||
`, funcName, funcName)
|
||||
}
|
||||
|
||||
return fmt.Sprintf(`
|
||||
(function() {
|
||||
const original = globalThis[%q];
|
||||
|
||||
globalThis[%q] = function(...args) {
|
||||
const [result, error] = original.apply(this, args);
|
||||
if (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
`, funcName, funcName)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user