go-html/node.go
Claude d7bb0b2b2c
feat: scaffold go-html module with Node interface
Introduces the go-html module with a Node rendering interface, Raw and El
constructors, void element handling, and attribute escaping. Includes a
minimal Context stub and tests for all node types.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 23:34:27 +00:00

98 lines
1.8 KiB
Go

package html
import "strings"
// Node is anything renderable.
type Node interface {
Render(ctx *Context) string
}
// voidElements is the set of HTML elements that must not have a closing tag.
var voidElements = map[string]bool{
"area": true,
"base": true,
"br": true,
"col": true,
"embed": true,
"hr": true,
"img": true,
"input": true,
"link": true,
"meta": true,
"source": true,
"track": true,
"wbr": true,
}
// escapeAttr escapes a string for use in an HTML attribute value.
func escapeAttr(s string) string {
s = strings.ReplaceAll(s, "&", "&amp;")
s = strings.ReplaceAll(s, "\"", "&quot;")
s = strings.ReplaceAll(s, "'", "&#39;")
s = strings.ReplaceAll(s, "<", "&lt;")
s = strings.ReplaceAll(s, ">", "&gt;")
return s
}
// --- rawNode ---
type rawNode struct {
content string
}
// Raw creates a node that renders without escaping (escape hatch for trusted content).
func Raw(content string) Node {
return &rawNode{content: content}
}
func (n *rawNode) Render(_ *Context) string {
return n.content
}
// --- elNode ---
type elNode struct {
tag string
children []Node
attrs map[string]string
}
// El creates an HTML element node with children.
func El(tag string, children ...Node) Node {
return &elNode{
tag: tag,
children: children,
attrs: make(map[string]string),
}
}
func (n *elNode) Render(ctx *Context) string {
var b strings.Builder
b.WriteByte('<')
b.WriteString(n.tag)
for key, val := range n.attrs {
b.WriteByte(' ')
b.WriteString(key)
b.WriteString(`="`)
b.WriteString(escapeAttr(val))
b.WriteByte('"')
}
b.WriteByte('>')
if voidElements[n.tag] {
return b.String()
}
for _, child := range n.children {
b.WriteString(child.Render(ctx))
}
b.WriteString("</")
b.WriteString(n.tag)
b.WriteByte('>')
return b.String()
}