Merge remote-tracking branch 'origin/main'
This commit is contained in:
commit
913bbb555a
3 changed files with 65 additions and 5 deletions
|
|
@ -27,7 +27,7 @@ See `docs/architecture.md` for full detail. Summary:
|
|||
- **Responsive**: Multi-variant breakpoint wrapper (`data-variant` attributes), renders all variants in insertion order
|
||||
- **Pipeline**: Render → StripTags → go-i18n/reversal Tokenise → GrammarImprint (server-side only)
|
||||
- **Codegen**: Web Component classes with closed Shadow DOM, generated at build time by `cmd/codegen/`
|
||||
- **WASM**: `cmd/wasm/` exports `renderToString()` only — size gate: < 3 MB raw, < 1 MB gzip
|
||||
- **WASM**: `cmd/wasm/` exports `renderToString()` only — size gate: < 3.5 MB raw, < 1 MB gzip
|
||||
|
||||
## Server/Client Split
|
||||
|
||||
|
|
@ -53,6 +53,8 @@ Files guarded with `//go:build !js` are excluded from WASM:
|
|||
- Licence: EUPL-1.2 — add `// SPDX-Licence-Identifier: EUPL-1.2` to new files
|
||||
- Safe-by-default: HTML escaping via `html.EscapeString()` on Text nodes and attribute values, void element handling, entitlement deny-by-default
|
||||
- Deterministic output: sorted attributes on El nodes, reproducible block ID paths
|
||||
- Errors: use `log.E("scope", "message", err)` from `go-log`, never `fmt.Errorf`
|
||||
- File I/O: use `coreio.Local` from `go-io`, never `os.ReadFile`/`os.WriteFile`
|
||||
- Commits: conventional commits + `Co-Authored-By: Virgil <virgil@lethean.io>`
|
||||
|
||||
## Test Conventions
|
||||
|
|
|
|||
8
Makefile
8
Makefile
|
|
@ -1,8 +1,8 @@
|
|||
.PHONY: wasm test clean
|
||||
|
||||
WASM_OUT := dist/go-html.wasm
|
||||
# Raw size limit: 3MB (Go WASM has ~2MB runtime floor)
|
||||
WASM_RAW_LIMIT := 3145728
|
||||
# Raw size limit: 3.5MB (Go 1.26 WASM runtime growth)
|
||||
WASM_RAW_LIMIT := 3670016
|
||||
# Gzip transfer size limit: 1MB (what users actually download)
|
||||
WASM_GZ_LIMIT := 1048576
|
||||
|
||||
|
|
@ -21,9 +21,9 @@ $(WASM_OUT): $(shell find . -name '*.go' -not -path './dist/*')
|
|||
echo "FAIL: gzip transfer size exceeds 1MB limit ($${GZ} bytes)"; \
|
||||
exit 1; \
|
||||
elif [ "$$RAW" -gt $(WASM_RAW_LIMIT) ]; then \
|
||||
echo "WARNING: raw binary exceeds 3MB ($${RAW} bytes) — check imports"; \
|
||||
echo "WARNING: raw binary exceeds 3.5MB ($${RAW} bytes) — check imports"; \
|
||||
else \
|
||||
echo "OK: gzip $${GZ} bytes (limit 1MB), raw $${RAW} bytes (limit 3MB)"; \
|
||||
echo "OK: gzip $${GZ} bytes (limit 1MB), raw $${RAW} bytes (limit 3.5MB)"; \
|
||||
fi
|
||||
|
||||
clean:
|
||||
|
|
|
|||
58
node_test.go
58
node_test.go
|
|
@ -3,6 +3,8 @@ package html
|
|||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
i18n "forge.lthn.ai/core/go-i18n"
|
||||
)
|
||||
|
||||
func TestRawNode_Render(t *testing.T) {
|
||||
|
|
@ -191,6 +193,62 @@ func TestAttr_NonElement(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestUnlessNode_True(t *testing.T) {
|
||||
ctx := NewContext()
|
||||
node := Unless(func(*Context) bool { return true }, Raw("hidden"))
|
||||
got := node.Render(ctx)
|
||||
if got != "" {
|
||||
t.Errorf("Unless(true) = %q, want %q", got, "")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttr_ThroughIfNode(t *testing.T) {
|
||||
ctx := NewContext()
|
||||
inner := El("div", Raw("content"))
|
||||
node := If(func(*Context) bool { return true }, inner)
|
||||
Attr(node, "class", "wrapped")
|
||||
got := node.Render(ctx)
|
||||
want := `<div class="wrapped">content</div>`
|
||||
if got != want {
|
||||
t.Errorf("Attr through If = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttr_ThroughUnlessNode(t *testing.T) {
|
||||
ctx := NewContext()
|
||||
inner := El("div", Raw("content"))
|
||||
node := Unless(func(*Context) bool { return false }, inner)
|
||||
Attr(node, "id", "test")
|
||||
got := node.Render(ctx)
|
||||
want := `<div id="test">content</div>`
|
||||
if got != want {
|
||||
t.Errorf("Attr through Unless = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttr_ThroughEntitledNode(t *testing.T) {
|
||||
ctx := NewContext()
|
||||
ctx.Entitlements = func(string) bool { return true }
|
||||
inner := El("div", Raw("content"))
|
||||
node := Entitled("feature", inner)
|
||||
Attr(node, "data-feat", "on")
|
||||
got := node.Render(ctx)
|
||||
want := `<div data-feat="on">content</div>`
|
||||
if got != want {
|
||||
t.Errorf("Attr through Entitled = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTextNode_WithService(t *testing.T) {
|
||||
svc, _ := i18n.New()
|
||||
ctx := NewContextWithService(svc)
|
||||
node := Text("hello")
|
||||
got := node.Render(ctx)
|
||||
if got != "hello" {
|
||||
t.Errorf("Text with service context = %q, want %q", got, "hello")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSwitchNode(t *testing.T) {
|
||||
ctx := NewContext()
|
||||
cases := map[string]Node{
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue