ruitl 0.2.2

Template compiler for type-safe, server-rendered HTML components in Rust
Documentation
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project

RUITL — Rust UI Template Language. Compiles `.ruitl` template files into type-safe Rust components at build time. Templ-inspired (`.templ` → `_templ.go` model) syntax, zero runtime overhead, server-side rendering focus. Cargo workspace with two members: root crate `ruitl` (library + `ruitl` binary) and `ruitl_compiler` (build-time parser + code generator, runtime-free).

## Common Commands

```bash
# Build (build.rs compiles any .ruitl in templates/ or src/templates/ to sibling *_ruitl.rs)
cargo build

# Compile .ruitl → sibling *_ruitl.rs via CLI (same engine as build.rs)
cargo run -- compile
cargo run -- compile --src-dir templates --watch --verbose

# Scaffold a new RUITL project
cargo run -- scaffold --name my-project --with-server --with-examples

# Run the showcase HTTP server example (port 3000)
cargo run --example server_integration

# Other examples
cargo run --example basic_usage
cargo run --example hello_world
cargo run --example html_output_demo
cargo run --example template_compiler_demo
cargo run --example advanced_features_demo

# Tests
cargo test                                  # all tests (workspace)
cargo test --test template_compilation      # integration file: tests/template_compilation.rs
cargo test --test cli_generated_code_test
cargo test --test component_composition     # validates generated code parses via syn
cargo test <test_name>                      # single test by name
cargo test -- --nocapture                   # show println! output

# Feature flags (default = ["server", "static", "dev"])
cargo build --no-default-features
cargo build --features minify
```

Release profile uses `lto = true`, `codegen-units = 1`, `panic = "abort"`.

## Architecture

Pipeline: `.ruitl` files → `ruitl_compiler::parser` → AST (`RuitlFile`) → `ruitl_compiler::codegen` (via `proc-macro2`/`quote`/`syn`) → formatted Rust → sibling `*_ruitl.rs` file (checked in, templ-style) → `rustc`.

Two entry points share the same compiler library and must stay in sync:

1. **`build.rs`** — invoked by Cargo. Scans both `src/templates/` and `templates/` relative to `CARGO_MANIFEST_DIR` and calls `ruitl_compiler::compile_dir_sibling(dir)` for each. Rerun triggers: `src/templates`, `templates`.
2. **`src/cli.rs`** — the `ruitl` binary (`src/main.rs` → `cli::run_cli`). `compile` subcommand walks `--src-dir` (default `templates`) and calls `ruitl_compiler::compile_file_sibling(path)` per `.ruitl` file. `scaffold` emits a complete project skeleton including its own vendored `bin/ruitl.rs` wrapper so scaffolded projects don't need a global install.

Both entry points produce the **same** sibling `*_ruitl.rs` output — there is no separate artifact directory. Generated files are committed to source control so diffs are reviewable, matching Go templ's `_templ.go` convention.

### Module map

**`ruitl_compiler/src/`** (build-time only, no runtime deps):
- `parser.rs` — hand-written parser. Produces `RuitlFile { components, templates, imports }`. `ComponentDef` holds props + generics; `TemplateDef` holds a `TemplateAst` (HTML elements, text, expressions, conditionals, loops, matches, component composition via `@Component`) + generics. `GenericParam { name, bounds }` represents a single type parameter.
- `codegen.rs` — `CodeGenerator` consumes `RuitlFile` and emits `TokenStream` using `quote!`. Generates `{Name}Props` struct + `impl ComponentProps` + unit struct `{Name}` + `impl Component` whose `render()` returns `Html`. Generic components parse but codegen currently returns an explicit error — full generics support is a follow-up (trait-bound ergonomics RFC pending).
- `lib.rs` — hub: `parse_str`, `generate`, `compile_file_sibling`, `compile_dir_sibling`, `format_rust`.
- `error.rs` — `CompileError` type used by parser + codegen.

**`src/`** (runtime library + CLI):
- `cli.rs` — `ruitl` binary. `compile` subcommand + `scaffold` project generator.
- `component.rs` — runtime traits: `Component`, `ComponentProps`, `ComponentContext`, `EmptyProps`. Generated code targets these.
- `html.rs` — `Html`, `HtmlElement`, `HtmlAttribute`. Output target of rendered components; `.render()` produces escaped HTML strings. Attributes stored as `Vec<(String, HtmlAttribute)>` to preserve insertion order for deterministic rendering.
- `config.rs` — `RuitlConfig` loaded from `ruitl.toml` (sections: `[project]`, `[build]`, `[server]`, `[dev]`).
- `error.rs` — `RuitlError` + `Result` alias used throughout runtime code.
- `generated.rs` — thin re-export module that pulls in `templates/mod.rs` (`#[path = "../templates/mod.rs"]`). Exposes committed sibling-generated components at the crate's root.
- `lib.rs` — public API. Re-exports `ruitl_compiler::{parser, codegen}` publicly so tests and downstream tooling can hit the compiler directly.

### Generated code contract

Each `.ruitl` file produces one sibling `.rs` file with the `_ruitl.rs` suffix (`Button.ruitl` → `Button_ruitl.rs` in the same directory). A `mod.rs` is auto-emitted listing the modules. Consumers import via `#[path = "../templates/mod.rs"] mod templates; use templates::*;` (scaffolded projects) or `mod generated; use generated::*;` (root crate via `src/generated.rs`). Changing this contract requires updating both `build.rs` and `ruitl_compiler::compile_dir_sibling()`.

Generated files use short type names relying on `use ruitl::prelude::*; use ruitl::html::*;` emitted at the top. Props structs derive `Debug + Clone` (no `serde`). Render methods name the context parameter `_context` when the template body doesn't invoke child components, to avoid unused-variable warnings.

### Template syntax (what the parser accepts)

- `component Name<T, U: Bound1 + Bound2> { props { field: Type = default, optional: Type?, ... } }` — generics parse but codegen currently errors
- `ruitl Name<T>(param: Type, ...) { <html>{expr}</html> }`
- Inline Rust exprs in `{}`; attribute interpolation `class={expr}`; boolean attrs `disabled?={expr}`
- Control flow: `if`/`else`, `for x in iter`, `match expr { arm => ... }`
- Component composition: `@ChildComponent(prop=value)` — threads `context` through
- Body-block children: `@ChildComponent(prop=value) { <inner/> }` passes the
  block as `children: Html` on the callee. Inside the callee, the bare slot
  `{children}` expands to `props.children.clone()`. `{Name}Props` auto-gains a
  `pub children: Html` field when the template body references `{children}`;
  if the user already declared a `children` prop explicitly, the user's
  declaration wins (no duplicate field). Dotted forms like `{my.children}`
  parse as ordinary expressions, not the slot.
- `import` statements at top of file
- Whitespace between `{expr}` and adjacent text is preserved (significant for HTML spacing)

## Working on templates

When modifying `.ruitl` files in `templates/`, `src/templates/`, or `examples/demo_templates/`, the build script recompiles automatically on next `cargo build` and updates the sibling `*_ruitl.rs`. `examples/syntax_showcase/` is intentionally **not** compiled — it holds reference material showing every syntax feature and depends on notional user-defined types. If iterating on parser/codegen, regenerate explicitly with `cargo run -- compile` and inspect diffs via `git diff templates/`. Committed `*_ruitl.rs` files serve as both the build product and a reference of current codegen behavior — review them like normal code.