first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,84 @@
# template-builder
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`TemplateBuilder` is a fluent API for assembling an `EmailTemplate<TProps>`
value step-by-step. It exists because constructing the template literal
inline reads poorly when defaults and subject functions are non-trivial,
and because the builder enforces presence of the three required fields
(`name`, `subject`, `component`) at `build()` time with a typed error.
The class itself is a typical builder — each setter mutates the internal
state and returns `this`. `build()` is the one terminal operation. Calls
made after `build()` continue to work (the builder is not consumed), so a
single builder can produce two slightly-different templates by switching
one setter and calling `build()` twice.
## Features
- Fluent setters: `name`, `subject` (literal or function-of-props),
`component`, `defaultProps`.
- Runtime validation in `build()` — missing required fields raise
`EmailTemplatesError(INVALID_TEMPLATE)` with a specific message key.
- `createTemplateBuilder<TProps>()` factory for inference-friendly use.
## Usage: Basic
```ts
import { createTemplateBuilder } from "@suaveplan/email-templates/template-builder";
import { h } from "@suaveplan/email-templates/vnode";
interface Props { name: string; ctaUrl: string }
const myTemplate = createTemplateBuilder<Props>()
.name("custom-welcome")
.subject((p) => `Hi ${p.name}`)
.component((p) => h("a", { href: p.ctaUrl }, "Click me"))
.build();
```
## Usage: Advanced
Reuse a single builder to produce a localised template family by mutating
between `build()` calls:
```ts
import { createTemplateBuilder } from "@suaveplan/email-templates/template-builder";
import { h } from "@suaveplan/email-templates/vnode";
interface Props { name: string }
const b = createTemplateBuilder<Props>()
.component((p) => h("p", {}, `Hi ${p.name}`));
const en = b.name("greet-en").subject("Hello").build();
const fr = b.name("greet-fr").subject("Bonjour").build();
```
`en.subject === "Hello"` and `fr.subject === "Bonjour"` — the builder
returns a fresh `EmailTemplate` object on each `build()` call.
## API Reference
| Symbol | Kind | Description |
| ------------------------ | -------- | ---------------------------------------------------- |
| `TemplateBuilder<T>` | class | The builder itself; instance methods are chainable. |
| `createTemplateBuilder` | function | `<T>() => TemplateBuilder<T>` factory. |
| `TemplateBuilderApi<T>` | type | The interface implemented by `TemplateBuilder<T>`. |
| `EmailComponentFn<T>` | type | `(props: T & { theme?; clock? }) => VNode`. |
## Implementation Notes
The builder never throws inside a setter — invariants are checked
exclusively in `build()`. Empty-string `name` is treated as missing
(consistent with upstream behaviour). The `defaultProps` field is
optional; omitting it leaves it `undefined` on the produced template.
Because `build()` returns a fresh object every time, callers can mutate
the builder and reuse it for closely-related templates — useful for
i18n where the body component is identical and only the subject text
changes by locale.