# token-emitters

Per-format emitters for the DTCG token generator (`../tokens-generator.js`).
The DTCG `.tokens.json` (`paths.design_tokens` in `baldart.config.yml`) is the
design-token SSOT; each emitter turns the resolved token set into ONE
stack-native artifact. Same REGISTRY pattern as `lsp-adapters/`,
`routine-adapters/`, `tool-adapters/`, `toolchain-adapters/`.

## Adding a format

1. Create `<format>.js` exporting `module.exports = function emit({ resolved, nested, marker }) { return '<file contents>'; }`.
   - `resolved`: `Map<dotId, { type, value }>` — fully ref-resolved leaf tokens, source order.
   - `nested`: nested plain object of resolved **values** (DTCG tree minus `$type`/`$value`).
   - `marker`: the generated-file banner line for your format (already comment-wrapped).
2. Register it in `index.js`'s `EMITTERS` map.
3. If the comment syntax differs from `//` and `/* */`, extend `markerFor()` in `index.js`.
4. **Optional — `emit.declarations(content) -> Set<string>`**: the top-level
   declaration names in a file of your format. It powers the content-loss guard
   (`tokens-generator.js` `contentLossFor`): a rebuild that would drop
   declarations an output has and a fresh render lacks is REFUSED instead of
   silently shrinking the module's surface (issue #15). No hook → no guard for
   that format, which is a valid choice — say so in your PR. `ts.js` is the
   reference. Two rules if you write one: key on **declarations, not exports** (a
   dropped non-exported type breaks the exported helper that uses it), and stay
   zero-dep (a regex, not a parser — the build path must run identically under
   Codex). It is advisory by contract: a miss degrades to the old behaviour, and
   a pristine generated file must yield exactly what `emit` produces, or every
   happy-path build turns into a false block.

That's it — `tokens-generator.js`, `baldart tokens build`, and `doctor` iterate
the registry; nothing else changes.

## Invariants

- **Deterministic, diff-stable output**: emit in source order, no timestamps, no
  randomness — so `isStale()` (on-disk vs fresh render byte-compare) is reliable.
- **Always emit the `marker`** as the first line — it is how humans and the
  `framework-edit-gate` know the file is generated and must not be hand-edited.
- **Pure**: emitters never touch the filesystem; the generator writes.
- **Zero dependency**: no Style Dictionary, no npm — keep it portable (the build
  path must run identically under Codex).

## Shipped formats

| format | output |
|--------|--------|
| `ts`   | `export const tokens = {…} as const;` + `export type Tokens` |
| `css`  | `:root { --<dot-id-as-dashes>: <value>; }` |
