diff --git a/.changeset/spicy-ways-sing.md b/.changeset/spicy-ways-sing.md new file mode 100644 index 000000000..a2f4dc87a --- /dev/null +++ b/.changeset/spicy-ways-sing.md @@ -0,0 +1,5 @@ +--- +'@astrojs/compiler': minor +--- + +Improve error recovery when using the `transform` function. The compiler will now properly reject the promise with a useful message and stacktrace rather than print internal errors to stdout. diff --git a/cmd/astro-wasm/astro-wasm.go b/cmd/astro-wasm/astro-wasm.go index 66dab2c71..529f860ba 100644 --- a/cmd/astro-wasm/astro-wasm.go +++ b/cmd/astro-wasm/astro-wasm.go @@ -260,15 +260,23 @@ func Transform() any { h := handler.NewHandler(source, transformOptions.Filename) styleError := []string{} - handler := js.FuncOf(func(this js.Value, args []js.Value) any { + promiseHandle := js.FuncOf(func(this js.Value, args []js.Value) any { resolve := args[0] + reject := args[1] go func() { var doc *astro.Node + defer func() { + if err := recover(); err != nil { + reject.Invoke(wasm_utils.ErrorToJSError(h, err.(error))) + return + } + }() doc, err := astro.ParseWithOptions(strings.NewReader(source), astro.ParseOptionWithHandler(h)) if err != nil { - fmt.Println(err) + reject.Invoke(wasm_utils.ErrorToJSError(h, err)) + return } // Hoist styles and scripts to the top-level @@ -403,11 +411,11 @@ func Transform() any { return nil }) - defer handler.Release() + defer promiseHandle.Release() // Create and return the Promise object promiseConstructor := js.Global().Get("Promise") - return promiseConstructor.New(handler) + return promiseConstructor.New(promiseHandle) }) } diff --git a/internal_wasm/utils/utils.go b/internal_wasm/utils/utils.go index 069c55bf6..187822390 100644 --- a/internal_wasm/utils/utils.go +++ b/internal_wasm/utils/utils.go @@ -1,9 +1,13 @@ package wasm_utils import ( + "runtime/debug" + "strings" "syscall/js" + "github.com/norunners/vert" astro "github.com/withastro/compiler/internal" + "github.com/withastro/compiler/internal/handler" ) // See https://stackoverflow.com/questions/68426700/how-to-wait-a-js-async-function-from-golang-wasm @@ -50,3 +54,22 @@ func GetAttrs(n *astro.Node) js.Value { } return attrs } + +type JSError struct { + Message string `js:"message"` + Stack string `js:"stack"` +} + +func (err *JSError) Value() js.Value { + return vert.ValueOf(err).Value +} + +func ErrorToJSError(h *handler.Handler, err error) js.Value { + stack := string(debug.Stack()) + message := strings.TrimSpace(err.Error()) + jsError := JSError{ + Message: message, + Stack: stack, + } + return jsError.Value() +}