diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..4fe9ca0 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,54 @@ +# CLAUDE.md + +## Project + +`go-html` is an HLCRF DOM compositor with grammar pipeline. Module path: `forge.lthn.ai/core/go-html` + +## Commands + +```bash +go test ./... # Run all tests +go test -run TestName ./... # Single test +go test -bench . ./... # Benchmarks +go vet ./... # Static analysis +GOOS=js GOARCH=wasm go build -o gohtml.wasm ./cmd/wasm/ # WASM build +``` + +## Architecture + +- **Node interface**: `Render(ctx *Context) string` — El, Text, Raw, If, Unless, Each[T], Switch, Entitled +- **HLCRF Layout**: Header/Left/Content/Right/Footer compositor with ARIA roles and deterministic `data-block` IDs +- **Responsive**: Multi-variant breakpoint wrapper (`data-variant` attributes) +- **Pipeline**: Render → StripTags → go-i18n/reversal Tokenise → GrammarImprint +- **Codegen**: Web Component classes with closed Shadow DOM +- **WASM**: `cmd/wasm/` exports `renderToString()` and `registerComponents()` to JS + +## Dependencies + +- `forge.lthn.ai/core/go-i18n` (replace directive → `../go-i18n`) +- go-i18n must be present alongside this repo for builds + +## Coding Standards + +- UK English (colour, organisation, centre) +- All types annotated +- Tests use `testify` assert/require +- Licence: EUPL-1.2 +- Safe-by-default: HTML escaping on Text nodes, void element handling, entitlement deny-by-default +- Deterministic output: sorted attributes, reproducible paths + +## Test Conventions + +No specific suffix pattern — use table-driven subtests with `t.Run()`. + +## Key Files + +| File | Purpose | +|------|---------| +| `node.go` | All node types (El, Text, Raw, If, Unless, Each, Switch, Entitled) | +| `layout.go` | HLCRF compositor | +| `pipeline.go` | StripTags, Imprint, CompareVariants | +| `responsive.go` | Multi-variant breakpoint wrapper | +| `context.go` | Rendering context (Identity, Locale, Entitlements, i18n Service) | +| `codegen/codegen.go` | Web Component class generation | +| `cmd/wasm/main.go` | WASM entry point | diff --git a/FINDINGS.md b/FINDINGS.md new file mode 100644 index 0000000..e9b6a3c --- /dev/null +++ b/FINDINGS.md @@ -0,0 +1,35 @@ +# Findings + +## Code Quality + +- **53 tests, 100% pass** — excellent coverage ratios across all packages +- **Zero TODOs/FIXMEs** in codebase — clean +- **`go vet` clean** — no static analysis warnings +- **Safe-by-default design** — XSS prevention verified in render_test.go, HTML escaping on all Text nodes, void elements self-close, entitlements deny-by-default + +## Architecture Strengths + +- Clean minimal API: 9 public constructors + Node interface +- Type-safe generics: `Each[T]` for iteration +- Deterministic output: sorted attributes, reproducible block IDs +- Fluent builder pattern: `NewLayout("HLCRF").H(node).C(node).F(node)` +- Pipeline bridges rendering to privacy layer (GrammarImprint via go-i18n reversal) + +## Known Issues + +1. **WASM size blocker** — 6.0 MB raw / 1.58 MB gzip. Root cause: stdlib imports (json, encoding, text/template) bloat the WASM binary. Makefile rejects at 1 MB gzip threshold. +2. **No WASM main_test.go** — cmd/wasm/ has register_test.go but no integration test for the JS exports. +3. **Layout accepts invalid variants silently** — `NewLayout("XYZ")` renders nothing, no error returned. +4. **Context.service is private** — Must use `NewContextWithService()`. Limits custom i18n adapter injection. +5. **Responsive only accepts *Layout** — Cannot nest arbitrary nodes in variants, must wrap in Layout first. + +## Coverage Gaps + +| File | Lines | Tests | +|------|-------|-------| +| node.go | 254 | 206 lines of tests (81%) | +| layout.go | 119 | 116 lines (97%) | +| pipeline.go | 83 | 128 lines (154%) | +| responsive.go | 39 | 89 lines (228%) | +| codegen.go | 90 | 54 lines (60%) | +| cmd/wasm/main.go | 78 | **0 lines (0%)** | diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..e0b7063 --- /dev/null +++ b/TODO.md @@ -0,0 +1,20 @@ +# TODO + +## High Priority + +- [ ] **Fix WASM binary size** — 1.58 MB gzip exceeds 1 MB limit. Move `buildComponentJS()` and JSON parsing to server-side. WASM should only expose `Render()`. Consider: pre-parsed slots, inline template execution, or TinyGo. +- [ ] **Add WASM integration tests** — No `cmd/wasm/main_test.go`. Need JS↔Go round-trip verification. + +## Medium Priority + +- [ ] **Performance benchmarks** — Add `BenchmarkRender`, `BenchmarkImprint`, `BenchmarkCompareVariants`, `BenchmarkLayout` with varying tree depths. +- [ ] **TypeScript type definitions** — Add `.d.ts` generation alongside `GenerateBundle()` for Web Component consumers. +- [ ] **Accessibility helpers** — Layout has semantic HTML + ARIA roles but no `aria-label` builder, alt text helpers, or focus management nodes. +- [ ] **Layout variant validation** — `NewLayout("XYZ")` silently produces empty output. Add warning or error for invalid slot characters. + +## Low Priority + +- [ ] **Unicode/RTL edge cases** — Test emoji, RTL text, zero-width characters in Text nodes. +- [ ] **Deep nesting stress test** — Verify performance with deeply nested Layouts and large `Each[T]` lists. +- [ ] **Browser polyfill documentation** — Document closed Shadow DOM support matrix. +- [ ] **CSS scoping helper** — Optional utility for responsive variant CSS targeting.