This commit is contained in:
2026-01-28 12:43:17 -05:00
parent f9d3753806
commit 604178341d
10 changed files with 188 additions and 172 deletions

View File

@@ -1,6 +1,7 @@
package runtime
import (
"context"
"fmt"
"io"
"os"
@@ -11,36 +12,37 @@ import (
)
type Runtime struct {
vm *quickjs.VM
stdout io.Writer
stderr io.Writer
consoleSetup bool
vm *quickjs.VM
ctx context.Context
stdout io.Writer
stderr io.Writer
}
func New() *Runtime {
func New(ctx context.Context) (*Runtime, error) {
// Create VM
vm, err := quickjs.NewVM()
if err != nil {
panic(err)
return nil, err
}
vm.SetCanBlock(true)
r := &Runtime{vm: vm, stdout: os.Stdout, stderr: os.Stderr}
r.setupConsole()
// Create Runtime
r := &Runtime{vm: vm, ctx: ctx, stdout: os.Stdout, stderr: os.Stderr}
if err := r.populateGlobals(); err != nil {
return nil, err
}
builtin.RegisterBuiltins(vm)
return r
return r, nil
}
func (r *Runtime) setupConsole() {
if r.consoleSetup {
return
}
func (r *Runtime) populateGlobals() error {
// Add Helpers
if err := r.vm.StdAddHelpers(); err != nil {
panic(fmt.Sprintf("failed to add std helpers: %v", err))
return err
}
// Add Log Hook
if err := r.vm.RegisterFunc("customLog", func(args ...any) {
for i, arg := range args {
if i > 0 {
@@ -50,24 +52,44 @@ func (r *Runtime) setupConsole() {
}
_, _ = fmt.Fprintln(r.stdout)
}, false); err != nil {
panic(fmt.Sprintf("failed to register customLog: %v", err))
return err
}
if _, err := r.vm.Eval("console.log = customLog;", quickjs.EvalGlobal); err != nil {
return err
}
_, _ = r.vm.Eval("console.log = customLog;", quickjs.EvalGlobal)
// Register Custom Functions
for name, builtin := range builtin.GetBuiltins() {
// Register Main Function
if err := r.vm.RegisterFunc(name, builtin.WrapFn(r.ctx), false); err != nil {
return err
}
r.consoleSetup = true
}
// 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 {
return err
}
}
func (r *Runtime) SetOutput(stdout, stderr io.Writer) {
r.stdout = stdout
r.stderr = stderr
r.setupConsole()
return nil
}
func (r *Runtime) RunFile(filePath string, stdout, stderr io.Writer) error {
r.stdout = stdout
r.stderr = stderr
r.setupConsole()
content, err := r.transformFile(filePath)
if err != nil {
@@ -95,7 +117,6 @@ func (r *Runtime) RunFile(filePath string, stdout, stderr io.Writer) error {
func (r *Runtime) RunCode(tsCode string, stdout, stderr io.Writer) error {
r.stdout = stdout
r.stderr = stderr
r.setupConsole()
content := r.transformCode(tsCode)
@@ -131,6 +152,14 @@ func (r *Runtime) transformFile(filePath string) (*transformResult, error) {
}
func (r *Runtime) transformCode(tsCode string) *transformResult {
// wrappedCode := `(async () => {
// try {
// ` + tsCode + `
// } catch (err) {
// console.error(err);
// }
// })()`
result := api.Transform(tsCode, api.TransformOptions{
Loader: api.LoaderTS,
Target: api.ES2022,