From 27c0d1fd097f594cce62cce5de8ef44d624055df Mon Sep 17 00:00:00 2001 From: Snider Date: Fri, 13 Mar 2026 13:38:01 +0000 Subject: [PATCH] docs: add CLAUDE.md project instructions Co-Authored-By: Virgil --- CLAUDE.md | 47 ++++++++++++++++++++++------------------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 80099f3..d6f6b5f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,5 +1,7 @@ # CLAUDE.md +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + Agent instructions for `go-html`. Module path: `forge.lthn.ai/core/go-html` ## Commands @@ -9,8 +11,10 @@ go test ./... # Run all tes go test -run TestName ./... # Single test go test -short ./... # Skip slow WASM build test go test -bench . ./... # Benchmarks +go test -bench . -benchmem ./... # Benchmarks with alloc stats go vet ./... # Static analysis GOOS=js GOARCH=wasm go build -ldflags="-s -w" -o gohtml.wasm ./cmd/wasm/ # WASM build +make wasm # WASM build with size gate echo '{"H":"nav-bar","C":"main-content"}' | go run ./cmd/codegen/ # Codegen CLI ``` @@ -18,12 +22,12 @@ echo '{"H":"nav-bar","C":"main-content"}' | go run ./cmd/codegen/ # Codegen CLI See `docs/architecture.md` for full detail. Summary: -- **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) +- **Node interface**: `Render(ctx *Context) string` — El, Text, Raw, If, Unless, Each[T], EachSeq[T], Switch, Entitled +- **HLCRF Layout**: Header/Left/Content/Right/Footer compositor with ARIA roles and deterministic `data-block` IDs. Variant string (e.g. "HCF", "HLCRF", "C") controls which slots render. Layouts nest via clone-on-render (thread-safe). +- **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 — 2.90 MB raw / 842 KB gzip +- **WASM**: `cmd/wasm/` exports `renderToString()` only — size gate: < 3 MB raw, < 1 MB gzip ## Server/Client Split @@ -32,37 +36,30 @@ Files guarded with `//go:build !js` are excluded from WASM: - `pipeline.go` — Imprint/CompareVariants use `go-i18n/reversal` (server-side only) - `cmd/wasm/register.go` — encoding/json + codegen (replaced by `cmd/codegen/` CLI) -Never import `encoding/json`, `text/template`, or `fmt` in WASM-linked code. Use string concatenation instead of `fmt.Sprintf` in `layout.go` and any other file without a `!js` guard. - -## 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 (!js only) | -| `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 (renderToString only) | -| `cmd/codegen/main.go` | Build-time CLI for WC bundle generation | -| `cmd/wasm/size_test.go` | WASM binary size gate (< 1 MB gzip, < 3 MB raw) | +**Critical WASM constraint**: Never import `encoding/json`, `text/template`, or `fmt` in WASM-linked code (files without a `!js` build tag). Use string concatenation instead of `fmt.Sprintf` in `layout.go`, `node.go`, `responsive.go`, `render.go`, `path.go`, and `context.go`. The `fmt` package alone adds ~500 KB to the WASM binary. ## Dependencies - `forge.lthn.ai/core/go-i18n` (replace directive → `../go-i18n`) -- `go-i18n` and `go-inference` must be present alongside this repo for builds +- `forge.lthn.ai/core/go-inference` (indirect, via go-i18n) +- Both `go-i18n` and `go-inference` must be cloned alongside this repo for builds +- Go 1.26+ required (uses `range` over integers, `iter.Seq`, `maps.Keys`, `slices.Collect`) ## Coding Standards -- UK English (colour, organisation, centre) -- All types annotated +- UK English (colour, organisation, centre, behaviour, licence, serialise) +- All types annotated; use `any` not `interface{}` - Tests use `testify` assert/require - Licence: EUPL-1.2 — add `// SPDX-Licence-Identifier: EUPL-1.2` to new files -- Safe-by-default: HTML escaping on Text nodes, void element handling, entitlement deny-by-default -- Deterministic output: sorted attributes, reproducible paths +- 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 - Commits: conventional commits + `Co-Authored-By: Virgil ` ## Test Conventions -No specific suffix pattern. Use table-driven subtests with `t.Run()`. Integration tests that use `Text` nodes must call `i18n.SetDefault(svc)` before rendering. +Use table-driven subtests with `t.Run()`. Integration tests that use `Text` nodes must initialise i18n before rendering: + +```go +svc, _ := i18n.New() +i18n.SetDefault(svc) +```