From dcd55a434cf6df0cb691c3327c7ae437f549aed3 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 17 Feb 2026 20:50:50 +0000 Subject: [PATCH] feat(wasm): add registerComponents export for WC codegen Exposes gohtml.registerComponents(slotsJSON) to browser context. Pure-Go buildComponentJS is testable without WASM; WASM glue uses Function constructor for browser-side execution. Co-Authored-By: Claude Opus 4.6 --- cmd/wasm/main.go | 21 ++++++++++++++++++++- cmd/wasm/register.go | 18 ++++++++++++++++++ cmd/wasm/register_test.go | 24 ++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 cmd/wasm/register.go create mode 100644 cmd/wasm/register_test.go diff --git a/cmd/wasm/main.go b/cmd/wasm/main.go index 254f08d..063ab73 100644 --- a/cmd/wasm/main.go +++ b/cmd/wasm/main.go @@ -50,9 +50,28 @@ func renderToString(_ js.Value, args []js.Value) any { return layout.Render(ctx) } +// registerComponentsJS wraps buildComponentJS for the WASM→JS bridge. +// Takes a JSON string of slot assignments, generates the WC bundle, +// and executes it in the browser via the Function constructor. +func registerComponentsJS(_ js.Value, args []js.Value) any { + if len(args) < 1 { + return js.ValueOf("error: slotsJSON argument required") + } + jsCode, err := buildComponentJS(args[0].String()) + if err != nil { + return js.ValueOf("error: " + err.Error()) + } + // Execute the generated WC definitions in the browser context. + // Uses the standard Function constructor — the normal Go WASM→JS pattern. + fn := js.Global().Call("Function", jsCode) + fn.Invoke() + return js.ValueOf(jsCode) +} + func main() { js.Global().Set("gohtml", js.ValueOf(map[string]any{ - "renderToString": js.FuncOf(renderToString), + "renderToString": js.FuncOf(renderToString), + "registerComponents": js.FuncOf(registerComponentsJS), })) select {} diff --git a/cmd/wasm/register.go b/cmd/wasm/register.go new file mode 100644 index 0000000..0a134b6 --- /dev/null +++ b/cmd/wasm/register.go @@ -0,0 +1,18 @@ +package main + +import ( + "encoding/json" + "fmt" + + "forge.lthn.ai/core/go-html/codegen" +) + +// buildComponentJS takes a JSON slot map and returns the WC bundle JS string. +// This is the pure-Go part testable without WASM. +func buildComponentJS(slotsJSON string) (string, error) { + var slots map[string]string + if err := json.Unmarshal([]byte(slotsJSON), &slots); err != nil { + return "", fmt.Errorf("registerComponents: %w", err) + } + return codegen.GenerateBundle(slots) +} diff --git a/cmd/wasm/register_test.go b/cmd/wasm/register_test.go new file mode 100644 index 0000000..65f2af2 --- /dev/null +++ b/cmd/wasm/register_test.go @@ -0,0 +1,24 @@ +//go:build !js + +package main + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestBuildComponentJS_Good(t *testing.T) { + slotsJSON := `{"H":"nav-bar","C":"main-content"}` + js, err := buildComponentJS(slotsJSON) + require.NoError(t, err) + assert.Contains(t, js, "NavBar") + assert.Contains(t, js, "MainContent") + assert.Contains(t, js, "customElements.define") +} + +func TestBuildComponentJS_Bad_InvalidJSON(t *testing.T) { + _, err := buildComponentJS("not json") + assert.Error(t, err) +}