go-html/cmd/wasm/main.go
Virgil 967182a676
Some checks are pending
Security Scan / security (push) Waiting to run
Test / test (push) Waiting to run
feat(wasm): add component registration export
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-03 20:13:14 +00:00

104 lines
2.5 KiB
Go

//go:build js && wasm
package main
import (
"syscall/js"
html "dappco.re/go/core/html"
)
// renderToString builds an HLCRF layout from JS arguments and returns HTML.
// Slot content is injected via Raw() — the caller is responsible for sanitisation.
// This is intentional: the WASM module is a rendering engine for trusted content
// produced server-side or by the application's own templates.
func renderToString(_ js.Value, args []js.Value) any {
if len(args) < 1 {
return ""
}
if args[0].Type() != js.TypeString {
return ""
}
variant := args[0].String()
locale := ""
if len(args) >= 2 && args[1].Type() == js.TypeString {
locale = args[1].String()
}
ctx := html.NewContext(locale)
layout := html.NewLayout(variant)
if len(args) >= 3 && args[2].Type() == js.TypeObject {
slots := args[2]
for _, slot := range []string{"H", "L", "C", "R", "F"} {
content := slots.Get(slot)
if content.Type() == js.TypeString && content.String() != "" {
switch slot {
case "H":
layout.H(html.Raw(content.String()))
case "L":
layout.L(html.Raw(content.String()))
case "C":
layout.C(html.Raw(content.String()))
case "R":
layout.R(html.Raw(content.String()))
case "F":
layout.F(html.Raw(content.String()))
}
}
}
}
return layout.Render(ctx)
}
// registerComponents defines custom elements from the HLCRF slot map.
// The input mirrors the codegen slot mapping: keys are HLCRF slot letters and
// values are custom element tag names.
func registerComponents(_ js.Value, args []js.Value) any {
if len(args) < 1 || args[0].Type() != js.TypeObject {
return 0
}
slots := args[0]
customElements := js.Global().Get("customElements")
seenTags := make(map[string]struct{}, len(canonicalSlotOrder))
registered := 0
for _, slot := range canonicalSlotOrder {
content := slots.Get(slot)
if content.Type() != js.TypeString {
continue
}
tag := content.String()
if !isValidCustomElementTag(tag) {
continue
}
if _, seen := seenTags[tag]; seen {
continue
}
if existing := customElements.Call("get", tag); existing.Truthy() {
seenTags[tag] = struct{}{}
continue
}
factory := js.Global().Get("Function").New("return " + customElementClassSource(tag, slot) + ";")
ctor := factory.Invoke()
customElements.Call("define", tag, ctor)
seenTags[tag] = struct{}{}
registered++
}
return registered
}
func main() {
js.Global().Set("gohtml", js.ValueOf(map[string]any{
"renderToString": js.FuncOf(renderToString),
"registerComponents": js.FuncOf(registerComponents),
}))
select {}
}